2 Based on the "jsonpath" spec/concept.
4 http://goessner.net/articles/JsonPath/
5 https://code.google.com/p/json-path/
14 const maxInt = int(^uint(0) >> 1)
16 type queryParser struct {
24 type queryParserStateFn func() queryParserStateFn
26 // Formats and panics an error message based on a token
27 func (p *queryParser) parseError(tok *token, msg string, args ...interface{}) queryParserStateFn {
28 p.err = fmt.Errorf(tok.Position.String()+": "+msg, args...)
29 return nil // trigger parse to end
32 func (p *queryParser) run() {
33 for state := p.parseStart; state != nil; {
38 func (p *queryParser) backup(tok *token) {
39 p.tokensBuffer = append(p.tokensBuffer, *tok)
42 func (p *queryParser) peek() *token {
43 if len(p.tokensBuffer) != 0 {
44 return &(p.tokensBuffer[0])
55 func (p *queryParser) lookahead(types ...tokenType) bool {
59 for _, typ := range types {
65 buffer = append(buffer, *tok)
71 // add the tokens back to the buffer, and return
72 p.tokensBuffer = append(p.tokensBuffer, buffer...)
76 func (p *queryParser) getToken() *token {
77 if len(p.tokensBuffer) != 0 {
78 tok := p.tokensBuffer[0]
79 p.tokensBuffer = p.tokensBuffer[1:]
89 func (p *queryParser) parseStart() queryParserStateFn {
92 if tok == nil || tok.typ == tokenEOF {
96 if tok.typ != tokenDollar {
97 return p.parseError(tok, "Expected '$' at start of expression")
100 return p.parseMatchExpr
103 // handle '.' prefix, '[]', and '..'
104 func (p *queryParser) parseMatchExpr() queryParserStateFn {
108 p.query.appendPath(&matchRecursiveFn{})
109 // nested parse for '..'
113 p.query.appendPath(newMatchKeyFn(tok.val))
114 return p.parseMatchExpr
115 case tokenLeftBracket:
116 return p.parseBracketExpr
118 // do nothing - the recursive predicate is enough
119 return p.parseMatchExpr
123 // nested parse for '.'
127 p.query.appendPath(newMatchKeyFn(tok.val))
128 return p.parseMatchExpr
130 p.query.appendPath(&matchAnyFn{})
131 return p.parseMatchExpr
134 case tokenLeftBracket:
135 return p.parseBracketExpr
138 return nil // allow EOF at this stage
140 return p.parseError(tok, "expected match expression")
143 func (p *queryParser) parseBracketExpr() queryParserStateFn {
144 if p.lookahead(tokenInteger, tokenColon) {
145 return p.parseSliceExpr
147 if p.peek().typ == tokenColon {
148 return p.parseSliceExpr
150 return p.parseUnionExpr
153 func (p *queryParser) parseUnionExpr() queryParserStateFn {
156 // this state can be traversed after some sub-expressions
157 // so be careful when setting up state in the parser
162 loop: // labeled loop for easy breaking
164 if len(p.union) > 0 {
165 // parse delimiter or terminator
170 case tokenRightBracket:
173 return p.parseError(tok, "expected ',' or ']', not '%s'", tok.val)
177 // parse sub expression
181 p.union = append(p.union, newMatchIndexFn(tok.Int()))
183 p.union = append(p.union, newMatchKeyFn(tok.val))
185 p.union = append(p.union, newMatchKeyFn(tok.val))
187 return p.parseFilterExpr
189 return p.parseError(tok, "expected union sub expression, not '%s', %d", tok.val, len(p.union))
193 // if there is only one sub-expression, use that instead
194 if len(p.union) == 1 {
195 p.query.appendPath(p.union[0])
197 p.query.appendPath(&matchUnionFn{p.union})
200 p.union = nil // clear out state
201 return p.parseMatchExpr
204 func (p *queryParser) parseSliceExpr() queryParserStateFn {
205 // init slice to grab all elements
206 start, end, step := 0, maxInt, 1
208 // parse optional start
210 if tok.typ == tokenInteger {
214 if tok.typ != tokenColon {
215 return p.parseError(tok, "expected ':'")
218 // parse optional end
220 if tok.typ == tokenInteger {
224 if tok.typ == tokenRightBracket {
225 p.query.appendPath(newMatchSliceFn(start, end, step))
226 return p.parseMatchExpr
228 if tok.typ != tokenColon {
229 return p.parseError(tok, "expected ']' or ':'")
232 // parse optional step
234 if tok.typ == tokenInteger {
237 return p.parseError(tok, "step must be a positive value")
241 if tok.typ != tokenRightBracket {
242 return p.parseError(tok, "expected ']'")
245 p.query.appendPath(newMatchSliceFn(start, end, step))
246 return p.parseMatchExpr
249 func (p *queryParser) parseFilterExpr() queryParserStateFn {
251 if tok.typ != tokenLeftParen {
252 return p.parseError(tok, "expected left-parenthesis for filter expression")
255 if tok.typ != tokenKey && tok.typ != tokenString {
256 return p.parseError(tok, "expected key or string for filter function name")
260 if tok.typ != tokenRightParen {
261 return p.parseError(tok, "expected right-parenthesis for filter expression")
263 p.union = append(p.union, newMatchFilterFn(name, tok.Position))
264 return p.parseUnionExpr
267 func parseQuery(flow chan token) (*Query, error) {
268 parser := &queryParser{
270 tokensBuffer: []token{},
274 return parser.query, parser.err