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.
- checksums.yaml +4 -4
- data/README.md +109 -38
- data/lib/janeway/ast/array_slice_selector.rb +9 -7
- data/lib/janeway/ast/binary_operator.rb +25 -23
- data/lib/janeway/ast/current_node.rb +8 -5
- data/lib/janeway/ast/descendant_segment.rb +1 -1
- data/lib/janeway/ast/expression.rb +11 -1
- data/lib/janeway/ast/filter_selector.rb +1 -1
- data/lib/janeway/ast/helpers.rb +1 -0
- data/lib/janeway/ast/index_selector.rb +2 -3
- data/lib/janeway/ast/name_selector.rb +2 -2
- data/lib/janeway/ast/null.rb +1 -0
- data/lib/janeway/ast/query.rb +42 -1
- data/lib/janeway/ast/root_node.rb +8 -5
- data/lib/janeway/ast/string_type.rb +2 -0
- data/lib/janeway/ast/unary_operator.rb +2 -2
- data/lib/janeway/ast/wildcard_selector.rb +1 -0
- data/lib/janeway/error.rb +1 -1
- data/lib/janeway/functions/count.rb +2 -4
- data/lib/janeway/functions/length.rb +2 -3
- data/lib/janeway/functions/match.rb +3 -9
- data/lib/janeway/functions/search.rb +4 -9
- data/lib/janeway/functions/value.rb +2 -3
- data/lib/janeway/interpreter.rb +29 -587
- data/lib/janeway/interpreters/array_slice_selector_interpreter.rb +44 -0
- data/lib/janeway/interpreters/base.rb +43 -0
- data/lib/janeway/interpreters/binary_operator_interpreter.rb +163 -0
- data/lib/janeway/interpreters/child_segment_interpreter.rb +44 -0
- data/lib/janeway/interpreters/current_node_interpreter.rb +33 -0
- data/lib/janeway/interpreters/descendant_segment_interpreter.rb +40 -0
- data/lib/janeway/interpreters/filter_selector_interpreter.rb +63 -0
- data/lib/janeway/interpreters/function_interpreter.rb +99 -0
- data/lib/janeway/interpreters/index_selector_interpreter.rb +39 -0
- data/lib/janeway/interpreters/name_selector_interpreter.rb +30 -0
- data/lib/janeway/interpreters/root_node_interpreter.rb +23 -0
- data/lib/janeway/interpreters/tree_constructor.rb +39 -0
- data/lib/janeway/interpreters/unary_operator_interpreter.rb +32 -0
- data/lib/janeway/interpreters/wildcard_selector_interpreter.rb +38 -0
- data/lib/janeway/interpreters/yielder.rb +34 -0
- data/lib/janeway/lexer.rb +12 -16
- data/lib/janeway/parser.rb +51 -90
- data/lib/janeway/token.rb +1 -0
- data/lib/janeway/version.rb +1 -1
- data/lib/janeway.rb +25 -5
- metadata +17 -3
- 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
|
-
#
|
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
|
data/lib/janeway/interpreter.rb
CHANGED
@@ -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
|
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
|
-
|
452
|
-
|
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
|
-
#
|
456
|
-
#
|
457
|
-
def
|
458
|
-
|
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
|
-
|
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
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
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
|
-
|
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
|