aboutsummaryrefslogtreecommitdiffstats
path: root/lexer.go
diff options
context:
space:
mode:
Diffstat (limited to 'lexer.go')
-rw-r--r--lexer.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/lexer.go b/lexer.go
new file mode 100644
index 0000000..ee72b33
--- /dev/null
+++ b/lexer.go
@@ -0,0 +1,105 @@
+package asspb
+
+import (
+ "iter"
+ "regexp"
+)
+
+type token struct {
+ i int
+ b []byte
+}
+
+type lexer struct {
+ data []byte
+ i int
+ yieldTok func(token, error) bool
+}
+
+func (l *lexer) error(reason string, args ...any) {
+ l.yieldTok(token{}, newSyntaxError(l.data, l.i, reason, args...))
+}
+
+func (l *lexer) yield(n int) bool {
+ if !l.yieldTok(token{l.i, l.data[l.i : l.i+n]}, nil) {
+ return false
+ }
+ l.i += n
+ return true
+}
+
+var spaceRE = regexp.MustCompile(`^([[:space:]\p{Zs}]|(#|//)[^\n]*|/\*([^*]|\*[^/])*\*?\*/)*`)
+
+func (l *lexer) skipSpace() {
+ l.i += len(spaceRE.Find(l.data[l.i:]))
+}
+
+var (
+ stringRE = regexp.MustCompile(`(?s)^(([^'\\]|\\.)*)'`)
+ doubleStringRE = regexp.MustCompile(`(?s)^(([^"\\]|\\.)*)"`)
+ lexNumRE = regexp.MustCompile(`^[-+.0-9][-+.0-9a-zA-Z]*`)
+ fieldRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z_0-9]*`)
+)
+
+func (l *lexer) tokens() {
+ for l.i = 0; ; {
+ l.skipSpace()
+ if l.i == len(l.data) {
+ break
+ }
+ switch l.data[l.i] {
+ case
+ '{',
+ '}',
+ '[',
+ ']',
+ ':',
+ ',':
+
+ if !l.yield(1) {
+ return
+ }
+ continue
+ case '\'':
+ str := stringRE.Find(l.data[l.i+1:])
+ if str == nil {
+ l.error("invaild string")
+ return
+ }
+ if !l.yield(1 + len(str)) {
+ return
+ }
+ continue
+ case '"':
+ str := doubleStringRE.Find(l.data[l.i+1:])
+ if str == nil {
+ l.error("invalid string")
+ return
+ }
+ if !l.yield(1 + len(str)) {
+ return
+ }
+ continue
+ }
+ if n := lexNumRE.Find(l.data[l.i:]); n != nil {
+ if !l.yield(len(n)) {
+ return
+ }
+ continue
+ }
+ if n := fieldRE.Find(l.data[l.i:]); n != nil {
+ if !l.yield(len(n)) {
+ return
+ }
+ continue
+ }
+ l.error("invalid lexeme")
+ return
+ }
+}
+
+func tokens(data []byte) iter.Seq2[token, error] {
+ return func(yield func(token, error) bool) {
+ (&lexer{data: data, yieldTok: yield}).tokens()
+ }
+}