jmespath 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of jmespath might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/jmespath.rb +5 -4
- data/lib/jmespath/errors.rb +2 -0
- data/lib/jmespath/lexer.rb +291 -84
- data/lib/jmespath/nodes.rb +40 -0
- data/lib/jmespath/nodes/comparator.rb +77 -0
- data/lib/jmespath/nodes/condition.rb +136 -0
- data/lib/jmespath/nodes/current.rb +10 -0
- data/lib/jmespath/nodes/expression.rb +25 -0
- data/lib/jmespath/nodes/field.rb +74 -0
- data/lib/jmespath/nodes/flatten.rb +29 -0
- data/lib/jmespath/nodes/function.rb +591 -0
- data/lib/jmespath/nodes/index.rb +6 -0
- data/lib/jmespath/nodes/literal.rb +16 -0
- data/lib/jmespath/nodes/multi_select_hash.rb +37 -0
- data/lib/jmespath/nodes/multi_select_list.rb +22 -0
- data/lib/jmespath/nodes/or.rb +24 -0
- data/lib/jmespath/nodes/pipe.rb +6 -0
- data/lib/jmespath/nodes/projection.rb +82 -0
- data/lib/jmespath/nodes/slice.rb +92 -0
- data/lib/jmespath/nodes/subexpression.rb +63 -0
- data/lib/jmespath/parser.rb +78 -116
- data/lib/jmespath/runtime.rb +2 -7
- data/lib/jmespath/token.rb +22 -23
- data/lib/jmespath/version.rb +1 -1
- metadata +29 -14
- data/lib/jmespath/expr_node.rb +0 -15
- data/lib/jmespath/tree_interpreter.rb +0 -523
data/lib/jmespath/runtime.rb
CHANGED
@@ -37,24 +37,19 @@ module JMESPath
|
|
37
37
|
#
|
38
38
|
# @option options [Parser,CachingParser] :parser
|
39
39
|
#
|
40
|
-
# @option options [Interpreter] :interpreter
|
41
|
-
#
|
42
40
|
def initialize(options = {})
|
43
41
|
@parser = options[:parser] || default_parser(options)
|
44
|
-
@interpreter = options[:interpreter] || TreeInterpreter.new
|
45
42
|
end
|
46
43
|
|
47
44
|
# @return [Parser, CachingParser]
|
48
45
|
attr_reader :parser
|
49
46
|
|
50
|
-
# @return [Interpreter]
|
51
|
-
attr_reader :interpreter
|
52
|
-
|
53
47
|
# @param [String<JMESPath>] expression
|
54
48
|
# @param [Hash] data
|
55
49
|
# @return [Mixed,nil]
|
56
50
|
def search(expression, data)
|
57
|
-
@
|
51
|
+
optimized_expression = @parser.parse(expression).optimize
|
52
|
+
optimized_expression.visit(data)
|
58
53
|
end
|
59
54
|
|
60
55
|
private
|
data/lib/jmespath/token.rb
CHANGED
@@ -2,32 +2,31 @@ module JMESPath
|
|
2
2
|
# @api private
|
3
3
|
class Token < Struct.new(:type, :value, :position, :binding_power)
|
4
4
|
|
5
|
-
# @api private
|
6
5
|
NULL_TOKEN = Token.new(:eof, '', nil)
|
7
6
|
|
8
|
-
# binding power
|
9
|
-
# @api private
|
10
7
|
BINDING_POWER = {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
8
|
+
Lexer::T_UNKNOWN => 0,
|
9
|
+
Lexer::T_EOF => 0,
|
10
|
+
Lexer::T_QUOTED_IDENTIFIER => 0,
|
11
|
+
Lexer::T_IDENTIFIER => 0,
|
12
|
+
Lexer::T_RBRACKET => 0,
|
13
|
+
Lexer::T_RPAREN => 0,
|
14
|
+
Lexer::T_COMMA => 0,
|
15
|
+
Lexer::T_RBRACE => 0,
|
16
|
+
Lexer::T_NUMBER => 0,
|
17
|
+
Lexer::T_CURRENT => 0,
|
18
|
+
Lexer::T_EXPREF => 0,
|
19
|
+
Lexer::T_COLON => 0,
|
20
|
+
Lexer::T_PIPE => 1,
|
21
|
+
Lexer::T_COMPARATOR => 2,
|
22
|
+
Lexer::T_OR => 5,
|
23
|
+
Lexer::T_FLATTEN => 6,
|
24
|
+
Lexer::T_STAR => 20,
|
25
|
+
Lexer::T_FILTER => 21,
|
26
|
+
Lexer::T_DOT => 40,
|
27
|
+
Lexer::T_LBRACE => 50,
|
28
|
+
Lexer::T_LBRACKET => 55,
|
29
|
+
Lexer::T_LPAREN => 60,
|
31
30
|
}
|
32
31
|
|
33
32
|
# @param [Symbol] type
|
data/lib/jmespath/version.rb
CHANGED
metadata
CHANGED
@@ -1,47 +1,62 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jmespath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Trevor Rowe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: json
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.8'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.8'
|
27
27
|
description: Implements JMESPath for Ruby
|
28
28
|
email: trevorrowe@gmail.com
|
29
29
|
executables: []
|
30
30
|
extensions: []
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
|
+
- LICENSE.txt
|
34
|
+
- lib/jmespath.rb
|
33
35
|
- lib/jmespath/caching_parser.rb
|
34
36
|
- lib/jmespath/errors.rb
|
35
|
-
- lib/jmespath/expr_node.rb
|
36
37
|
- lib/jmespath/lexer.rb
|
38
|
+
- lib/jmespath/nodes.rb
|
39
|
+
- lib/jmespath/nodes/comparator.rb
|
40
|
+
- lib/jmespath/nodes/condition.rb
|
41
|
+
- lib/jmespath/nodes/current.rb
|
42
|
+
- lib/jmespath/nodes/expression.rb
|
43
|
+
- lib/jmespath/nodes/field.rb
|
44
|
+
- lib/jmespath/nodes/flatten.rb
|
45
|
+
- lib/jmespath/nodes/function.rb
|
46
|
+
- lib/jmespath/nodes/index.rb
|
47
|
+
- lib/jmespath/nodes/literal.rb
|
48
|
+
- lib/jmespath/nodes/multi_select_hash.rb
|
49
|
+
- lib/jmespath/nodes/multi_select_list.rb
|
50
|
+
- lib/jmespath/nodes/or.rb
|
51
|
+
- lib/jmespath/nodes/pipe.rb
|
52
|
+
- lib/jmespath/nodes/projection.rb
|
53
|
+
- lib/jmespath/nodes/slice.rb
|
54
|
+
- lib/jmespath/nodes/subexpression.rb
|
37
55
|
- lib/jmespath/parser.rb
|
38
56
|
- lib/jmespath/runtime.rb
|
39
57
|
- lib/jmespath/token.rb
|
40
58
|
- lib/jmespath/token_stream.rb
|
41
|
-
- lib/jmespath/tree_interpreter.rb
|
42
59
|
- lib/jmespath/version.rb
|
43
|
-
- lib/jmespath.rb
|
44
|
-
- LICENSE.txt
|
45
60
|
homepage: http://github.com/trevorrowe/jmespath.rb
|
46
61
|
licenses:
|
47
62
|
- Apache 2.0
|
@@ -52,17 +67,17 @@ require_paths:
|
|
52
67
|
- lib
|
53
68
|
required_ruby_version: !ruby/object:Gem::Requirement
|
54
69
|
requirements:
|
55
|
-
- -
|
70
|
+
- - ">="
|
56
71
|
- !ruby/object:Gem::Version
|
57
72
|
version: '0'
|
58
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
74
|
requirements:
|
60
|
-
- -
|
75
|
+
- - ">="
|
61
76
|
- !ruby/object:Gem::Version
|
62
77
|
version: '0'
|
63
78
|
requirements: []
|
64
79
|
rubyforge_project:
|
65
|
-
rubygems_version: 2.
|
80
|
+
rubygems_version: 2.4.5
|
66
81
|
signing_key:
|
67
82
|
specification_version: 4
|
68
83
|
summary: JMESPath - Ruby Edition
|
data/lib/jmespath/expr_node.rb
DELETED
@@ -1,523 +0,0 @@
|
|
1
|
-
module JMESPath
|
2
|
-
# @api private
|
3
|
-
class TreeInterpreter
|
4
|
-
|
5
|
-
def visit(node, data)
|
6
|
-
dispatch(node, data)
|
7
|
-
end
|
8
|
-
|
9
|
-
# @api private
|
10
|
-
def method_missing(method_name, *args)
|
11
|
-
if matches = method_name.to_s.match(/^function_(.*)/)
|
12
|
-
raise Errors::UnknownFunctionError, "unknown function #{matches[1]}()"
|
13
|
-
else
|
14
|
-
super
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def dispatch(node, value)
|
21
|
-
case node[:type]
|
22
|
-
|
23
|
-
when :field
|
24
|
-
# hash_like?
|
25
|
-
key = node[:key]
|
26
|
-
case value
|
27
|
-
when Hash then value.key?(key) ? value[key] : value[key.to_sym]
|
28
|
-
when Struct then value.respond_to?(key) ? value[key] : nil
|
29
|
-
else nil
|
30
|
-
end
|
31
|
-
|
32
|
-
when :subexpression
|
33
|
-
dispatch(node[:children][1], dispatch(node[:children][0], value))
|
34
|
-
|
35
|
-
when :index
|
36
|
-
if Array === value
|
37
|
-
value[node[:index]]
|
38
|
-
else
|
39
|
-
nil
|
40
|
-
end
|
41
|
-
|
42
|
-
when :projection
|
43
|
-
# Interprets a projection node, passing the values of the left
|
44
|
-
# child through the values of the right child and aggregating
|
45
|
-
# the non-null results into the return value.
|
46
|
-
left = dispatch(node[:children][0], value)
|
47
|
-
if node[:from] == :object && hash_like?(left)
|
48
|
-
projection(left.values, node)
|
49
|
-
elsif node[:from] == :object && left == []
|
50
|
-
projection(left, node)
|
51
|
-
elsif node[:from] == :array && Array === left
|
52
|
-
projection(left, node)
|
53
|
-
else
|
54
|
-
nil
|
55
|
-
end
|
56
|
-
|
57
|
-
when :flatten
|
58
|
-
value = dispatch(node[:children][0], value)
|
59
|
-
if Array === value
|
60
|
-
value.inject([]) do |values, v|
|
61
|
-
values + (Array === v ? v : [v])
|
62
|
-
end
|
63
|
-
else
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
|
67
|
-
when :literal
|
68
|
-
node[:value]
|
69
|
-
|
70
|
-
when :current
|
71
|
-
value
|
72
|
-
|
73
|
-
when :or
|
74
|
-
result = dispatch(node[:children][0], value)
|
75
|
-
if result.nil? or result.empty?
|
76
|
-
dispatch(node[:children][1], value)
|
77
|
-
else
|
78
|
-
result
|
79
|
-
end
|
80
|
-
|
81
|
-
when :pipe
|
82
|
-
dispatch(node[:children][1], dispatch(node[:children][0], value))
|
83
|
-
|
84
|
-
when :multi_select_list
|
85
|
-
if value.nil?
|
86
|
-
value
|
87
|
-
else
|
88
|
-
node[:children].map { |n| dispatch(n, value) }
|
89
|
-
end
|
90
|
-
|
91
|
-
when :multi_select_hash
|
92
|
-
if value.nil?
|
93
|
-
nil
|
94
|
-
else
|
95
|
-
node[:children].each.with_object({}) do |child, hash|
|
96
|
-
hash[child[:key]] = dispatch(child[:children][0], value)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
when :comparator
|
102
|
-
left = dispatch(node[:children][0], value)
|
103
|
-
right = dispatch(node[:children][1], value)
|
104
|
-
case node[:relation]
|
105
|
-
when '==' then compare_values(left, right)
|
106
|
-
when '!=' then !compare_values(left, right)
|
107
|
-
when '>' then is_int(left) && is_int(right) && left > right
|
108
|
-
when '>=' then is_int(left) && is_int(right) && left >= right
|
109
|
-
when '<' then is_int(left) && is_int(right) && left < right
|
110
|
-
when '<=' then is_int(left) && is_int(right) && left <= right
|
111
|
-
end
|
112
|
-
|
113
|
-
when :condition
|
114
|
-
true == dispatch(node[:children][0], value) ?
|
115
|
-
dispatch(node[:children][1], value) :
|
116
|
-
nil
|
117
|
-
|
118
|
-
when :function
|
119
|
-
args = node[:children].map { |child| dispatch(child, value) }
|
120
|
-
send("function_#{node[:fn]}", *args)
|
121
|
-
|
122
|
-
when :slice
|
123
|
-
function_slice(value, *node[:args])
|
124
|
-
|
125
|
-
when :expression
|
126
|
-
ExprNode.new(self, node[:children][0])
|
127
|
-
|
128
|
-
else
|
129
|
-
raise NotImplementedError
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def hash_like?(value)
|
134
|
-
Hash === value || Struct === value
|
135
|
-
end
|
136
|
-
|
137
|
-
def projection(values, node)
|
138
|
-
values.inject([]) do |list, v|
|
139
|
-
list << dispatch(node[:children][1], v)
|
140
|
-
end.compact
|
141
|
-
end
|
142
|
-
|
143
|
-
def function_abs(*args)
|
144
|
-
if args.count == 1
|
145
|
-
value = args.first
|
146
|
-
else
|
147
|
-
raise Errors::InvalidArityError, "function abs() expects one argument"
|
148
|
-
end
|
149
|
-
if Numeric === value
|
150
|
-
value.abs
|
151
|
-
else
|
152
|
-
raise Errors::InvalidTypeError, "function abs() expects a number"
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def function_avg(*args)
|
157
|
-
if args.count == 1
|
158
|
-
values = args.first
|
159
|
-
else
|
160
|
-
raise Errors::InvalidArityError, "function avg() expects one argument"
|
161
|
-
end
|
162
|
-
if Array === values
|
163
|
-
values.inject(0) do |total,n|
|
164
|
-
if Numeric === n
|
165
|
-
total + n
|
166
|
-
else
|
167
|
-
raise Errors::InvalidTypeError, "function avg() expects numeric values"
|
168
|
-
end
|
169
|
-
end / values.size.to_f
|
170
|
-
else
|
171
|
-
raise Errors::InvalidTypeError, "function avg() expects a number"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def function_ceil(*args)
|
176
|
-
if args.count == 1
|
177
|
-
value = args.first
|
178
|
-
else
|
179
|
-
raise Errors::InvalidArityError, "function ceil() expects one argument"
|
180
|
-
end
|
181
|
-
if Numeric === value
|
182
|
-
value.ceil
|
183
|
-
else
|
184
|
-
raise Errors::InvalidTypeError, "function ceil() expects a numeric value"
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
def function_contains(*args)
|
189
|
-
if args.count == 2
|
190
|
-
if String === args[0] || Array === args[0]
|
191
|
-
args[0].include?(args[1])
|
192
|
-
else
|
193
|
-
raise Errors::InvalidTypeError, "contains expects 2nd arg to be a list"
|
194
|
-
end
|
195
|
-
else
|
196
|
-
raise Errors::InvalidArityError, "function contains() expects 2 arguments"
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def function_floor(*args)
|
201
|
-
if args.count == 1
|
202
|
-
value = args.first
|
203
|
-
else
|
204
|
-
raise Errors::InvalidArityError, "function floor() expects one argument"
|
205
|
-
end
|
206
|
-
if Numeric === value
|
207
|
-
value.floor
|
208
|
-
else
|
209
|
-
raise Errors::InvalidTypeError, "function floor() expects a numeric value"
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def function_length(*args)
|
214
|
-
if args.count == 1
|
215
|
-
value = args.first
|
216
|
-
else
|
217
|
-
raise Errors::InvalidArityError, "function length() expects one argument"
|
218
|
-
end
|
219
|
-
case value
|
220
|
-
when Hash, Array, String then value.size
|
221
|
-
else raise Errors::InvalidTypeError, "function length() expects string, array or object"
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def function_max(*args)
|
226
|
-
if args.count == 1
|
227
|
-
values = args.first
|
228
|
-
else
|
229
|
-
raise Errors::InvalidArityError, "function max() expects one argument"
|
230
|
-
end
|
231
|
-
if Array === values
|
232
|
-
values.inject(values.first) do |max, v|
|
233
|
-
if Numeric === v
|
234
|
-
v > max ? v : max
|
235
|
-
else
|
236
|
-
raise Errors::InvalidTypeError, "function max() expects numeric values"
|
237
|
-
end
|
238
|
-
end
|
239
|
-
else
|
240
|
-
raise Errors::InvalidTypeError, "function max() expects an array"
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
def function_min(*args)
|
245
|
-
if args.count == 1
|
246
|
-
values = args.first
|
247
|
-
else
|
248
|
-
raise Errors::InvalidArityError, "function min() expects one argument"
|
249
|
-
end
|
250
|
-
if Array === values
|
251
|
-
values.inject(values.first) do |min, v|
|
252
|
-
if Numeric === v
|
253
|
-
v < min ? v : min
|
254
|
-
else
|
255
|
-
raise Errors::InvalidTypeError, "function min() expects numeric values"
|
256
|
-
end
|
257
|
-
end
|
258
|
-
else
|
259
|
-
raise Errors::InvalidTypeError, "function min() expects an array"
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
def function_type(*args)
|
264
|
-
if args.count == 1
|
265
|
-
get_type(args.first)
|
266
|
-
else
|
267
|
-
raise Errors::InvalidArityError, "function type() expects one argument"
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
def function_keys(*args)
|
272
|
-
if args.count == 1
|
273
|
-
value = args.first
|
274
|
-
if hash_like?(value)
|
275
|
-
case value
|
276
|
-
when Hash then value.keys.map(&:to_s)
|
277
|
-
when Struct then value.members.map(&:to_s)
|
278
|
-
else raise NotImplementedError
|
279
|
-
end
|
280
|
-
else
|
281
|
-
raise Errors::InvalidTypeError, "function keys() expects a hash"
|
282
|
-
end
|
283
|
-
else
|
284
|
-
raise Errors::InvalidArityError, "function keys() expects one argument"
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
def function_values(*args)
|
289
|
-
if args.count == 1
|
290
|
-
value = args.first
|
291
|
-
if hash_like?(value)
|
292
|
-
value.values
|
293
|
-
elsif Array === value
|
294
|
-
value
|
295
|
-
else
|
296
|
-
raise Errors::InvalidTypeError, "function values() expects an array or a hash"
|
297
|
-
end
|
298
|
-
else
|
299
|
-
raise Errors::InvalidArityError, "function values() expects one argument"
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
def function_join(*args)
|
304
|
-
if args.count == 2
|
305
|
-
glue = args[0]
|
306
|
-
values = args[1]
|
307
|
-
if !(String === glue)
|
308
|
-
raise Errors::InvalidTypeError, "function join() expects the first argument to be a string"
|
309
|
-
elsif Array === values && values.all? { |v| String === v }
|
310
|
-
values.join(glue)
|
311
|
-
else
|
312
|
-
raise Errors::InvalidTypeError, "function join() expects values to be an array of strings"
|
313
|
-
end
|
314
|
-
else
|
315
|
-
raise Errors::InvalidArityError, "function join() expects an array of strings"
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
def function_to_string(*args)
|
320
|
-
if args.count == 1
|
321
|
-
value = args.first
|
322
|
-
String === value ? value : MultiJson.dump(value)
|
323
|
-
else
|
324
|
-
raise Errors::InvalidArityError, "function to_string() expects one argument"
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
def function_to_number(*args)
|
329
|
-
if args.count == 1
|
330
|
-
begin
|
331
|
-
value = Float(args.first)
|
332
|
-
Integer(value) === value ? value.to_i : value
|
333
|
-
rescue
|
334
|
-
nil
|
335
|
-
end
|
336
|
-
else
|
337
|
-
raise Errors::InvalidArityError, "function to_number() expects one argument"
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
def function_sum(*args)
|
342
|
-
if args.count == 1 && Array === args.first
|
343
|
-
args.first.inject(0) do |sum,n|
|
344
|
-
if Numeric === n
|
345
|
-
sum + n
|
346
|
-
else
|
347
|
-
raise Errors::InvalidTypeError, "function sum() expects values to be numeric"
|
348
|
-
end
|
349
|
-
end
|
350
|
-
else
|
351
|
-
raise Errors::InvalidArityError, "function sum() expects one argument"
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
def function_not_null(*args)
|
356
|
-
if args.count > 0
|
357
|
-
args.find { |value| !value.nil? }
|
358
|
-
else
|
359
|
-
raise Errors::InvalidArityError, "function not_null() expects one or more arguments"
|
360
|
-
end
|
361
|
-
end
|
362
|
-
|
363
|
-
def function_sort(*args)
|
364
|
-
if args.count == 1
|
365
|
-
value = args.first
|
366
|
-
if Array === value
|
367
|
-
value.sort do |a, b|
|
368
|
-
a_type = get_type(a)
|
369
|
-
b_type = get_type(b)
|
370
|
-
if ['string', 'number'].include?(a_type) && a_type == b_type
|
371
|
-
a <=> b
|
372
|
-
else
|
373
|
-
raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
|
374
|
-
end
|
375
|
-
end
|
376
|
-
else
|
377
|
-
raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
|
378
|
-
end
|
379
|
-
else
|
380
|
-
raise Errors::InvalidArityError, "function sort() expects one argument"
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
def function_sort_by(*args)
|
385
|
-
if args.count == 2
|
386
|
-
if get_type(args[0]) == 'array' && get_type(args[1]) == 'expression'
|
387
|
-
values = args[0]
|
388
|
-
expression = args[1]
|
389
|
-
values.sort do |a,b|
|
390
|
-
a_value = expression.interpreter.visit(expression.node, a)
|
391
|
-
b_value = expression.interpreter.visit(expression.node, b)
|
392
|
-
a_type = get_type(a_value)
|
393
|
-
b_type = get_type(b_value)
|
394
|
-
if ['string', 'number'].include?(a_type) && a_type == b_type
|
395
|
-
a_value <=> b_value
|
396
|
-
else
|
397
|
-
raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
|
398
|
-
end
|
399
|
-
end
|
400
|
-
else
|
401
|
-
raise Errors::InvalidTypeError, "function sort_by() expects an array and an expression"
|
402
|
-
end
|
403
|
-
else
|
404
|
-
raise Errors::InvalidArityError, "function sort_by() expects two arguments"
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
def function_max_by(*args)
|
409
|
-
number_compare(:max, *args)
|
410
|
-
end
|
411
|
-
|
412
|
-
def function_min_by(*args)
|
413
|
-
number_compare(:min, *args)
|
414
|
-
end
|
415
|
-
|
416
|
-
def number_compare(mode, *args)
|
417
|
-
if args.count == 2
|
418
|
-
if get_type(args[0]) == 'array' && get_type(args[1]) == 'expression'
|
419
|
-
values = args[0]
|
420
|
-
expression = args[1]
|
421
|
-
args[0].send("#{mode}_by") do |entry|
|
422
|
-
value = expression.interpreter.visit(expression.node, entry)
|
423
|
-
if get_type(value) == 'number'
|
424
|
-
value
|
425
|
-
else
|
426
|
-
raise Errors::InvalidTypeError, "function #{mode}_by() expects values to be an numbers"
|
427
|
-
end
|
428
|
-
end
|
429
|
-
else
|
430
|
-
raise Errors::InvalidTypeError, "function #{mode}_by() expects an array and an expression"
|
431
|
-
end
|
432
|
-
else
|
433
|
-
raise Errors::InvalidArityError, "function #{mode}_by() expects two arguments"
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
def function_slice(values, *args)
|
438
|
-
if String === values || Array === values
|
439
|
-
_slice(values, *args)
|
440
|
-
else
|
441
|
-
nil
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
def _slice(values, start, stop, step)
|
446
|
-
start, stop, step = _adjust_slice(values.size, start, stop, step)
|
447
|
-
result = []
|
448
|
-
if step > 0
|
449
|
-
i = start
|
450
|
-
while i < stop
|
451
|
-
result << values[i]
|
452
|
-
i += step
|
453
|
-
end
|
454
|
-
else
|
455
|
-
i = start
|
456
|
-
while i > stop
|
457
|
-
result << values[i]
|
458
|
-
i += step
|
459
|
-
end
|
460
|
-
end
|
461
|
-
String === values ? result.join : result
|
462
|
-
end
|
463
|
-
|
464
|
-
def _adjust_slice(length, start, stop, step)
|
465
|
-
if step.nil?
|
466
|
-
step = 1
|
467
|
-
elsif step == 0
|
468
|
-
raise Errors::RuntimeError, 'slice step cannot be 0'
|
469
|
-
end
|
470
|
-
|
471
|
-
if start.nil?
|
472
|
-
start = step < 0 ? length - 1 : 0
|
473
|
-
else
|
474
|
-
start = _adjust_endpoint(length, start, step)
|
475
|
-
end
|
476
|
-
|
477
|
-
if stop.nil?
|
478
|
-
stop = step < 0 ? -1 : length
|
479
|
-
else
|
480
|
-
stop = _adjust_endpoint(length, stop, step)
|
481
|
-
end
|
482
|
-
|
483
|
-
[start, stop, step]
|
484
|
-
end
|
485
|
-
|
486
|
-
def _adjust_endpoint(length, endpoint, step)
|
487
|
-
if endpoint < 0
|
488
|
-
endpoint += length
|
489
|
-
endpoint = 0 if endpoint < 0
|
490
|
-
endpoint
|
491
|
-
elsif endpoint >= length
|
492
|
-
step < 0 ? length - 1 : length
|
493
|
-
else
|
494
|
-
endpoint
|
495
|
-
end
|
496
|
-
end
|
497
|
-
|
498
|
-
def compare_values(a, b)
|
499
|
-
if a == b
|
500
|
-
true
|
501
|
-
else
|
502
|
-
false
|
503
|
-
end
|
504
|
-
end
|
505
|
-
|
506
|
-
def is_int(value)
|
507
|
-
Integer === value
|
508
|
-
end
|
509
|
-
|
510
|
-
def get_type(value)
|
511
|
-
case
|
512
|
-
when ExprNode === value then 'expression'
|
513
|
-
when String === value then 'string'
|
514
|
-
when hash_like?(value) then 'object'
|
515
|
-
when Array === value then 'array'
|
516
|
-
when [true, false].include?(value) then 'boolean'
|
517
|
-
when value.nil? then 'null'
|
518
|
-
when Numeric === value then 'number'
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
|
-
end
|
523
|
-
end
|