1 // Copyright 2013-2022 Frank Schroeder. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 //
5 // Parts of the lexer are from the template/text/parser package
6 // For these parts the following applies:
7 //
8 // Copyright 2011 The Go Authors. All rights reserved.
9 // Use of this source code is governed by a BSD-style
10 // license that can be found in the LICENSE file of the go 1.2
11 // distribution.
12 13 package properties
14 15 import (
16 "fmt"
17 "strconv"
18 "strings"
19 "unicode/utf8"
20 )
21 22 // item represents a token or text string returned from the scanner.
23 type item struct {
24 typ itemType // The type of this item.
25 pos int // The starting position, in bytes, of this item in the input string.
26 val string // The value of this item.
27 }
28 29 func (i item) String() string {
30 switch {
31 case i.typ == itemEOF:
32 return "EOF"
33 case i.typ == itemError:
34 return i.val
35 case len(i.val) > 10:
36 return fmt.Sprintf("%.10q...", i.val)
37 }
38 return fmt.Sprintf("%q", i.val)
39 }
40 41 // itemType identifies the type of lex items.
42 type itemType int
43 44 const (
45 itemError itemType = iota // error occurred; value is text of error
46 itemEOF
47 itemKey // a key
48 itemValue // a value
49 itemComment // a comment
50 )
51 52 // defines a constant for EOF
53 const eof = -1
54 55 // permitted whitespace characters space, FF and TAB
56 const whitespace = " \f\t"
57 58 // stateFn represents the state of the scanner as a function that returns the next state.
59 type stateFn func(*lexer) stateFn
60 61 // lexer holds the state of the scanner.
62 type lexer struct {
63 input string // the string being scanned
64 state stateFn // the next lexing function to enter
65 pos int // current position in the input
66 start int // start position of this item
67 width int // width of last rune read from input
68 lastPos int // position of most recent item returned by nextItem
69 runes []rune // scanned runes for this item
70 items chan item // channel of scanned items
71 }
72 73 // next returns the next rune in the input.
74 func (l *lexer) next() rune {
75 if l.pos >= len(l.input) {
76 l.width = 0
77 return eof
78 }
79 r, w := utf8.DecodeRuneInString(l.input[l.pos:])
80 l.width = w
81 l.pos += l.width
82 return r
83 }
84 85 // peek returns but does not consume the next rune in the input.
86 func (l *lexer) peek() rune {
87 r := l.next()
88 l.backup()
89 return r
90 }
91 92 // backup steps back one rune. Can only be called once per call of next.
93 func (l *lexer) backup() {
94 l.pos -= l.width
95 }
96 97 // emit passes an item back to the client.
98 func (l *lexer) emit(t itemType) {
99 i := item{t, l.start, string(l.runes)}
100 l.items <- i
101 l.start = l.pos
102 l.runes = l.runes[:0]
103 }
104 105 // ignore skips over the pending input before this point.
106 func (l *lexer) ignore() {
107 l.start = l.pos
108 }
109 110 // appends the rune to the current value
111 func (l *lexer) appendRune(r rune) {
112 l.runes = append(l.runes, r)
113 }
114 115 // accept consumes the next rune if it's from the valid set.
116 func (l *lexer) accept(valid string) bool {
117 if strings.ContainsRune(valid, l.next()) {
118 return true
119 }
120 l.backup()
121 return false
122 }
123 124 // acceptRun consumes a run of runes from the valid set.
125 func (l *lexer) acceptRun(valid string) {
126 for strings.ContainsRune(valid, l.next()) {
127 }
128 l.backup()
129 }
130 131 // lineNumber reports which line we're on, based on the position of
132 // the previous item returned by nextItem. Doing it this way
133 // means we don't have to worry about peek double counting.
134 func (l *lexer) lineNumber() int {
135 return 1 + strings.Count(l.input[:l.lastPos], "\n")
136 }
137 138 // errorf returns an error token and terminates the scan by passing
139 // back a nil pointer that will be the next state, terminating l.nextItem.
140 func (l *lexer) errorf(format string, args ...interface{}) stateFn {
141 l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
142 return nil
143 }
144 145 // nextItem returns the next item from the input.
146 func (l *lexer) nextItem() item {
147 i := <-l.items
148 l.lastPos = i.pos
149 return i
150 }
151 152 // lex creates a new scanner for the input string.
153 func lex(input string) *lexer {
154 l := &lexer{
155 input: input,
156 items: make(chan item),
157 runes: make([]rune, 0, 32),
158 }
159 go l.run()
160 return l
161 }
162 163 // run runs the state machine for the lexer.
164 func (l *lexer) run() {
165 for l.state = lexBeforeKey(l); l.state != nil; {
166 l.state = l.state(l)
167 }
168 }
169 170 // state functions
171 172 // lexBeforeKey scans until a key begins.
173 func lexBeforeKey(l *lexer) stateFn {
174 switch r := l.next(); {
175 case isEOF(r):
176 l.emit(itemEOF)
177 return nil
178 179 case isEOL(r):
180 l.ignore()
181 return lexBeforeKey
182 183 case isComment(r):
184 return lexComment
185 186 case isWhitespace(r):
187 l.ignore()
188 return lexBeforeKey
189 190 default:
191 l.backup()
192 return lexKey
193 }
194 }
195 196 // lexComment scans a comment line. The comment character has already been scanned.
197 func lexComment(l *lexer) stateFn {
198 l.acceptRun(whitespace)
199 l.ignore()
200 for {
201 switch r := l.next(); {
202 case isEOF(r):
203 l.ignore()
204 l.emit(itemEOF)
205 return nil
206 case isEOL(r):
207 l.emit(itemComment)
208 return lexBeforeKey
209 default:
210 l.appendRune(r)
211 }
212 }
213 }
214 215 // lexKey scans the key up to a delimiter
216 func lexKey(l *lexer) stateFn {
217 var r rune
218 219 Loop:
220 for {
221 switch r = l.next(); {
222 223 case isEscape(r):
224 err := l.scanEscapeSequence()
225 if err != nil {
226 return l.errorf(err.Error())
227 }
228 229 case isEndOfKey(r):
230 l.backup()
231 break Loop
232 233 case isEOF(r):
234 break Loop
235 236 default:
237 l.appendRune(r)
238 }
239 }
240 241 if len(l.runes) > 0 {
242 l.emit(itemKey)
243 }
244 245 if isEOF(r) {
246 l.emit(itemEOF)
247 return nil
248 }
249 250 return lexBeforeValue
251 }
252 253 // lexBeforeValue scans the delimiter between key and value.
254 // Leading and trailing whitespace is ignored.
255 // We expect to be just after the key.
256 func lexBeforeValue(l *lexer) stateFn {
257 l.acceptRun(whitespace)
258 l.accept(":=")
259 l.acceptRun(whitespace)
260 l.ignore()
261 return lexValue
262 }
263 264 // lexValue scans text until the end of the line. We expect to be just after the delimiter.
265 func lexValue(l *lexer) stateFn {
266 for {
267 switch r := l.next(); {
268 case isEscape(r):
269 if isEOL(l.peek()) {
270 l.next()
271 l.acceptRun(whitespace)
272 } else {
273 err := l.scanEscapeSequence()
274 if err != nil {
275 return l.errorf(err.Error())
276 }
277 }
278 279 case isEOL(r):
280 l.emit(itemValue)
281 l.ignore()
282 return lexBeforeKey
283 284 case isEOF(r):
285 l.emit(itemValue)
286 l.emit(itemEOF)
287 return nil
288 289 default:
290 l.appendRune(r)
291 }
292 }
293 }
294 295 // scanEscapeSequence scans either one of the escaped characters
296 // or a unicode literal. We expect to be after the escape character.
297 func (l *lexer) scanEscapeSequence() error {
298 switch r := l.next(); {
299 300 case isEscapedCharacter(r):
301 l.appendRune(decodeEscapedCharacter(r))
302 return nil
303 304 case atUnicodeLiteral(r):
305 return l.scanUnicodeLiteral()
306 307 case isEOF(r):
308 return fmt.Errorf("premature EOF")
309 310 // silently drop the escape character and append the rune as is
311 default:
312 l.appendRune(r)
313 return nil
314 }
315 }
316 317 // scans a unicode literal in the form \uXXXX. We expect to be after the \u.
318 func (l *lexer) scanUnicodeLiteral() error {
319 // scan the digits
320 d := make([]rune, 4)
321 for i := 0; i < 4; i++ {
322 d[i] = l.next()
323 if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {
324 return fmt.Errorf("invalid unicode literal")
325 }
326 }
327 328 // decode the digits into a rune
329 r, err := strconv.ParseInt(string(d), 16, 0)
330 if err != nil {
331 return err
332 }
333 334 l.appendRune(rune(r))
335 return nil
336 }
337 338 // decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character.
339 func decodeEscapedCharacter(r rune) rune {
340 switch r {
341 case 'f':
342 return '\f'
343 case 'n':
344 return '\n'
345 case 'r':
346 return '\r'
347 case 't':
348 return '\t'
349 default:
350 return r
351 }
352 }
353 354 // atUnicodeLiteral reports whether we are at a unicode literal.
355 // The escape character has already been consumed.
356 func atUnicodeLiteral(r rune) bool {
357 return r == 'u'
358 }
359 360 // isComment reports whether we are at the start of a comment.
361 func isComment(r rune) bool {
362 return r == '#' || r == '!'
363 }
364 365 // isEndOfKey reports whether the rune terminates the current key.
366 func isEndOfKey(r rune) bool {
367 return strings.ContainsRune(" \f\t\r\n:=", r)
368 }
369 370 // isEOF reports whether we are at EOF.
371 func isEOF(r rune) bool {
372 return r == eof
373 }
374 375 // isEOL reports whether we are at a new line character.
376 func isEOL(r rune) bool {
377 return r == '\n' || r == '\r'
378 }
379 380 // isEscape reports whether the rune is the escape character which
381 // prefixes unicode literals and other escaped characters.
382 func isEscape(r rune) bool {
383 return r == '\\'
384 }
385 386 // isEscapedCharacter reports whether we are at one of the characters that need escaping.
387 // The escape character has already been consumed.
388 func isEscapedCharacter(r rune) bool {
389 return strings.ContainsRune(" :=fnrt", r)
390 }
391 392 // isWhitespace reports whether the rune is a whitespace character.
393 func isWhitespace(r rune) bool {
394 return strings.ContainsRune(whitespace, r)
395 }
396