github.com/jflude/taocp@v0.0.0-20240210234939-99f2a91af3c2/mix/punch.go (about) 1 package mix 2 3 import ( 4 "io" 5 "strings" 6 ) 7 8 type CardPunch struct { 9 wc io.WriteCloser 10 } 11 12 // see https://en.wikipedia.org/wiki/IBM_2540 13 func NewCardPunch(wc io.WriteCloser) (*CardPunch, error) { 14 return &CardPunch{wc}, nil 15 } 16 17 func (*CardPunch) Name() string { 18 return "PUNCH" 19 } 20 21 func (*CardPunch) BlockSize() int { 22 return 16 23 } 24 25 func (*CardPunch) Read([]Word) (int64, error) { 26 return 0, ErrInvalidCommand 27 } 28 29 func (p *CardPunch) Write(block []Word) (int64, error) { 30 s := strings.TrimRight(ConvertToUTF8(block), " ") 31 if ch, ok := IsPunchable(s); !ok { 32 return 0, charError(ch) 33 } 34 _, err := io.WriteString(p.wc, s+"\n") 35 return 200000, err 36 } 37 38 func (p *CardPunch) Control(m int) (int64, error) { 39 return 0, ErrInvalidCommand 40 } 41 42 func (p *CardPunch) Close() error { 43 return p.wc.Close() 44 } 45 46 func IsPunchable(s string) (rune, bool) { 47 // see Ex. 26, Section 1.3.1 for characters which cannot be punched 48 // (see also https://homepage.divms.uiowa.edu/~jones/cards/codes.html) 49 // I am assuming a colon can be punched as it is required by MIXAL. 50 if i := strings.IndexAny(s, "ΦΠ$<>@;'"); i != -1 { 51 return rune(s[i]), false 52 } 53 return 0, true 54 } 55 56 func OverPunch(digit rune) rune { 57 // see Ex. 26, Section 1.3.1 for digits which have been overpunched 58 return []rune("ΔJKLMNOPQR")[digit-'0'] 59 }