package grammar import ( "bufio" "io" ) // Reader is used for reading Runes from a stream. type Reader interface { // ReadRune reads the next Rune off the stream, or returns io.EOF. ReadRune() (Located[rune], error) // UnreadRune can be used to place a Rune onto an internal buffer, such that // the Rune will be the next to be read using ReadRune. If called multiple // times then ReadRune will produce the given Runes in LIFO order. UnreadRune(Located[rune]) // NextLocation returns the Location of the next Rune which will be returned // with ReadRune. NextLocation() Location } type reader struct { br *bufio.Reader brNextLoc Location unread []Located[rune] } // NewReader wraps the io.Reader as a Reader. The given Reader should not be // read from after this call. func NewReader(r io.Reader) Reader { return &reader{ br: bufio.NewReader(r), brNextLoc: Location{Row: 1, Col: 1}, } } func (rr *reader) ReadRune() (Located[rune], error) { if len(rr.unread) > 0 { r := rr.unread[len(rr.unread)-1] rr.unread = rr.unread[:len(rr.unread)-1] return r, nil } loc := rr.brNextLoc r, _, err := rr.br.ReadRune() if err != nil { return Located[rune]{}, err } if r == '\n' { rr.brNextLoc.Row++ rr.brNextLoc.Col = 1 } else { rr.brNextLoc.Col++ } return Located[rune]{loc, r}, nil } func (rr *reader) UnreadRune(r Located[rune]) { rr.unread = append(rr.unread, r) } func (rr *reader) NextLocation() Location { if len(rr.unread) > 0 { return rr.unread[len(rr.unread)-1].Location } return rr.brNextLoc }