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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
// Based on design first introduced in: http://blog.golang.org/two-go-talks-lexical-scanning-in-go-and
// Portions copied and modified from: https://github.com/golang/go/blob/master/src/text/template/parse/lex.go
package lexer
import (
"fmt"
"strings"
"sync"
"unicode/utf8"
"github.com/authzed/spicedb/pkg/schemadsl/input"
)
const EOFRUNE = -1
// createLexer creates a new scanner for the input string.
func createLexer(source input.Source, input string) *Lexer {
l := &Lexer{
source: source,
input: input,
tokens: make(chan Lexeme),
closed: make(chan struct{}),
}
go l.run()
return l
}
// run runs the state machine for the lexer.
func (l *Lexer) run() {
defer func() {
close(l.tokens)
}()
l.withLock(func() {
l.state = lexSource
})
var state stateFn
for {
l.withRLock(func() {
state = l.state
})
if state == nil {
break
}
next := state(l)
l.withLock(func() {
l.state = next
})
}
}
// Close stops the lexer from running.
func (l *Lexer) Close() {
close(l.closed)
l.withLock(func() {
l.state = nil
})
}
// withLock runs f protected by l's lock
func (l *Lexer) withLock(f func()) {
l.Lock()
defer l.Unlock()
f()
}
// withRLock runs f protected by l's read lock
func (l *Lexer) withRLock(f func()) {
l.RLock()
defer l.RUnlock()
f()
}
// Lexeme represents a token returned from scanning the contents of a file.
type Lexeme struct {
Kind TokenType // The type of this lexeme.
Position input.BytePosition // The starting position of this token in the input string.
Value string // The textual value of this token.
Error string // The error associated with the lexeme, if any.
}
// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*Lexer) stateFn
// Lexer holds the state of the scanner.
type Lexer struct {
sync.RWMutex
state stateFn // the next lexing function to enter. GUARDED_BY(RWMutex)
source input.Source // the name of the input; used only for error reports
input string // the string being scanned
pos input.BytePosition // current position in the input
start input.BytePosition // start position of this token
width input.BytePosition // width of last rune read from input
lastPos input.BytePosition // position of most recent token returned by nextToken
tokens chan Lexeme // channel of scanned lexemes
currentToken Lexeme // The current token if any
lastNonIgnoredToken Lexeme // The last token returned that is non-whitespace and non-comment
closed chan struct{} // Holds the closed channel
}
// nextToken returns the next token from the input.
func (l *Lexer) nextToken() Lexeme {
token := <-l.tokens
l.lastPos = token.Position
return token
}
// next returns the next rune in the input.
func (l *Lexer) next() rune {
if int(l.pos) >= len(l.input) {
l.width = 0
return EOFRUNE
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = input.BytePosition(w)
l.pos += l.width
return r
}
// peek returns but does not consume the next rune in the input.
func (l *Lexer) peek() rune {
r := l.next()
l.backup()
return r
}
// backup steps back one rune. Can only be called once per call of next.
func (l *Lexer) backup() {
l.pos -= l.width
}
// value returns the current value of the token in the lexer.
func (l *Lexer) value() string {
return l.input[l.start:l.pos]
}
// emit passes an token back to the client.
func (l *Lexer) emit(t TokenType) {
currentToken := Lexeme{t, l.start, l.value(), ""}
if t != TokenTypeWhitespace && t != TokenTypeMultilineComment && t != TokenTypeSinglelineComment {
l.lastNonIgnoredToken = currentToken
}
select {
case l.tokens <- currentToken:
l.currentToken = currentToken
l.start = l.pos
case <-l.closed:
return
}
}
// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nexttoken.
func (l *Lexer) errorf(currentRune rune, format string, args ...interface{}) stateFn {
l.tokens <- Lexeme{TokenTypeError, l.start, string(currentRune), fmt.Sprintf(format, args...)}
return nil
}
// peekValue looks forward for the given value string. If found, returns true.
func (l *Lexer) peekValue(value string) bool {
for index, runeValue := range value {
r := l.next()
if r != runeValue {
for j := 0; j <= index; j++ {
l.backup()
}
return false
}
}
for i := 0; i < len(value); i++ {
l.backup()
}
return true
}
// accept consumes the next rune if it's from the valid set.
func (l *Lexer) accept(valid string) bool {
if nextRune := l.next(); strings.ContainsRune(valid, nextRune) {
return true
}
l.backup()
return false
}
// acceptString consumes the full given string, if the next tokens in the stream.
func (l *Lexer) acceptString(value string) bool {
for index, runeValue := range value {
if l.next() != runeValue {
for i := 0; i <= index; i++ {
l.backup()
}
return false
}
}
return true
}
// lexSource scans until EOFRUNE
func lexSource(l *Lexer) stateFn {
return lexerEntrypoint(l)
}
// checkFn returns whether a rune matches for continue looping.
type checkFn func(r rune) (bool, error)
func buildLexUntil(findType TokenType, checker checkFn) stateFn {
return func(l *Lexer) stateFn {
for {
r := l.next()
isValid, err := checker(r)
if err != nil {
return l.errorf(r, "%v", err)
}
if !isValid {
l.backup()
break
}
}
l.emit(findType)
return lexSource
}
}
|