package source import ( "bufio" "errors" "io" ) type Walker struct { source *bufio.Reader row int col int } func NewWalker(source io.Reader) *Walker { return &Walker{ source: bufio.NewReader(source), row: 0, col: 0, } } func (w *Walker) Loc() Loc { return Loc{ Row: w.row, Col: w.col, } } func (w *Walker) Next() (rune, bool, error) { r, _, err := w.source.ReadRune() if err != nil { if errors.Is(err, io.EOF) { return 0, true, nil } return 0, false, err } if r == '\n' { w.row++ w.col = 0 } else { w.col++ } return r, false, nil } func (w *Walker) Consume(want rune) (bool, error) { c, _, err := w.Next() if err != nil { return false, err } if c != want { return true, ErrUnexpectedChar{ Expected: want, Actual: c, } } return true, nil } func (w *Walker) Peek() (rune, bool, error) { r, _, err := w.source.ReadRune() defer w.source.UnreadRune() if err != nil { if errors.Is(err, io.EOF) { return 0, true, nil } return 0, false, err } return r, false, nil }