about summary refs log tree commit diff
path: root/pkg/libs
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/libs')
-rw-r--r--pkg/libs/source/errors.go10
-rw-r--r--pkg/libs/source/loc.go10
-rw-r--r--pkg/libs/source/walker.go80
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
+}