1 package jsonpath
2
3 import (
4 "context"
5 "fmt"
6 "math"
7 "text/scanner"
8
9 "github.com/PaesslerAG/gval"
10 )
11
12 type parser struct {
13 *gval.Parser
14 path path
15 }
16
17 func parseRootPath(ctx context.Context, gParser *gval.Parser) (r gval.Evaluable, err error) {
18 p := newParser(gParser)
19 return p.parse(ctx)
20 }
21
22 func parseCurrentPath(ctx context.Context, gParser *gval.Parser) (r gval.Evaluable, err error) {
23 p := newParser(gParser)
24 p.appendPlainSelector(currentElementSelector())
25 return p.parse(ctx)
26 }
27
28 func newParser(p *gval.Parser) *parser {
29 return &parser{Parser: p, path: plainPath{}}
30 }
31
32 func (p *parser) parse(c context.Context) (r gval.Evaluable, err error) {
33 err = p.parsePath(c)
34
35 if err != nil {
36 return nil, err
37 }
38 return p.path.evaluate, nil
39 }
40
41 func (p *parser) parsePath(c context.Context) error {
42 switch p.Scan() {
43 case '.':
44 return p.parseSelect(c)
45 case '[':
46 keys, seperator, err := p.parseBracket(c)
47
48 if err != nil {
49 return err
50 }
51
52 switch seperator {
53 case ':':
54 if len(keys) > 3 {
55 return fmt.Errorf("range query has at least the parameter [min:max:step]")
56 }
57 keys = append(keys, []gval.Evaluable{
58 p.Const(0), p.Const(float64(math.MaxInt32)), p.Const(1)}[len(keys):]...)
59 p.appendAmbiguousSelector(rangeSelector(keys[0], keys[1], keys[2]))
60 case '?':
61 if len(keys) != 1 {
62 return fmt.Errorf("filter needs exactly one key")
63 }
64 p.appendAmbiguousSelector(filterSelector(keys[0]))
65 default:
66 if len(keys) == 1 {
67 p.appendPlainSelector(directSelector(keys[0]))
68 } else {
69 p.appendAmbiguousSelector(multiSelector(keys))
70 }
71 }
72 return p.parsePath(c)
73 case '(':
74 return p.parseScript(c)
75 default:
76 p.Camouflage("jsonpath", '.', '[', '(')
77 return nil
78 }
79 }
80
81 func (p *parser) parseSelect(c context.Context) error {
82 scan := p.Scan()
83 switch scan {
84 case scanner.Ident:
85 p.appendPlainSelector(directSelector(p.Const(p.TokenText())))
86 return p.parsePath(c)
87 case '.':
88 p.appendAmbiguousSelector(mapperSelector())
89 return p.parseMapper(c)
90 case '*':
91 p.appendAmbiguousSelector(starSelector())
92 return p.parsePath(c)
93 default:
94 return p.Expected("JSON select", scanner.Ident, '.', '*')
95 }
96 }
97
98 func (p *parser) parseBracket(c context.Context) (keys []gval.Evaluable, seperator rune, err error) {
99 for {
100 scan := p.Scan()
101 skipScan := false
102 switch scan {
103 case '?':
104 skipScan = true
105 case ':':
106 i := float64(0)
107 if len(keys) == 1 {
108 i = math.MaxInt32
109 }
110 keys = append(keys, p.Const(i))
111 skipScan = true
112 case '*':
113 if p.Scan() != ']' {
114 return nil, 0, p.Expected("JSON bracket star", ']')
115 }
116 return []gval.Evaluable{}, 0, nil
117 case ']':
118 if seperator == ':' {
119 skipScan = true
120 break
121 }
122 fallthrough
123 default:
124 p.Camouflage("jsonpath brackets")
125 key, err := p.ParseExpression(c)
126 if err != nil {
127 return nil, 0, err
128 }
129 keys = append(keys, key)
130 }
131 if !skipScan {
132 scan = p.Scan()
133 }
134 if seperator == 0 {
135 seperator = scan
136 }
137 switch scan {
138 case ':', ',':
139 case ']':
140 return
141 case '?':
142 if len(keys) != 0 {
143 return nil, 0, p.Expected("JSON filter", ']')
144 }
145 default:
146 return nil, 0, p.Expected("JSON bracket separator", ':', ',')
147 }
148 if seperator != scan {
149 return nil, 0, fmt.Errorf("mixed %v and %v in JSON bracket", seperator, scan)
150 }
151 }
152 }
153
154 func (p *parser) parseMapper(c context.Context) error {
155 scan := p.Scan()
156 switch scan {
157 case scanner.Ident:
158 p.appendPlainSelector(directSelector(p.Const(p.TokenText())))
159 case '[':
160 keys, seperator, err := p.parseBracket(c)
161
162 if err != nil {
163 return err
164 }
165 switch seperator {
166 case ':':
167 return fmt.Errorf("mapper can not be combined with range query")
168 case '?':
169 if len(keys) != 1 {
170 return fmt.Errorf("filter needs exactly one key")
171 }
172 p.appendAmbiguousSelector(filterSelector(keys[0]))
173 default:
174 p.appendAmbiguousSelector(multiSelector(keys))
175 }
176 case '*':
177 p.appendAmbiguousSelector(starSelector())
178 case '(':
179 return p.parseScript(c)
180 default:
181 return p.Expected("JSON mapper", '[', scanner.Ident, '*')
182 }
183 return p.parsePath(c)
184 }
185
186 func (p *parser) parseScript(c context.Context) error {
187 script, err := p.ParseExpression(c)
188 if err != nil {
189 return err
190 }
191 if p.Scan() != ')' {
192 return p.Expected("jsnopath script", ')')
193 }
194 p.appendPlainSelector(newScript(script))
195 return p.parsePath(c)
196 }
197
198 func (p *parser) appendPlainSelector(next plainSelector) {
199 p.path = p.path.withPlainSelector(next)
200 }
201
202 func (p *parser) appendAmbiguousSelector(next ambiguousSelector) {
203 p.path = p.path.withAmbiguousSelector(next)
204 }
205
View as plain text