aboutsummaryrefslogtreecommitdiff
path: root/pkg/libs/source
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/libs/source')
-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
+}