summaryrefslogtreecommitdiff
path: root/jsonpath/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'jsonpath/parser.go')
-rw-r--r--jsonpath/parser.go170
1 files changed, 170 insertions, 0 deletions
diff --git a/jsonpath/parser.go b/jsonpath/parser.go
new file mode 100644
index 0000000..748479f
--- /dev/null
+++ b/jsonpath/parser.go
@@ -0,0 +1,170 @@
+package jsonpath
+
+import (
+ "fmt"
+ "strings"
+
+ //base "src.userspace.com.au/query"
+ "src.userspace.com.au/query/json"
+ "src.userspace.com.au/query/lexer"
+)
+
+type Parser struct {
+ l *lexer.Lexer
+ pos int
+ tok *lexer.Token
+}
+
+func NewParser() (*Parser, error) {
+ return &Parser{}, nil
+}
+
+func (p *Parser) next() (done bool) {
+ p.tok, done = p.l.NextToken()
+ if p.tok != nil {
+ p.pos = p.tok.Position
+ }
+ fmt.Printf("%s(%d): '%s'\n", tokenNames[p.tok.Type], p.tok.Type, p.tok.Value)
+ return p.tok != nil && !done
+}
+
+func (p *Parser) Parse(input string) (Selector, error) {
+ p.l = lexer.New(input, pathState)
+ p.l.Start()
+
+ // First token
+ p.next()
+ if p.tok.Type != TAbsolute {
+ return nil, fmt.Errorf("expected root, got %s", p.tok.Value)
+ }
+
+ result, err := p.parseQualifiedSelector()
+ if err != nil {
+ return nil, err
+ }
+ return childSelector(rootSelector, result), nil
+}
+
+// parseQualifiedSelector
+func (p *Parser) parseQualifiedSelector() (result Selector, err error) {
+ p.next()
+
+ switch p.tok.Type {
+ case TRecursive:
+ nr, _ := p.parseStepSelector()
+ result = recursiveSelector(nr)
+
+ case TChildDot, TChildStart:
+ result, err = p.parseStepSelector()
+
+ default:
+ return nil, fmt.Errorf("expected . or .. or something, got %s", p.tok.Value)
+ }
+ return result, nil
+}
+
+func (p *Parser) parseStepSelector() (result Selector, err error) {
+ p.next()
+ result, err = p.parseNodeTestSelector()
+ if err != nil {
+ return nil, err
+ }
+ p.next()
+ if p.tok.Type == TPredicateStart {
+ // TODO
+ }
+ return result, nil
+}
+
+func (p *Parser) parseNodeTestSelector() (result Selector, err error) {
+ switch p.tok.Type {
+ case TName:
+ /*
+ switch p.tok.Value {
+ case "object", "array", "string", "number", "boolean", "null":
+ // TODO
+ //result = typeSelector(p.tok.Value)
+ default:
+ }
+ */
+ result = nameSelector(p.tok.Value)
+ case TWildcard:
+ result = wildcardSelector
+ default:
+ fmt.Println("here: ", tokenNames[p.tok.Type])
+ }
+ return result, err
+}
+
+func (p *Parser) parseChildSelector() Selector {
+ var result Selector
+ p.next()
+ switch p.tok.Type {
+ case TQuotedName:
+ result = nameSelector(strings.Trim(p.tok.Value, `"'`))
+ case TName:
+ result = nameSelector(p.tok.Value)
+ }
+ p.next()
+ return result
+}
+
+// rootSelector checks node is root
+func rootSelector(n *json.Node) bool {
+ result := (n.Type == json.DocumentNode)
+ fmt.Printf("rootSelector => type: %s, val: %s, result: %t\n", json.NodeNames[n.Type], n.Data, result)
+ return result
+}
+
+// wildcardSelector returns true
+func wildcardSelector(n *json.Node) bool {
+ return true
+}
+
+// childSelector creates a selector for c being a child of p
+func childSelector(p, c Selector) Selector {
+ return func(n *json.Node) bool {
+ fmt.Printf("childSelector => type: %s, val: %s\n", json.NodeNames[n.Type], n.Data)
+ result := (c(n) && n.Parent != nil && p(n.Parent))
+ fmt.Printf("childSelector => type: %s, val: %s, result: %t\n", json.NodeNames[n.Type], n.Data, result)
+ return result
+ }
+}
+
+// nameSelector generates selector for object key == k
+func nameSelector(k string) Selector {
+ return func(n *json.Node) bool {
+ result := (n.Type == json.ElementNode && n.Data == k)
+ fmt.Printf("nameSelector => type: %s, val: %s, result: %t\n", json.NodeNames[n.Type], n.Data, result)
+ return result
+ }
+}
+
+// recursiveSelector matches any node below which matches a
+func recursiveSelector(a Selector) Selector {
+ return func(n *json.Node) bool {
+ if n.Type != json.ElementNode {
+ return false
+ }
+ return hasRecursiveMatch(n, a)
+ }
+}
+
+func hasRecursiveMatch(n *json.Node, a Selector) bool {
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if a(c) || (c.Type == json.ElementNode && hasRecursiveMatch(c, a)) {
+ return true
+ }
+ }
+ return false
+}
+
+// typeSelector matches a node with type t
+func typeSelector(t string) Selector {
+ return func(n *json.Node) bool {
+ if n.DataType == t {
+ return true
+ }
+ return false
+ }
+}