aboutsummaryrefslogtreecommitdiffstats
path: root/lexer.go
blob: ee72b33945076d88c4597a127abc0fb47d483a85 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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()
	}
}