json_p3 0.4.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.rubocop.yml +26 -9
- data/.ruby-version +1 -1
- data/CHANGELOG.md +54 -0
- data/README.md +125 -123
- data/Rakefile +3 -3
- data/certs/jgrp.pem +21 -21
- data/lib/json_p3/errors.rb +51 -43
- data/lib/json_p3/patch/op.rb +23 -0
- data/lib/json_p3/patch/op_add.rb +51 -0
- data/lib/json_p3/patch/op_copy.rb +64 -0
- data/lib/json_p3/patch/op_move.rb +74 -0
- data/lib/json_p3/patch/op_remove.rb +56 -0
- data/lib/json_p3/patch/op_replace.rb +54 -0
- data/lib/json_p3/patch/op_test.rb +31 -0
- data/lib/json_p3/patch.rb +15 -330
- data/lib/json_p3/path/environment.rb +113 -0
- data/lib/json_p3/path/filter.rb +463 -0
- data/lib/json_p3/path/function.rb +12 -0
- data/lib/json_p3/path/function_extensions/count.rb +15 -0
- data/lib/json_p3/path/function_extensions/length.rb +17 -0
- data/lib/json_p3/path/function_extensions/match.rb +62 -0
- data/lib/json_p3/path/function_extensions/pattern.rb +42 -0
- data/lib/json_p3/path/function_extensions/search.rb +44 -0
- data/lib/json_p3/path/function_extensions/value.rb +15 -0
- data/lib/json_p3/path/lexer.rb +220 -0
- data/lib/json_p3/path/node.rb +48 -0
- data/lib/json_p3/path/parser.rb +676 -0
- data/lib/json_p3/path/query.rb +74 -0
- data/lib/json_p3/path/segment.rb +172 -0
- data/lib/json_p3/path/selector.rb +304 -0
- data/lib/json_p3/path/serialize.rb +16 -0
- data/lib/json_p3/path/unescape.rb +134 -0
- data/lib/json_p3/pointer.rb +15 -76
- data/lib/json_p3/relative_pointer.rb +69 -0
- data/lib/json_p3/version.rb +1 -1
- data/lib/json_p3.rb +50 -13
- data/sig/json_p3/cache.rbs +21 -0
- data/sig/json_p3/errors.rbs +55 -0
- data/sig/json_p3/patch.rbs +145 -0
- data/sig/json_p3/path/environment.rbs +81 -0
- data/sig/json_p3/path/filter.rbs +196 -0
- data/sig/json_p3/path/function.rbs +94 -0
- data/sig/json_p3/path/lexer.rbs +62 -0
- data/sig/json_p3/path/node.rbs +46 -0
- data/sig/json_p3/path/parser.rbs +92 -0
- data/sig/json_p3/path/query.rbs +47 -0
- data/sig/json_p3/path/segment.rbs +54 -0
- data/sig/json_p3/path/selector.rbs +100 -0
- data/sig/json_p3/path/serialize.rbs +9 -0
- data/sig/json_p3/path/unescape.rbs +12 -0
- data/sig/json_p3/pointer.rbs +64 -0
- data/sig/json_p3/relative_pointer.rbs +30 -0
- data/sig/json_p3.rbs +24 -1318
- data.tar.gz.sig +0 -0
- metadata +66 -46
- metadata.gz.sig +0 -0
- data/lib/json_p3/environment.rb +0 -111
- data/lib/json_p3/filter.rb +0 -459
- data/lib/json_p3/function.rb +0 -10
- data/lib/json_p3/function_extensions/count.rb +0 -15
- data/lib/json_p3/function_extensions/length.rb +0 -17
- data/lib/json_p3/function_extensions/match.rb +0 -62
- data/lib/json_p3/function_extensions/pattern.rb +0 -39
- data/lib/json_p3/function_extensions/search.rb +0 -44
- data/lib/json_p3/function_extensions/value.rb +0 -15
- data/lib/json_p3/lexer.rb +0 -440
- data/lib/json_p3/node.rb +0 -44
- data/lib/json_p3/parser.rb +0 -553
- data/lib/json_p3/path.rb +0 -72
- data/lib/json_p3/segment.rb +0 -158
- data/lib/json_p3/selector.rb +0 -306
- data/lib/json_p3/serialize.rb +0 -13
- data/lib/json_p3/token.rb +0 -36
- data/lib/json_p3/unescape.rb +0 -112
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
require_relative "filter"
|
|
6
|
+
require_relative "function"
|
|
7
|
+
require_relative "segment"
|
|
8
|
+
require_relative "selector"
|
|
9
|
+
require_relative "unescape"
|
|
10
|
+
|
|
11
|
+
module JSONP3
|
|
12
|
+
# JSONPath query expressions.
|
|
13
|
+
module Path
|
|
14
|
+
# JSONPath query parser.
|
|
15
|
+
class Parser
|
|
16
|
+
class Precedence
|
|
17
|
+
LOWEST = 1
|
|
18
|
+
LOGICAL_OR = 3
|
|
19
|
+
LOGICAL_AND = 4
|
|
20
|
+
RELATIONAL = 5
|
|
21
|
+
PREFIX = 7
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
PRECEDENCES = {
|
|
25
|
+
token_and: Precedence::LOGICAL_AND,
|
|
26
|
+
token_or: Precedence::LOGICAL_OR,
|
|
27
|
+
token_not: Precedence::PREFIX,
|
|
28
|
+
token_eq: Precedence::RELATIONAL,
|
|
29
|
+
token_ge: Precedence::RELATIONAL,
|
|
30
|
+
token_gt: Precedence::RELATIONAL,
|
|
31
|
+
token_le: Precedence::RELATIONAL,
|
|
32
|
+
token_lt: Precedence::RELATIONAL,
|
|
33
|
+
token_ne: Precedence::RELATIONAL,
|
|
34
|
+
token_rparen: Precedence::LOWEST
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
BINARY_OPERATORS = {
|
|
38
|
+
token_and: "&&",
|
|
39
|
+
token_or: "||",
|
|
40
|
+
token_eq: "==",
|
|
41
|
+
token_ge: ">=",
|
|
42
|
+
token_gt: ">",
|
|
43
|
+
token_le: "<=",
|
|
44
|
+
token_lt: "<",
|
|
45
|
+
token_ne: "!="
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
COMPARISON_OPERATORS = Set[
|
|
49
|
+
:token_eq,
|
|
50
|
+
:token_ge,
|
|
51
|
+
:token_gt,
|
|
52
|
+
:token_le,
|
|
53
|
+
:token_lt,
|
|
54
|
+
:token_ne
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# @param env [JSONPathEnvironment]
|
|
58
|
+
# @param query [String]
|
|
59
|
+
# @param tokens [Array[t_token]]
|
|
60
|
+
def initialize(env, query, tokens)
|
|
61
|
+
@env = env
|
|
62
|
+
@query = query
|
|
63
|
+
@tokens = tokens
|
|
64
|
+
@pos = 0
|
|
65
|
+
@eoi = [:token_eoi, query.size, query.size] #: t_token
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def next
|
|
69
|
+
if (token = @tokens[@pos])
|
|
70
|
+
@pos += 1
|
|
71
|
+
token
|
|
72
|
+
else
|
|
73
|
+
@eoi
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def eat(kind, message = nil)
|
|
78
|
+
token = self.next
|
|
79
|
+
unless token.first == kind
|
|
80
|
+
raise SyntaxError.new(
|
|
81
|
+
message || "expected #{kind}, found #{token.first}",
|
|
82
|
+
token,
|
|
83
|
+
@query
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
token
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def skip(kind)
|
|
91
|
+
@pos += 1 if (@tokens[@pos] || @eoi).first == kind
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def kind = (@tokens[@pos] || @eoi).first
|
|
95
|
+
def peek = @tokens[@pos] || @eoi
|
|
96
|
+
|
|
97
|
+
def parse
|
|
98
|
+
eat(:token_dollar)
|
|
99
|
+
segments = parse_segments
|
|
100
|
+
eat(:token_eoi)
|
|
101
|
+
segments
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def parse_segments
|
|
105
|
+
segments = [] #: Array[Segment]
|
|
106
|
+
|
|
107
|
+
loop do
|
|
108
|
+
case peek.first
|
|
109
|
+
when :token_trivia
|
|
110
|
+
@pos += 1
|
|
111
|
+
if peek.first == :token_eoi
|
|
112
|
+
raise SyntaxError.new(
|
|
113
|
+
"unexpected trailing whitespace",
|
|
114
|
+
@tokens[@pos - 1],
|
|
115
|
+
@query
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
when :token_double_dot
|
|
119
|
+
segments << DescendantSegment.new(
|
|
120
|
+
@env,
|
|
121
|
+
self.next,
|
|
122
|
+
parse_descendant_selectors
|
|
123
|
+
)
|
|
124
|
+
when :token_dot
|
|
125
|
+
segments << ChildSegment.new(
|
|
126
|
+
@env,
|
|
127
|
+
self.next,
|
|
128
|
+
[parse_shorthand_selector]
|
|
129
|
+
)
|
|
130
|
+
when :token_lbracket
|
|
131
|
+
segments << ChildSegment.new(
|
|
132
|
+
@env,
|
|
133
|
+
peek,
|
|
134
|
+
parse_bracketed_selectors
|
|
135
|
+
)
|
|
136
|
+
else
|
|
137
|
+
break
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
segments
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def parse_descendant_selectors
|
|
145
|
+
case peek.first
|
|
146
|
+
when :token_name, :token_asterisk
|
|
147
|
+
[parse_shorthand_selector]
|
|
148
|
+
when :token_lbracket
|
|
149
|
+
parse_bracketed_selectors
|
|
150
|
+
else
|
|
151
|
+
raise SyntaxError.new(
|
|
152
|
+
"expected a selector",
|
|
153
|
+
peek,
|
|
154
|
+
@query
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def parse_shorthand_selector
|
|
160
|
+
token = self.next
|
|
161
|
+
|
|
162
|
+
case token.first
|
|
163
|
+
when :token_name
|
|
164
|
+
@env.class::NAME_SELECTOR.new(
|
|
165
|
+
@env,
|
|
166
|
+
token,
|
|
167
|
+
JSONP3::Path.get_token_value(token, @query)
|
|
168
|
+
)
|
|
169
|
+
when :token_asterisk
|
|
170
|
+
WildcardSelector.new(@env, token)
|
|
171
|
+
else
|
|
172
|
+
raise SyntaxError.new(
|
|
173
|
+
"expected a shorthand selector",
|
|
174
|
+
token,
|
|
175
|
+
@query
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def parse_bracketed_selectors
|
|
181
|
+
segment_token = eat(:token_lbracket)
|
|
182
|
+
selectors = [] #: Array[Selector]
|
|
183
|
+
|
|
184
|
+
loop do
|
|
185
|
+
skip(:token_trivia)
|
|
186
|
+
|
|
187
|
+
case peek.first
|
|
188
|
+
when :token_rbracket
|
|
189
|
+
break
|
|
190
|
+
when :token_index
|
|
191
|
+
selectors << parse_index_or_slice
|
|
192
|
+
when :token_double_quoted_string, :token_single_quoted_string
|
|
193
|
+
token = self.next
|
|
194
|
+
selectors << @env.class::NAME_SELECTOR.new(
|
|
195
|
+
@env,
|
|
196
|
+
token,
|
|
197
|
+
JSONP3::Path.get_token_value(token, @query)
|
|
198
|
+
)
|
|
199
|
+
when :token_double_quoted_esc_string, :token_single_quoted_esc_string
|
|
200
|
+
token = self.next
|
|
201
|
+
selectors << @env.class::NAME_SELECTOR.new(
|
|
202
|
+
@env,
|
|
203
|
+
token,
|
|
204
|
+
JSONP3::Path.unescape(
|
|
205
|
+
JSONP3::Path.get_token_value(token, @query), token, @query
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
when :token_colon
|
|
209
|
+
selectors << parse_slice_selector
|
|
210
|
+
when :token_asterisk
|
|
211
|
+
selectors << WildcardSelector.new(@env, self.next)
|
|
212
|
+
when :token_question
|
|
213
|
+
selectors << parse_filter_selector
|
|
214
|
+
when :token_eoi
|
|
215
|
+
raise SyntaxError.new(
|
|
216
|
+
"unexpected end of query",
|
|
217
|
+
peek,
|
|
218
|
+
@query
|
|
219
|
+
)
|
|
220
|
+
else
|
|
221
|
+
raise SyntaxError.new(
|
|
222
|
+
"unexpected token #{JSONP3::Path.get_token_value(peek, @query).inspect}",
|
|
223
|
+
self.next,
|
|
224
|
+
@query
|
|
225
|
+
)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
skip(:token_trivia)
|
|
229
|
+
|
|
230
|
+
case peek.first
|
|
231
|
+
when :token_eoi
|
|
232
|
+
raise SyntaxError.new(
|
|
233
|
+
"unexpected end of query",
|
|
234
|
+
peek,
|
|
235
|
+
@query
|
|
236
|
+
)
|
|
237
|
+
when :token_rbracket
|
|
238
|
+
break
|
|
239
|
+
else
|
|
240
|
+
eat(:token_comma)
|
|
241
|
+
if peek.first == :token_rbracket
|
|
242
|
+
raise SyntaxError.new(
|
|
243
|
+
"unexpected trailing comma",
|
|
244
|
+
peek,
|
|
245
|
+
@query
|
|
246
|
+
)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
skip(:token_trivia)
|
|
252
|
+
eat(:token_rbracket)
|
|
253
|
+
|
|
254
|
+
if selectors.empty?
|
|
255
|
+
raise SyntaxError.new(
|
|
256
|
+
"unexpected empty segment",
|
|
257
|
+
segment_token,
|
|
258
|
+
@query
|
|
259
|
+
)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
selectors
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def parse_index_or_slice
|
|
266
|
+
token = self.next
|
|
267
|
+
index = parse_i_json_int(token)
|
|
268
|
+
skip(:token_trivia)
|
|
269
|
+
|
|
270
|
+
return @env.class::INDEX_SELECTOR.new(@env, token, index) unless peek.first == :token_colon
|
|
271
|
+
|
|
272
|
+
stop = nil
|
|
273
|
+
step = nil
|
|
274
|
+
|
|
275
|
+
eat(:token_colon)
|
|
276
|
+
skip(:token_trivia)
|
|
277
|
+
|
|
278
|
+
if peek.first == :token_index
|
|
279
|
+
stop = parse_i_json_int(self.next)
|
|
280
|
+
skip(:token_trivia)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
if peek.first == :token_colon
|
|
284
|
+
self.next
|
|
285
|
+
skip(:token_trivia)
|
|
286
|
+
step = parse_i_json_int(self.next) if peek.first == :token_index
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
SliceSelector.new(@env, token, index, stop, step)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def parse_slice_selector
|
|
293
|
+
token = eat(:token_colon)
|
|
294
|
+
skip(:token_trivia)
|
|
295
|
+
|
|
296
|
+
stop = nil
|
|
297
|
+
step = nil
|
|
298
|
+
|
|
299
|
+
if peek.first == :token_index
|
|
300
|
+
stop = parse_i_json_int(self.next)
|
|
301
|
+
skip(:token_trivia)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
if peek.first == :token_colon
|
|
305
|
+
self.next
|
|
306
|
+
skip(:token_trivia)
|
|
307
|
+
step = parse_i_json_int(self.next) if peek.first == :token_index
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
SliceSelector.new(@env, token, nil, stop, step)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def parse_filter_selector
|
|
314
|
+
token = eat(:token_question)
|
|
315
|
+
expr = parse_filter_expression(Precedence::LOWEST)
|
|
316
|
+
throw_for_not_compared(expr)
|
|
317
|
+
FilterSelector.new(@env, token, FilterExpression.new(token, expr))
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def parse_filter_expression(precedence)
|
|
321
|
+
left = parse_primary
|
|
322
|
+
|
|
323
|
+
loop do
|
|
324
|
+
skip(:token_trivia)
|
|
325
|
+
kind = peek.first
|
|
326
|
+
|
|
327
|
+
if kind == :token_eoi ||
|
|
328
|
+
kind == :token_rbracket ||
|
|
329
|
+
!BINARY_OPERATORS.include?(kind) ||
|
|
330
|
+
PRECEDENCES.fetch(kind, Precedence::LOWEST) < precedence
|
|
331
|
+
break
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
left = parse_infix_expression(left)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
left
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def parse_function_expression
|
|
341
|
+
token = eat(:token_name)
|
|
342
|
+
eat(:token_lparen)
|
|
343
|
+
|
|
344
|
+
args = [] #: Array[Expression]
|
|
345
|
+
|
|
346
|
+
while peek.first != :token_rparen
|
|
347
|
+
expr = parse_primary
|
|
348
|
+
skip(:token_trivia)
|
|
349
|
+
|
|
350
|
+
expr = parse_infix_expression(expr) while BINARY_OPERATORS.include?(peek.first)
|
|
351
|
+
args << expr
|
|
352
|
+
|
|
353
|
+
if peek.first != :token_rparen
|
|
354
|
+
skip(:token_trivia)
|
|
355
|
+
eat(:token_comma)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
skip(:token_trivia)
|
|
361
|
+
eat(:token_rparen)
|
|
362
|
+
|
|
363
|
+
name = JSONP3::Path.get_token_value(token, @query)
|
|
364
|
+
func = @env.function_extensions[name]
|
|
365
|
+
|
|
366
|
+
unless func
|
|
367
|
+
raise JSONPathNameError.new(
|
|
368
|
+
"unknown function extension #{name}",
|
|
369
|
+
token,
|
|
370
|
+
@query
|
|
371
|
+
)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
validate_function_signature(name, func, args, token)
|
|
375
|
+
|
|
376
|
+
FunctionExpression.new(
|
|
377
|
+
token,
|
|
378
|
+
name,
|
|
379
|
+
func,
|
|
380
|
+
args
|
|
381
|
+
)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def parse_primary
|
|
385
|
+
skip(:token_trivia)
|
|
386
|
+
peeked = peek
|
|
387
|
+
|
|
388
|
+
case peeked.first
|
|
389
|
+
when :token_single_quoted_string, :token_double_quoted_string
|
|
390
|
+
token = self.next
|
|
391
|
+
StringLiteral.new(token, JSONP3::Path.get_token_value(token, @query))
|
|
392
|
+
when :token_single_quoted_esc_string, :token_double_quoted_esc_string
|
|
393
|
+
token = self.next
|
|
394
|
+
StringLiteral.new(
|
|
395
|
+
token,
|
|
396
|
+
JSONP3::Path.unescape(
|
|
397
|
+
JSONP3::Path.get_token_value(token, @query), token, @query
|
|
398
|
+
)
|
|
399
|
+
)
|
|
400
|
+
when :token_name
|
|
401
|
+
case JSONP3::Path.get_token_value(peeked, @query)
|
|
402
|
+
when "null"
|
|
403
|
+
NullLiteral.new(self.next, nil)
|
|
404
|
+
when "false"
|
|
405
|
+
BooleanLiteral.new(self.next, false)
|
|
406
|
+
when "true"
|
|
407
|
+
BooleanLiteral.new(self.next, true)
|
|
408
|
+
else
|
|
409
|
+
parse_function_expression
|
|
410
|
+
end
|
|
411
|
+
when :token_lparen
|
|
412
|
+
parse_grouped_expression
|
|
413
|
+
when :token_index, :token_int
|
|
414
|
+
parse_integer_literal
|
|
415
|
+
when :token_float
|
|
416
|
+
parse_float_literal
|
|
417
|
+
when :token_dollar
|
|
418
|
+
parse_absolute_query
|
|
419
|
+
when :token_at
|
|
420
|
+
parse_relative_query
|
|
421
|
+
when :token_not
|
|
422
|
+
parse_prefix_expression
|
|
423
|
+
else
|
|
424
|
+
raise SyntaxError.new(
|
|
425
|
+
"unexpected token #{peek.first}",
|
|
426
|
+
self.next,
|
|
427
|
+
@query
|
|
428
|
+
)
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def parse_grouped_expression
|
|
433
|
+
eat(:token_lparen)
|
|
434
|
+
expr = parse_filter_expression(Precedence::LOWEST)
|
|
435
|
+
|
|
436
|
+
loop do
|
|
437
|
+
skip(:token_trivia)
|
|
438
|
+
peeked = peek
|
|
439
|
+
|
|
440
|
+
break if peeked.first == :token_rparen
|
|
441
|
+
|
|
442
|
+
if peeked.first == :token_eoi
|
|
443
|
+
raise SyntaxError.new(
|
|
444
|
+
"unbalanced parentheses",
|
|
445
|
+
peeked,
|
|
446
|
+
@query
|
|
447
|
+
)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
expr = parse_infix_expression(expr)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
skip(:token_trivia)
|
|
454
|
+
eat(:token_rparen)
|
|
455
|
+
expr
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def parse_prefix_expression
|
|
459
|
+
token = eat(:token_not)
|
|
460
|
+
LogicalNotExpression.new(
|
|
461
|
+
token,
|
|
462
|
+
parse_filter_expression(Precedence::PREFIX)
|
|
463
|
+
)
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def parse_infix_expression(left)
|
|
467
|
+
token = self.next
|
|
468
|
+
kind = token.first
|
|
469
|
+
precedence = PRECEDENCES[kind] || Precedence::LOWEST
|
|
470
|
+
right = parse_filter_expression(precedence)
|
|
471
|
+
|
|
472
|
+
if COMPARISON_OPERATORS.include?(kind)
|
|
473
|
+
throw_for_non_comparable(left)
|
|
474
|
+
throw_for_non_comparable(right)
|
|
475
|
+
|
|
476
|
+
case kind
|
|
477
|
+
when :token_eq
|
|
478
|
+
EqExpression.new(token, left, right)
|
|
479
|
+
when :token_ne
|
|
480
|
+
NeExpression.new(token, left, right)
|
|
481
|
+
when :token_lt
|
|
482
|
+
LtExpression.new(token, left, right)
|
|
483
|
+
when :token_le
|
|
484
|
+
LeExpression.new(token, left, right)
|
|
485
|
+
when :token_gt
|
|
486
|
+
GtExpression.new(token, left, right)
|
|
487
|
+
when :token_ge
|
|
488
|
+
GeExpression.new(token, left, right)
|
|
489
|
+
else
|
|
490
|
+
raise SyntaxError.new(
|
|
491
|
+
"expected an infix operator",
|
|
492
|
+
token,
|
|
493
|
+
@query
|
|
494
|
+
)
|
|
495
|
+
end
|
|
496
|
+
else
|
|
497
|
+
throw_for_not_compared(left)
|
|
498
|
+
throw_for_not_compared(right)
|
|
499
|
+
|
|
500
|
+
case kind
|
|
501
|
+
when :token_and
|
|
502
|
+
LogicalAndExpression.new(token, left, right)
|
|
503
|
+
when :token_or
|
|
504
|
+
LogicalOrExpression.new(token, left, right)
|
|
505
|
+
else
|
|
506
|
+
raise SyntaxError.new(
|
|
507
|
+
"expected an infix operator",
|
|
508
|
+
token,
|
|
509
|
+
@query
|
|
510
|
+
)
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def parse_integer_literal
|
|
516
|
+
token = self.next
|
|
517
|
+
value = JSONP3::Path.get_token_value(token, @query)
|
|
518
|
+
|
|
519
|
+
if value.start_with?("0") && value.length > 1
|
|
520
|
+
raise SyntaxError.new(
|
|
521
|
+
"invalid integer literal",
|
|
522
|
+
token,
|
|
523
|
+
@query
|
|
524
|
+
)
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
IntegerLiteral.new(token, Integer(Float(value)))
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def parse_float_literal
|
|
531
|
+
token = self.next
|
|
532
|
+
value = JSONP3::Path.get_token_value(token, @query)
|
|
533
|
+
|
|
534
|
+
if value.start_with?("0") && value.split(".").first.length > 1
|
|
535
|
+
raise SyntaxError.new(
|
|
536
|
+
"invalid float literal",
|
|
537
|
+
token,
|
|
538
|
+
@query
|
|
539
|
+
)
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
FloatLiteral.new(token, Float(value))
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def parse_absolute_query
|
|
546
|
+
token = eat(:token_dollar)
|
|
547
|
+
AbsoluteQueryExpression.new(token, Query.new(@env, parse_segments))
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def parse_relative_query
|
|
551
|
+
token = eat(:token_at)
|
|
552
|
+
RelativeQueryExpression.new(token, Query.new(@env, parse_segments))
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def parse_i_json_int(token)
|
|
556
|
+
value = JSONP3::Path.get_token_value(token, @query)
|
|
557
|
+
|
|
558
|
+
if value.length > 1 && value.start_with?("0", "-0")
|
|
559
|
+
raise SyntaxError.new(
|
|
560
|
+
"invalid index '#{value}'",
|
|
561
|
+
token,
|
|
562
|
+
@query
|
|
563
|
+
)
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
begin
|
|
567
|
+
int = Integer(value)
|
|
568
|
+
rescue ArgumentError
|
|
569
|
+
raise SyntaxError.new(
|
|
570
|
+
"invalid I-JSON integer",
|
|
571
|
+
token,
|
|
572
|
+
@query
|
|
573
|
+
)
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
if int < @env.class::MIN_INT_INDEX || int > @env.class::MAX_INT_INDEX
|
|
577
|
+
raise SyntaxError.new(
|
|
578
|
+
"index out of range",
|
|
579
|
+
token,
|
|
580
|
+
@query
|
|
581
|
+
)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
int
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def throw_for_not_compared(expression)
|
|
588
|
+
if expression.is_a?(FilterExpressionLiteral)
|
|
589
|
+
raise TypeError.new(
|
|
590
|
+
"filter expression literals must be compared",
|
|
591
|
+
expression.token,
|
|
592
|
+
@query
|
|
593
|
+
)
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
if expression.is_a?(FunctionExpression) &&
|
|
597
|
+
expression.func.class::RETURN_TYPE == :value_expression
|
|
598
|
+
raise TypeError.new(
|
|
599
|
+
"result of #{expression.name}() must be compared",
|
|
600
|
+
expression.token,
|
|
601
|
+
@query
|
|
602
|
+
)
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def throw_for_non_comparable(expression)
|
|
607
|
+
if expression.is_a?(QueryExpression) && !expression.query.singular?
|
|
608
|
+
raise TypeError.new(
|
|
609
|
+
"non-singular query is not comparable",
|
|
610
|
+
expression.token,
|
|
611
|
+
@query
|
|
612
|
+
)
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
if expression.is_a?(FunctionExpression) &&
|
|
616
|
+
expression.func.class::RETURN_TYPE != :value_expression
|
|
617
|
+
raise TypeError.new(
|
|
618
|
+
"result of #{expression.name}() is not comparable",
|
|
619
|
+
expression.token,
|
|
620
|
+
@query
|
|
621
|
+
)
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def validate_function_signature(name, func, args, token)
|
|
626
|
+
count = func.class::ARG_TYPES.length
|
|
627
|
+
|
|
628
|
+
unless args.length == count
|
|
629
|
+
raise TypeError.new(
|
|
630
|
+
"#{name}() takes #{count} argument#{"s" unless count == 1} (#{args.length} given)",
|
|
631
|
+
token,
|
|
632
|
+
@query
|
|
633
|
+
)
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
func.class::ARG_TYPES.each_with_index do |t, i|
|
|
637
|
+
arg = args[i]
|
|
638
|
+
case t
|
|
639
|
+
when :value_expression
|
|
640
|
+
unless arg.is_a?(FilterExpressionLiteral) ||
|
|
641
|
+
(arg.is_a?(QueryExpression) && arg.query.singular?) ||
|
|
642
|
+
(function_return_type(arg) == :value_expression)
|
|
643
|
+
raise TypeError.new(
|
|
644
|
+
"#{name}() argument #{i} must be of ValueType",
|
|
645
|
+
arg.token,
|
|
646
|
+
@query
|
|
647
|
+
)
|
|
648
|
+
end
|
|
649
|
+
when :logical_expression
|
|
650
|
+
unless arg.is_a?(QueryExpression) || arg.is_a?(InfixExpression)
|
|
651
|
+
raise TypeError.new(
|
|
652
|
+
"#{name}() argument #{i} must be of LogicalType",
|
|
653
|
+
arg.token,
|
|
654
|
+
@query
|
|
655
|
+
)
|
|
656
|
+
end
|
|
657
|
+
when :nodes_expression
|
|
658
|
+
unless arg.is_a?(QueryExpression) || function_return_type(arg) == :nodes_expression
|
|
659
|
+
raise TypeError.new(
|
|
660
|
+
"#{name}() argument #{i} must be of NodesType",
|
|
661
|
+
arg.token,
|
|
662
|
+
@query
|
|
663
|
+
)
|
|
664
|
+
end
|
|
665
|
+
end
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
def function_return_type(expression)
|
|
670
|
+
return nil unless expression.is_a? FunctionExpression
|
|
671
|
+
|
|
672
|
+
expression.func.class::RETURN_TYPE
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
end
|