OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / pelletier / go-toml / query / parser.go
diff --git a/vendor/github.com/pelletier/go-toml/query/parser.go b/vendor/github.com/pelletier/go-toml/query/parser.go
new file mode 100644 (file)
index 0000000..5f69b70
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+  Based on the "jsonpath" spec/concept.
+
+  http://goessner.net/articles/JsonPath/
+  https://code.google.com/p/json-path/
+*/
+
+package query
+
+import (
+       "fmt"
+)
+
+const maxInt = int(^uint(0) >> 1)
+
+type queryParser struct {
+       flow         chan token
+       tokensBuffer []token
+       query        *Query
+       union        []pathFn
+       err          error
+}
+
+type queryParserStateFn func() queryParserStateFn
+
+// Formats and panics an error message based on a token
+func (p *queryParser) parseError(tok *token, msg string, args ...interface{}) queryParserStateFn {
+       p.err = fmt.Errorf(tok.Position.String()+": "+msg, args...)
+       return nil // trigger parse to end
+}
+
+func (p *queryParser) run() {
+       for state := p.parseStart; state != nil; {
+               state = state()
+       }
+}
+
+func (p *queryParser) backup(tok *token) {
+       p.tokensBuffer = append(p.tokensBuffer, *tok)
+}
+
+func (p *queryParser) peek() *token {
+       if len(p.tokensBuffer) != 0 {
+               return &(p.tokensBuffer[0])
+       }
+
+       tok, ok := <-p.flow
+       if !ok {
+               return nil
+       }
+       p.backup(&tok)
+       return &tok
+}
+
+func (p *queryParser) lookahead(types ...tokenType) bool {
+       result := true
+       buffer := []token{}
+
+       for _, typ := range types {
+               tok := p.getToken()
+               if tok == nil {
+                       result = false
+                       break
+               }
+               buffer = append(buffer, *tok)
+               if tok.typ != typ {
+                       result = false
+                       break
+               }
+       }
+       // add the tokens back to the buffer, and return
+       p.tokensBuffer = append(p.tokensBuffer, buffer...)
+       return result
+}
+
+func (p *queryParser) getToken() *token {
+       if len(p.tokensBuffer) != 0 {
+               tok := p.tokensBuffer[0]
+               p.tokensBuffer = p.tokensBuffer[1:]
+               return &tok
+       }
+       tok, ok := <-p.flow
+       if !ok {
+               return nil
+       }
+       return &tok
+}
+
+func (p *queryParser) parseStart() queryParserStateFn {
+       tok := p.getToken()
+
+       if tok == nil || tok.typ == tokenEOF {
+               return nil
+       }
+
+       if tok.typ != tokenDollar {
+               return p.parseError(tok, "Expected '$' at start of expression")
+       }
+
+       return p.parseMatchExpr
+}
+
+// handle '.' prefix, '[]', and '..'
+func (p *queryParser) parseMatchExpr() queryParserStateFn {
+       tok := p.getToken()
+       switch tok.typ {
+       case tokenDotDot:
+               p.query.appendPath(&matchRecursiveFn{})
+               // nested parse for '..'
+               tok := p.getToken()
+               switch tok.typ {
+               case tokenKey:
+                       p.query.appendPath(newMatchKeyFn(tok.val))
+                       return p.parseMatchExpr
+               case tokenLeftBracket:
+                       return p.parseBracketExpr
+               case tokenStar:
+                       // do nothing - the recursive predicate is enough
+                       return p.parseMatchExpr
+               }
+
+       case tokenDot:
+               // nested parse for '.'
+               tok := p.getToken()
+               switch tok.typ {
+               case tokenKey:
+                       p.query.appendPath(newMatchKeyFn(tok.val))
+                       return p.parseMatchExpr
+               case tokenStar:
+                       p.query.appendPath(&matchAnyFn{})
+                       return p.parseMatchExpr
+               }
+
+       case tokenLeftBracket:
+               return p.parseBracketExpr
+
+       case tokenEOF:
+               return nil // allow EOF at this stage
+       }
+       return p.parseError(tok, "expected match expression")
+}
+
+func (p *queryParser) parseBracketExpr() queryParserStateFn {
+       if p.lookahead(tokenInteger, tokenColon) {
+               return p.parseSliceExpr
+       }
+       if p.peek().typ == tokenColon {
+               return p.parseSliceExpr
+       }
+       return p.parseUnionExpr
+}
+
+func (p *queryParser) parseUnionExpr() queryParserStateFn {
+       var tok *token
+
+       // this state can be traversed after some sub-expressions
+       // so be careful when setting up state in the parser
+       if p.union == nil {
+               p.union = []pathFn{}
+       }
+
+loop: // labeled loop for easy breaking
+       for {
+               if len(p.union) > 0 {
+                       // parse delimiter or terminator
+                       tok = p.getToken()
+                       switch tok.typ {
+                       case tokenComma:
+                               // do nothing
+                       case tokenRightBracket:
+                               break loop
+                       default:
+                               return p.parseError(tok, "expected ',' or ']', not '%s'", tok.val)
+                       }
+               }
+
+               // parse sub expression
+               tok = p.getToken()
+               switch tok.typ {
+               case tokenInteger:
+                       p.union = append(p.union, newMatchIndexFn(tok.Int()))
+               case tokenKey:
+                       p.union = append(p.union, newMatchKeyFn(tok.val))
+               case tokenString:
+                       p.union = append(p.union, newMatchKeyFn(tok.val))
+               case tokenQuestion:
+                       return p.parseFilterExpr
+               default:
+                       return p.parseError(tok, "expected union sub expression, not '%s', %d", tok.val, len(p.union))
+               }
+       }
+
+       // if there is only one sub-expression, use that instead
+       if len(p.union) == 1 {
+               p.query.appendPath(p.union[0])
+       } else {
+               p.query.appendPath(&matchUnionFn{p.union})
+       }
+
+       p.union = nil // clear out state
+       return p.parseMatchExpr
+}
+
+func (p *queryParser) parseSliceExpr() queryParserStateFn {
+       // init slice to grab all elements
+       start, end, step := 0, maxInt, 1
+
+       // parse optional start
+       tok := p.getToken()
+       if tok.typ == tokenInteger {
+               start = tok.Int()
+               tok = p.getToken()
+       }
+       if tok.typ != tokenColon {
+               return p.parseError(tok, "expected ':'")
+       }
+
+       // parse optional end
+       tok = p.getToken()
+       if tok.typ == tokenInteger {
+               end = tok.Int()
+               tok = p.getToken()
+       }
+       if tok.typ == tokenRightBracket {
+               p.query.appendPath(newMatchSliceFn(start, end, step))
+               return p.parseMatchExpr
+       }
+       if tok.typ != tokenColon {
+               return p.parseError(tok, "expected ']' or ':'")
+       }
+
+       // parse optional step
+       tok = p.getToken()
+       if tok.typ == tokenInteger {
+               step = tok.Int()
+               if step < 0 {
+                       return p.parseError(tok, "step must be a positive value")
+               }
+               tok = p.getToken()
+       }
+       if tok.typ != tokenRightBracket {
+               return p.parseError(tok, "expected ']'")
+       }
+
+       p.query.appendPath(newMatchSliceFn(start, end, step))
+       return p.parseMatchExpr
+}
+
+func (p *queryParser) parseFilterExpr() queryParserStateFn {
+       tok := p.getToken()
+       if tok.typ != tokenLeftParen {
+               return p.parseError(tok, "expected left-parenthesis for filter expression")
+       }
+       tok = p.getToken()
+       if tok.typ != tokenKey && tok.typ != tokenString {
+               return p.parseError(tok, "expected key or string for filter function name")
+       }
+       name := tok.val
+       tok = p.getToken()
+       if tok.typ != tokenRightParen {
+               return p.parseError(tok, "expected right-parenthesis for filter expression")
+       }
+       p.union = append(p.union, newMatchFilterFn(name, tok.Position))
+       return p.parseUnionExpr
+}
+
+func parseQuery(flow chan token) (*Query, error) {
+       parser := &queryParser{
+               flow:         flow,
+               tokensBuffer: []token{},
+               query:        newQuery(),
+       }
+       parser.run()
+       return parser.query, parser.err
+}