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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.rubocop.yml +26 -7
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +58 -0
  6. data/README.md +125 -123
  7. data/Rakefile +3 -3
  8. data/certs/jgrp.pem +21 -21
  9. data/lib/json_p3/errors.rb +51 -43
  10. data/lib/json_p3/patch/op.rb +23 -0
  11. data/lib/json_p3/patch/op_add.rb +51 -0
  12. data/lib/json_p3/patch/op_copy.rb +64 -0
  13. data/lib/json_p3/patch/op_move.rb +74 -0
  14. data/lib/json_p3/patch/op_remove.rb +56 -0
  15. data/lib/json_p3/patch/op_replace.rb +54 -0
  16. data/lib/json_p3/patch/op_test.rb +31 -0
  17. data/lib/json_p3/patch.rb +15 -330
  18. data/lib/json_p3/path/environment.rb +113 -0
  19. data/lib/json_p3/path/filter.rb +463 -0
  20. data/lib/json_p3/path/function.rb +12 -0
  21. data/lib/json_p3/path/function_extensions/count.rb +15 -0
  22. data/lib/json_p3/path/function_extensions/length.rb +17 -0
  23. data/lib/json_p3/path/function_extensions/match.rb +62 -0
  24. data/lib/json_p3/path/function_extensions/pattern.rb +42 -0
  25. data/lib/json_p3/path/function_extensions/search.rb +44 -0
  26. data/lib/json_p3/path/function_extensions/value.rb +15 -0
  27. data/lib/json_p3/path/lexer.rb +220 -0
  28. data/lib/json_p3/path/node.rb +48 -0
  29. data/lib/json_p3/path/parser.rb +676 -0
  30. data/lib/json_p3/path/query.rb +74 -0
  31. data/lib/json_p3/path/segment.rb +172 -0
  32. data/lib/json_p3/path/selector.rb +304 -0
  33. data/lib/json_p3/path/serialize.rb +16 -0
  34. data/lib/json_p3/path/unescape.rb +134 -0
  35. data/lib/json_p3/pointer.rb +15 -76
  36. data/lib/json_p3/relative_pointer.rb +69 -0
  37. data/lib/json_p3/version.rb +1 -1
  38. data/lib/json_p3.rb +50 -13
  39. data/sig/json_p3/cache.rbs +21 -0
  40. data/sig/json_p3/errors.rbs +55 -0
  41. data/sig/json_p3/patch.rbs +145 -0
  42. data/sig/json_p3/path/environment.rbs +81 -0
  43. data/sig/json_p3/path/filter.rbs +196 -0
  44. data/sig/json_p3/path/function.rbs +94 -0
  45. data/sig/json_p3/path/lexer.rbs +62 -0
  46. data/sig/json_p3/path/node.rbs +46 -0
  47. data/sig/json_p3/path/parser.rbs +92 -0
  48. data/sig/json_p3/path/query.rbs +47 -0
  49. data/sig/json_p3/path/segment.rbs +54 -0
  50. data/sig/json_p3/path/selector.rbs +100 -0
  51. data/sig/json_p3/path/serialize.rbs +9 -0
  52. data/sig/json_p3/path/unescape.rbs +12 -0
  53. data/sig/json_p3/pointer.rbs +64 -0
  54. data/sig/json_p3/relative_pointer.rbs +30 -0
  55. data/sig/json_p3.rbs +24 -1313
  56. data.tar.gz.sig +0 -0
  57. metadata +66 -46
  58. metadata.gz.sig +0 -0
  59. data/lib/json_p3/environment.rb +0 -111
  60. data/lib/json_p3/filter.rb +0 -459
  61. data/lib/json_p3/function.rb +0 -10
  62. data/lib/json_p3/function_extensions/count.rb +0 -15
  63. data/lib/json_p3/function_extensions/length.rb +0 -17
  64. data/lib/json_p3/function_extensions/match.rb +0 -62
  65. data/lib/json_p3/function_extensions/pattern.rb +0 -39
  66. data/lib/json_p3/function_extensions/search.rb +0 -44
  67. data/lib/json_p3/function_extensions/value.rb +0 -15
  68. data/lib/json_p3/lexer.rb +0 -419
  69. data/lib/json_p3/node.rb +0 -44
  70. data/lib/json_p3/parser.rb +0 -553
  71. data/lib/json_p3/path.rb +0 -72
  72. data/lib/json_p3/segment.rb +0 -158
  73. data/lib/json_p3/selector.rb +0 -306
  74. data/lib/json_p3/serialize.rb +0 -13
  75. data/lib/json_p3/token.rb +0 -36
  76. data/lib/json_p3/unescape.rb +0 -112
@@ -0,0 +1,463 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "function"
4
+ require_relative "serialize"
5
+
6
+ module JSONP3
7
+ # JSONPath query expressions.
8
+ module Path
9
+ # Base class for all filter expression nodes.
10
+ class Expression
11
+ # @dynamic token
12
+ attr_reader :token
13
+
14
+ def initialize(token)
15
+ @token = token
16
+ end
17
+
18
+ # Evaluate the filter expression in the given context.
19
+ def evaluate(_context)
20
+ raise "filter expressions must implement `evaluate(context)`"
21
+ end
22
+ end
23
+
24
+ # An expression that evaluates to true or false.
25
+ class FilterExpression < Expression
26
+ attr_reader :expression
27
+
28
+ def initialize(token, expression)
29
+ super(token)
30
+ @expression = expression
31
+ end
32
+
33
+ def evaluate(context)
34
+ JSONP3::Path.truthy?(@expression.evaluate(context))
35
+ end
36
+
37
+ def to_s
38
+ to_canonical_string(@expression, Precedence::LOWEST)
39
+ end
40
+
41
+ def ==(other)
42
+ self.class == other.class &&
43
+ @expression == other.expression &&
44
+ @token == other.token
45
+ end
46
+
47
+ alias eql? ==
48
+
49
+ def hash
50
+ [@expression, @token].hash
51
+ end
52
+
53
+ private
54
+
55
+ class Precedence
56
+ LOWEST = 1
57
+ LOGICAL_OR = 3
58
+ LOGICAL_AND = 4
59
+ PREFIX = 7
60
+ end
61
+
62
+ def to_canonical_string(expression, parent_precedence)
63
+ if expression.instance_of? LogicalAndExpression
64
+ left = to_canonical_string(expression.left, Precedence::LOGICAL_AND)
65
+ right = to_canonical_string(expression.right, Precedence::LOGICAL_AND)
66
+ expr = "#{left} && #{right}"
67
+ return parent_precedence >= Precedence::LOGICAL_AND ? "(#{expr})" : expr
68
+ end
69
+
70
+ if expression.instance_of? LogicalOrExpression
71
+ left = to_canonical_string(expression.left, Precedence::LOGICAL_OR)
72
+ right = to_canonical_string(expression.right, Precedence::LOGICAL_OR)
73
+ expr = "#{left} || #{right}"
74
+ return parent_precedence >= Precedence::LOGICAL_OR ? "(#{expr})" : expr
75
+ end
76
+
77
+ if expression.instance_of? LogicalNotExpression
78
+ operand = to_canonical_string(expression.expression, Precedence::PREFIX)
79
+ expr = "!#{operand}"
80
+ return parent_precedence > Precedence::PREFIX ? `(#{expr})` : expr
81
+ end
82
+
83
+ expression.to_s
84
+ end
85
+ end
86
+
87
+ # Base class for expression literals.
88
+ class FilterExpressionLiteral < Expression
89
+ attr_reader :value
90
+
91
+ def initialize(token, value)
92
+ super(token)
93
+ @value = value
94
+ end
95
+
96
+ def evaluate(_context)
97
+ @value
98
+ end
99
+
100
+ def to_s
101
+ @value.to_s
102
+ end
103
+
104
+ def ==(other)
105
+ self.class == other.class &&
106
+ @value == other.value &&
107
+ @token == other.token
108
+ end
109
+
110
+ alias eql? ==
111
+
112
+ def hash
113
+ [@value, @token].hash
114
+ end
115
+ end
116
+
117
+ # Literal true or false.
118
+ class BooleanLiteral < FilterExpressionLiteral; end
119
+
120
+ # A double or single quoted string literal.
121
+ class StringLiteral < FilterExpressionLiteral
122
+ def to_s
123
+ JSONP3::Path.canonical_string(@value)
124
+ end
125
+ end
126
+
127
+ # A literal integer.
128
+ class IntegerLiteral < FilterExpressionLiteral; end
129
+
130
+ # A literal float
131
+ class FloatLiteral < FilterExpressionLiteral; end
132
+
133
+ # A literal null
134
+ class NullLiteral < FilterExpressionLiteral
135
+ def to_s
136
+ "null"
137
+ end
138
+ end
139
+
140
+ # An expression prefixed with the logical not operator.
141
+ class LogicalNotExpression < Expression
142
+ attr_reader :expression
143
+
144
+ def initialize(token, expression)
145
+ super(token)
146
+ @expression = expression
147
+ end
148
+
149
+ def evaluate(context)
150
+ !JSONP3::Path.truthy?(@expression.evaluate(context))
151
+ end
152
+
153
+ def to_s
154
+ "!#{@expression}"
155
+ end
156
+
157
+ def ==(other)
158
+ self.class == other.class &&
159
+ @expression == other.expression &&
160
+ @token == other.token
161
+ end
162
+
163
+ alias eql? ==
164
+
165
+ def hash
166
+ [@expression, @token].hash
167
+ end
168
+ end
169
+
170
+ # Base class for expression with a left expression, operator and right expression.
171
+ class InfixExpression < Expression
172
+ attr_reader :left, :right
173
+
174
+ def initialize(token, left, right)
175
+ super(token)
176
+ @left = left
177
+ @right = right
178
+ end
179
+
180
+ def evaluate(_context)
181
+ raise "infix expressions must implement `evaluate(context)`"
182
+ end
183
+
184
+ def to_s
185
+ raise "infix expressions must implement `to_s`"
186
+ end
187
+
188
+ def ==(other)
189
+ self.class == other.class &&
190
+ @left == other.left &&
191
+ @right == other.right &&
192
+ @token == other.token
193
+ end
194
+
195
+ alias eql? ==
196
+
197
+ def hash
198
+ [@left, @right, @token].hash
199
+ end
200
+ end
201
+
202
+ # A logical `&&` expression.
203
+ class LogicalAndExpression < InfixExpression
204
+ def evaluate(context)
205
+ JSONP3::Path.truthy?(@left.evaluate(context)) && JSONP3::Path.truthy?(@right.evaluate(context))
206
+ end
207
+
208
+ def to_s
209
+ "#{@left} && #{@right}"
210
+ end
211
+ end
212
+
213
+ # A logical `||` expression.
214
+ class LogicalOrExpression < InfixExpression
215
+ def evaluate(context)
216
+ JSONP3::Path.truthy?(@left.evaluate(context)) || JSONP3::Path.truthy?(@right.evaluate(context))
217
+ end
218
+
219
+ def to_s
220
+ "#{@left} || #{@right}"
221
+ end
222
+ end
223
+
224
+ # An `==` expression.
225
+ class EqExpression < InfixExpression
226
+ def evaluate(context)
227
+ JSONP3::Path.eq?(@left.evaluate(context), @right.evaluate(context))
228
+ end
229
+
230
+ def to_s
231
+ "#{@left} == #{@right}"
232
+ end
233
+ end
234
+
235
+ # A `!=` expression.
236
+ class NeExpression < InfixExpression
237
+ def evaluate(context)
238
+ !JSONP3::Path.eq?(@left.evaluate(context), @right.evaluate(context))
239
+ end
240
+
241
+ def to_s
242
+ "#{@left} != #{@right}"
243
+ end
244
+ end
245
+
246
+ # A `<=` expression.
247
+ class LeExpression < InfixExpression
248
+ def evaluate(context)
249
+ left = @left.evaluate(context)
250
+ right = @right.evaluate(context)
251
+ JSONP3::Path.eq?(left, right) || JSONP3::Path.lt?(left, right)
252
+ end
253
+
254
+ def to_s
255
+ "#{@left} <= #{@right}"
256
+ end
257
+ end
258
+
259
+ # A `>=` expression.
260
+ class GeExpression < InfixExpression
261
+ def evaluate(context)
262
+ left = @left.evaluate(context)
263
+ right = @right.evaluate(context)
264
+ JSONP3::Path.eq?(left, right) || JSONP3::Path.lt?(right, left)
265
+ end
266
+
267
+ def to_s
268
+ "#{@left} >= #{@right}"
269
+ end
270
+ end
271
+
272
+ # A `<` expression.
273
+ class LtExpression < InfixExpression
274
+ def evaluate(context)
275
+ JSONP3::Path.lt?(@left.evaluate(context), @right.evaluate(context))
276
+ end
277
+
278
+ def to_s
279
+ "#{@left} < #{@right}"
280
+ end
281
+ end
282
+
283
+ # A `>` expression.
284
+ class GtExpression < InfixExpression
285
+ def evaluate(context)
286
+ JSONP3::Path.lt?(@right.evaluate(context), @left.evaluate(context))
287
+ end
288
+
289
+ def to_s
290
+ "#{@left} > #{@right}"
291
+ end
292
+ end
293
+
294
+ # Base class for all embedded filter queries
295
+ class QueryExpression < Expression
296
+ attr_reader :query
297
+
298
+ def initialize(token, query)
299
+ super(token)
300
+ @query = query
301
+ end
302
+
303
+ def evaluate(_context)
304
+ raise "query expressions must implement `evaluate(context)`"
305
+ end
306
+
307
+ def to_s
308
+ raise "query expressions must implement `to_s`"
309
+ end
310
+
311
+ def ==(other)
312
+ self.class == other.class &&
313
+ @query == other.query &&
314
+ @token == other.token
315
+ end
316
+
317
+ alias eql? ==
318
+
319
+ def hash
320
+ [@query, @token].hash
321
+ end
322
+ end
323
+
324
+ # An embedded query starting at the current node.
325
+ class RelativeQueryExpression < QueryExpression
326
+ def evaluate(context)
327
+ unless context.current.is_a?(Array) || context.current.is_a?(Hash)
328
+ return @query.empty? ? context.current : NodeList.new
329
+ end
330
+
331
+ @query.find(context.current)
332
+ end
333
+
334
+ def to_s
335
+ "@#{@query.to_s[1..]}"
336
+ end
337
+ end
338
+
339
+ # An embedded query starting at the root node.
340
+ class AbsoluteQueryExpression < QueryExpression
341
+ def evaluate(context)
342
+ @query.find(context.root)
343
+ end
344
+
345
+ def to_s
346
+ @query.to_s
347
+ end
348
+ end
349
+
350
+ # A filter function call.
351
+ class FunctionExpression < Expression
352
+ attr_reader :name, :args, :func
353
+
354
+ # @param name [String]
355
+ # @param func [FunctionExtension]
356
+ # @param args [Array<Expression>]
357
+ def initialize(token, name, func, args)
358
+ super(token)
359
+ @name = name
360
+ @func = func
361
+ @args = args
362
+ end
363
+
364
+ def evaluate(context)
365
+ args = @args.map { |arg| arg.evaluate(context) }
366
+ unpacked_args = unpack_node_lists(@func, args)
367
+ @func.call(*unpacked_args)
368
+ rescue KeyError
369
+ :nothing
370
+ end
371
+
372
+ def to_s
373
+ args = @args.join(", ")
374
+ "#{@name}(#{args})"
375
+ end
376
+
377
+ def ==(other)
378
+ self.class == other.class &&
379
+ @name == other.name &&
380
+ @args == other.args &&
381
+ @token == other.token
382
+ end
383
+
384
+ alias eql? ==
385
+
386
+ def hash
387
+ [@name, @args, @token].hash
388
+ end
389
+
390
+ private
391
+
392
+ # @param func [Proc]
393
+ # @param args [Array<Object>]
394
+ # @return [Array<Object>]
395
+ def unpack_node_lists(func, args)
396
+ unpacked_args = [] # : Array[Object]
397
+ args.each_with_index do |arg, i|
398
+ unless arg.is_a?(NodeList) && func.class::ARG_TYPES[i] != :nodes_expression
399
+ unpacked_args << arg
400
+ next
401
+ end
402
+
403
+ unpacked_args << case arg.length
404
+ when 0
405
+ :nothing
406
+ when 1
407
+ arg.first.value
408
+ else
409
+ arg
410
+ end
411
+ end
412
+ unpacked_args
413
+ end
414
+ end
415
+
416
+ def self.truthy?(obj)
417
+ return !obj.empty? if obj.is_a?(NodeList)
418
+ return false if obj == :nothing
419
+
420
+ obj != false
421
+ end
422
+
423
+ def self.eq?(left, right)
424
+ left = left.first.value if left.is_a?(NodeList) && left.length == 1
425
+ right = right.first.value if right.is_a?(NodeList) && right.length == 1
426
+
427
+ right, left = left, right if right.is_a?(NodeList)
428
+
429
+ if left.is_a? NodeList
430
+ return left == right if right.is_a? NodeList
431
+ return right == :nothing if left.empty?
432
+ return left.first == right if left.length == 1
433
+
434
+ return false
435
+ end
436
+
437
+ return true if left == :nothing && right == :nothing
438
+
439
+ left == right
440
+ end
441
+
442
+ def self.lt?(left, right)
443
+ left = left.first.value if left.is_a?(NodeList) && left.length == 1
444
+ right = right.first.value if right.is_a?(NodeList) && right.length == 1
445
+ return left < right if left.is_a?(String) && right.is_a?(String)
446
+ return left < right if (left.is_a?(Integer) || left.is_a?(Float)) &&
447
+ (right.is_a?(Integer) || right.is_a?(Float))
448
+
449
+ false
450
+ end
451
+
452
+ # Contextual information and data used for evaluating a filter expression.
453
+ class FilterContext
454
+ attr_reader :env, :current, :root
455
+
456
+ def initialize(env, current, root)
457
+ @env = env
458
+ @current = current
459
+ @root = root
460
+ end
461
+ end
462
+ end
463
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONP3
4
+ module Path
5
+ # Base class for all filter functions.
6
+ class FunctionExtension
7
+ def call(*_args, **_kwargs)
8
+ raise "function extensions must implement `call()`"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONP3
4
+ module Path
5
+ # The standard `count` function.
6
+ class Count < FunctionExtension
7
+ ARG_TYPES = [:nodes_expression].freeze
8
+ RETURN_TYPE = :value_expression
9
+
10
+ def call(node_list)
11
+ node_list.length
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONP3
4
+ module Path
5
+ # The standard `length` function.
6
+ class Length < FunctionExtension
7
+ ARG_TYPES = [:value_expression].freeze
8
+ RETURN_TYPE = :value_expression
9
+
10
+ def call(obj)
11
+ return :nothing unless obj.is_a?(Array) || obj.is_a?(Hash) || obj.is_a?(String)
12
+
13
+ obj.length
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "pattern"
4
+
5
+ module JSONP3
6
+ module Path
7
+ # The standard `match` function.
8
+ class Match < FunctionExtension
9
+ ARG_TYPES = %i[value_expression value_expression].freeze
10
+ RETURN_TYPE = :logical_expression
11
+
12
+ # @param cache_size [Integer] the maximum size of the regexp cache. Set it to
13
+ # zero or negative to disable the cache.
14
+ # @param raise_errors [Boolean] if _false_ (the default), return _false_ when this
15
+ # function causes a RegexpError instead of raising the exception.
16
+ def initialize(cache_size = 128, raise_errors: false)
17
+ super()
18
+ @cache_size = cache_size
19
+ @raise_errors = raise_errors
20
+ @cache = LRUCache.new(cache_size)
21
+ end
22
+
23
+ # @param value [String]
24
+ # @param pattern [String]
25
+ # @return Boolean
26
+ def call(value, pattern)
27
+ return false unless pattern.is_a?(String) && value.is_a?(String)
28
+
29
+ if @cache_size.positive?
30
+ re = @cache[pattern] || Regexp.new(full_match(pattern))
31
+ else
32
+ re = Regexp.new(full_match(pattern))
33
+ @cache[pattern] = re
34
+ end
35
+
36
+ re.match?(value)
37
+ rescue RegexpError
38
+ raise if @raise_errors
39
+
40
+ false
41
+ end
42
+
43
+ private
44
+
45
+ def full_match(pattern)
46
+ parts = [] # : Array[String]
47
+ explicit_caret = pattern.start_with?("^")
48
+ explicit_dollar = pattern.end_with?("$")
49
+
50
+ # Replace '^' with '\A' and '$' with '\z'
51
+ pattern = pattern.sub("^", "\\A") if explicit_caret
52
+ pattern = "#{pattern[..-1]}\\z" if explicit_dollar
53
+
54
+ # Wrap with '\A' and '\z' if they are not already part of the pattern.
55
+ parts << "\\A(?:" if !explicit_caret && !explicit_dollar
56
+ parts << JSONP3::Path.map_iregexp(pattern)
57
+ parts << ")\\z" if !explicit_caret && !explicit_dollar
58
+ parts.join
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONP3
4
+ # JSONPath query expressions.
5
+ module Path
6
+ # Map I-Regexp pattern to Ruby regex pattern.
7
+ # @param pattern [String]
8
+ # @return [String]
9
+ def self.map_iregexp(pattern)
10
+ escaped = false
11
+ char_class = false
12
+ mapped = String.new(encoding: "UTF-8")
13
+
14
+ pattern.each_char do |c|
15
+ if escaped
16
+ mapped << c
17
+ escaped = false
18
+ next
19
+ end
20
+
21
+ case c
22
+ when "."
23
+ # mapped << (char_class ? c : "(?:(?![\\r\\n])\\P{Cs}|\\p{Cs}\\p{Cs})")
24
+ mapped << (char_class ? c : "[^\\n\\r]")
25
+ when "\\"
26
+ escaped = true
27
+ mapped << "\\"
28
+ when "["
29
+ char_class = true
30
+ mapped << "["
31
+ when "]"
32
+ char_class = false
33
+ mapped << "]"
34
+ else
35
+ mapped << c
36
+ end
37
+ end
38
+
39
+ mapped
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "pattern"
4
+
5
+ module JSONP3
6
+ module Path
7
+ # The standard `search` function.
8
+ class Search < FunctionExtension
9
+ ARG_TYPES = %i[value_expression value_expression].freeze
10
+ RETURN_TYPE = :logical_expression
11
+
12
+ # @param cache_size [Integer] the maximum size of the regexp cache. Set it to
13
+ # zero or negative to disable the cache.
14
+ # @param raise_errors [Boolean] if _false_ (the default), return _false_ when this
15
+ # function causes a RegexpError instead of raising the exception.
16
+ def initialize(cache_size = 128, raise_errors: false)
17
+ super()
18
+ @cache_size = cache_size
19
+ @raise_errors = raise_errors
20
+ @cache = LRUCache.new(cache_size)
21
+ end
22
+
23
+ # @param value [String]
24
+ # @param pattern [String]
25
+ # @return Boolean
26
+ def call(value, pattern)
27
+ return false unless pattern.is_a?(String) && value.is_a?(String)
28
+
29
+ if @cache_size.positive?
30
+ re = @cache[pattern] || Regexp.new(JSONP3::Path.map_iregexp(pattern))
31
+ else
32
+ re = Regexp.new(JSONP3::Path.map_iregexp(pattern))
33
+ @cache[pattern] = re
34
+ end
35
+
36
+ re.match?(value)
37
+ rescue RegexpError
38
+ raise if @raise_errors
39
+
40
+ false
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONP3
4
+ module Path
5
+ # The standard `value` function.
6
+ class Value < FunctionExtension
7
+ ARG_TYPES = [:nodes_expression].freeze
8
+ RETURN_TYPE = :value_expression
9
+
10
+ def call(node_list)
11
+ node_list.length == 1 ? node_list.first.value : :nothing
12
+ end
13
+ end
14
+ end
15
+ end