OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / pelletier / go-toml / query / parser.go
1 /*
2   Based on the "jsonpath" spec/concept.
3
4   http://goessner.net/articles/JsonPath/
5   https://code.google.com/p/json-path/
6 */
7
8 package query
9
10 import (
11         "fmt"
12 )
13
14 const maxInt = int(^uint(0) >> 1)
15
16 type queryParser struct {
17         flow         chan token
18         tokensBuffer []token
19         query        *Query
20         union        []pathFn
21         err          error
22 }
23
24 type queryParserStateFn func() queryParserStateFn
25
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
30 }
31
32 func (p *queryParser) run() {
33         for state := p.parseStart; state != nil; {
34                 state = state()
35         }
36 }
37
38 func (p *queryParser) backup(tok *token) {
39         p.tokensBuffer = append(p.tokensBuffer, *tok)
40 }
41
42 func (p *queryParser) peek() *token {
43         if len(p.tokensBuffer) != 0 {
44                 return &(p.tokensBuffer[0])
45         }
46
47         tok, ok := <-p.flow
48         if !ok {
49                 return nil
50         }
51         p.backup(&tok)
52         return &tok
53 }
54
55 func (p *queryParser) lookahead(types ...tokenType) bool {
56         result := true
57         buffer := []token{}
58
59         for _, typ := range types {
60                 tok := p.getToken()
61                 if tok == nil {
62                         result = false
63                         break
64                 }
65                 buffer = append(buffer, *tok)
66                 if tok.typ != typ {
67                         result = false
68                         break
69                 }
70         }
71         // add the tokens back to the buffer, and return
72         p.tokensBuffer = append(p.tokensBuffer, buffer...)
73         return result
74 }
75
76 func (p *queryParser) getToken() *token {
77         if len(p.tokensBuffer) != 0 {
78                 tok := p.tokensBuffer[0]
79                 p.tokensBuffer = p.tokensBuffer[1:]
80                 return &tok
81         }
82         tok, ok := <-p.flow
83         if !ok {
84                 return nil
85         }
86         return &tok
87 }
88
89 func (p *queryParser) parseStart() queryParserStateFn {
90         tok := p.getToken()
91
92         if tok == nil || tok.typ == tokenEOF {
93                 return nil
94         }
95
96         if tok.typ != tokenDollar {
97                 return p.parseError(tok, "Expected '$' at start of expression")
98         }
99
100         return p.parseMatchExpr
101 }
102
103 // handle '.' prefix, '[]', and '..'
104 func (p *queryParser) parseMatchExpr() queryParserStateFn {
105         tok := p.getToken()
106         switch tok.typ {
107         case tokenDotDot:
108                 p.query.appendPath(&matchRecursiveFn{})
109                 // nested parse for '..'
110                 tok := p.getToken()
111                 switch tok.typ {
112                 case tokenKey:
113                         p.query.appendPath(newMatchKeyFn(tok.val))
114                         return p.parseMatchExpr
115                 case tokenLeftBracket:
116                         return p.parseBracketExpr
117                 case tokenStar:
118                         // do nothing - the recursive predicate is enough
119                         return p.parseMatchExpr
120                 }
121
122         case tokenDot:
123                 // nested parse for '.'
124                 tok := p.getToken()
125                 switch tok.typ {
126                 case tokenKey:
127                         p.query.appendPath(newMatchKeyFn(tok.val))
128                         return p.parseMatchExpr
129                 case tokenStar:
130                         p.query.appendPath(&matchAnyFn{})
131                         return p.parseMatchExpr
132                 }
133
134         case tokenLeftBracket:
135                 return p.parseBracketExpr
136
137         case tokenEOF:
138                 return nil // allow EOF at this stage
139         }
140         return p.parseError(tok, "expected match expression")
141 }
142
143 func (p *queryParser) parseBracketExpr() queryParserStateFn {
144         if p.lookahead(tokenInteger, tokenColon) {
145                 return p.parseSliceExpr
146         }
147         if p.peek().typ == tokenColon {
148                 return p.parseSliceExpr
149         }
150         return p.parseUnionExpr
151 }
152
153 func (p *queryParser) parseUnionExpr() queryParserStateFn {
154         var tok *token
155
156         // this state can be traversed after some sub-expressions
157         // so be careful when setting up state in the parser
158         if p.union == nil {
159                 p.union = []pathFn{}
160         }
161
162 loop: // labeled loop for easy breaking
163         for {
164                 if len(p.union) > 0 {
165                         // parse delimiter or terminator
166                         tok = p.getToken()
167                         switch tok.typ {
168                         case tokenComma:
169                                 // do nothing
170                         case tokenRightBracket:
171                                 break loop
172                         default:
173                                 return p.parseError(tok, "expected ',' or ']', not '%s'", tok.val)
174                         }
175                 }
176
177                 // parse sub expression
178                 tok = p.getToken()
179                 switch tok.typ {
180                 case tokenInteger:
181                         p.union = append(p.union, newMatchIndexFn(tok.Int()))
182                 case tokenKey:
183                         p.union = append(p.union, newMatchKeyFn(tok.val))
184                 case tokenString:
185                         p.union = append(p.union, newMatchKeyFn(tok.val))
186                 case tokenQuestion:
187                         return p.parseFilterExpr
188                 default:
189                         return p.parseError(tok, "expected union sub expression, not '%s', %d", tok.val, len(p.union))
190                 }
191         }
192
193         // if there is only one sub-expression, use that instead
194         if len(p.union) == 1 {
195                 p.query.appendPath(p.union[0])
196         } else {
197                 p.query.appendPath(&matchUnionFn{p.union})
198         }
199
200         p.union = nil // clear out state
201         return p.parseMatchExpr
202 }
203
204 func (p *queryParser) parseSliceExpr() queryParserStateFn {
205         // init slice to grab all elements
206         start, end, step := 0, maxInt, 1
207
208         // parse optional start
209         tok := p.getToken()
210         if tok.typ == tokenInteger {
211                 start = tok.Int()
212                 tok = p.getToken()
213         }
214         if tok.typ != tokenColon {
215                 return p.parseError(tok, "expected ':'")
216         }
217
218         // parse optional end
219         tok = p.getToken()
220         if tok.typ == tokenInteger {
221                 end = tok.Int()
222                 tok = p.getToken()
223         }
224         if tok.typ == tokenRightBracket {
225                 p.query.appendPath(newMatchSliceFn(start, end, step))
226                 return p.parseMatchExpr
227         }
228         if tok.typ != tokenColon {
229                 return p.parseError(tok, "expected ']' or ':'")
230         }
231
232         // parse optional step
233         tok = p.getToken()
234         if tok.typ == tokenInteger {
235                 step = tok.Int()
236                 if step < 0 {
237                         return p.parseError(tok, "step must be a positive value")
238                 }
239                 tok = p.getToken()
240         }
241         if tok.typ != tokenRightBracket {
242                 return p.parseError(tok, "expected ']'")
243         }
244
245         p.query.appendPath(newMatchSliceFn(start, end, step))
246         return p.parseMatchExpr
247 }
248
249 func (p *queryParser) parseFilterExpr() queryParserStateFn {
250         tok := p.getToken()
251         if tok.typ != tokenLeftParen {
252                 return p.parseError(tok, "expected left-parenthesis for filter expression")
253         }
254         tok = p.getToken()
255         if tok.typ != tokenKey && tok.typ != tokenString {
256                 return p.parseError(tok, "expected key or string for filter function name")
257         }
258         name := tok.val
259         tok = p.getToken()
260         if tok.typ != tokenRightParen {
261                 return p.parseError(tok, "expected right-parenthesis for filter expression")
262         }
263         p.union = append(p.union, newMatchFilterFn(name, tok.Position))
264         return p.parseUnionExpr
265 }
266
267 func parseQuery(flow chan token) (*Query, error) {
268         parser := &queryParser{
269                 flow:         flow,
270                 tokensBuffer: []token{},
271                 query:        newQuery(),
272         }
273         parser.run()
274         return parser.query, parser.err
275 }