1 // TOML JSONPath lexer.
3 // Written using the principles developed by Rob Pike in
4 // http://www.youtube.com/watch?v=HxaD_trXwRE
10 "github.com/pelletier/go-toml"
16 // Lexer state function
17 type queryLexStateFn func() queryLexStateFn
20 type queryLexer struct {
32 func (l *queryLexer) run() {
33 for state := l.lexVoid; state != nil; {
39 func (l *queryLexer) nextStart() {
40 // iterate by runes (utf8 characters)
41 // search for newlines and advance line/col counts
42 for i := l.start; i < l.pos; {
43 r, width := utf8.DecodeRuneInString(l.input[i:])
52 // advance start position to next token
56 func (l *queryLexer) emit(t tokenType) {
58 Position: toml.Position{Line: l.line, Col: l.col},
60 val: l.input[l.start:l.pos],
65 func (l *queryLexer) emitWithValue(t tokenType, value string) {
67 Position: toml.Position{Line: l.line, Col: l.col},
74 func (l *queryLexer) next() rune {
75 if l.pos >= len(l.input) {
80 r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
85 func (l *queryLexer) ignore() {
89 func (l *queryLexer) backup() {
93 func (l *queryLexer) errorf(format string, args ...interface{}) queryLexStateFn {
95 Position: toml.Position{Line: l.line, Col: l.col},
97 val: fmt.Sprintf(format, args...),
102 func (l *queryLexer) peek() rune {
108 func (l *queryLexer) accept(valid string) bool {
109 if strings.ContainsRune(valid, l.next()) {
116 func (l *queryLexer) follow(next string) bool {
117 return strings.HasPrefix(l.input[l.pos:], next)
120 func (l *queryLexer) lexVoid() queryLexStateFn {
139 l.emit(tokenLeftBracket)
143 l.emit(tokenRightBracket)
155 l.emit(tokenLeftParen)
159 l.emit(tokenRightParen)
163 l.emit(tokenQuestion)
171 l.stringTerm = string(next)
175 l.stringTerm = string(next)
185 if isAlphanumeric(next) {
189 if next == '+' || next == '-' || isDigit(next) {
197 return l.errorf("unexpected char: '%v'", next)
203 func (l *queryLexer) lexKey() queryLexStateFn {
206 if !isAlphanumeric(next) {
219 func (l *queryLexer) lexString() queryLexStateFn {
225 if l.follow(l.stringTerm) {
226 l.emitWithValue(tokenString, growingString)
232 if l.follow("\\\"") {
234 growingString += "\""
235 } else if l.follow("\\'") {
238 } else if l.follow("\\n") {
240 growingString += "\n"
241 } else if l.follow("\\b") {
243 growingString += "\b"
244 } else if l.follow("\\f") {
246 growingString += "\f"
247 } else if l.follow("\\/") {
250 } else if l.follow("\\t") {
252 growingString += "\t"
253 } else if l.follow("\\r") {
255 growingString += "\r"
256 } else if l.follow("\\\\") {
258 growingString += "\\"
259 } else if l.follow("\\u") {
262 for i := 0; i < 4; i++ {
266 return l.errorf("unfinished unicode escape")
268 code = code + string(c)
271 intcode, err := strconv.ParseInt(code, 16, 32)
273 return l.errorf("invalid unicode escape: \\u" + code)
275 growingString += string(rune(intcode))
276 } else if l.follow("\\U") {
279 for i := 0; i < 8; i++ {
283 return l.errorf("unfinished unicode escape")
285 code = code + string(c)
288 intcode, err := strconv.ParseInt(code, 16, 32)
290 return l.errorf("invalid unicode escape: \\u" + code)
292 growingString += string(rune(intcode))
293 } else if l.follow("\\") {
295 return l.errorf("invalid escape sequence: \\" + string(l.peek()))
297 growingString += string(l.peek())
305 return l.errorf("unclosed string")
308 func (l *queryLexer) lexNumber() queryLexStateFn {
319 return l.errorf("cannot have two dots in one float")
321 if !isDigit(l.peek()) {
322 return l.errorf("float cannot end with a dot")
325 } else if isDigit(next) {
331 if pointSeen && !digitSeen {
332 return l.errorf("cannot start float with a dot")
337 return l.errorf("no digit in that number")
348 func lexQuery(input string) chan token {
351 tokens: make(chan token),