diff options
Diffstat (limited to 'pkg/libs/source')
| -rw-r--r-- | pkg/libs/source/errors.go | 10 | ||||
| -rw-r--r-- | pkg/libs/source/loc.go | 10 | ||||
| -rw-r--r-- | pkg/libs/source/walker.go | 80 |
3 files changed, 100 insertions, 0 deletions
diff --git a/pkg/libs/source/errors.go b/pkg/libs/source/errors.go new file mode 100644 index 0000000..cfcd9e3 --- /dev/null +++ b/pkg/libs/source/errors.go @@ -0,0 +1,10 @@ +package source + +type ErrUnexpectedChar struct { + Expected rune + Actual rune +} + +func (e ErrUnexpectedChar) Error() string { + return "unexpected character: expected " + string(e.Expected) + ", actual " + string(e.Actual) +} diff --git a/pkg/libs/source/loc.go b/pkg/libs/source/loc.go new file mode 100644 index 0000000..3089d3b --- /dev/null +++ b/pkg/libs/source/loc.go @@ -0,0 +1,10 @@ +package source + +type Loc struct { + Row int + Col int +} + +func NewLoc(row, col int) Loc { + return Loc{row, col} +} diff --git a/pkg/libs/source/walker.go b/pkg/libs/source/walker.go new file mode 100644 index 0000000..aeac82a --- /dev/null +++ b/pkg/libs/source/walker.go @@ -0,0 +1,80 @@ +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 +} |
