json_p3 0.4.0 → 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 -7
- data/.ruby-version +1 -1
- data/CHANGELOG.md +58 -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 -1313
- 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 -419
- 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
data/lib/json_p3/parser.rb
DELETED
|
@@ -1,553 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
require "set"
|
|
5
|
-
|
|
6
|
-
require_relative "errors"
|
|
7
|
-
require_relative "filter"
|
|
8
|
-
require_relative "function"
|
|
9
|
-
require_relative "segment"
|
|
10
|
-
require_relative "selector"
|
|
11
|
-
require_relative "token"
|
|
12
|
-
require_relative "unescape"
|
|
13
|
-
|
|
14
|
-
module JSONP3
|
|
15
|
-
# Step through tokens
|
|
16
|
-
class Stream
|
|
17
|
-
def initialize(tokens)
|
|
18
|
-
@tokens = tokens
|
|
19
|
-
@index = 0
|
|
20
|
-
@eoi = tokens.last
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def next
|
|
24
|
-
token = @tokens.fetch(@index)
|
|
25
|
-
@index += 1
|
|
26
|
-
token
|
|
27
|
-
rescue IndexError
|
|
28
|
-
@eor
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def peek
|
|
32
|
-
@tokens.fetch(@index)
|
|
33
|
-
rescue IndexError
|
|
34
|
-
@eor
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def expect(token_type)
|
|
38
|
-
return if peek.type == token_type
|
|
39
|
-
|
|
40
|
-
token = self.next
|
|
41
|
-
raise JSONPathSyntaxError.new("expected #{token_type}, found #{token.type}", token)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def expect_not(token_type, message)
|
|
45
|
-
return unless peek.type == token_type
|
|
46
|
-
|
|
47
|
-
token = self.next
|
|
48
|
-
raise JSONPathSyntaxError.new(message, token)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def to_s
|
|
52
|
-
"JSONP3::stream(head=#{peek.inspect})"
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
class Precedence
|
|
57
|
-
LOWEST = 1
|
|
58
|
-
LOGICAL_OR = 3
|
|
59
|
-
LOGICAL_AND = 4
|
|
60
|
-
RELATIONAL = 5
|
|
61
|
-
PREFIX = 7
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# A JSONPath expression parser.
|
|
65
|
-
class Parser
|
|
66
|
-
def initialize(env)
|
|
67
|
-
@env = env
|
|
68
|
-
@name_selector = env.class::NAME_SELECTOR
|
|
69
|
-
@index_selector = env.class::INDEX_SELECTOR
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Parse an array of tokens into an abstract syntax tree.
|
|
73
|
-
# @param tokens [Array<Token>] tokens from the lexer.
|
|
74
|
-
# @return [Array<Segment>]
|
|
75
|
-
def parse(tokens)
|
|
76
|
-
stream = Stream.new(tokens)
|
|
77
|
-
stream.expect(:token_root)
|
|
78
|
-
stream.next
|
|
79
|
-
parse_query(stream)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
protected
|
|
83
|
-
|
|
84
|
-
def parse_query(stream)
|
|
85
|
-
segments = [] # : Array[Segment]
|
|
86
|
-
|
|
87
|
-
loop do
|
|
88
|
-
case stream.peek.type
|
|
89
|
-
when :token_double_dot
|
|
90
|
-
token = stream.next
|
|
91
|
-
selectors = parse_selectors(stream)
|
|
92
|
-
segments << RecursiveDescentSegment.new(@env, token, selectors)
|
|
93
|
-
when :token_lbracket, :token_name, :token_wild
|
|
94
|
-
token = stream.peek
|
|
95
|
-
selectors = parse_selectors(stream)
|
|
96
|
-
segments << ChildSegment.new(@env, token, selectors)
|
|
97
|
-
else
|
|
98
|
-
break
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
segments
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def parse_selectors(stream)
|
|
106
|
-
case stream.peek.type
|
|
107
|
-
when :token_name
|
|
108
|
-
token = stream.next
|
|
109
|
-
[@name_selector.new(@env, token, token.value)]
|
|
110
|
-
when :token_wild
|
|
111
|
-
[WildcardSelector.new(@env, stream.next)]
|
|
112
|
-
when :token_lbracket
|
|
113
|
-
parse_bracketed_selection(stream)
|
|
114
|
-
else
|
|
115
|
-
[]
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def parse_bracketed_selection(stream)
|
|
120
|
-
stream.expect(:token_lbracket)
|
|
121
|
-
segment_token = stream.next
|
|
122
|
-
|
|
123
|
-
selectors = [] # : Array[Selector]
|
|
124
|
-
|
|
125
|
-
loop do # rubocop:disable Metrics/BlockLength
|
|
126
|
-
case stream.peek.type
|
|
127
|
-
when :token_rbracket
|
|
128
|
-
break
|
|
129
|
-
when :token_index
|
|
130
|
-
selectors << parse_index_or_slice(stream)
|
|
131
|
-
when :token_double_quote_string, :token_single_quote_string
|
|
132
|
-
token = stream.next
|
|
133
|
-
selectors << @name_selector.new(@env, token, decode_string_literal(token))
|
|
134
|
-
when :token_colon
|
|
135
|
-
selectors << parse_slice_selector(stream)
|
|
136
|
-
when :token_wild
|
|
137
|
-
selectors << WildcardSelector.new(@env, stream.next)
|
|
138
|
-
when :token_filter
|
|
139
|
-
selectors << parse_filter_selector(stream)
|
|
140
|
-
when :token_eoi
|
|
141
|
-
raise JSONPathSyntaxError.new("unexpected end of query", stream.next)
|
|
142
|
-
else
|
|
143
|
-
raise JSONPathSyntaxError.new("unexpected token in bracketed selection", stream.next)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
case stream.peek.type
|
|
147
|
-
when :token_eoi
|
|
148
|
-
raise JSONPathSyntaxError.new("unexpected end of selector list", stream.next)
|
|
149
|
-
when :token_rbracket
|
|
150
|
-
break
|
|
151
|
-
else
|
|
152
|
-
stream.expect(:token_comma)
|
|
153
|
-
stream.next
|
|
154
|
-
stream.expect_not(:token_rbracket, "unexpected trailing comma")
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
stream.expect(:token_rbracket)
|
|
159
|
-
stream.next
|
|
160
|
-
|
|
161
|
-
raise JSONPathSyntaxError.new("empty segment", segment_token) if selectors.empty?
|
|
162
|
-
|
|
163
|
-
selectors
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def parse_index_or_slice(stream)
|
|
167
|
-
token = stream.next
|
|
168
|
-
index = parse_i_json_int(token)
|
|
169
|
-
|
|
170
|
-
return @index_selector.new(@env, token, index) unless stream.peek.type == :token_colon
|
|
171
|
-
|
|
172
|
-
stream.next # move past colon
|
|
173
|
-
stop = nil
|
|
174
|
-
step = nil
|
|
175
|
-
|
|
176
|
-
case stream.peek.type
|
|
177
|
-
when :token_index
|
|
178
|
-
stop = parse_i_json_int(stream.next)
|
|
179
|
-
when :token_colon
|
|
180
|
-
stream.next # move past colon
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
stream.next if stream.peek.type == :token_colon
|
|
184
|
-
|
|
185
|
-
case stream.peek.type
|
|
186
|
-
when :token_index
|
|
187
|
-
step = parse_i_json_int(stream.next)
|
|
188
|
-
when :token_rbracket
|
|
189
|
-
nil
|
|
190
|
-
else
|
|
191
|
-
error_token = stream.next
|
|
192
|
-
raise JSONPathSyntaxError.new("expected a slice, found '#{error_token.value}'", error_token)
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
SliceSelector.new(@env, token, index, stop, step)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def parse_slice_selector(stream)
|
|
199
|
-
stream.expect(:token_colon)
|
|
200
|
-
token = stream.next
|
|
201
|
-
|
|
202
|
-
start = nil
|
|
203
|
-
stop = nil
|
|
204
|
-
step = nil
|
|
205
|
-
|
|
206
|
-
case stream.peek.type
|
|
207
|
-
when :token_index
|
|
208
|
-
stop = parse_i_json_int(stream.next)
|
|
209
|
-
when :token_colon
|
|
210
|
-
stream.next # move past colon
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
stream.next if stream.peek.type == :token_colon
|
|
214
|
-
|
|
215
|
-
case stream.peek.type
|
|
216
|
-
when :token_index
|
|
217
|
-
step = parse_i_json_int(stream.next)
|
|
218
|
-
when :token_rbracket
|
|
219
|
-
nil
|
|
220
|
-
else
|
|
221
|
-
error_token = stream.next
|
|
222
|
-
raise JSONPathSyntaxError.new("expected a slice, found '#{token.value}'", error_token)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
SliceSelector.new(@env, token, start, stop, step)
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
def parse_filter_selector(stream)
|
|
229
|
-
token = stream.next
|
|
230
|
-
expression = parse_filter_expression(stream)
|
|
231
|
-
|
|
232
|
-
# Raise if expression must be compared.
|
|
233
|
-
if expression.is_a? FunctionExpression
|
|
234
|
-
func = @env.function_extensions[expression.name]
|
|
235
|
-
if func.class::RETURN_TYPE == :value_expression
|
|
236
|
-
raise JSONPathTypeError.new("result of #{expression.name}() must be compared", expression.token)
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Raise if expression is a literal.
|
|
241
|
-
if expression.is_a? FilterExpressionLiteral
|
|
242
|
-
raise JSONPathSyntaxError.new("filter expression literals must be compared", expression.token)
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
FilterSelector.new(@env, token, FilterExpression.new(token, expression))
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def parse_filter_expression(stream, precedence = Precedence::LOWEST)
|
|
249
|
-
left = case stream.peek.type
|
|
250
|
-
when :token_double_quote_string, :token_single_quote_string
|
|
251
|
-
token = stream.next
|
|
252
|
-
StringLiteral.new(token, decode_string_literal(token))
|
|
253
|
-
when :token_false
|
|
254
|
-
BooleanLiteral.new(stream.next, false)
|
|
255
|
-
when :token_true
|
|
256
|
-
BooleanLiteral.new(stream.next, true)
|
|
257
|
-
when :token_float
|
|
258
|
-
parse_float_literal(stream)
|
|
259
|
-
when :token_function
|
|
260
|
-
parse_function_expression(stream)
|
|
261
|
-
when :token_int
|
|
262
|
-
parse_integer_literal(stream)
|
|
263
|
-
when :token_lparen
|
|
264
|
-
parse_grouped_expression(stream)
|
|
265
|
-
when :token_not
|
|
266
|
-
parse_prefix_expression(stream)
|
|
267
|
-
when :token_null
|
|
268
|
-
NullLiteral.new(stream.next, nil)
|
|
269
|
-
when :token_root
|
|
270
|
-
parse_root_query(stream)
|
|
271
|
-
when :token_current
|
|
272
|
-
parse_relative_query(stream)
|
|
273
|
-
else
|
|
274
|
-
token = stream.next
|
|
275
|
-
raise JSONPathSyntaxError.new("unexpected '#{token.value}'", token)
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
loop do
|
|
279
|
-
peeked = stream.peek
|
|
280
|
-
if peeked.type == :token_eoi ||
|
|
281
|
-
peeked.type == :token_rbracket ||
|
|
282
|
-
PRECEDENCES.fetch(peeked.type, Precedence::LOWEST) < precedence
|
|
283
|
-
break
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
return left unless BINARY_OPERATORS.key?(peeked.type)
|
|
287
|
-
|
|
288
|
-
left = parse_infix_expression(stream, left)
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
left
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def parse_integer_literal(stream)
|
|
295
|
-
token = stream.next
|
|
296
|
-
value = token.value
|
|
297
|
-
raise JSONPathSyntaxError.new("invalid integer literal", token) if value.start_with?("0") && value.length > 1
|
|
298
|
-
|
|
299
|
-
IntegerLiteral.new(token, Integer(Float(token.value)))
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
def parse_float_literal(stream)
|
|
303
|
-
token = stream.next
|
|
304
|
-
value = token.value
|
|
305
|
-
if value.start_with?("0") && value.split(".").first.length > 1
|
|
306
|
-
raise JSONPathSyntaxError.new("invalid float literal", token)
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
begin
|
|
310
|
-
FloatLiteral.new(token, Float(value))
|
|
311
|
-
rescue ArgumentError
|
|
312
|
-
raise JSONPathSyntaxError.new("invalid float literal", token)
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def parse_function_expression(stream)
|
|
317
|
-
token = stream.next
|
|
318
|
-
args = [] # : Array[Expression]
|
|
319
|
-
|
|
320
|
-
while stream.peek.type != :token_rparen
|
|
321
|
-
expr = case stream.peek.type
|
|
322
|
-
when :token_double_quote_string, :token_single_quote_string
|
|
323
|
-
arg_token = stream.next
|
|
324
|
-
StringLiteral.new(arg_token, decode_string_literal(arg_token))
|
|
325
|
-
when :token_false
|
|
326
|
-
BooleanLiteral.new(stream.next, false)
|
|
327
|
-
when :token_true
|
|
328
|
-
BooleanLiteral.new(stream.next, true)
|
|
329
|
-
when :token_float
|
|
330
|
-
parse_float_literal(stream)
|
|
331
|
-
when :token_function
|
|
332
|
-
parse_function_expression(stream)
|
|
333
|
-
when :token_int
|
|
334
|
-
parse_integer_literal(stream)
|
|
335
|
-
when :token_null
|
|
336
|
-
NullLiteral.new(stream.next, nil)
|
|
337
|
-
when :token_root
|
|
338
|
-
parse_root_query(stream)
|
|
339
|
-
when :token_current
|
|
340
|
-
parse_relative_query(stream)
|
|
341
|
-
else
|
|
342
|
-
arg_token = stream.next
|
|
343
|
-
raise JSONPathSyntaxError.new("unexpected '#{arg_token.value}'", arg_token)
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
expr = parse_infix_expression(stream, expr) while BINARY_OPERATORS.key? stream.peek.type
|
|
347
|
-
|
|
348
|
-
args << expr
|
|
349
|
-
|
|
350
|
-
if stream.peek.type != :token_rparen
|
|
351
|
-
stream.expect(:token_comma)
|
|
352
|
-
stream.next
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
stream.expect(:token_rparen)
|
|
357
|
-
stream.next
|
|
358
|
-
|
|
359
|
-
validate_function_extension_signature(token, args)
|
|
360
|
-
FunctionExpression.new(token, token.value, args)
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
def parse_grouped_expression(stream)
|
|
364
|
-
stream.next # discard "("
|
|
365
|
-
expr = parse_filter_expression(stream)
|
|
366
|
-
|
|
367
|
-
while stream.peek.type != :token_rparen
|
|
368
|
-
raise JSONPathSyntaxError.new("unbalanced parentheses", stream.peek) if stream.peek.type == :token_eoi
|
|
369
|
-
|
|
370
|
-
expr = parse_infix_expression(stream, expr)
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
stream.expect(:token_rparen)
|
|
374
|
-
stream.next
|
|
375
|
-
expr
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
def parse_prefix_expression(stream)
|
|
379
|
-
token = stream.next
|
|
380
|
-
LogicalNotExpression.new(token, parse_filter_expression(stream, Precedence::PREFIX))
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
def parse_root_query(stream)
|
|
384
|
-
token = stream.next
|
|
385
|
-
RootQueryExpression.new(token, JSONPath.new(@env, parse_query(stream)))
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
def parse_relative_query(stream)
|
|
389
|
-
token = stream.next
|
|
390
|
-
RelativeQueryExpression.new(token, JSONPath.new(@env, parse_query(stream)))
|
|
391
|
-
end
|
|
392
|
-
|
|
393
|
-
def parse_infix_expression(stream, left)
|
|
394
|
-
token = stream.next
|
|
395
|
-
precedence = PRECEDENCES.fetch(token.type, Precedence::LOWEST)
|
|
396
|
-
right = parse_filter_expression(stream, precedence)
|
|
397
|
-
|
|
398
|
-
if COMPARISON_OPERATORS.member? token.value
|
|
399
|
-
raise_for_non_comparable_function(left)
|
|
400
|
-
raise_for_non_comparable_function(right)
|
|
401
|
-
case token.type
|
|
402
|
-
when :token_eq
|
|
403
|
-
EqExpression.new(token, left, right)
|
|
404
|
-
when :token_ge
|
|
405
|
-
GeExpression.new(token, left, right)
|
|
406
|
-
when :token_gt
|
|
407
|
-
GtExpression.new(token, left, right)
|
|
408
|
-
when :token_le
|
|
409
|
-
LeExpression.new(token, left, right)
|
|
410
|
-
when :token_lt
|
|
411
|
-
LtExpression.new(token, left, right)
|
|
412
|
-
when :token_ne
|
|
413
|
-
NeExpression.new(token, left, right)
|
|
414
|
-
else
|
|
415
|
-
raise JSONPathSyntaxError.new("unexpected token", token)
|
|
416
|
-
end
|
|
417
|
-
else
|
|
418
|
-
raise_for_not_compared_literal(left)
|
|
419
|
-
raise_for_not_compared_literal(right)
|
|
420
|
-
case token.type
|
|
421
|
-
when :token_and
|
|
422
|
-
LogicalAndExpression.new(token, left, right)
|
|
423
|
-
when :token_or
|
|
424
|
-
LogicalOrExpression.new(token, left, right)
|
|
425
|
-
else
|
|
426
|
-
raise JSONPathSyntaxError.new("unexpected token", token)
|
|
427
|
-
end
|
|
428
|
-
end
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
def parse_i_json_int(token)
|
|
432
|
-
value = token.value
|
|
433
|
-
|
|
434
|
-
if value.length > 1 && value.start_with?("0", "-0")
|
|
435
|
-
raise JSONPathSyntaxError.new("invalid index '#{value}'", token)
|
|
436
|
-
end
|
|
437
|
-
|
|
438
|
-
begin
|
|
439
|
-
int = Integer(value)
|
|
440
|
-
rescue ArgumentError
|
|
441
|
-
raise JSONPathSyntaxError.new("invalid I-JSON integer", token)
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
if int < @env.class::MIN_INT_INDEX || int > @env.class::MAX_INT_INDEX
|
|
445
|
-
raise JSONPathSyntaxError.new("index out of range",
|
|
446
|
-
token)
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
int
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
def decode_string_literal(token)
|
|
453
|
-
if token.type == :token_single_quote_string
|
|
454
|
-
JSONP3.unescape_string(token.value, "'", token)
|
|
455
|
-
else
|
|
456
|
-
JSONP3.unescape_string(token.value, '"', token)
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
def raise_for_non_comparable_function(expression)
|
|
461
|
-
if expression.is_a?(QueryExpression) && !expression.query.singular?
|
|
462
|
-
raise JSONPathSyntaxError.new("non-singular query is not comparable", expression.token)
|
|
463
|
-
end
|
|
464
|
-
|
|
465
|
-
return unless expression.is_a?(FunctionExpression)
|
|
466
|
-
|
|
467
|
-
func = @env.function_extensions[expression.name]
|
|
468
|
-
return unless func.class::RETURN_TYPE != :value_expression
|
|
469
|
-
|
|
470
|
-
raise JSONPathTypeError.new("result of #{expression.name}() is not comparable", expression.token)
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
def raise_for_not_compared_literal(expression)
|
|
474
|
-
return unless expression.is_a? FilterExpressionLiteral
|
|
475
|
-
|
|
476
|
-
raise JSONPathSyntaxError.new("expression literals must be compared",
|
|
477
|
-
expression.token)
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
def validate_function_extension_signature(token, args)
|
|
481
|
-
func = @env.function_extensions.fetch(token.value)
|
|
482
|
-
count = func.class::ARG_TYPES.length
|
|
483
|
-
|
|
484
|
-
unless args.length == count
|
|
485
|
-
raise JSONPathTypeError.new(
|
|
486
|
-
"#{token.value}() takes #{count} argument#{count == 1 ? "" : "s"} (#{args.length} given)",
|
|
487
|
-
token
|
|
488
|
-
)
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
func.class::ARG_TYPES.each_with_index do |t, i|
|
|
492
|
-
arg = args[i]
|
|
493
|
-
case t
|
|
494
|
-
when :value_expression
|
|
495
|
-
unless arg.is_a?(FilterExpressionLiteral) ||
|
|
496
|
-
(arg.is_a?(QueryExpression) && arg.query.singular?) ||
|
|
497
|
-
(function_return_type(arg) == :value_expression)
|
|
498
|
-
raise JSONPathTypeError.new("#{token.value}() argument #{i} must be of ValueType", arg.token)
|
|
499
|
-
end
|
|
500
|
-
when :logical_expression
|
|
501
|
-
unless arg.is_a?(QueryExpression) || arg.is_a?(InfixExpression)
|
|
502
|
-
raise JSONPathTypeError.new("#{token.value}() argument #{i} must be of LogicalType", arg.token)
|
|
503
|
-
end
|
|
504
|
-
when :nodes_expression
|
|
505
|
-
unless arg.is_a?(QueryExpression) || function_return_type(arg) == :nodes_expression
|
|
506
|
-
raise JSONPathTypeError.new("#{token.value}() argument #{i} must be of NodesType", arg.token)
|
|
507
|
-
end
|
|
508
|
-
end
|
|
509
|
-
end
|
|
510
|
-
rescue KeyError
|
|
511
|
-
raise JSONPathNameError.new("function '#{token.value}' is not defined", token)
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
def function_return_type(expression)
|
|
515
|
-
return nil unless expression.is_a? FunctionExpression
|
|
516
|
-
|
|
517
|
-
@env.function_extensions[expression.name].class::RETURN_TYPE
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
PRECEDENCES = {
|
|
521
|
-
token_and: Precedence::LOGICAL_AND,
|
|
522
|
-
token_or: Precedence::LOGICAL_OR,
|
|
523
|
-
token_not: Precedence::PREFIX,
|
|
524
|
-
token_eq: Precedence::RELATIONAL,
|
|
525
|
-
token_ge: Precedence::RELATIONAL,
|
|
526
|
-
token_gt: Precedence::RELATIONAL,
|
|
527
|
-
token_le: Precedence::RELATIONAL,
|
|
528
|
-
token_lt: Precedence::RELATIONAL,
|
|
529
|
-
token_ne: Precedence::RELATIONAL,
|
|
530
|
-
token_rparen: Precedence::LOWEST
|
|
531
|
-
}.freeze
|
|
532
|
-
|
|
533
|
-
BINARY_OPERATORS = {
|
|
534
|
-
token_and: "&&",
|
|
535
|
-
token_or: "||",
|
|
536
|
-
token_eq: "==",
|
|
537
|
-
token_ge: ">=",
|
|
538
|
-
token_gt: ">",
|
|
539
|
-
token_le: "<=",
|
|
540
|
-
token_lt: "<",
|
|
541
|
-
token_ne: "!="
|
|
542
|
-
}.freeze
|
|
543
|
-
|
|
544
|
-
COMPARISON_OPERATORS = Set[
|
|
545
|
-
"==",
|
|
546
|
-
">=",
|
|
547
|
-
">",
|
|
548
|
-
"<=",
|
|
549
|
-
"<",
|
|
550
|
-
"!=",
|
|
551
|
-
]
|
|
552
|
-
end
|
|
553
|
-
end
|
data/lib/json_p3/path.rb
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "node"
|
|
4
|
-
|
|
5
|
-
module JSONP3
|
|
6
|
-
# A compiled JSONPath expression ready to be applied to JSON-like values.
|
|
7
|
-
class JSONPath
|
|
8
|
-
def initialize(env, segments)
|
|
9
|
-
@env = env
|
|
10
|
-
@segments = segments
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def to_s
|
|
14
|
-
"$#{@segments.map(&:to_s).join}"
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Apply this JSONPath expression to JSON-like value _root_.
|
|
18
|
-
# @param root [Array, Hash, String, Integer, nil] the root JSON-like value to apply this query to.
|
|
19
|
-
# @return [Array<JSONPathNode>] the sequence of nodes found while applying this query to _root_.
|
|
20
|
-
def find(root)
|
|
21
|
-
nodes = [JSONPathNode.new(root, [], root)]
|
|
22
|
-
@segments.each { |segment| nodes = segment.resolve(nodes) }
|
|
23
|
-
JSONPathNodeList.new(nodes) # TODO: use JSONPathNodeList internally?
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
alias apply find
|
|
27
|
-
|
|
28
|
-
# Apply this JSONPath expression to JSON-like value _root_.
|
|
29
|
-
# @param root [Array, Hash, String, Integer, nil] the root JSON-like value to apply this query to.
|
|
30
|
-
# @return [Enumerable<JSONPathNode>] the sequence of nodes found while applying this query to _root_.
|
|
31
|
-
def find_enum(root)
|
|
32
|
-
nodes = [JSONPathNode.new(root, [], root)] # : Enumerable[JSONPathNode]
|
|
33
|
-
@segments.each { |segment| nodes = segment.resolve_enum(nodes) }
|
|
34
|
-
nodes
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Return the first node from applying this JSONPath expression to JSON-like value _root_.
|
|
38
|
-
# @param root [Array, Hash, String, Integer, nil] the root JSON-like value to apply this query to.
|
|
39
|
-
# @return [JSONPathNode | nil] the first available node or nil if there were no matches.
|
|
40
|
-
def match(root)
|
|
41
|
-
find_enum(root).first
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Return `true` if this query results in at least one node, or `false` otherwise.
|
|
45
|
-
# @param root [Array, Hash, String, Integer, nil] the root JSON-like value to apply this query to.
|
|
46
|
-
# @return [bool] `true` if this query results in at least one node, or `false` otherwise.
|
|
47
|
-
def match?(root)
|
|
48
|
-
!find_enum(root).first.nil?
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Return the first node from applying this JSONPath expression to JSON-like value _root_.
|
|
52
|
-
# @param root [Array, Hash, String, Integer, nil] the root JSON-like value to apply this query to.
|
|
53
|
-
# @return [JSONPathNode | nil] the first available node or nil if there were no matches.
|
|
54
|
-
def first(root)
|
|
55
|
-
find_enum(root).first
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Return _true_ if this JSONPath expression is a singular query.
|
|
59
|
-
def singular?
|
|
60
|
-
@segments.each do |segment|
|
|
61
|
-
return false if segment.instance_of? RecursiveDescentSegment
|
|
62
|
-
return false unless segment.selectors.length == 1 && segment.selectors[0].singular?
|
|
63
|
-
end
|
|
64
|
-
true
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Return _true_ if this JSONPath expression has no segments.
|
|
68
|
-
def empty?
|
|
69
|
-
@segments.empty?
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|