1 // Copyright 2017 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.
5 // Parts of the lexer are from the template/text/parser package
6 // For these parts the following applies:
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
22 // item represents a token or text string returned from the scanner.
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.
29 func (i item) String() string {
31 case i.typ == itemEOF:
33 case i.typ == itemError:
36 return fmt.Sprintf("%.10q...", i.val)
38 return fmt.Sprintf("%q", i.val)
41 // itemType identifies the type of lex items.
45 itemError itemType = iota // error occurred; value is text of error
49 itemComment // a comment
52 // defines a constant for EOF
55 // permitted whitespace characters space, FF and TAB
56 const whitespace = " \f\t"
58 // stateFn represents the state of the scanner as a function that returns the next state.
59 type stateFn func(*lexer) stateFn
61 // lexer holds the state of the scanner.
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
73 // next returns the next rune in the input.
74 func (l *lexer) next() rune {
75 if l.pos >= len(l.input) {
79 r, w := utf8.DecodeRuneInString(l.input[l.pos:])
85 // peek returns but does not consume the next rune in the input.
86 func (l *lexer) peek() rune {
92 // backup steps back one rune. Can only be called once per call of next.
93 func (l *lexer) backup() {
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)}
102 l.runes = l.runes[:0]
105 // ignore skips over the pending input before this point.
106 func (l *lexer) ignore() {
110 // appends the rune to the current value
111 func (l *lexer) appendRune(r rune) {
112 l.runes = append(l.runes, r)
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()) {
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()) {
131 // acceptRunUntil consumes a run of runes up to a terminator.
132 func (l *lexer) acceptRunUntil(term rune) {
133 for term != l.next() {
138 // hasText returns true if the current parsed text is not empty.
139 func (l *lexer) isNotEmpty() bool {
140 return l.pos > l.start
143 // lineNumber reports which line we're on, based on the position of
144 // the previous item returned by nextItem. Doing it this way
145 // means we don't have to worry about peek double counting.
146 func (l *lexer) lineNumber() int {
147 return 1 + strings.Count(l.input[:l.lastPos], "\n")
150 // errorf returns an error token and terminates the scan by passing
151 // back a nil pointer that will be the next state, terminating l.nextItem.
152 func (l *lexer) errorf(format string, args ...interface{}) stateFn {
153 l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
157 // nextItem returns the next item from the input.
158 func (l *lexer) nextItem() item {
164 // lex creates a new scanner for the input string.
165 func lex(input string) *lexer {
168 items: make(chan item),
169 runes: make([]rune, 0, 32),
175 // run runs the state machine for the lexer.
176 func (l *lexer) run() {
177 for l.state = lexBeforeKey(l); l.state != nil; {
184 // lexBeforeKey scans until a key begins.
185 func lexBeforeKey(l *lexer) stateFn {
186 switch r := l.next(); {
198 case isWhitespace(r):
199 l.acceptRun(whitespace)
209 // lexComment scans a comment line. The comment character has already been scanned.
210 func lexComment(l *lexer) stateFn {
211 l.acceptRun(whitespace)
214 switch r := l.next(); {
228 // lexKey scans the key up to a delimiter
229 func lexKey(l *lexer) stateFn {
234 switch r = l.next(); {
237 err := l.scanEscapeSequence()
239 return l.errorf(err.Error())
254 if len(l.runes) > 0 {
263 return lexBeforeValue
266 // lexBeforeValue scans the delimiter between key and value.
267 // Leading and trailing whitespace is ignored.
268 // We expect to be just after the key.
269 func lexBeforeValue(l *lexer) stateFn {
270 l.acceptRun(whitespace)
272 l.acceptRun(whitespace)
277 // lexValue scans text until the end of the line. We expect to be just after the delimiter.
278 func lexValue(l *lexer) stateFn {
280 switch r := l.next(); {
284 l.acceptRun(whitespace)
286 err := l.scanEscapeSequence()
288 return l.errorf(err.Error())
308 // scanEscapeSequence scans either one of the escaped characters
309 // or a unicode literal. We expect to be after the escape character.
310 func (l *lexer) scanEscapeSequence() error {
311 switch r := l.next(); {
313 case isEscapedCharacter(r):
314 l.appendRune(decodeEscapedCharacter(r))
317 case atUnicodeLiteral(r):
318 return l.scanUnicodeLiteral()
321 return fmt.Errorf("premature EOF")
323 // silently drop the escape character and append the rune as is
330 // scans a unicode literal in the form \uXXXX. We expect to be after the \u.
331 func (l *lexer) scanUnicodeLiteral() error {
334 for i := 0; i < 4; i++ {
336 if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {
337 return fmt.Errorf("invalid unicode literal")
341 // decode the digits into a rune
342 r, err := strconv.ParseInt(string(d), 16, 0)
347 l.appendRune(rune(r))
351 // decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character.
352 func decodeEscapedCharacter(r rune) rune {
367 // atUnicodeLiteral reports whether we are at a unicode literal.
368 // The escape character has already been consumed.
369 func atUnicodeLiteral(r rune) bool {
373 // isComment reports whether we are at the start of a comment.
374 func isComment(r rune) bool {
375 return r == '#' || r == '!'
378 // isEndOfKey reports whether the rune terminates the current key.
379 func isEndOfKey(r rune) bool {
380 return strings.ContainsRune(" \f\t\r\n:=", r)
383 // isEOF reports whether we are at EOF.
384 func isEOF(r rune) bool {
388 // isEOL reports whether we are at a new line character.
389 func isEOL(r rune) bool {
390 return r == '\n' || r == '\r'
393 // isEscape reports whether the rune is the escape character which
394 // prefixes unicode literals and other escaped characters.
395 func isEscape(r rune) bool {
399 // isEscapedCharacter reports whether we are at one of the characters that need escaping.
400 // The escape character has already been consumed.
401 func isEscapedCharacter(r rune) bool {
402 return strings.ContainsRune(" :=fnrt", r)
405 // isWhitespace reports whether the rune is a whitespace character.
406 func isWhitespace(r rune) bool {
407 return strings.ContainsRune(whitespace, r)