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  }