1require 'set'
2
3module JMESPath
4 # @api private
5 class Parser
6
7 AFTER_DOT = Set.new([
8 Lexer::T_IDENTIFIER, # foo.bar
9 Lexer::T_QUOTED_IDENTIFIER, # foo."bar"
10 Lexer::T_STAR, # foo.*
11 Lexer::T_LBRACE, # foo{a: 0}
12 Lexer::T_LBRACKET, # foo[1]
13 Lexer::T_FILTER, # foo.[?bar==10]
14 ])
15
16 NUM_COLON_RBRACKET = Set.new([
17 Lexer::T_NUMBER,
18 Lexer::T_COLON,
19 Lexer::T_RBRACKET,
20 ])
21
22 COLON_RBRACKET = Set.new([
23 Lexer::T_COLON,
24 Lexer::T_RBRACKET,
25 ])
26
27 CURRENT_NODE = Nodes::Current.new
28
29 # @option options [Lexer] :lexer
30 def initialize(options = {})
31 @lexer = options[:lexer] || Lexer.new
…lines 32–55 unchanged (24 lines)
56 private
57
58 # @param [TokenStream] stream
59 # @param [Integer] rbp Right binding power
60 def expr(stream, rbp = 0)
61 left = send("nud_#{stream.token.type}", stream)
62 while rbp < (stream.token.binding_power || 0)
63 left = send("led_#{stream.token.type}", stream, left)
64 end
65 left
66 end
67
68 def nud_current(stream)
69 stream.next
70 CURRENT_NODE
…lines 71–79 unchanged (9 lines)
80 Nodes::Not.new(expr(stream, Token::BINDING_POWER[:not]))
81 end
82
83 def nud_lparen(stream)
84 stream.next
85 result = expr(stream, 0)
86 if stream.token.type != Lexer::T_RPAREN
87 raise Errors::SyntaxError, 'Unclosed `(`'
88 end
89 stream.next
90 result
91 end
92
93 def nud_filter(stream)
94 led_filter(stream, CURRENT_NODE)
…lines 95–105 unchanged (11 lines)
106 else
107 Nodes::Field.new(token.value)
108 end
109 end
110
111 def nud_lbrace(stream)
112 valid_keys = Set.new([:quoted_identifier, :identifier])
113 stream.next(match:valid_keys)
114 pairs = []
115 begin
116 pairs << parse_key_value_pair(stream)
117 if stream.token.type == :comma
118 stream.next(match:valid_keys)
119 end
120 end while stream.token.type != :rbrace
121 stream.next
122 Nodes::MultiSelectHash.new(pairs)
123 end
124
125 def nud_lbracket(stream)
126 stream.next
127 type = stream.token.type
128 if type == :number || type == :colon
129 parse_array_index_expression(stream)
130 elsif type == :star && stream.lookahead(1).type == :rbracket
131 parse_wildcard_array(stream)
132 else
133 parse_multi_select_list(stream)
134 end
135 end
…lines 136–175 unchanged (40 lines)
176 end
177 end
178
179 def led_filter(stream, left)
180 stream.next
181 expression = expr(stream)
182 if stream.token.type != Lexer::T_RBRACKET
183 raise Errors::SyntaxError, 'expected a closing rbracket for the filter'
184 end
185 stream.next
186 rhs = parse_projection(stream, Token::BINDING_POWER[Lexer::T_FILTER])
187 left ||= CURRENT_NODE
188 right = Nodes::Condition.new(expression, rhs)
189 Nodes::ArrayProjection.new(left, right)
190 end
191
192 def led_flatten(stream, left)
193 stream.next
194 left = Nodes::Flatten.new(left)
195 right = parse_projection(stream, Token::BINDING_POWER[:flatten])
196 Nodes::ArrayProjection.new(left, right)
197 end
198
199 def led_lbracket(stream, left)
200 stream.next(match: Set.new([:number, :colon, :star]))
201 type = stream.token.type
202 if type == :number || type == :colon
203 right = parse_array_index_expression(stream)
204 Nodes::Subexpression.new(left, right)
205 else
206 parse_wildcard_array(stream, left)
207 end
208 end
209
210 def led_lparen(stream, left)
211 args = []
212 if Nodes::Function::FunctionName === left
213 name = left.name
214 else
215 raise Errors::SyntaxError, 'invalid function invocation'
216 end
217 stream.next
218 while stream.token.type != :rparen
219 args << expr(stream, 0)
220 if stream.token.type == :comma
221 stream.next
222 end
223 end
224 stream.next
225 Nodes::Function.create(name, args, :disable_visit_errors => @disable_visit_errors)
226 end
227
228 def led_or(stream, left)
229 stream.next
230 right = expr(stream, Token::BINDING_POWER[:or])
231 Nodes::Or.new(left, right)
…lines 232–244 unchanged (13 lines)
245
246 # parse array index expressions, for example [0], [1:2:3], etc.
247 def parse_array_index_expression(stream)
248 pos = 0
249 parts = [nil, nil, nil]
250 expected = NUM_COLON_RBRACKET
251
252 begin
253 if stream.token.type == Lexer::T_COLON
254 pos += 1
255 expected = NUM_COLON_RBRACKET
256 elsif stream.token.type == Lexer::T_NUMBER
257 parts[pos] = stream.token.value
258 expected = COLON_RBRACKET
259 end
260 stream.next(match: expected)
261 end while stream.token.type != Lexer::T_RBRACKET
262
263 stream.next # consume the closing bracket
264
265 if pos == 0
266 # no colons found, this is a single index extraction
267 Nodes::Index.new(parts[0])
268 elsif pos > 2
269 raise Errors::SyntaxError, 'invalid array slice syntax: too many colons'
270 else
271 Nodes::ArrayProjection.new(
…lines 272–289 unchanged (18 lines)
290 stream.next
291 Nodes::MultiSelectHash::KeyValuePair.new(key, expr(stream))
292 end
293
294 def parse_multi_select_list(stream)
295 nodes = []
296 begin
297 nodes << expr(stream)
298 if stream.token.type == :comma
299 stream.next
300 if stream.token.type == :rbracket
301 raise Errors::SyntaxError, 'expression epxected, found rbracket'
302 end
303 end
304 end while stream.token.type != :rbracket
305 stream.next
306 Nodes::MultiSelectList.new(nodes)
307 end
308
309 def parse_projection(stream, binding_power)
310 type = stream.token.type
311 if stream.token.binding_power < 10
312 CURRENT_NODE
313 elsif type == :dot
314 stream.next(match:AFTER_DOT)
315 parse_dot(stream, binding_power)
316 elsif type == :lbracket || type == :filter
317 expr(stream, binding_power)
318 else
319 raise Errors::SyntaxError, 'syntax error after projection'
320 end
321 end
322
323 def parse_wildcard_array(stream, left = nil)
…lines 324–340 unchanged (17 lines)