janeway-jsonpath 0.2.0 → 0.3.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +109 -38
  3. data/lib/janeway/ast/array_slice_selector.rb +9 -7
  4. data/lib/janeway/ast/binary_operator.rb +25 -23
  5. data/lib/janeway/ast/current_node.rb +8 -5
  6. data/lib/janeway/ast/descendant_segment.rb +1 -1
  7. data/lib/janeway/ast/expression.rb +11 -1
  8. data/lib/janeway/ast/filter_selector.rb +1 -1
  9. data/lib/janeway/ast/helpers.rb +1 -0
  10. data/lib/janeway/ast/index_selector.rb +2 -3
  11. data/lib/janeway/ast/name_selector.rb +2 -2
  12. data/lib/janeway/ast/null.rb +1 -0
  13. data/lib/janeway/ast/query.rb +42 -1
  14. data/lib/janeway/ast/root_node.rb +8 -5
  15. data/lib/janeway/ast/string_type.rb +2 -0
  16. data/lib/janeway/ast/unary_operator.rb +2 -2
  17. data/lib/janeway/ast/wildcard_selector.rb +1 -0
  18. data/lib/janeway/error.rb +1 -1
  19. data/lib/janeway/functions/count.rb +2 -4
  20. data/lib/janeway/functions/length.rb +2 -3
  21. data/lib/janeway/functions/match.rb +3 -9
  22. data/lib/janeway/functions/search.rb +4 -9
  23. data/lib/janeway/functions/value.rb +2 -3
  24. data/lib/janeway/interpreter.rb +29 -587
  25. data/lib/janeway/interpreters/array_slice_selector_interpreter.rb +44 -0
  26. data/lib/janeway/interpreters/base.rb +43 -0
  27. data/lib/janeway/interpreters/binary_operator_interpreter.rb +163 -0
  28. data/lib/janeway/interpreters/child_segment_interpreter.rb +44 -0
  29. data/lib/janeway/interpreters/current_node_interpreter.rb +33 -0
  30. data/lib/janeway/interpreters/descendant_segment_interpreter.rb +40 -0
  31. data/lib/janeway/interpreters/filter_selector_interpreter.rb +63 -0
  32. data/lib/janeway/interpreters/function_interpreter.rb +99 -0
  33. data/lib/janeway/interpreters/index_selector_interpreter.rb +39 -0
  34. data/lib/janeway/interpreters/name_selector_interpreter.rb +30 -0
  35. data/lib/janeway/interpreters/root_node_interpreter.rb +23 -0
  36. data/lib/janeway/interpreters/tree_constructor.rb +39 -0
  37. data/lib/janeway/interpreters/unary_operator_interpreter.rb +32 -0
  38. data/lib/janeway/interpreters/wildcard_selector_interpreter.rb +38 -0
  39. data/lib/janeway/interpreters/yielder.rb +34 -0
  40. data/lib/janeway/lexer.rb +12 -16
  41. data/lib/janeway/parser.rb +51 -90
  42. data/lib/janeway/token.rb +1 -0
  43. data/lib/janeway/version.rb +1 -1
  44. data/lib/janeway.rb +25 -5
  45. metadata +17 -3
  46. data/lib/janeway/ast/identifier.rb +0 -35
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Janeway
4
+ # Parses jsonpath function calls, and defines the code for jsonpath builtin functions
4
5
  module Functions
5
6
  # 2.4.7. search() Function Extension
6
7
  # Parameters:
@@ -35,22 +36,16 @@ module Janeway
35
36
  # Read first parameter
36
37
  parameters = []
37
38
  parameters << parse_function_parameter
39
+ raise Error, 'Insufficient parameters for search() function call' unless current.type == :union
38
40
 
39
- # Consume comma
40
- if current.type == :union
41
- consume # ,
42
- else
43
- raise Error, 'Insufficient parameters for search() function call'
44
- end
41
+ consume # ,
45
42
 
46
43
  # Read second parameter (the regexp)
47
44
  # This could be a string, in which case it is available now.
48
45
  # Otherwise it is an expression that takes the regexp from the input document,
49
46
  # and the iregexp will not be available until interpretation.
50
47
  parameters << parse_function_parameter
51
- unless current.type == :group_end
52
- raise Error, 'Too many parameters for match() function call'
53
- end
48
+ raise Error, 'Too many parameters for match() function call' unless current.type == :group_end
54
49
 
55
50
  AST::Function.new('search', parameters) do |str, str_iregexp|
56
51
  if str.is_a?(String) && str_iregexp.is_a?(String)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Janeway
4
+ # Parses jsonpath function calls, and defines the code for jsonpath builtin functions
4
5
  module Functions
5
6
  # 2.4.8. value() Function Extension
6
7
 
@@ -33,9 +34,7 @@ module Janeway
33
34
 
34
35
  # Read parameter
35
36
  parameters = [parse_function_parameter]
36
- unless current.type == :group_end
37
- raise Error, 'Too many parameters for value() function call'
38
- end
37
+ raise Error, 'Too many parameters for value() function call' unless current.type == :group_end
39
38
 
40
39
  AST::Function.new('value', parameters) do |nodes|
41
40
  if nodes.is_a?(Array) && nodes.size == 1
@@ -1,25 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'parser'
4
+ require_relative 'interpreters/tree_constructor'
5
+ Dir.children("#{__dir__}/interpreters").each { |path| require_relative "interpreters/#{path}" }
4
6
 
5
7
  module Janeway
6
- # Tree-walk interpreter to apply the operations from the abstract syntax tree to the input
8
+ # Tree-walk interpreter to apply the operations from the abstract syntax tree to the input.
9
+ #
10
+ # This is not intended to be thread-safe, so create this inside a thread as needed.
11
+ # It should be created for a single query and then discarded.
7
12
  class Interpreter
8
- attr_reader :jsonpath, :output, :env, :call_stack
9
-
10
- class Error < Janeway::Error; end
11
-
12
- # Specify the parameter types that built-in JsonPath functions require
13
- FUNCTION_PARAMETER_TYPES = {
14
- length: [:value_type],
15
- count: [:nodes_type],
16
- match: %i[value_type value_type],
17
- search: %i[value_type value_type],
18
- value: [:nodes_type],
19
- }.freeze
20
-
21
- # Functions may accept or return this special value
22
- NOTHING = :nothing
13
+ attr_reader :jsonpath, :output
23
14
 
24
15
  # Interpret a query on the given input, return result
25
16
  # @param input [Hash, Array]
@@ -37,6 +28,7 @@ module Janeway
37
28
  @query = query
38
29
  @jsonpath = query.jsonpath
39
30
  @input = nil
31
+ @pipeline = query_to_interpreter_pipeline(@query)
40
32
  end
41
33
 
42
34
  # @param input [Array, Hash] object to be searched
@@ -44,585 +36,35 @@ module Janeway
44
36
  def interpret(input)
45
37
  @input = input
46
38
  unless @input.is_a?(Hash) || @input.is_a?(Array)
47
- return [] # can't query on any other types
48
- end
49
-
50
- interpret_node(@query.root, nil)
51
- end
52
-
53
- private
54
-
55
- # Interpret AST::RootNode, which refers to the complete original input.
56
- #
57
- # Ignore the given _input from the current interpretation state.
58
- # RootNode starts from the top level regardless of current state.
59
- # @return [Array]
60
- def interpret_root_node(node, _input)
61
- case node&.value
62
- when AST::ChildSegment then interpret_child_segment(node.value, @input)
63
- when AST::DescendantSegment then interpret_descendant_segment(node.value, @input)
64
- when AST::Selector then interpret_selector(node.value, @input)
65
- when nil then [@input]
66
- else
67
- raise err("don't know how to interpret #{node.value.class}")
68
- end
69
- end
70
-
71
- # Interpret a list of 1 or more selectors, seperated by the union operator.
72
- #
73
- # @param child_segment [AST::ChildSegment]
74
- # @param input [Array, Hash]
75
- # @return [Array]
76
- def interpret_child_segment(child_segment, input)
77
- # For each node in the input nodelist, the resulting nodelist of a child
78
- # segment is the concatenation of the nodelists from each of its
79
- # selectors in the order that the selectors appear in the list. Note: Any
80
- # node matched by more than one selector is kept as many times in the nodelist.
81
- # combine results from all selectors
82
- results = nil
83
- if child_segment.size == 1
84
- selector = child_segment.first
85
- results = send(:"interpret_#{selector.type}", selector, input)
86
- else
87
- results = []
88
- child_segment.each do |selector|
89
- result = send(:"interpret_#{selector.type}", selector, input)
90
- results.concat(result)
91
- end
92
- end
93
-
94
- # Send result to the next node in the AST, if any
95
- child = child_segment.next
96
- unless child
97
- return child_segment.size == 1 ? [results] : results
98
- end
99
-
100
- send(:"interpret_#{child.type}", child, results)
101
- end
102
-
103
- # Filter the input by returning the key that has the given name.
104
- #
105
- # Must differentiate between a null value of a key that exists (nil)
106
- # and a key that does not exist ([])
107
- #
108
- # @param selector [NameSelector]
109
- # @return [Array]
110
- def interpret_name_selector(selector, input)
111
- return [] unless input.is_a?(Hash) && input.key?(selector.name)
112
-
113
- result = input[selector.name]
114
-
115
- # early exit, no point continuing the chain with no results
116
-
117
- return [result] unless selector.next
118
-
119
- # Interpret child using output of this name selector, and return result
120
- child = selector.next
121
- send(:"interpret_#{child.type}", child, result)
122
- end
123
-
124
- # Filter the input by returning the array element with the given index.
125
- # Return empty list if input is not an array, or if array does not contain index.
126
- #
127
- # Output is an array because all selectors must return node lists, even if
128
- # they only select a single element.
129
- #
130
- # @param selector [IndexSelector]
131
- # @param input [Array]
132
- # @return [Array]
133
- def interpret_index_selector(selector, input)
134
- return [] unless input.is_a?(Array)
135
-
136
- result = input.fetch(selector.value) # raises IndexError if no such index
137
-
138
- # Interpret child using output of this name selector, and return result
139
- child = selector.next
140
- return [result] unless child
141
-
142
- send(:"interpret_#{child.type}", child, result)
143
- rescue IndexError
144
- [] # returns empty array if no such index
145
- end
146
-
147
- # Return values from the input.
148
- # For array, return the array.
149
- # For Hash, return hash values.
150
- # For anything else, return empty list.
151
- #
152
- # @param selector [AST::WildcardSelector]
153
- # @param input [Object] usually Array or Hash
154
- # @return [Array] matching values
155
- def interpret_wildcard_selector(selector, input)
156
- values =
157
- case input
158
- when Array then input
159
- when Hash then input.values
160
- else []
161
- end
162
-
163
- return values if values.empty? # early exit, no need for further processing on empty list
164
- return values unless selector.next
165
-
166
- # Apply child selector to each node in the output node list
167
- child = selector.next
168
- results = []
169
- values.each do |value|
170
- results.concat send(:"interpret_#{child.type}", child, value)
171
- end
172
- results
173
- end
174
-
175
- # Filter the input by applying the array slice selector.
176
- # Returns at most 1 element.
177
- #
178
- # @param selector [ArraySliceSelector]
179
- # @param input [Array]
180
- # @return [Array]
181
- def interpret_array_slice_selector(selector, input)
182
- return [] unless input.is_a?(Array)
183
- return [] if selector&.step&.zero? # IETF: When step is 0, no elements are selected.
184
-
185
- # Calculate the upper and lower indices of the target range
186
- lower = selector.lower_index(input.size)
187
- upper = selector.upper_index(input.size)
188
-
189
- # Collect values from target indices. Omit the value from the final index.
190
- results =
191
- if selector.step.positive?
192
- lower.step(to: upper - 1, by: selector.step).map { input[_1] }
193
- else
194
- upper.step(to: lower + 1, by: selector.step).map { input[_1] }
195
- end
196
-
197
- # Interpret child using output of this name selector, and return result
198
- child = selector.next
199
- return results unless child
200
-
201
- # Apply child selector to each node in the output node list
202
- node_list = results
203
- results = []
204
- node_list.each do |node|
205
- results.concat send(:"interpret_#{child.type}", child, node)
206
- end
207
- results
208
- end
209
-
210
- # Return the set of values from the input for which the filter is true.
211
- # For array, acts on array values.
212
- # For hash, acts on hash values.
213
- # Otherwise returns empty node list.
214
- #
215
- # @param selector [AST::FilterSelector]
216
- # @param input [Object]
217
- # @return [Array] list of matched values, or nil if no matched values
218
- def interpret_filter_selector(selector, input)
219
- values =
220
- case input
221
- when Array then input
222
- when Hash then input.values
223
- else return [] # early exit
224
- end
225
-
226
- results = []
227
- values.each do |value|
228
- # Run filter and interpret result
229
- result = interpret_node(selector.value, value)
230
-
231
- case result
232
- when TrueClass then results << value # comparison test - pass
233
- when FalseClass then nil # comparison test - fail
234
- when Array then results << value unless result.empty? # existence test - node list
235
- else
236
- results << value # existence test. Null values here == success.
237
- end
238
- end
239
-
240
- child = selector.next
241
- return results unless child
242
-
243
- # Apply child selector to each node in the output node list
244
- node_list = results
245
- results = []
246
- node_list.each do |node|
247
- results.concat send(:"interpret_#{child.type}", child, node)
248
- end
249
- results
250
- end
251
-
252
- # Combine results from selectors into a single list.
253
- # Duplicate elements are allowed.
254
- #
255
- # @param lhs [Array] left hand side
256
- # @param rhs [Array] right hand side
257
- # @return [Array]
258
- def interpret_union(lhs, rhs)
259
- if lhs.is_a?(Array) && rhs.is_a?(Array)
260
- # can't use ruby's array union operator "|" here because it eliminates duplicates
261
- lhs.concat rhs
262
- else
263
- [lhs, rhs]
264
- end
265
- end
266
-
267
- # Find all descendants of the current input that match the selector in the DescendantSegment
268
- #
269
- # @param descendant_segment [DescendantSegment]
270
- # @param input [Object]
271
- # @return [Array<AST::Expression>] node list
272
- def interpret_descendant_segment(descendant_segment, input)
273
- visit(input) { |node| interpret_node(descendant_segment.next, node) }
274
- end
275
-
276
- # Visit all descendants of `root`.
277
- # Return results of applying `action` on each.
278
- def visit(root, &action)
279
- results = [yield(root)]
280
-
281
- case root
282
- when Array
283
- results.concat(root.map { |elt| visit(elt, &action) })
284
- when Hash
285
- results.concat(root.values.map { |value| visit(value, &action) })
286
- else
287
- root
288
- end
289
-
290
- results.flatten(1).compact
291
- end
292
-
293
- def interpret_nodes(nodes)
294
- last_value = nil
295
-
296
- nodes.each do |node|
297
- last_value = interpret_node(node, last_value)
298
- end
299
-
300
- last_value
301
- end
302
-
303
- # Interpret an AST node.
304
- # Return the result, which may be a node list or basic type.
305
- # @return [Object]
306
- def interpret_node(node, input)
307
- interpreter_method = "interpret_#{node.type}"
308
- send(interpreter_method, node, input)
309
- end
310
-
311
- # Interpret a node and extract its value, in preparation for using the node
312
- # in a comparison operator.
313
- # Basic literals such as AST::Number and AST::StringType evaluate to a number or string,
314
- # but selectors and segments evaluate to a node list. Extract the value (if any)
315
- # from the node list, or return basic type.
316
- #
317
- # @param node [AST::Expression]
318
- # @param input [Object]
319
- def interpret_node_as_value(node, input)
320
- # nodes must be singular queries or literals
321
- case node
322
- when AST::CurrentNode, AST::RootNode
323
- raise err("Expression #{node} does not produce a singular value for comparison") unless node.singular_query?
324
- when AST::Number, AST::StringType, AST::Null, AST::Function, AST::Boolean then nil
325
- else
326
- raise err("Invalid expression for comparison: #{node}")
327
- end
328
-
329
- result = interpret_node(node, input)
330
-
331
- # Return basic types (ie. from AST::Number, AST::StringType)
332
- return result unless result.is_a?(Array)
333
-
334
- # Node lists are returned by Selectors, ChildSegment, DescendantSegment.
335
- #
336
- # This is for a comparison operator.
337
- # An empty node list represents a missing element.
338
- # This must not match any literal value (including null /nil) but must match another missing value.
339
- return result if result.empty?
340
-
341
- # Return the only node in the node list
342
- raise err('node list contains multiple elements but this is a comparison') unless result.size == 1
343
-
344
- result.first
345
- end
346
-
347
- # Given the result of evaluating an expression which is presumed to be a node list,
348
- # convert the result into a basic ruby value (ie. Integer, String, nil)
349
- # @param node_list [Array]
350
- # @return [String, Integer, Float, nil]
351
- def node_result_to_value(node_list)
352
- return nil if node_list.empty?
353
-
354
- return node_list.first if node_list.size == 1
355
-
356
- raise err("don't know how to handle node list with size > 1: #{node_list.inspect}")
357
- end
358
-
359
- # Evaluate a selector and return the result
360
- # @return [Array] node list containing evaluation result
361
- def interpret_selector(selector, input)
362
- case selector
363
- when AST::NameSelector then interpret_name_selector(selector, input)
364
- when AST::WildcardSelector then interpret_wildcard_selector(selector, input)
365
- when AST::IndexSelector then interpret_index_selector(selector, input)
366
- when AST::ArraySliceSelector then interpret_array_slice_selector(selector, input)
367
- when AST::FilterSelector then interpret_filter_selector(selector, input)
368
- else
369
- raise err("Not a selector: #{selector.inspect}")
39
+ return [] # can't query on any other types, but need to check because a string is also valid json
370
40
  end
371
- end
372
-
373
- # Apply selector to each value in the current node and return result.
374
- #
375
- # The result is an Array containing all results of evaluating the CurrentNode's selector (if any.)
376
- #
377
- # If the selector extracted values from nodes such as strings, numbers or nil/null,
378
- # these will be included in the array.
379
- # If the selector did not match any node, the array may be empty.
380
- # If there was no selector, then the current input node is returned in the array.
381
- #
382
- # @param current_node [CurrentNode] current node identifer
383
- # @param input [Hash, Array]
384
- # @return [Array] Node List containing all results from evaluating this node's selectors.
385
- def interpret_current_node(current_node, input)
386
- next_expr = current_node.value
387
- # All of these return a node list
388
- case next_expr
389
- when AST::NameSelector then interpret_name_selector(next_expr, input)
390
- when AST::WildcardSelector then interpret_wildcard_selector(next_expr, input)
391
- when AST::IndexSelector then interpret_index_selector(next_expr, input)
392
- when AST::ArraySliceSelector then interpret_array_slice_selector(next_expr, input)
393
- when AST::FilterSelector then interpret_filter_selector(next_expr, input)
394
- when AST::ChildSegment then interpret_child_segment(next_expr, input)
395
- when AST::DescendantSegment then interpret_descendant_segment(next_expr, input)
396
- when NilClass then input
397
- else
398
- raise err("don't know how to interpret @#{next_expr}")
399
- end
400
- end
401
-
402
- def interpret_identifier(identifier, _input)
403
- if env.key?(identifier.name)
404
- # Global variable.
405
- env[identifier.name]
406
- elsif call_stack.length.positive? && call_stack.last.env.key?(identifier.name)
407
- # Local variable.
408
- call_stack.last.env[identifier.name]
409
- else
410
- # Undefined variable.
411
- raise err("Undefined identifier: #{identifier.name}")
412
- end
413
- end
414
-
415
- # The binary operators are all comparison operators that test equality.
416
- #
417
- # * boolean values specified in the query
418
- # * JSONPath expressions which must be evaluated
419
- #
420
- # After a JSONPath expression is evaluated, it results in a node list.
421
- # This may contain literal values or nodes, whose value must be extracted before comparison.
422
- #
423
- # @return [Boolean]
424
- def interpret_binary_operator(binary_op, input)
425
- case binary_op.operator
426
- when :and, :or
427
- # handle node list for existence check
428
- lhs = interpret_node(binary_op.left, input)
429
- rhs = interpret_node(binary_op.right, input)
430
- when :equal, :not_equal, :less_than, :greater_than, :less_than_or_equal, :greater_than_or_equal
431
- # handle node values for comparison check
432
- lhs = interpret_node_as_value(binary_op.left, input)
433
- rhs = interpret_node_as_value(binary_op.right, input)
434
- else
435
- raise err("Don't know how to handle binary operator #{binary_op.inspect}")
436
- end
437
- send(:"interpret_#{binary_op.operator}", lhs, rhs)
438
- end
439
-
440
- def interpret_equal(lhs, rhs)
441
- # When either side of a comparison results in an empty nodelist or the
442
- # special result Nothing (see Section 2.4.1):
443
- # A comparison using the operator == yields true if and only if the other
444
- # side also results in an empty nodelist or the special result Nothing.
445
- lhs = NOTHING if lhs == []
446
- rhs = NOTHING if rhs == []
447
-
448
- lhs == rhs
449
- end
450
41
 
451
- def interpret_not_equal(lhs, rhs)
452
- !interpret_equal(lhs, rhs)
42
+ @pipeline.first.interpret(nil, input)
43
+ rescue StandardError => e
44
+ # Error during interpretation. Convert it to a Janeway::Error and include the query in the message
45
+ error = err(e.message)
46
+ error.set_backtrace e.backtrace
47
+ raise error
453
48
  end
454
49
 
455
- # Interpret && operator
456
- # May receive node lists, in which case empty node list == false
457
- def interpret_and(lhs, rhs)
458
- # non-empty array is already truthy, so that works properly without conversion
459
- lhs = false if lhs == []
460
- rhs = false if rhs == []
461
- lhs && rhs
50
+ # Append an interpreter onto the end of the pipeline
51
+ # @param [Interpreters::Base]
52
+ def push(node)
53
+ @pipeline.last.next = node
462
54
  end
463
55
 
464
- # Interpret || operator
465
- # May receive node lists, in which case empty node list == false
466
- def interpret_or(lhs, rhs)
467
- # non-empty array is already truthy, so that works properly without conversion
468
- lhs = false if lhs.is_a?(Array) && lhs.empty?
469
- rhs = false if rhs.is_a?(Array) && rhs.empty?
470
- lhs || rhs
471
- end
472
-
473
- def interpret_less_than(lhs, rhs)
474
- lhs < rhs
475
- rescue StandardError
476
- false
477
- end
478
-
479
- def interpret_less_than_or_equal(lhs, rhs)
480
- # Must be done in 2 comparisons, because the equality comparison is
481
- # valid for many types that do not support the < operator.
482
- return true if interpret_equal(lhs, rhs)
483
-
484
- lhs < rhs
485
- rescue StandardError
486
- # This catches type mismatches like { a: 1 } <= 1
487
- # IETF says that both < and > return false for such comparisons
488
- false
489
- end
490
-
491
- def interpret_greater_than(lhs, rhs)
492
- lhs > rhs
493
- rescue StandardError
494
- false
495
- end
496
-
497
- def interpret_greater_than_or_equal(lhs, rhs)
498
- return true if interpret_equal(lhs, rhs)
499
-
500
- lhs > rhs
501
- rescue StandardError
502
- false
503
- end
504
-
505
- # @param boolean [AST::Boolean]
506
- # @return [Boolean]
507
- def interpret_boolean(boolean, _input)
508
- boolean.value
509
- end
510
-
511
- # @param number [AST::Number]
512
- # @return [Integer, Float]
513
- def interpret_number(number, _input)
514
- number.value
515
- end
516
-
517
- # @param string [AST::StringType]
518
- # @return [String]
519
- def interpret_string_type(string, _input)
520
- string.value
521
- end
522
-
523
- # @param _null [AST::Null] ignored
524
- # @param _input [Object] ignored
525
- def interpret_null(_null, _input)
526
- nil
527
- end
528
-
529
- # @param op [AST::UnaryOperator]
530
- # @param input [Object]
531
- def interpret_unary_operator(op, input)
532
- # The only other unary operator, "-", is consumed in the parsing stage and never in the AST
533
- node_list = send(:"interpret_#{op.operand.type}", op.operand, input)
534
- case op.operator
535
- when :not then interpret_not(node_list)
536
- else raise err("unknown unary operator #{op.inspect}")
537
- end
538
- end
539
-
540
- # Interpret unary operator "!".
541
- # For a node list, this is an existence check that just determines if the list is empty.
542
- # For a boolean, this inverts the meaning of the input.
543
- # @return [Boolean]
544
- def interpret_not(input)
545
- case input
546
- when Array then input.empty?
547
- when TrueClass, FalseClass then !input
548
- else
549
- raise err("don't know how to apply not operator to #{input.inspect}")
550
- end
551
- end
552
-
553
- # @param function [AST::Function]
554
- # @param input [Hash, Array]
555
- def interpret_function(function, input)
556
- params = evaluate_function_parameters(function.parameters, function.name, input)
557
- function.body.call(*params)
558
- end
559
-
560
- # Evaluate the expressions in the parameter list to make the parameter values
561
- # to pass in to a JsonPath function.
562
- #
563
- # The node lists returned by a singular query must be deconstructed into a single value for
564
- # parameters of ValueType, this is done here.
565
- # For explanation:
566
- # @see https://www.rfc-editor.org/rfc/rfc9535.html#name-well-typedness-of-function-
567
- #
568
- # @param parameters [Array] parameters before evaluation
569
- # @param func [String] function name (eg. "length", "count")
570
- # @param input [Object]
571
- # @return [Array] parameters after evaluation
572
- def evaluate_function_parameters(parameters, func, input)
573
- param_types = FUNCTION_PARAMETER_TYPES[func.to_sym]
56
+ private
574
57
 
575
- parameters.map.with_index do |parameter, i|
576
- type = param_types[i]
577
- case parameter
578
- when AST::CurrentNode, AST::RootNode
579
- # Selectors always return a node list.
580
- # Deconstruct the resulting node list if function parameter type is ValueType.
581
- result = interpret_node(parameter, input)
582
- if type == :value_type && parameter.singular_query?
583
- deconstruct(result)
584
- else
585
- result
586
- end
587
- when AST::Function
588
- interpret_function(parameter, input)
589
- when AST::StringType, AST::Number
590
- interpret_string_type(parameter, input)
591
- else
592
- # invalid parameter type. Function must accept it and return Nothing result
593
- parameter
58
+ # @return [Interpreters::RootNodeInterpreter]
59
+ def query_to_interpreter_pipeline(query)
60
+ pipeline =
61
+ query.node_list.map do |node|
62
+ Interpreters::TreeConstructor.ast_node_to_interpreter(node)
594
63
  end
64
+ pipeline.each_with_index do |node, i|
65
+ node.next = pipeline[i + 1]
595
66
  end
596
- end
597
-
598
- # Prepare a value to be passed to as a parameter with type ValueType.
599
- # Singular queries (see RFC) produce a node list containing one value.
600
- # Return the value.
601
- #
602
- # Implements this part of the RFC:
603
- # > When the declared type of the parameter is ValueType and
604
- # the argument is one of the following:
605
- # > ...
606
- # >
607
- # > A singular query. In this case:
608
- # > * If the query results in a nodelist consisting of a single node,
609
- # the argument is the value of the node.
610
- # > * If the query results in an empty nodelist, the argument is
611
- # the special result Nothing.
612
- #
613
- # @param input [Object] usually an array - sometimes a basic type like String, Numeric
614
- # @return [Object] basic type -- string or number
615
- def deconstruct(input)
616
- return input unless input.is_a?(Array)
617
-
618
- if input.size == 1
619
- # FIXME: what if it was a size 1 array that was intended to be a node not a node list? How to detect this?
620
- input.first
621
- elsif input.empty?
622
- NOTHING
623
- else
624
- input # input is a single node, which happens to be an Array
625
- end
67
+ pipeline
626
68
  end
627
69
 
628
70
  # Return an Interpreter::Error with the specified message, include the query.
@@ -630,7 +72,7 @@ module Janeway
630
72
  # @param msg [String] error message
631
73
  # @return [Parser::Error]
632
74
  def err(msg)
633
- Error.new(msg, @jsonpath)
75
+ Janeway::Error.new(msg, @jsonpath)
634
76
  end
635
77
  end
636
78
  end