aboutsummaryrefslogtreecommitdiffstats
path: root/lexer.go
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-10-30 18:45:11 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-10-30 18:50:19 -0700
commitaa8a935f620a053cc9c3c65587b433f3e8d3a6e9 (patch)
treea786b3174c1a901f19fc10485c4856d60ef93d6b /lexer.go
parentDon't allocate an intermediate map[string]any (diff)
downloadccl-aa8a935f620a053cc9c3c65587b433f3e8d3a6e9.tar.zst
Use a classic struct iterator instead of iter.Pull
iter.Pull doesn't provide much value here. Actually the lexer is probably simpler this way, and iter.Pull also destroys stack traces for panics in the lexer. Ouch goos: linux goarch: amd64 pkg: roseh.moe/pkg/ccl cpu: AMD Ryzen 9 5900X 12-Core Processor │ baseline.txt │ itern't.txt │ │ sec/op │ sec/op vs base │ Lex-24 1.436µ ± 0% 1.320µ ± 0% -8.05% (p=0.000 n=20) Parse-24 7.846µ ± 0% 4.801µ ± 0% -38.82% (p=0.000 n=20) geomean 3.356µ 2.517µ -24.99%
Diffstat (limited to 'lexer.go')
-rw-r--r--lexer.go112
1 files changed, 45 insertions, 67 deletions
diff --git a/lexer.go b/lexer.go
index c7c0631..6561e38 100644
--- a/lexer.go
+++ b/lexer.go
@@ -2,7 +2,6 @@ package ccl
import (
"bytes"
- "iter"
"unicode"
"unicode/utf8"
)
@@ -13,21 +12,18 @@ type token struct {
}
type lexer struct {
- data []byte
- i int
- yieldTok func(token, error) bool
+ data []byte
+ i int
}
-func (l *lexer) error(reason string, args ...any) {
- l.yieldTok(token{}, newSyntaxError(l.data, l.i, reason, args...))
+func (l *lexer) error(reason string, args ...any) error {
+ return 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
- }
+func (l *lexer) yield(n int) token {
+ t := token{l.i, l.data[l.i : l.i+n]}
l.i += n
- return true
+ return t
}
func (l *lexer) skipSpace() {
@@ -75,67 +71,49 @@ func fieldTailByte(b byte) bool {
'0' <= b && b <= '9'
}
-func (l *lexer) tokens() {
- for l.i = 0; ; {
- l.skipSpace()
- if l.i == len(l.data) {
- break
- }
- switch l.data[l.i] {
- case
- '{',
- '}',
- '[',
- ']',
- ':',
- ',':
+func (l *lexer) next() (token, error) {
+ l.skipSpace()
+ if l.i == len(l.data) {
+ return token{}, errEOF
+ }
+ switch l.data[l.i] {
+ case
+ '{',
+ '}',
+ '[',
+ ']',
+ ':',
+ ',':
- if !l.yield(1) {
- return
- }
- continue
- case '\'', '"':
- q := l.data[l.i]
- i := l.i + 1
- for ; i < len(l.data) && l.data[i] != q; i++ {
- if l.data[i] == '\\' {
- i++
- }
+ return l.yield(1), nil
+ case '\'', '"':
+ q := l.data[l.i]
+ i := l.i + 1
+ for ; i < len(l.data) && l.data[i] != q; i++ {
+ if l.data[i] == '\\' {
+ i++
}
- if i >= len(l.data) {
- l.error("unterminated string")
- return
- }
- if !l.yield(i + 1 - l.i) {
- return
- }
- continue
}
- switch b := l.data[l.i]; {
- case numFirstByte(b):
- i := l.i + 1
- for ; i < len(l.data) && numTailByte(l.data[i]); i++ {
- }
- if !l.yield(i - l.i) {
- return
- }
- continue
- case fieldFirstByte(b):
- i := l.i + 1
- for ; i < len(l.data) && fieldTailByte(l.data[i]); i++ {
- }
- if !l.yield(i - l.i) {
- return
- }
- continue
+ if i >= len(l.data) {
+ return token{}, l.error("unterminated string")
}
- l.error("invalid lexeme")
- return
+ return l.yield(i + 1 - l.i), nil
}
+ switch b := l.data[l.i]; {
+ case numFirstByte(b):
+ i := l.i + 1
+ for ; i < len(l.data) && numTailByte(l.data[i]); i++ {
+ }
+ return l.yield(i - l.i), nil
+ case fieldFirstByte(b):
+ i := l.i + 1
+ for ; i < len(l.data) && fieldTailByte(l.data[i]); i++ {
+ }
+ return l.yield(i - l.i), nil
+ }
+ return token{}, l.error("invalid lexeme")
}
-func tokens(data []byte) iter.Seq2[token, error] {
- return func(yield func(token, error) bool) {
- (&lexer{data: data, yieldTok: yield}).tokens()
- }
+func newLexer(data []byte) *lexer {
+ return &lexer{data: data}
}