json_p3 0.2.1 → 0.3.1
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 +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/lexer.rb
CHANGED
@@ -15,7 +15,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
15
15
|
lexer.run
|
16
16
|
tokens = lexer.tokens
|
17
17
|
|
18
|
-
if !tokens.empty? && tokens.last.type ==
|
18
|
+
if !tokens.empty? && tokens.last.type == :token_error
|
19
19
|
raise JSONPathSyntaxError.new(tokens.last.message || raise,
|
20
20
|
tokens.last)
|
21
21
|
end
|
@@ -26,7 +26,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
26
26
|
# JSONPath query expression lexical scanner.
|
27
27
|
#
|
28
28
|
# @see tokenize
|
29
|
-
class Lexer
|
29
|
+
class Lexer
|
30
30
|
RE_INT = /-?[0-9]+/
|
31
31
|
RE_NAME = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/
|
32
32
|
RE_WHITESPACE = /[ \n\r\t]+/
|
@@ -46,7 +46,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
46
46
|
|
47
47
|
def run
|
48
48
|
state = :lex_root
|
49
|
-
state =
|
49
|
+
state = send(state) until state.nil?
|
50
50
|
end
|
51
51
|
|
52
52
|
protected
|
@@ -56,12 +56,12 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
56
56
|
# @param value [String | nil] a the token's value, if it is known, otherwise the
|
57
57
|
# value will be sliced from @query. This is a performance optimization.
|
58
58
|
def emit(token_type, value = nil)
|
59
|
-
@tokens << Token.new(token_type, value || @query[@start
|
59
|
+
@tokens << Token.new(token_type, value || @query[@start, @scanner.charpos - @start], @start, @query)
|
60
60
|
@start = @scanner.charpos
|
61
61
|
end
|
62
62
|
|
63
63
|
def next
|
64
|
-
@scanner.
|
64
|
+
@scanner.get_byte || ""
|
65
65
|
end
|
66
66
|
|
67
67
|
def ignore
|
@@ -69,7 +69,6 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def backup
|
72
|
-
# Assumes we're backing-up from a single byte character.
|
73
72
|
@scanner.pos -= 1
|
74
73
|
end
|
75
74
|
|
@@ -78,7 +77,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
78
77
|
@scanner.peek(1)
|
79
78
|
end
|
80
79
|
|
81
|
-
# Advance the lexer if
|
80
|
+
# Advance the lexer if _pattern_ matches from the current position.
|
82
81
|
def accept?(pattern)
|
83
82
|
!@scanner.scan(pattern).nil?
|
84
83
|
end
|
@@ -100,7 +99,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
100
99
|
|
101
100
|
def error(message)
|
102
101
|
@tokens << Token.new(
|
103
|
-
|
102
|
+
:token_error, @query[@start, @scanner.charpos - @start] || "", @start, @query, message: message
|
104
103
|
)
|
105
104
|
end
|
106
105
|
|
@@ -112,11 +111,11 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
112
111
|
return nil
|
113
112
|
end
|
114
113
|
|
115
|
-
emit(
|
114
|
+
emit(:token_root, "$")
|
116
115
|
:lex_segment
|
117
116
|
end
|
118
117
|
|
119
|
-
def lex_segment
|
118
|
+
def lex_segment
|
120
119
|
if accept?(RE_WHITESPACE) && peek.empty?
|
121
120
|
error "unexpected trailing whitespace"
|
122
121
|
return nil
|
@@ -127,16 +126,16 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
127
126
|
|
128
127
|
case c
|
129
128
|
when ""
|
130
|
-
emit(
|
129
|
+
emit(:token_eoi, "")
|
131
130
|
nil
|
132
131
|
when "."
|
133
132
|
return :lex_shorthand_selector unless peek == "."
|
134
133
|
|
135
134
|
self.next
|
136
|
-
emit(
|
135
|
+
emit(:token_double_dot, "..")
|
137
136
|
:lex_descendant_segment
|
138
137
|
when "["
|
139
|
-
emit(
|
138
|
+
emit(:token_lbracket, "[")
|
140
139
|
:lex_inside_bracketed_segment
|
141
140
|
else
|
142
141
|
if @filter_depth.positive?
|
@@ -149,21 +148,21 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
149
148
|
end
|
150
149
|
end
|
151
150
|
|
152
|
-
def lex_descendant_segment
|
151
|
+
def lex_descendant_segment
|
153
152
|
case self.next
|
154
153
|
when ""
|
155
154
|
error "bald descendant segment"
|
156
155
|
nil
|
157
156
|
when "*"
|
158
|
-
emit(
|
157
|
+
emit(:token_wild, "*")
|
159
158
|
:lex_segment
|
160
159
|
when "["
|
161
|
-
emit(
|
160
|
+
emit(:token_lbracket, "[")
|
162
161
|
:lex_inside_bracketed_segment
|
163
162
|
else
|
164
163
|
backup
|
165
164
|
if accept?(RE_NAME)
|
166
|
-
emit(
|
165
|
+
emit(:token_name)
|
167
166
|
:lex_segment
|
168
167
|
else
|
169
168
|
c = self.next
|
@@ -173,7 +172,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
173
172
|
end
|
174
173
|
end
|
175
174
|
|
176
|
-
def lex_shorthand_selector
|
175
|
+
def lex_shorthand_selector
|
177
176
|
if peek == ""
|
178
177
|
error "unexpected trailing dot"
|
179
178
|
return nil
|
@@ -188,12 +187,12 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
188
187
|
|
189
188
|
if peek == "*"
|
190
189
|
self.next
|
191
|
-
emit(
|
190
|
+
emit(:token_wild, "*")
|
192
191
|
return :lex_segment
|
193
192
|
end
|
194
193
|
|
195
194
|
if accept?(RE_NAME)
|
196
|
-
emit(
|
195
|
+
emit(:token_name)
|
197
196
|
return :lex_segment
|
198
197
|
end
|
199
198
|
|
@@ -202,28 +201,28 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
202
201
|
nil
|
203
202
|
end
|
204
203
|
|
205
|
-
def lex_inside_bracketed_segment
|
204
|
+
def lex_inside_bracketed_segment
|
206
205
|
loop do # rubocop:disable Metrics/BlockLength
|
207
206
|
ignore_whitespace?
|
208
207
|
c = self.next
|
209
208
|
|
210
209
|
case c
|
211
210
|
when "]"
|
212
|
-
emit(
|
211
|
+
emit(:token_rbracket, "]")
|
213
212
|
return @filter_depth.positive? ? :lex_inside_filter : :lex_segment
|
214
213
|
when ""
|
215
214
|
error "unclosed bracketed selection"
|
216
215
|
return nil
|
217
216
|
when "*"
|
218
|
-
emit(
|
217
|
+
emit(:token_wild, "*")
|
219
218
|
when "?"
|
220
|
-
emit(
|
219
|
+
emit(:token_filter, "?")
|
221
220
|
@filter_depth += 1
|
222
221
|
return :lex_inside_filter
|
223
222
|
when ","
|
224
|
-
emit(
|
223
|
+
emit(:token_comma, ",")
|
225
224
|
when ":"
|
226
|
-
emit(
|
225
|
+
emit(:token_colon, ":")
|
227
226
|
when "'"
|
228
227
|
return :lex_single_quoted_string_inside_bracketed_segment
|
229
228
|
when '"'
|
@@ -232,7 +231,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
232
231
|
backup
|
233
232
|
if accept_int?
|
234
233
|
# Index selector or part of a slice selector.
|
235
|
-
emit
|
234
|
+
emit(:token_index)
|
236
235
|
else
|
237
236
|
error "unexpected token '#{c}' in bracketed selection"
|
238
237
|
return nil
|
@@ -259,7 +258,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
259
258
|
backup
|
260
259
|
return :lex_inside_bracketed_segment
|
261
260
|
when ","
|
262
|
-
emit(
|
261
|
+
emit(:token_comma, ",")
|
263
262
|
# If we have unbalanced parens, we are inside a function call and a
|
264
263
|
# comma separates arguments. Otherwise a comma separates selectors.
|
265
264
|
next if @paren_stack.length.positive?
|
@@ -271,11 +270,11 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
271
270
|
when '"'
|
272
271
|
return :lex_double_quoted_string_inside_filter_expression
|
273
272
|
when "("
|
274
|
-
emit(
|
273
|
+
emit(:token_lparen, "(")
|
275
274
|
# Are we in a function call? If so, a function argument contains parens.
|
276
275
|
@paren_stack[-1] += 1 if @paren_stack.length.positive?
|
277
276
|
when ")"
|
278
|
-
emit(
|
277
|
+
emit(:token_rparen, ")")
|
279
278
|
# Are we closing a function call or a parenthesized expression?
|
280
279
|
if @paren_stack.length.positive?
|
281
280
|
if @paren_stack[-1] == 1
|
@@ -285,10 +284,10 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
285
284
|
end
|
286
285
|
end
|
287
286
|
when "$"
|
288
|
-
emit(
|
287
|
+
emit(:token_root, "$")
|
289
288
|
return :lex_segment
|
290
289
|
when "@"
|
291
|
-
emit(
|
290
|
+
emit(:token_current, "@")
|
292
291
|
return :lex_segment
|
293
292
|
when "."
|
294
293
|
backup
|
@@ -296,32 +295,32 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
296
295
|
when "!"
|
297
296
|
if peek == "="
|
298
297
|
self.next
|
299
|
-
emit(
|
298
|
+
emit(:token_ne, "!=")
|
300
299
|
else
|
301
|
-
emit(
|
300
|
+
emit(:token_not, "!")
|
302
301
|
end
|
303
302
|
when "="
|
304
303
|
if peek == "="
|
305
304
|
self.next
|
306
|
-
emit(
|
305
|
+
emit(:token_eq, "==")
|
307
306
|
else
|
308
307
|
backup
|
309
|
-
error "
|
308
|
+
error "found '=', did you mean '==', '!=', '<=' or '>='?"
|
310
309
|
return nil
|
311
310
|
end
|
312
311
|
when "<"
|
313
312
|
if peek == "="
|
314
313
|
self.next
|
315
|
-
emit(
|
314
|
+
emit(:token_le, "<=")
|
316
315
|
else
|
317
|
-
emit(
|
316
|
+
emit(:token_lt, "<")
|
318
317
|
end
|
319
318
|
when ">"
|
320
319
|
if peek == "="
|
321
320
|
self.next
|
322
|
-
emit(
|
321
|
+
emit(:token_ge, ">=")
|
323
322
|
else
|
324
|
-
emit(
|
323
|
+
emit(:token_gt, ">")
|
325
324
|
end
|
326
325
|
else
|
327
326
|
backup
|
@@ -335,24 +334,24 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
335
334
|
end
|
336
335
|
|
337
336
|
accept?(/[eE][+-]?[0-9]+/)
|
338
|
-
emit
|
337
|
+
emit :token_float
|
339
338
|
# An int, or float if exponent is negative
|
340
339
|
elsif accept?(/[eE]-[0-9]+/)
|
341
|
-
emit
|
340
|
+
emit :token_float
|
342
341
|
else
|
343
342
|
accept?(/[eE][+-]?[0-9]+/)
|
344
|
-
emit
|
343
|
+
emit :token_int
|
345
344
|
end
|
346
345
|
elsif accept?("&&")
|
347
|
-
emit(
|
346
|
+
emit(:token_and, "&&")
|
348
347
|
elsif accept?("||")
|
349
|
-
emit(
|
348
|
+
emit(:token_or, "||")
|
350
349
|
elsif accept?("true")
|
351
|
-
emit(
|
350
|
+
emit(:token_true, "true")
|
352
351
|
elsif accept?("false")
|
353
|
-
emit(
|
352
|
+
emit(:token_false, "false")
|
354
353
|
elsif accept?("null")
|
355
|
-
emit(
|
354
|
+
emit(:token_null, "null")
|
356
355
|
elsif accept?(/[a-z][a-z_0-9]*/)
|
357
356
|
unless peek == "("
|
358
357
|
error "unexpected filter selector token"
|
@@ -361,7 +360,7 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
361
360
|
# Function name
|
362
361
|
# Keep track of parentheses for this function call.
|
363
362
|
@paren_stack << 1
|
364
|
-
emit
|
363
|
+
emit :token_function
|
365
364
|
self.next
|
366
365
|
ignore # move past LPAREN
|
367
366
|
else
|
@@ -373,20 +372,20 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
373
372
|
end
|
374
373
|
|
375
374
|
class << self
|
376
|
-
def lex_string_factory(quote, state, token)
|
375
|
+
def lex_string_factory(quote, state, token)
|
377
376
|
proc {
|
378
377
|
# @type self: Lexer
|
379
378
|
ignore # move past opening quote
|
380
379
|
|
381
380
|
loop do
|
382
381
|
c = self.next
|
383
|
-
peeked = peek
|
384
382
|
|
385
383
|
case c
|
386
384
|
when ""
|
387
385
|
error "unclosed string starting at index #{@start}"
|
388
386
|
return nil
|
389
387
|
when "\\"
|
388
|
+
peeked = peek
|
390
389
|
if S_ESCAPES.member?(peeked) || peeked == quote
|
391
390
|
self.next
|
392
391
|
else
|
@@ -406,15 +405,15 @@ module JSONP3 # rubocop:disable Style/Documentation
|
|
406
405
|
end
|
407
406
|
|
408
407
|
define_method(:lex_double_quoted_string_inside_bracketed_segment,
|
409
|
-
lex_string_factory('"', :lex_inside_bracketed_segment,
|
408
|
+
lex_string_factory('"', :lex_inside_bracketed_segment, :token_double_quote_string))
|
410
409
|
|
411
410
|
define_method(:lex_single_quoted_string_inside_bracketed_segment,
|
412
|
-
lex_string_factory("'", :lex_inside_bracketed_segment,
|
411
|
+
lex_string_factory("'", :lex_inside_bracketed_segment, :token_single_quote_string))
|
413
412
|
|
414
413
|
define_method(:lex_double_quoted_string_inside_filter_expression,
|
415
|
-
lex_string_factory('"', :lex_inside_filter,
|
414
|
+
lex_string_factory('"', :lex_inside_filter, :token_double_quote_string))
|
416
415
|
|
417
416
|
define_method(:lex_single_quoted_string_inside_filter_expression,
|
418
|
-
lex_string_factory("'", :lex_inside_filter,
|
417
|
+
lex_string_factory("'", :lex_inside_filter, :token_single_quote_string))
|
419
418
|
end
|
420
419
|
end
|