json_p3 0.2.1 → 0.3.0
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 +5 -0
- data/README.md +142 -16
- 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 +441 -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 +4 -1
- metadata +5 -2
- 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
|