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
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interprets array slice selector on the given input
8
+ class ArraySliceSelectorInterpreter < Base
9
+ alias selector node
10
+
11
+ # Filter the input by applying the array slice selector.
12
+ #
13
+ # @param selector [ArraySliceSelector]
14
+ # @param input [Array, Hash] the results of processing so far
15
+ # @param root [Array, Hash] the entire input
16
+ # @return [Array]
17
+ def interpret(input, root)
18
+ return [] unless input.is_a?(Array)
19
+ return [] if selector&.step&.zero? # RFC: When step is 0, no elements are selected.
20
+
21
+ # Calculate the upper and lower indices of the target range
22
+ lower = selector.lower_index(input.size)
23
+ upper = selector.upper_index(input.size)
24
+
25
+ # Collect values from target indices. Omit the value from the final index.
26
+ results =
27
+ if selector.step.positive?
28
+ lower.step(to: upper - 1, by: selector.step).map { input[_1] }
29
+ else
30
+ upper.step(to: lower + 1, by: selector.step).map { input[_1] }
31
+ end
32
+ return results unless @next
33
+
34
+ # Apply child selector to each node in the output node list
35
+ node_list = results
36
+ results = []
37
+ node_list.each do |node|
38
+ results.concat @next.interpret(node, root)
39
+ end
40
+ results
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'tree_constructor'
4
+
5
+ module Janeway
6
+ # Interpreters module contains interpreter classes which correspond to each
7
+ # type of AST::Node.
8
+ module Interpreters
9
+ # Base class for interpreters.
10
+ class Base
11
+ # The special result NOTHING represents the absence of a JSON value and
12
+ # is distinct from any JSON value, including null.
13
+ # It represents:
14
+ # * object keys referred to by name that do not exist in the input
15
+ # * array values referred to by index that are out of range
16
+ # * return value of function calls with an invalid input data type
17
+ NOTHING = :nothing
18
+
19
+ # Subsequent expression interpreter that filters the output of this one
20
+ # @return [Interpreters::Base, nil]
21
+ attr_accessor :next
22
+
23
+ # @return [AST::Expression]
24
+ attr_reader :node
25
+
26
+ def initialize(node)
27
+ @node = node
28
+
29
+ return unless node.next
30
+
31
+ @next = TreeConstructor.ast_node_to_interpreter(node.next)
32
+ end
33
+
34
+ # Interpret the input, return result or forward to next node.
35
+ #
36
+ # @param input [Array, Hash] the results of processing so far
37
+ # @param root [Array, Hash] the entire input
38
+ def interpret(_input, _root)
39
+ raise NotImplementedError, 'subclass must implement #interpret'
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interprets a binary operator within filter selector.
8
+ class BinaryOperatorInterpreter < Base
9
+ alias operator node
10
+
11
+ # Set up the internal interpreter chain for the BinaryOperator.
12
+ def initialize(operator)
13
+ super
14
+ @left = TreeConstructor.ast_node_to_interpreter(operator.left)
15
+ @right = TreeConstructor.ast_node_to_interpreter(operator.right)
16
+ end
17
+
18
+ # The binary operators are all comparison operators that test equality.
19
+ #
20
+ # * boolean values specified in the query
21
+ # * JSONPath expressions which must be evaluated
22
+ #
23
+ # After a JSONPath expression is evaluated, it results in a node list.
24
+ # This may contain literal values or nodes, whose value must be extracted before comparison.
25
+ #
26
+ # @param input [Array, Hash] the results of processing so far
27
+ # @param root [Array, Hash] the entire input
28
+ def interpret(input, root)
29
+ case operator.name
30
+ when :and, :or
31
+ # handle node list for existence check
32
+ lhs = @left.interpret(input, root)
33
+ rhs = @right.interpret(input, root)
34
+ when :equal, :not_equal, :less_than, :greater_than, :less_than_or_equal, :greater_than_or_equal
35
+ # handle node values for comparison check
36
+ lhs = to_single_value @left.interpret(input, root)
37
+ rhs = to_single_value @right.interpret(input, root)
38
+ else
39
+ raise "Don't know how to handle binary operator #{operator.name.inspect}"
40
+ end
41
+ send(:"interpret_#{operator.name}", lhs, rhs)
42
+ end
43
+
44
+ # Interpret a node and extract its value, in preparation for using the node
45
+ # in a comparison operator.
46
+ # Basic literals such as AST::Number and AST::StringType evaluate to a number or string,
47
+ # but selectors and segments evaluate to a node list. Extract the value (if any)
48
+ # from the node list, or return basic type.
49
+
50
+ # Convert an expression result into a single value suitable for use by a comparison operator.
51
+ # Expression result may already ba single value (ie. from a literal like AST::String)
52
+ # or it may be a node list from a singular query.
53
+ #
54
+ # Any node list is guaranteed not to contain multiple values because the expression that
55
+ # produce it was already verified to be a singular query.
56
+ #
57
+ # @param node [AST::Expression]
58
+ # @param input [Object]
59
+ def to_single_value(result)
60
+ # Return basic types (ie. from AST::Number, AST::StringType, AST::Null)
61
+ return result unless result.is_a?(Array)
62
+
63
+ # Node lists are returned by Selectors, ChildSegment, DescendantSegment.
64
+ #
65
+ # For a comparison operator, an empty node list represents a missing element.
66
+ # This must not match any literal value (including null/nil) but must match another missing value.
67
+ return NOTHING if result.empty?
68
+
69
+ # The parsing stage has already verified that both the left and right
70
+ # expressions evaluate to a single value. Both are either a literal or a singular query.
71
+ # So, this check will never raise an error.
72
+ raise 'node list contains multiple elements but this is a comparison' unless result.size == 1
73
+
74
+ result.first # Return the only node in the node list
75
+ end
76
+
77
+ # @param lhs [String, Numeric, Symbol, nil] string/number/null or NOTHING
78
+ # @param rhs [String, Numeric, Symbol, nil] string/number/null or NOTHING
79
+ def interpret_equal(lhs, rhs)
80
+ lhs == rhs
81
+ end
82
+
83
+ # Interpret != operator
84
+ def interpret_not_equal(lhs, rhs)
85
+ !interpret_equal(lhs, rhs)
86
+ end
87
+
88
+ # Interpret && operator
89
+ # May receive node lists, in which case empty node list == false
90
+ def interpret_and(lhs, rhs)
91
+ # non-empty array is already truthy, so that works properly without conversion
92
+ lhs = false if lhs == []
93
+ rhs = false if rhs == []
94
+ lhs && rhs
95
+ end
96
+
97
+ # Interpret || operator
98
+ # May receive node lists, in which case empty node list == false
99
+ def interpret_or(lhs, rhs)
100
+ # non-empty array is already truthy, so that works properly without conversion
101
+ lhs = false if lhs.is_a?(Array) && lhs.empty?
102
+ rhs = false if rhs.is_a?(Array) && rhs.empty?
103
+ lhs || rhs
104
+ end
105
+
106
+ def interpret_less_than(lhs, rhs)
107
+ lhs < rhs
108
+ rescue StandardError
109
+ false
110
+ end
111
+
112
+ def interpret_less_than_or_equal(lhs, rhs)
113
+ # Must be done in 2 comparisons, because the equality comparison is
114
+ # valid for many types that do not support the < operator.
115
+ return true if interpret_equal(lhs, rhs)
116
+
117
+ lhs < rhs
118
+ rescue StandardError
119
+ # This catches type mismatches like {} <= 1
120
+ # RFC says that both < and > return false for such comparisons
121
+ false
122
+ end
123
+
124
+ def interpret_greater_than(lhs, rhs)
125
+ lhs > rhs
126
+ rescue StandardError
127
+ false
128
+ end
129
+
130
+ def interpret_greater_than_or_equal(lhs, rhs)
131
+ return true if interpret_equal(lhs, rhs)
132
+
133
+ lhs > rhs
134
+ rescue StandardError
135
+ false
136
+ end
137
+
138
+ # @param boolean [AST::Boolean]
139
+ # @return [Boolean]
140
+ def interpret_boolean(boolean, _input)
141
+ boolean.value
142
+ end
143
+
144
+ # @param number [AST::Number]
145
+ # @return [Integer, Float]
146
+ def interpret_number(number, _input)
147
+ number.value
148
+ end
149
+
150
+ # @param string [AST::StringType]
151
+ # @return [String]
152
+ def interpret_string_type(string, _input)
153
+ string.value
154
+ end
155
+
156
+ # @param _null [AST::Null] ignored
157
+ # @param _input [Object] ignored
158
+ def interpret_null(_null, _input)
159
+ nil
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # For each node in the input nodelist, the resulting nodelist of a child
8
+ # segment is the concatenation of the nodelists from each of its
9
+ # selectors in the order that the selectors appear in the list. Note: Any
10
+ # node matched by more than one selector is kept as many times in the nodelist.
11
+ #
12
+ # A child segment can only contain selectors according to the RFC's ABNF grammar, so it
13
+ # cannot contain another child segment, or a new expression starting with the root identifier.
14
+ class ChildSegmentInterpreter < Base
15
+ # @param child_segment [AST::ChildSegment]
16
+ def initialize(child_segment)
17
+ super
18
+ @nodes =
19
+ child_segment.map do |expr|
20
+ TreeConstructor.ast_node_to_interpreter(expr)
21
+ end
22
+ end
23
+
24
+ # Interpret a set of 2 or more selectors, seperated by the union operator.
25
+ # All selectors are sent the identical input, the results are combined.
26
+ #
27
+ # @param input [Array, Hash] the results of processing so far
28
+ # @param root [Array, Hash] the entire input
29
+ # @return [Array]
30
+ def interpret(input, root)
31
+ # Apply each expression to the input, collect results
32
+ results = []
33
+ @nodes.each do |node|
34
+ results.concat node.interpret(input, root)
35
+ end
36
+
37
+ # Return results, or forward them to the next selector
38
+ return results unless @next
39
+
40
+ @next.interpret(results, root)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interpets current node identifier.
8
+ # This applies the following selector to the input.
9
+ class CurrentNodeInterpreter < Base
10
+ alias current_node node
11
+
12
+ # Apply selector to each value in the current node and return result.
13
+ #
14
+ # The result is an Array containing all results of evaluating the CurrentNode's selector (if any.)
15
+ #
16
+ # If the selector extracted values from nodes such as strings, numbers or nil/null,
17
+ # these will be included in the array.
18
+ # If the selector did not match any node, the array may be empty.
19
+ # If there was no selector, then the current input node is returned in the array.
20
+ #
21
+ # @param _input [Array, Hash] the results of processing so far
22
+ # @param root [Array, Hash] the entire input
23
+ # @return [Array] Node List containing all results from evaluating this node's selectors.
24
+ def interpret(input, root)
25
+ if @next
26
+ @next.interpret(input, root)
27
+ else
28
+ input
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Find all descendants of the current input that match the selector in the DescendantSegment
8
+ class DescendantSegmentInterpreter < Base
9
+ alias descendant_segment node
10
+
11
+ # Find all descendants of the current input that match the selector in the DescendantSegment
12
+ #
13
+ # @param input [Array, Hash] the results of processing so far
14
+ # @param root [Array, Hash] the entire input
15
+ # @return [Array<AST::Expression>] node list
16
+ def interpret(input, root)
17
+ visit(input) do |node|
18
+ @next.interpret(node, root)
19
+ end
20
+ end
21
+
22
+ # Visit all descendants of `input`.
23
+ # Return results of applying `action` on each.
24
+ def visit(input, &action)
25
+ results = [yield(input)]
26
+
27
+ case input
28
+ when Array
29
+ results.concat(input.map { |elt| visit(elt, &action) })
30
+ when Hash
31
+ results.concat(input.values.map { |value| visit(value, &action) })
32
+ else
33
+ input
34
+ end
35
+
36
+ results.flatten(1).compact
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interprets a filter selector, returns results or forwards them to next selector
8
+ class FilterSelectorInterpreter < Base
9
+ alias selector node
10
+
11
+ # Set up the internal interpreter chain for the FilterSelector.
12
+ # @param selector [AST::FilterSelector]
13
+ def initialize(selector)
14
+ super
15
+ @expr = self.class.setup_interpreter_tree(selector)
16
+ end
17
+
18
+ # FIXME: should this be combined with similar func in Interpreter?
19
+ # FIXME: move this to a separate module?
20
+ #
21
+ # Set up a tree of interpreters which can process input for the filter expression.
22
+ # For a jsonpath query like '$.a[?@.b == $.x]', this sets up interpreters for '@.b == $.x'.
23
+ # @return [Interpreters::Base] root of the filter expression
24
+ def self.setup_interpreter_tree(selector)
25
+ TreeConstructor.ast_node_to_interpreter(selector.value)
26
+ end
27
+
28
+ # Interpret selector on the input.
29
+ # @param input [Array, Hash] the results of processing so far
30
+ # @param root [Array, Hash] the entire input
31
+ def interpret(input, root)
32
+ values =
33
+ case input
34
+ when Array then input
35
+ when Hash then input.values
36
+ else return [] # early exit
37
+ end
38
+
39
+ # Apply filter expressions to the input data
40
+ node_list = []
41
+ values.each do |value|
42
+ # Run filter and interpret result
43
+ result = @expr.interpret(value, root)
44
+ case result
45
+ when TrueClass then node_list << value # comparison test - pass
46
+ when FalseClass then nil # comparison test - fail
47
+ when Array then node_list << value unless result.empty? # existence test - node list
48
+ else
49
+ node_list << value # existence test. Null values here == success.
50
+ end
51
+ end
52
+ return node_list unless @next
53
+
54
+ # Apply child selector to each node in the output node list
55
+ results = []
56
+ node_list.each do |node|
57
+ results.concat @next.interpret(node, root)
58
+ end
59
+ results
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interprets a jsonpath function call within a filter selector.
8
+ class FunctionInterpreter < Base
9
+ alias function node
10
+
11
+ # Specify the parameter types that built-in JsonPath functions require
12
+ # @return [Hash<Symbol, Array<Symbol>] function name => list of parameter types
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
+ # @param [AST::Function]
22
+ def initialize(function)
23
+ super
24
+ @params = function.parameters.map { |param| TreeConstructor.ast_node_to_interpreter(param) }
25
+ end
26
+
27
+ # @param input [Array, Hash] the results of processing so far
28
+ # @param root [Array, Hash] the entire input
29
+ def interpret(input, root)
30
+ params = interpret_function_parameters(@params, input, root)
31
+ function.body.call(*params)
32
+ end
33
+
34
+ # Evaluate the expressions in the parameter list to make the parameter values
35
+ # to pass in to a JsonPath function.
36
+ #
37
+ # The node lists returned by a singular query must be deconstructed into a single value for
38
+ # parameters of ValueType, this is done here.
39
+ # For explanation:
40
+ # @see https://www.rfc-editor.org/rfc/rfc9535.html#name-well-typedness-of-function-
41
+ #
42
+ # @param parameters [Array] parameters before evaluation
43
+ # @param func [String] function name (eg. "length", "count")
44
+ # @param input [Object]
45
+ # @return [Array] parameters after evaluation
46
+ def interpret_function_parameters(parameters, input, root)
47
+ param_types = FUNCTION_PARAMETER_TYPES[function.name.to_sym]
48
+
49
+ parameters.map.with_index do |parameter, i|
50
+ type = param_types[i]
51
+ case parameter.node
52
+ when AST::CurrentNode, AST::RootNode
53
+ result = parameter.interpret(input, root)
54
+ if type == :value_type && parameter.node.singular_query?
55
+ deconstruct(result)
56
+ else
57
+ result
58
+ end
59
+ when AST::Function, AST::StringType then parameter.interpret(input, root)
60
+ when String then parameter.value # from a TreeConstructor::Literal
61
+ else
62
+ # invalid parameter type. Function must accept it and return Nothing result
63
+ parameter
64
+ end
65
+ end
66
+ end
67
+
68
+ # Prepare a value to be passed to as a parameter with type ValueType.
69
+ # Singular queries (see RFC) produce a node list containing one value.
70
+ # Return the value.
71
+ #
72
+ # Implements this part of the RFC:
73
+ # > When the declared type of the parameter is ValueType and
74
+ # the argument is one of the following:
75
+ # > ...
76
+ # >
77
+ # > A singular query. In this case:
78
+ # > * If the query results in a nodelist consisting of a single node,
79
+ # the argument is the value of the node.
80
+ # > * If the query results in an empty nodelist, the argument is
81
+ # the special result Nothing.
82
+ #
83
+ # @param input [Object] usually an array - sometimes a basic type like String, Numeric
84
+ # @return [Object] basic type -- string or number
85
+ def deconstruct(input)
86
+ return input unless input.is_a?(Array)
87
+
88
+ if input.size == 1
89
+ # FIXME: what if it was a size 1 array that was intended to be a node not a node list? How to detect this?
90
+ input.first
91
+ elsif input.empty?
92
+ NOTHING
93
+ else
94
+ input # input is a single node, which happens to be an Array
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interprets an index selector, returns results or forwards them to next selector
8
+ #
9
+ # Filter the input by returning the array element with the given index.
10
+ # Return empty list if input is not an array, or if array does not contain index.
11
+ #
12
+ # Output is an array because all selectors must return node lists, even if
13
+ # they only select a single element.
14
+ #
15
+ # @param selector [IndexSelector]
16
+ # @param input [Array]
17
+ # @return [Array]
18
+ class IndexSelectorInterpreter < Base
19
+ alias selector node
20
+
21
+ # Interpret an index selector on the given input.
22
+ # NOTHING is selected, and it is not an error, if the index lies outside the range of the array.
23
+ # NOTHING is selected from a value that is not an array.
24
+ #
25
+ # @param input [Array, Hash] the results of processing so far
26
+ # @param root [Array, Hash] the entire input
27
+ def interpret(input, root)
28
+ return [] unless input.is_a?(Array)
29
+
30
+ result = input.fetch(selector.value) # raises IndexError if no such index
31
+ return [result] unless @next
32
+
33
+ @next.interpret(result, root)
34
+ rescue IndexError
35
+ []
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interprets a name selector, returns results or forwards them to next selector
8
+ #
9
+ # Filters the input by returning the key that has the given name.
10
+ #
11
+ # Must differentiate between a null value of a key that exists (nil)
12
+ # and a key that does not exist ([])
13
+ class NameSelectorInterpreter < Base
14
+ alias selector node
15
+
16
+ # Interpret selector on the given input.
17
+ # @param input [Array, Hash] the results of processing so far
18
+ # @param root [Array, Hash] the entire input
19
+ def interpret(input, root)
20
+ return [] unless input.is_a?(Hash) && input.key?(selector.name)
21
+
22
+ result = input[selector.name]
23
+ return [result] unless @next
24
+
25
+ # Forward result to next selector
26
+ @next.interpret(result, root)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Interprets the root identifer, returns results or forwards them to next selector.
8
+ #
9
+ # This is required at the beginning of every jsonpath query.
10
+ # It may also be used within filter selector expressions.
11
+ class RootNodeInterpreter < Base
12
+ # Start an expression chain using the entire, unfiltered input.
13
+ #
14
+ # @param _input [Array, Hash] the results of processing so far
15
+ # @param root [Array, Hash] the entire input
16
+ def interpret(_input, root)
17
+ return [root] unless @next
18
+
19
+ @next.interpret(root, root)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Janeway
6
+ module Interpreters
7
+ # Constructs a tree of interpreter objects
8
+ module TreeConstructor
9
+ # Fake interpreter which just returns the given value
10
+ Literal = Struct.new(:value) do
11
+ def interpret(*)
12
+ value
13
+ end
14
+ alias_method :node, :interpret
15
+ end
16
+
17
+ def self.ast_node_to_interpreter(expr)
18
+ case expr
19
+ when AST::RootNode then Interpreters::RootNodeInterpreter.new(expr)
20
+ when AST::ArraySliceSelector then Interpreters::ArraySliceSelectorInterpreter.new(expr)
21
+ when AST::IndexSelector then Interpreters::IndexSelectorInterpreter.new(expr)
22
+ when AST::NameSelector then Interpreters::NameSelectorInterpreter.new(expr)
23
+ when AST::WildcardSelector then Interpreters::WildcardSelectorInterpreter.new(expr)
24
+ when AST::FilterSelector then Interpreters::FilterSelectorInterpreter.new(expr)
25
+ when AST::ChildSegment then Interpreters::ChildSegmentInterpreter.new(expr)
26
+ when AST::DescendantSegment then Interpreters::DescendantSegmentInterpreter.new(expr)
27
+ when AST::BinaryOperator then Interpreters::BinaryOperatorInterpreter.new(expr)
28
+ when AST::UnaryOperator then Interpreters::UnaryOperatorInterpreter.new(expr)
29
+ when AST::CurrentNode then Interpreters::CurrentNodeInterpreter.new(expr)
30
+ when AST::Function then Interpreters::FunctionInterpreter.new(expr)
31
+ when AST::StringType, AST::Number, AST::Null, AST::Boolean then Literal.new expr.value
32
+ when nil then nil # caller has no @next node
33
+ else
34
+ raise "Unknown AST expression: #{expr.inspect}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end