about summary refs log tree commit diff
path: root/pkg/libs/source/walker.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/libs/source/walker.go')
-rw-r--r--pkg/libs/source/walker.go80
1 files changed, 80 insertions, 0 deletions
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
+}