json_p3 0.2.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.rubocop.yml +19 -0
- data/CHANGELOG.md +9 -0
- data/README.md +149 -17
- data/certs/jgrp.pem +27 -0
- data/lib/json_p3/cache.rb +1 -1
- data/lib/json_p3/environment.rb +1 -1
- data/lib/json_p3/errors.rb +9 -1
- data/lib/json_p3/filter.rb +4 -4
- data/lib/json_p3/function.rb +0 -6
- data/lib/json_p3/function_extensions/count.rb +2 -2
- data/lib/json_p3/function_extensions/length.rb +2 -2
- data/lib/json_p3/function_extensions/match.rb +3 -3
- data/lib/json_p3/function_extensions/pattern.rb +1 -1
- data/lib/json_p3/function_extensions/search.rb +3 -3
- data/lib/json_p3/function_extensions/value.rb +2 -2
- data/lib/json_p3/lexer.rb +54 -55
- data/lib/json_p3/parser.rb +112 -112
- data/lib/json_p3/patch.rb +449 -0
- data/lib/json_p3/pointer.rb +236 -0
- data/lib/json_p3/segment.rb +3 -3
- data/lib/json_p3/selector.rb +4 -4
- data/lib/json_p3/token.rb +0 -38
- data/lib/json_p3/unescape.rb +5 -5
- data/lib/json_p3/version.rb +1 -1
- data/lib/json_p3.rb +10 -0
- data/sig/json_p3.rbs +322 -104
- data.tar.gz.sig +0 -0
- metadata +6 -3
- metadata.gz.sig +0 -0
data/lib/json_p3/parser.rb
CHANGED
@@ -62,7 +62,7 @@ module JSONP3
|
|
62
62
|
end
|
63
63
|
|
64
64
|
# A JSONPath expression parser.
|
65
|
-
class Parser
|
65
|
+
class Parser
|
66
66
|
def initialize(env)
|
67
67
|
@env = env
|
68
68
|
@name_selector = env.class::NAME_SELECTOR
|
@@ -74,23 +74,23 @@ module JSONP3
|
|
74
74
|
# @return [Array<Segment>]
|
75
75
|
def parse(tokens)
|
76
76
|
stream = Stream.new(tokens)
|
77
|
-
stream.expect(
|
77
|
+
stream.expect(:token_root)
|
78
78
|
stream.next
|
79
79
|
parse_query(stream)
|
80
80
|
end
|
81
81
|
|
82
82
|
protected
|
83
83
|
|
84
|
-
def parse_query(stream)
|
85
|
-
segments = []
|
84
|
+
def parse_query(stream)
|
85
|
+
segments = [] # : Array[Segment]
|
86
86
|
|
87
87
|
loop do
|
88
88
|
case stream.peek.type
|
89
|
-
when
|
89
|
+
when :token_double_dot
|
90
90
|
token = stream.next
|
91
91
|
selectors = parse_selectors(stream)
|
92
92
|
segments << RecursiveDescentSegment.new(@env, token, selectors)
|
93
|
-
when
|
93
|
+
when :token_lbracket, :token_name, :token_wild
|
94
94
|
token = stream.peek
|
95
95
|
selectors = parse_selectors(stream)
|
96
96
|
segments << ChildSegment.new(@env, token, selectors)
|
@@ -102,60 +102,60 @@ module JSONP3
|
|
102
102
|
segments
|
103
103
|
end
|
104
104
|
|
105
|
-
def parse_selectors(stream)
|
105
|
+
def parse_selectors(stream)
|
106
106
|
case stream.peek.type
|
107
|
-
when
|
107
|
+
when :token_name
|
108
108
|
token = stream.next
|
109
109
|
[@name_selector.new(@env, token, token.value)]
|
110
|
-
when
|
110
|
+
when :token_wild
|
111
111
|
[WildcardSelector.new(@env, stream.next)]
|
112
|
-
when
|
112
|
+
when :token_lbracket
|
113
113
|
parse_bracketed_selection(stream)
|
114
114
|
else
|
115
115
|
[]
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
-
def parse_bracketed_selection(stream)
|
120
|
-
stream.expect
|
119
|
+
def parse_bracketed_selection(stream)
|
120
|
+
stream.expect(:token_lbracket)
|
121
121
|
segment_token = stream.next
|
122
122
|
|
123
|
-
selectors = []
|
123
|
+
selectors = [] # : Array[Selector]
|
124
124
|
|
125
125
|
loop do # rubocop:disable Metrics/BlockLength
|
126
126
|
case stream.peek.type
|
127
|
-
when
|
127
|
+
when :token_rbracket
|
128
128
|
break
|
129
|
-
when
|
129
|
+
when :token_index
|
130
130
|
selectors << parse_index_or_slice(stream)
|
131
|
-
when
|
131
|
+
when :token_double_quote_string, :token_single_quote_string
|
132
132
|
token = stream.next
|
133
133
|
selectors << @name_selector.new(@env, token, decode_string_literal(token))
|
134
|
-
when
|
134
|
+
when :token_colon
|
135
135
|
selectors << parse_slice_selector(stream)
|
136
|
-
when
|
136
|
+
when :token_wild
|
137
137
|
selectors << WildcardSelector.new(@env, stream.next)
|
138
|
-
when
|
138
|
+
when :token_filter
|
139
139
|
selectors << parse_filter_selector(stream)
|
140
|
-
when
|
140
|
+
when :token_eoi
|
141
141
|
raise JSONPathSyntaxError.new("unexpected end of query", stream.next)
|
142
142
|
else
|
143
143
|
raise JSONPathSyntaxError.new("unexpected token in bracketed selection", stream.next)
|
144
144
|
end
|
145
145
|
|
146
146
|
case stream.peek.type
|
147
|
-
when
|
147
|
+
when :token_eoi
|
148
148
|
raise JSONPathSyntaxError.new("unexpected end of selector list", stream.next)
|
149
|
-
when
|
149
|
+
when :token_rbracket
|
150
150
|
break
|
151
151
|
else
|
152
|
-
stream.expect
|
152
|
+
stream.expect(:token_comma)
|
153
153
|
stream.next
|
154
|
-
stream.expect_not(
|
154
|
+
stream.expect_not(:token_rbracket, "unexpected trailing comma")
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
-
stream.expect(
|
158
|
+
stream.expect(:token_rbracket)
|
159
159
|
stream.next
|
160
160
|
|
161
161
|
raise JSONPathSyntaxError.new("empty segment", segment_token) if selectors.empty?
|
@@ -163,29 +163,29 @@ module JSONP3
|
|
163
163
|
selectors
|
164
164
|
end
|
165
165
|
|
166
|
-
def parse_index_or_slice(stream)
|
166
|
+
def parse_index_or_slice(stream)
|
167
167
|
token = stream.next
|
168
168
|
index = parse_i_json_int(token)
|
169
169
|
|
170
|
-
return @index_selector.new(@env, token, index) unless stream.peek.type ==
|
170
|
+
return @index_selector.new(@env, token, index) unless stream.peek.type == :token_colon
|
171
171
|
|
172
172
|
stream.next # move past colon
|
173
173
|
stop = nil
|
174
174
|
step = nil
|
175
175
|
|
176
176
|
case stream.peek.type
|
177
|
-
when
|
177
|
+
when :token_index
|
178
178
|
stop = parse_i_json_int(stream.next)
|
179
|
-
when
|
179
|
+
when :token_colon
|
180
180
|
stream.next # move past colon
|
181
181
|
end
|
182
182
|
|
183
|
-
stream.next if stream.peek.type ==
|
183
|
+
stream.next if stream.peek.type == :token_colon
|
184
184
|
|
185
185
|
case stream.peek.type
|
186
|
-
when
|
186
|
+
when :token_index
|
187
187
|
step = parse_i_json_int(stream.next)
|
188
|
-
when
|
188
|
+
when :token_rbracket
|
189
189
|
nil
|
190
190
|
else
|
191
191
|
error_token = stream.next
|
@@ -195,8 +195,8 @@ module JSONP3
|
|
195
195
|
SliceSelector.new(@env, token, index, stop, step)
|
196
196
|
end
|
197
197
|
|
198
|
-
def parse_slice_selector(stream)
|
199
|
-
stream.expect(
|
198
|
+
def parse_slice_selector(stream)
|
199
|
+
stream.expect(:token_colon)
|
200
200
|
token = stream.next
|
201
201
|
|
202
202
|
start = nil
|
@@ -204,18 +204,18 @@ module JSONP3
|
|
204
204
|
step = nil
|
205
205
|
|
206
206
|
case stream.peek.type
|
207
|
-
when
|
207
|
+
when :token_index
|
208
208
|
stop = parse_i_json_int(stream.next)
|
209
|
-
when
|
209
|
+
when :token_colon
|
210
210
|
stream.next # move past colon
|
211
211
|
end
|
212
212
|
|
213
|
-
stream.next if stream.peek.type ==
|
213
|
+
stream.next if stream.peek.type == :token_colon
|
214
214
|
|
215
215
|
case stream.peek.type
|
216
|
-
when
|
216
|
+
when :token_index
|
217
217
|
step = parse_i_json_int(stream.next)
|
218
|
-
when
|
218
|
+
when :token_rbracket
|
219
219
|
nil
|
220
220
|
else
|
221
221
|
error_token = stream.next
|
@@ -225,14 +225,14 @@ module JSONP3
|
|
225
225
|
SliceSelector.new(@env, token, start, stop, step)
|
226
226
|
end
|
227
227
|
|
228
|
-
def parse_filter_selector(stream)
|
228
|
+
def parse_filter_selector(stream)
|
229
229
|
token = stream.next
|
230
230
|
expression = parse_filter_expression(stream)
|
231
231
|
|
232
232
|
# Raise if expression must be compared.
|
233
233
|
if expression.is_a? FunctionExpression
|
234
234
|
func = @env.function_extensions[expression.name]
|
235
|
-
if func.class::RETURN_TYPE ==
|
235
|
+
if func.class::RETURN_TYPE == :value_expression
|
236
236
|
raise JSONPathTypeError.new("result of #{expression.name}() must be compared", expression.token)
|
237
237
|
end
|
238
238
|
end
|
@@ -245,30 +245,30 @@ module JSONP3
|
|
245
245
|
FilterSelector.new(@env, token, FilterExpression.new(token, expression))
|
246
246
|
end
|
247
247
|
|
248
|
-
def parse_filter_expression(stream, precedence = Precedence::LOWEST) # rubocop:disable Metrics/
|
248
|
+
def parse_filter_expression(stream, precedence = Precedence::LOWEST) # rubocop:disable Metrics/CyclomaticComplexity
|
249
249
|
left = case stream.peek.type
|
250
|
-
when
|
250
|
+
when :token_double_quote_string, :token_single_quote_string
|
251
251
|
token = stream.next
|
252
252
|
StringLiteral.new(token, decode_string_literal(token))
|
253
|
-
when
|
253
|
+
when :token_false
|
254
254
|
BooleanLiteral.new(stream.next, false)
|
255
|
-
when
|
255
|
+
when :token_true
|
256
256
|
BooleanLiteral.new(stream.next, true)
|
257
|
-
when
|
257
|
+
when :token_float
|
258
258
|
parse_float_literal(stream)
|
259
|
-
when
|
259
|
+
when :token_function
|
260
260
|
parse_function_expression(stream)
|
261
|
-
when
|
261
|
+
when :token_int
|
262
262
|
parse_integer_literal(stream)
|
263
|
-
when
|
263
|
+
when :token_lparen
|
264
264
|
parse_grouped_expression(stream)
|
265
|
-
when
|
265
|
+
when :token_not
|
266
266
|
parse_prefix_expression(stream)
|
267
|
-
when
|
267
|
+
when :token_null
|
268
268
|
NullLiteral.new(stream.next, nil)
|
269
|
-
when
|
269
|
+
when :token_root
|
270
270
|
parse_root_query(stream)
|
271
|
-
when
|
271
|
+
when :token_current
|
272
272
|
parse_relative_query(stream)
|
273
273
|
else
|
274
274
|
token = stream.next
|
@@ -277,8 +277,8 @@ module JSONP3
|
|
277
277
|
|
278
278
|
loop do
|
279
279
|
peeked = stream.peek
|
280
|
-
if peeked.type ==
|
281
|
-
peeked.type ==
|
280
|
+
if peeked.type == :token_eoi ||
|
281
|
+
peeked.type == :token_rbracket ||
|
282
282
|
PRECEDENCES.fetch(peeked.type, Precedence::LOWEST) < precedence
|
283
283
|
break
|
284
284
|
end
|
@@ -313,30 +313,30 @@ module JSONP3
|
|
313
313
|
end
|
314
314
|
end
|
315
315
|
|
316
|
-
def parse_function_expression(stream)
|
316
|
+
def parse_function_expression(stream)
|
317
317
|
token = stream.next
|
318
|
-
args = []
|
318
|
+
args = [] # : Array[Expression]
|
319
319
|
|
320
|
-
while stream.peek.type !=
|
320
|
+
while stream.peek.type != :token_rparen
|
321
321
|
expr = case stream.peek.type
|
322
|
-
when
|
322
|
+
when :token_double_quote_string, :token_single_quote_string
|
323
323
|
arg_token = stream.next
|
324
324
|
StringLiteral.new(arg_token, decode_string_literal(arg_token))
|
325
|
-
when
|
325
|
+
when :token_false
|
326
326
|
BooleanLiteral.new(stream.next, false)
|
327
|
-
when
|
327
|
+
when :token_true
|
328
328
|
BooleanLiteral.new(stream.next, true)
|
329
|
-
when
|
329
|
+
when :token_float
|
330
330
|
parse_float_literal(stream)
|
331
|
-
when
|
331
|
+
when :token_function
|
332
332
|
parse_function_expression(stream)
|
333
|
-
when
|
333
|
+
when :token_int
|
334
334
|
parse_integer_literal(stream)
|
335
|
-
when
|
335
|
+
when :token_null
|
336
336
|
NullLiteral.new(stream.next, nil)
|
337
|
-
when
|
337
|
+
when :token_root
|
338
338
|
parse_root_query(stream)
|
339
|
-
when
|
339
|
+
when :token_current
|
340
340
|
parse_relative_query(stream)
|
341
341
|
else
|
342
342
|
arg_token = stream.next
|
@@ -347,13 +347,13 @@ module JSONP3
|
|
347
347
|
|
348
348
|
args << expr
|
349
349
|
|
350
|
-
if stream.peek.type !=
|
351
|
-
stream.expect(
|
350
|
+
if stream.peek.type != :token_rparen
|
351
|
+
stream.expect(:token_comma)
|
352
352
|
stream.next
|
353
353
|
end
|
354
354
|
end
|
355
355
|
|
356
|
-
stream.expect(
|
356
|
+
stream.expect(:token_rparen)
|
357
357
|
stream.next
|
358
358
|
|
359
359
|
validate_function_extension_signature(token, args)
|
@@ -364,13 +364,13 @@ module JSONP3
|
|
364
364
|
stream.next # discard "("
|
365
365
|
expr = parse_filter_expression(stream)
|
366
366
|
|
367
|
-
while stream.peek.type !=
|
368
|
-
raise JSONPathSyntaxError.new("unbalanced parentheses", stream.peek) if stream.peek.type ==
|
367
|
+
while stream.peek.type != :token_rparen
|
368
|
+
raise JSONPathSyntaxError.new("unbalanced parentheses", stream.peek) if stream.peek.type == :token_eoi
|
369
369
|
|
370
370
|
expr = parse_infix_expression(stream, expr)
|
371
371
|
end
|
372
372
|
|
373
|
-
stream.expect(
|
373
|
+
stream.expect(:token_rparen)
|
374
374
|
stream.next
|
375
375
|
expr
|
376
376
|
end
|
@@ -390,7 +390,7 @@ module JSONP3
|
|
390
390
|
RelativeQueryExpression.new(token, JSONPath.new(@env, parse_query(stream)))
|
391
391
|
end
|
392
392
|
|
393
|
-
def parse_infix_expression(stream, left)
|
393
|
+
def parse_infix_expression(stream, left)
|
394
394
|
token = stream.next
|
395
395
|
precedence = PRECEDENCES.fetch(token.type, Precedence::LOWEST)
|
396
396
|
right = parse_filter_expression(stream, precedence)
|
@@ -399,28 +399,28 @@ module JSONP3
|
|
399
399
|
raise_for_non_comparable_function(left)
|
400
400
|
raise_for_non_comparable_function(right)
|
401
401
|
case token.type
|
402
|
-
when
|
402
|
+
when :token_eq
|
403
403
|
EqExpression.new(token, left, right)
|
404
|
-
when
|
404
|
+
when :token_ge
|
405
405
|
GeExpression.new(token, left, right)
|
406
|
-
when
|
406
|
+
when :token_gt
|
407
407
|
GtExpression.new(token, left, right)
|
408
|
-
when
|
408
|
+
when :token_le
|
409
409
|
LeExpression.new(token, left, right)
|
410
|
-
when
|
410
|
+
when :token_lt
|
411
411
|
LtExpression.new(token, left, right)
|
412
|
-
when
|
412
|
+
when :token_ne
|
413
413
|
NeExpression.new(token, left, right)
|
414
414
|
else
|
415
415
|
raise JSONPathSyntaxError.new("unexpected token", token)
|
416
416
|
end
|
417
417
|
else
|
418
|
-
|
419
|
-
|
418
|
+
raise_for_not_compared_literal(left)
|
419
|
+
raise_for_not_compared_literal(right)
|
420
420
|
case token.type
|
421
|
-
when
|
421
|
+
when :token_and
|
422
422
|
LogicalAndExpression.new(token, left, right)
|
423
|
-
when
|
423
|
+
when :token_or
|
424
424
|
LogicalOrExpression.new(token, left, right)
|
425
425
|
else
|
426
426
|
raise JSONPathSyntaxError.new("unexpected token", token)
|
@@ -428,7 +428,7 @@ module JSONP3
|
|
428
428
|
end
|
429
429
|
end
|
430
430
|
|
431
|
-
def parse_i_json_int(token)
|
431
|
+
def parse_i_json_int(token)
|
432
432
|
value = token.value
|
433
433
|
|
434
434
|
if value.length > 1 && value.start_with?("0", "-0")
|
@@ -450,7 +450,7 @@ module JSONP3
|
|
450
450
|
end
|
451
451
|
|
452
452
|
def decode_string_literal(token)
|
453
|
-
if token.type ==
|
453
|
+
if token.type == :token_single_quote_string
|
454
454
|
JSONP3.unescape_string(token.value, "'", token)
|
455
455
|
else
|
456
456
|
JSONP3.unescape_string(token.value, '"', token)
|
@@ -465,19 +465,19 @@ module JSONP3
|
|
465
465
|
return unless expression.is_a?(FunctionExpression)
|
466
466
|
|
467
467
|
func = @env.function_extensions[expression.name]
|
468
|
-
return unless func.class::RETURN_TYPE !=
|
468
|
+
return unless func.class::RETURN_TYPE != :value_expression
|
469
469
|
|
470
470
|
raise JSONPathTypeError.new("result of #{expression.name}() is not comparable", expression.token)
|
471
471
|
end
|
472
472
|
|
473
|
-
def
|
473
|
+
def raise_for_not_compared_literal(expression)
|
474
474
|
return unless expression.is_a? FilterExpressionLiteral
|
475
475
|
|
476
476
|
raise JSONPathSyntaxError.new("expression literals must be compared",
|
477
477
|
expression.token)
|
478
478
|
end
|
479
479
|
|
480
|
-
def validate_function_extension_signature(token, args) # rubocop:disable Metrics/
|
480
|
+
def validate_function_extension_signature(token, args) # rubocop:disable Metrics/CyclomaticComplexity
|
481
481
|
func = @env.function_extensions.fetch(token.value)
|
482
482
|
count = func.class::ARG_TYPES.length
|
483
483
|
|
@@ -491,18 +491,18 @@ module JSONP3
|
|
491
491
|
func.class::ARG_TYPES.each_with_index do |t, i|
|
492
492
|
arg = args[i]
|
493
493
|
case t
|
494
|
-
when
|
494
|
+
when :value_expression
|
495
495
|
unless arg.is_a?(FilterExpressionLiteral) ||
|
496
496
|
(arg.is_a?(QueryExpression) && arg.query.singular?) ||
|
497
|
-
(function_return_type(arg) ==
|
497
|
+
(function_return_type(arg) == :value_expression)
|
498
498
|
raise JSONPathTypeError.new("#{token.value}() argument #{i} must be of ValueType", arg.token)
|
499
499
|
end
|
500
|
-
when
|
500
|
+
when :logical_expression
|
501
501
|
unless arg.is_a?(QueryExpression) || arg.is_a?(InfixExpression)
|
502
502
|
raise JSONPathTypeError.new("#{token.value}() argument #{i} must be of LogicalType", arg.token)
|
503
503
|
end
|
504
|
-
when
|
505
|
-
unless arg.is_a?(QueryExpression) || function_return_type(arg) ==
|
504
|
+
when :nodes_expression
|
505
|
+
unless arg.is_a?(QueryExpression) || function_return_type(arg) == :nodes_expression
|
506
506
|
raise JSONPathTypeError.new("#{token.value}() argument #{i} must be of NodesType", arg.token)
|
507
507
|
end
|
508
508
|
end
|
@@ -518,27 +518,27 @@ module JSONP3
|
|
518
518
|
end
|
519
519
|
|
520
520
|
PRECEDENCES = {
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
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
531
|
}.freeze
|
532
532
|
|
533
533
|
BINARY_OPERATORS = {
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
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
542
|
}.freeze
|
543
543
|
|
544
544
|
COMPARISON_OPERATORS = Set[
|