cel 0.3.1 → 0.4.1
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/CHANGELOG.md +32 -1
- data/README.md +26 -3
- data/lib/cel/ast/cel_methods.rb +58 -0
- data/lib/cel/ast/elements/bool.rb +34 -0
- data/lib/cel/ast/elements/bytes.rb +34 -0
- data/lib/cel/ast/elements/condition.rb +22 -0
- data/lib/cel/ast/elements/duration.rb +114 -0
- data/lib/cel/ast/elements/function.rb +33 -0
- data/lib/cel/ast/elements/group.rb +19 -0
- data/lib/cel/ast/elements/identifier.rb +32 -0
- data/lib/cel/ast/elements/invoke.rb +64 -0
- data/lib/cel/ast/elements/list.rb +70 -0
- data/lib/cel/ast/elements/literal.rb +74 -0
- data/lib/cel/ast/elements/map.rb +120 -0
- data/lib/cel/ast/elements/message.rb +50 -0
- data/lib/cel/ast/elements/null.rb +11 -0
- data/lib/cel/ast/elements/number.rb +70 -0
- data/lib/cel/ast/elements/operation.rb +39 -0
- data/lib/cel/ast/elements/protobuf.rb +4 -0
- data/lib/cel/ast/elements/string.rb +54 -0
- data/lib/cel/ast/elements/timestamp.rb +109 -0
- data/lib/cel/ast/elements.rb +18 -828
- data/lib/cel/ast/types.rb +49 -4
- data/lib/cel/checker.rb +47 -31
- data/lib/cel/context.rb +1 -1
- data/lib/cel/environment.rb +15 -2
- data/lib/cel/errors.rb +11 -0
- data/lib/cel/extensions/bind.rb +35 -0
- data/lib/cel/extensions/encoders.rb +41 -0
- data/lib/cel/extensions/math.rb +211 -0
- data/lib/cel/extensions/string.rb +230 -0
- data/lib/cel/extensions.rb +15 -0
- data/lib/cel/parser.rb +14 -13
- data/lib/cel/program.rb +28 -13
- data/lib/cel/version.rb +1 -1
- data/lib/cel.rb +27 -12
- metadata +39 -2
data/lib/cel/ast/elements.rb
CHANGED
|
@@ -1,834 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "time"
|
|
4
|
-
require "delegate"
|
|
5
|
-
|
|
6
3
|
module Cel
|
|
7
4
|
LOGICAL_OPERATORS = %w[<= >= < > == != in].freeze
|
|
8
5
|
MULTI_OPERATORS = %w[* / %].freeze
|
|
9
|
-
|
|
10
|
-
class Identifier < SimpleDelegator
|
|
11
|
-
attr_reader :id
|
|
12
|
-
|
|
13
|
-
attr_accessor :type
|
|
14
|
-
|
|
15
|
-
def initialize(identifier, package = nil)
|
|
16
|
-
@id = identifier
|
|
17
|
-
@type = TYPES[:any]
|
|
18
|
-
@package = package
|
|
19
|
-
super(@id)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def ==(other)
|
|
23
|
-
super || other.to_s == @id.to_s
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def to_s
|
|
27
|
-
@id.to_s
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
alias_method :to_ruby_type, :to_s
|
|
31
|
-
|
|
32
|
-
def try_convert_to_proto_type
|
|
33
|
-
Protobuf.convert_to_proto_type(@id.to_s, @package)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
class Message
|
|
38
|
-
EMPTY_STRUCT = {}.freeze
|
|
39
|
-
|
|
40
|
-
attr_reader :name, :struct, :package, :depth
|
|
41
|
-
|
|
42
|
-
def initialize(name, struct, package: nil, depth: 1)
|
|
43
|
-
@struct = struct ? struct.transform_keys(&:to_sym) : EMPTY_STRUCT
|
|
44
|
-
@name = name
|
|
45
|
-
@package = package
|
|
46
|
-
@depth = depth
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def type
|
|
50
|
-
@type ||= if proto_type
|
|
51
|
-
Protobuf.convert_to_cel_type(proto_type, @package) || proto_type
|
|
52
|
-
else
|
|
53
|
-
Protobuf.try_convert_from_wrapper_type(@name.to_s)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def message_type
|
|
58
|
-
@message_type ||= proto_type || container
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def proto_type
|
|
62
|
-
@proto_type ||= Protobuf.convert_to_proto_type(@name.to_s, @package)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def ==(other)
|
|
66
|
-
other.is_a?(Cel::Message) && other.name == @name && other.struct == @struct
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def to_s
|
|
70
|
-
if proto_type
|
|
71
|
-
proto_type.descriptor.name
|
|
72
|
-
else
|
|
73
|
-
message_type.to_s
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
private
|
|
78
|
-
|
|
79
|
-
def container
|
|
80
|
-
@container ||= Cel.message_container(@name, @struct)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
class Invoke
|
|
85
|
-
attr_accessor :var
|
|
86
|
-
|
|
87
|
-
attr_reader :func, :args, :depth
|
|
88
|
-
|
|
89
|
-
def self.new(func:, var: nil, args: nil, **rest)
|
|
90
|
-
Protobuf.try_invoke_from(var, func, args) || super
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def type
|
|
94
|
-
TYPES[:any]
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def initialize(func:, var: nil, args: nil, depth: 1, package: nil)
|
|
98
|
-
@var = var
|
|
99
|
-
@func = func.to_sym
|
|
100
|
-
@args = args
|
|
101
|
-
@package = package
|
|
102
|
-
@depth = depth
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def ==(other)
|
|
106
|
-
case other
|
|
107
|
-
when Invoke
|
|
108
|
-
@var == other.var && @func == other.func && @args == other.args
|
|
109
|
-
when Array
|
|
110
|
-
[@var, @func, @args].compact == other
|
|
111
|
-
else
|
|
112
|
-
super
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def to_s
|
|
117
|
-
if var
|
|
118
|
-
if indexing?
|
|
119
|
-
"#{var}[#{args}]"
|
|
120
|
-
else
|
|
121
|
-
"#{var}.#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
|
|
122
|
-
end
|
|
123
|
-
else
|
|
124
|
-
"#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def try_convert_to_proto_type
|
|
129
|
-
return unless @var
|
|
130
|
-
|
|
131
|
-
proto_type = Protobuf.convert_to_proto_type(@var.to_s, @package)
|
|
132
|
-
|
|
133
|
-
return unless proto_type
|
|
134
|
-
|
|
135
|
-
return proto_type unless proto_type.const_defined?(@func)
|
|
136
|
-
|
|
137
|
-
proto_type.const_get(@func)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def indexing?
|
|
141
|
-
@func == :[]
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
class Function
|
|
146
|
-
attr_reader :label, :types, :type
|
|
147
|
-
|
|
148
|
-
def initialize(*types, label: nil, return_type: nil, &func)
|
|
149
|
-
unless func.nil?
|
|
150
|
-
types = Array.new(func.arity) { TYPES[:any] } if types.empty?
|
|
151
|
-
raise(Error, "number of arg types does not match number of yielded args") unless types.size == func.arity
|
|
152
|
-
end
|
|
153
|
-
@types = types.map { |typ| typ.is_a?(Type) ? typ : TYPES[typ] }
|
|
154
|
-
@type = if return_type.nil?
|
|
155
|
-
TYPES[:any]
|
|
156
|
-
else
|
|
157
|
-
return_type.is_a?(Type) ? return_type : TYPES[return_type]
|
|
158
|
-
end
|
|
159
|
-
@func = func
|
|
160
|
-
@label = label
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def call(*args)
|
|
164
|
-
Literal.to_cel_type(@func.call(*args))
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
mod = self
|
|
169
|
-
mod.define_singleton_method(:Function) do |*args, **kwargs, &blk|
|
|
170
|
-
mod::Function.new(*args, **kwargs, &blk)
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
class Literal < SimpleDelegator
|
|
174
|
-
attr_reader :type, :value
|
|
175
|
-
|
|
176
|
-
def initialize(type, value)
|
|
177
|
-
@type = type.is_a?(Type) ? type : TYPES[type]
|
|
178
|
-
@value = value
|
|
179
|
-
super(value)
|
|
180
|
-
check
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def ==(other)
|
|
184
|
-
case other
|
|
185
|
-
when Literal
|
|
186
|
-
@type == other.type && @value == other.value
|
|
187
|
-
else
|
|
188
|
-
@value == other
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def self.to_cel_type(val)
|
|
193
|
-
if val.is_a?(Protobuf.base_class) ||
|
|
194
|
-
val.is_a?(Struct) # no-protobuf mode
|
|
195
|
-
val = Protobuf.try_convert_from_wrapper(val)
|
|
196
|
-
|
|
197
|
-
return val if val.is_a?(Protobuf.base_class) # still
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
case val
|
|
201
|
-
when Literal, Identifier, Invoke, Operation, Condition, Group, # already cel
|
|
202
|
-
Protobuf.enum_class # already a usable class a la protobuf
|
|
203
|
-
val
|
|
204
|
-
when Protobuf.map_class
|
|
205
|
-
Map.new(val.to_h)
|
|
206
|
-
when ::String
|
|
207
|
-
String.new(val)
|
|
208
|
-
when ::Symbol
|
|
209
|
-
Identifier.new(val)
|
|
210
|
-
when ::Integer
|
|
211
|
-
Number.new(:int, val)
|
|
212
|
-
when ::Float, ::BigDecimal
|
|
213
|
-
Number.new(:double, val)
|
|
214
|
-
when ::Hash
|
|
215
|
-
Map.new(val)
|
|
216
|
-
when ::Array
|
|
217
|
-
List.new(val)
|
|
218
|
-
when true, false
|
|
219
|
-
Bool.cast(val)
|
|
220
|
-
when nil
|
|
221
|
-
Null::INSTANCE
|
|
222
|
-
when Time
|
|
223
|
-
Timestamp.new(val)
|
|
224
|
-
else
|
|
225
|
-
raise BindingError, "can't convert #{val} to CEL type"
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
alias_method :to_ruby_type, :value
|
|
230
|
-
|
|
231
|
-
private
|
|
232
|
-
|
|
233
|
-
def check; end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
class Number < Literal
|
|
237
|
-
def ==(other)
|
|
238
|
-
case other
|
|
239
|
-
when Number
|
|
240
|
-
@value == other.value
|
|
241
|
-
else
|
|
242
|
-
super
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
([:+, :-, *(MULTI_OPERATORS - %w[%])] - %i[%]).each do |op|
|
|
247
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
248
|
-
def #{op}(other)
|
|
249
|
-
value = super
|
|
250
|
-
check_overflow(value)
|
|
251
|
-
Number.new(@type, value)
|
|
252
|
-
end
|
|
253
|
-
OUT
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
def %(other)
|
|
257
|
-
raise EvaluateError, "no matching overload" if @type == :double && other.type == :double
|
|
258
|
-
|
|
259
|
-
value = if negative?
|
|
260
|
-
if other.negative?
|
|
261
|
-
-(abs % other.abs)
|
|
262
|
-
else
|
|
263
|
-
-(abs % other)
|
|
264
|
-
end
|
|
265
|
-
else
|
|
266
|
-
@value % other.abs
|
|
267
|
-
end
|
|
268
|
-
check_overflow(value)
|
|
269
|
-
Number.new(@type, value)
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
%i[+ -].each do |op|
|
|
273
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
274
|
-
def #{op}@
|
|
275
|
-
raise EvaluateError, "no such overload" if @type == TYPES[:uint]
|
|
276
|
-
value = super
|
|
277
|
-
Number.new(@type, value)
|
|
278
|
-
end
|
|
279
|
-
OUT
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
def check_overflow(value = @value)
|
|
283
|
-
case @type
|
|
284
|
-
when TYPES[:double]
|
|
285
|
-
return if value.nan? || value.infinite?
|
|
286
|
-
raise EvaluateError, "return error for overflow" unless (-(MAX_FLOAT - 1)...MAX_FLOAT).cover?(value)
|
|
287
|
-
when TYPES[:uint]
|
|
288
|
-
raise EvaluateError, "return error for overflow" unless (0...MAX_INT).cover?(value)
|
|
289
|
-
else
|
|
290
|
-
raise EvaluateError, "return error for overflow" unless (-(MAX_INT - 1)...MAX_INT).cover?(value)
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
class Bool < Literal
|
|
296
|
-
def initialize(value)
|
|
297
|
-
super(:bool, value)
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
(LOGICAL_OPERATORS - %w[== != in]).each do |op|
|
|
301
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
302
|
-
def #{op}(other)
|
|
303
|
-
return super unless other.is_a?(Bool)
|
|
304
|
-
|
|
305
|
-
lhs = @value ? 1 : 0
|
|
306
|
-
rhs = other.value ? 1 : 0
|
|
307
|
-
lhs.__send__(__method__, rhs)
|
|
308
|
-
end
|
|
309
|
-
OUT
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
def !
|
|
313
|
-
Bool.cast(super)
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
TRUE_VALUE = new(true)
|
|
317
|
-
FALSE_VALUE = new(false)
|
|
318
|
-
|
|
319
|
-
def self.cast(val)
|
|
320
|
-
val ? TRUE_VALUE : FALSE_VALUE
|
|
321
|
-
end
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
class Null < Literal
|
|
325
|
-
def initialize
|
|
326
|
-
super(:null_type, nil)
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
INSTANCE = new.freeze
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
class String < Literal
|
|
333
|
-
def initialize(value)
|
|
334
|
-
super(:string, value)
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
# override base String#%
|
|
338
|
-
def %(_other)
|
|
339
|
-
raise NoMethodError, "undefined method '%' for #{self}"
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
# CEL string functions
|
|
343
|
-
|
|
344
|
-
def contains(string)
|
|
345
|
-
Bool.cast(@value.include?(string))
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
def endsWith(string)
|
|
349
|
-
Bool.cast(@value.end_with?(string))
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
def startsWith(string)
|
|
353
|
-
Bool.cast(@value.start_with?(string))
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def matches(pattern)
|
|
357
|
-
Macro.matches(self, pattern)
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
%i[+ -].each do |op|
|
|
361
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
362
|
-
def #{op}(other)
|
|
363
|
-
String.new(super)
|
|
364
|
-
end
|
|
365
|
-
OUT
|
|
366
|
-
end
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
class Bytes < Literal
|
|
370
|
-
attr_reader :string
|
|
371
|
-
|
|
372
|
-
def initialize(value)
|
|
373
|
-
super(:bytes, value)
|
|
374
|
-
@string = value.pack("C*")
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
def to_ary
|
|
378
|
-
[self]
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
alias_method :to_ruby_type, :string
|
|
382
|
-
|
|
383
|
-
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
|
384
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
385
|
-
def #{op}(other)
|
|
386
|
-
other.is_a?(Cel::Bytes) ? Bool.cast(@string.__send__(__method__, other.string)) : super
|
|
387
|
-
end
|
|
388
|
-
OUT
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
%i[+ -].each do |op|
|
|
392
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
393
|
-
def #{op}(other)
|
|
394
|
-
Bytes.new(@value + other.value)
|
|
395
|
-
end
|
|
396
|
-
OUT
|
|
397
|
-
end
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
class List < Literal
|
|
401
|
-
attr_reader :depth
|
|
402
|
-
|
|
403
|
-
def initialize(value, depth: 1)
|
|
404
|
-
value = value.map do |v|
|
|
405
|
-
Literal.to_cel_type(v)
|
|
406
|
-
end
|
|
407
|
-
super(TYPES[:list], value)
|
|
408
|
-
@depth = depth
|
|
409
|
-
end
|
|
410
|
-
|
|
411
|
-
def [](key)
|
|
412
|
-
case key
|
|
413
|
-
when Number
|
|
414
|
-
if key.type == :double
|
|
415
|
-
val = key.value
|
|
416
|
-
|
|
417
|
-
raise InvalidArgumentError, key unless (val % 1).zero?
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
super
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
def to_ary
|
|
425
|
-
[self]
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
def to_ruby_type
|
|
429
|
-
value.map(&:to_ruby_type)
|
|
430
|
-
end
|
|
431
|
-
|
|
432
|
-
%i[+ -].each do |op|
|
|
433
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
434
|
-
def #{op}(other)
|
|
435
|
-
List.new(@value.send(__method__, other.value))
|
|
436
|
-
end
|
|
437
|
-
OUT
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
def by_max_depth
|
|
441
|
-
max = @value.max { |a1, a2| calc_depth(a1, 0) <=> calc_depth(a2, 0) }
|
|
442
|
-
|
|
443
|
-
# return the last value if all options have the same depth
|
|
444
|
-
return @value.last if (max == @value.first) && (calc_depth(max, 0) == calc_depth(@value.last, 0))
|
|
445
|
-
|
|
446
|
-
max
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
private
|
|
450
|
-
|
|
451
|
-
def calc_depth(element, acc)
|
|
452
|
-
case element
|
|
453
|
-
when List
|
|
454
|
-
element.value.map { |el| calc_depth(el, acc + 1) }.max || (acc + 1)
|
|
455
|
-
when Map
|
|
456
|
-
element.value.map { |(_, el)| calc_depth(el, acc + 1) }.max || (acc + 1)
|
|
457
|
-
else
|
|
458
|
-
acc
|
|
459
|
-
end
|
|
460
|
-
end
|
|
461
|
-
end
|
|
462
|
-
|
|
463
|
-
class Map < Literal
|
|
464
|
-
ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
|
|
465
|
-
|
|
466
|
-
attr_reader :depth
|
|
467
|
-
|
|
468
|
-
def initialize(value, depth: 1)
|
|
469
|
-
# store array internally to support repeated keys
|
|
470
|
-
value = value.map do |k, v|
|
|
471
|
-
[Literal.to_cel_type(k), Literal.to_cel_type(v)]
|
|
472
|
-
end
|
|
473
|
-
super(TYPES[:map], value)
|
|
474
|
-
@depth = depth
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
def ==(other)
|
|
478
|
-
case other
|
|
479
|
-
when Map
|
|
480
|
-
@value.all? do |args|
|
|
481
|
-
other.value.include?(args)
|
|
482
|
-
end
|
|
483
|
-
when Array
|
|
484
|
-
# calls to != will downgrade maps to arrays
|
|
485
|
-
@value.all? do |args|
|
|
486
|
-
other.include?(args)
|
|
487
|
-
end
|
|
488
|
-
else
|
|
489
|
-
super
|
|
490
|
-
end
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
(LOGICAL_OPERATORS - %w[== != in]).each do |op|
|
|
494
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
495
|
-
def #{op}(*)
|
|
496
|
-
raise NoMethodError
|
|
497
|
-
end
|
|
498
|
-
OUT
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
def key?(key)
|
|
502
|
-
@value.any? { |k, _v| k == key }
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
alias_method :include?, :key?
|
|
506
|
-
|
|
507
|
-
def [](key)
|
|
508
|
-
values = @value.filter_map { |k, v| v if k == key }
|
|
509
|
-
|
|
510
|
-
raise NoSuchKeyError.new(self, key) if values.empty?
|
|
511
|
-
|
|
512
|
-
raise EvaluateError, "Failed with repeated key" if values.size > 1
|
|
513
|
-
|
|
514
|
-
values.first
|
|
515
|
-
end
|
|
516
|
-
|
|
517
|
-
def to_ruby_type
|
|
518
|
-
value.to_h { |args| args.map(&:to_ruby_type) }
|
|
519
|
-
end
|
|
520
|
-
|
|
521
|
-
def respond_to_missing?(meth, *args)
|
|
522
|
-
return true if super
|
|
523
|
-
return false unless @value
|
|
524
|
-
|
|
525
|
-
meth_s = meth.to_s
|
|
526
|
-
|
|
527
|
-
@value.any? { |k, _| k == meth_s }
|
|
528
|
-
end
|
|
529
|
-
|
|
530
|
-
def method_missing(meth, *args)
|
|
531
|
-
return super unless @value
|
|
532
|
-
|
|
533
|
-
meth_s = meth.to_s
|
|
534
|
-
values = @value.filter_map { |k, v| v if k == meth_s }
|
|
535
|
-
|
|
536
|
-
return super if values.empty?
|
|
537
|
-
|
|
538
|
-
values.first
|
|
539
|
-
end
|
|
540
|
-
|
|
541
|
-
private
|
|
542
|
-
|
|
543
|
-
# For a map, the entry keys are sub-expressions that must evaluate to values
|
|
544
|
-
# of an allowed type (int, uint, bool, or string)
|
|
545
|
-
def check
|
|
546
|
-
return if @value.all? do |key, _|
|
|
547
|
-
key.is_a?(Identifier) || ALLOWED_TYPES.include?(key.type)
|
|
548
|
-
end
|
|
549
|
-
|
|
550
|
-
raise CheckError, "#{self} is invalid (keys must be of an allowed type (int, uint, bool, or string)"
|
|
551
|
-
end
|
|
552
|
-
end
|
|
553
|
-
|
|
554
|
-
class Timestamp < Literal
|
|
555
|
-
TIMESTAMP_RANGE = (Time.parse("0001-01-01T00:00:00Z")..Time.parse("9999-12-31T23:59:59.999999999Z"))
|
|
556
|
-
|
|
557
|
-
def initialize(value)
|
|
558
|
-
value = case value
|
|
559
|
-
when ::String then Time.parse(value)
|
|
560
|
-
when Numeric then Time.at(value)
|
|
561
|
-
else value
|
|
562
|
-
end
|
|
563
|
-
|
|
564
|
-
raise EvaluateError, "out of range" unless TIMESTAMP_RANGE.include?(value)
|
|
565
|
-
|
|
566
|
-
super(:timestamp, value.round(9))
|
|
567
|
-
end
|
|
568
|
-
|
|
569
|
-
def +(other)
|
|
570
|
-
Timestamp.new(@value + other.to_f)
|
|
571
|
-
end
|
|
572
|
-
|
|
573
|
-
def -(other)
|
|
574
|
-
case other
|
|
575
|
-
when Timestamp
|
|
576
|
-
Duration.new(@value - other.value)
|
|
577
|
-
when Duration
|
|
578
|
-
Timestamp.new(@value - other.to_f)
|
|
579
|
-
end
|
|
580
|
-
end
|
|
581
|
-
|
|
582
|
-
LOGICAL_OPERATORS.each do |op|
|
|
583
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
584
|
-
def #{op}(other)
|
|
585
|
-
other.is_a?(Cel::Literal) ? Bool.cast(super) : super
|
|
586
|
-
end
|
|
587
|
-
OUT
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
# Cel Functions
|
|
591
|
-
|
|
592
|
-
def getDate(tz = nil)
|
|
593
|
-
Number.new(:int, to_local_time(tz).day)
|
|
594
|
-
end
|
|
595
|
-
|
|
596
|
-
def getDayOfMonth(tz = nil)
|
|
597
|
-
getDate(tz) - 1
|
|
598
|
-
end
|
|
599
|
-
|
|
600
|
-
def getDayOfWeek(tz = nil)
|
|
601
|
-
Number.new(:int, to_local_time(tz).wday)
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
def getDayOfYear(tz = nil)
|
|
605
|
-
Number.new(:int, to_local_time(tz).yday - 1)
|
|
606
|
-
end
|
|
607
|
-
|
|
608
|
-
def getMonth(tz = nil)
|
|
609
|
-
Number.new(:int, to_local_time(tz).month - 1)
|
|
610
|
-
end
|
|
611
|
-
|
|
612
|
-
def getFullYear(tz = nil)
|
|
613
|
-
Number.new(:int, to_local_time(tz).year)
|
|
614
|
-
end
|
|
615
|
-
|
|
616
|
-
def getHours(tz = nil)
|
|
617
|
-
Number.new(:int, to_local_time(tz).hour)
|
|
618
|
-
end
|
|
619
|
-
|
|
620
|
-
def getMinutes(tz = nil)
|
|
621
|
-
Number.new(:int, to_local_time(tz).min)
|
|
622
|
-
end
|
|
623
|
-
|
|
624
|
-
def getSeconds(tz = nil)
|
|
625
|
-
Number.new(:int, to_local_time(tz).sec)
|
|
626
|
-
end
|
|
627
|
-
|
|
628
|
-
def getMilliseconds(tz = nil)
|
|
629
|
-
Number.new(:int, to_local_time(tz).nsec / 1_000_000)
|
|
630
|
-
end
|
|
631
|
-
|
|
632
|
-
def to_ruby_type
|
|
633
|
-
Protobuf.timestamp_class.from_time(@value)
|
|
634
|
-
end
|
|
635
|
-
|
|
636
|
-
private
|
|
637
|
-
|
|
638
|
-
def to_local_time(tz = nil)
|
|
639
|
-
time = @value
|
|
640
|
-
if tz
|
|
641
|
-
tz = tz.value
|
|
642
|
-
if tz.match?(/\A[+-]?\d{2,}:\d{2,}\z/)
|
|
643
|
-
tz.prepend("+") unless tz.start_with?("+", "-")
|
|
644
|
-
else
|
|
645
|
-
tz = TZInfo::Timezone.get(tz)
|
|
646
|
-
end
|
|
647
|
-
time = time.getlocal(tz)
|
|
648
|
-
end
|
|
649
|
-
time
|
|
650
|
-
end
|
|
651
|
-
end
|
|
652
|
-
|
|
653
|
-
class Duration < Literal
|
|
654
|
-
def initialize(value)
|
|
655
|
-
value =
|
|
656
|
-
case value
|
|
657
|
-
when ::String
|
|
658
|
-
init_from_string(value)
|
|
659
|
-
when Hash
|
|
660
|
-
seconds, nanos = value.values_at(:seconds, :nanos)
|
|
661
|
-
seconds ||= 0
|
|
662
|
-
nanos ||= 0
|
|
663
|
-
seconds + (nanos / 1_000_000_000.0)
|
|
664
|
-
else
|
|
665
|
-
value
|
|
666
|
-
end
|
|
667
|
-
|
|
668
|
-
raise EvaluateError, "out of range" unless (value * 1_000_000_000).between?(-MAX_INT, MAX_INT)
|
|
669
|
-
|
|
670
|
-
super(:duration, value)
|
|
671
|
-
end
|
|
672
|
-
|
|
673
|
-
def to_s
|
|
674
|
-
seconds = getSeconds
|
|
675
|
-
millis = getMilliseconds
|
|
676
|
-
millis.positive? ? "#{seconds}s#{millis}m" : "#{seconds}s"
|
|
677
|
-
end
|
|
678
|
-
|
|
679
|
-
def +(other)
|
|
680
|
-
case other
|
|
681
|
-
when Cel::Duration
|
|
682
|
-
Cel::Duration.new(@value + other.value)
|
|
683
|
-
when Cel::Timestamp
|
|
684
|
-
Cel::Timestamp.new(other.value + @value)
|
|
685
|
-
end
|
|
686
|
-
end
|
|
687
|
-
|
|
688
|
-
def -(other)
|
|
689
|
-
case other
|
|
690
|
-
when Cel::Duration
|
|
691
|
-
Cel::Duration.new(@value - other.value)
|
|
692
|
-
else
|
|
693
|
-
raise Error, "invalid operand #{other} for `-`"
|
|
694
|
-
end
|
|
695
|
-
end
|
|
696
|
-
|
|
697
|
-
LOGICAL_OPERATORS.each do |op|
|
|
698
|
-
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
699
|
-
def #{op}(other)
|
|
700
|
-
case other
|
|
701
|
-
when Cel::Literal
|
|
702
|
-
Bool.cast(super)
|
|
703
|
-
when Numeric
|
|
704
|
-
@value == other
|
|
705
|
-
|
|
706
|
-
else
|
|
707
|
-
super
|
|
708
|
-
end
|
|
709
|
-
end
|
|
710
|
-
OUT
|
|
711
|
-
end
|
|
712
|
-
|
|
713
|
-
# Cel Functions
|
|
714
|
-
|
|
715
|
-
def getHours
|
|
716
|
-
Cel::Number.new(:int, (getMinutes / 60).to_i)
|
|
717
|
-
end
|
|
718
|
-
|
|
719
|
-
def getMinutes
|
|
720
|
-
Cel::Number.new(:int, (getSeconds / 60).to_i)
|
|
721
|
-
end
|
|
722
|
-
|
|
723
|
-
def getSeconds
|
|
724
|
-
Cel::Number.new(:int, @value.divmod(1).first)
|
|
725
|
-
end
|
|
726
|
-
|
|
727
|
-
def getMilliseconds
|
|
728
|
-
Cel::Number.new(:int, (@value.divmod(1).last * 1000).round)
|
|
729
|
-
end
|
|
730
|
-
|
|
731
|
-
def to_ruby_type
|
|
732
|
-
Protobuf.duration_class.new(seconds: getSeconds.value, nanos: getMilliseconds.value * 1_000_000_000.0)
|
|
733
|
-
end
|
|
734
|
-
|
|
735
|
-
private
|
|
736
|
-
|
|
737
|
-
def init_from_string(value)
|
|
738
|
-
seconds = 0
|
|
739
|
-
nanos = 0
|
|
740
|
-
value.scan(/([0-9]*(?:\.[0-9]*)?)([a-z]+)/) do |duration, units|
|
|
741
|
-
case units
|
|
742
|
-
when "h"
|
|
743
|
-
seconds += Cel.to_numeric(duration) * 60 * 60
|
|
744
|
-
when "m"
|
|
745
|
-
seconds += Cel.to_numeric(duration) * 60
|
|
746
|
-
when "s"
|
|
747
|
-
seconds += Cel.to_numeric(duration)
|
|
748
|
-
when "ms"
|
|
749
|
-
nanos += Cel.to_numeric(duration) * 1000 * 1000
|
|
750
|
-
when "us"
|
|
751
|
-
nanos += Cel.to_numeric(duration) * 1000
|
|
752
|
-
when "ns"
|
|
753
|
-
nanos += Cel.to_numeric(duration)
|
|
754
|
-
else
|
|
755
|
-
raise EvaluateError, "#{units} is unsupported"
|
|
756
|
-
end
|
|
757
|
-
end
|
|
758
|
-
duration = seconds + (nanos / 1_000_000_000.0)
|
|
759
|
-
duration = -duration if value.start_with?("-")
|
|
760
|
-
duration
|
|
761
|
-
end
|
|
762
|
-
end
|
|
763
|
-
|
|
764
|
-
class Group
|
|
765
|
-
attr_reader :value
|
|
766
|
-
|
|
767
|
-
def initialize(value)
|
|
768
|
-
@value = value
|
|
769
|
-
end
|
|
770
|
-
|
|
771
|
-
def type
|
|
772
|
-
TYPES[:any]
|
|
773
|
-
end
|
|
774
|
-
|
|
775
|
-
def ==(other)
|
|
776
|
-
other.is_a?(Group) && @value == other.value
|
|
777
|
-
end
|
|
778
|
-
end
|
|
779
|
-
|
|
780
|
-
class Operation
|
|
781
|
-
attr_reader :op, :operands, :depth
|
|
782
|
-
|
|
783
|
-
attr_accessor :type
|
|
784
|
-
|
|
785
|
-
def initialize(op, operands, depth: 1)
|
|
786
|
-
@op = op
|
|
787
|
-
@operands = operands
|
|
788
|
-
@type = TYPES[:any]
|
|
789
|
-
@depth = depth
|
|
790
|
-
end
|
|
791
|
-
|
|
792
|
-
def ==(other)
|
|
793
|
-
case other
|
|
794
|
-
when Array
|
|
795
|
-
other.size == @operands.size + 1 &&
|
|
796
|
-
other.first == @op &&
|
|
797
|
-
other.slice(1..-1).zip(@operands).all? { |x1, x2| x1 == x2 }
|
|
798
|
-
when Operation
|
|
799
|
-
@op == other.op && @type == other.type && @operands == other.operands
|
|
800
|
-
else
|
|
801
|
-
super
|
|
802
|
-
end
|
|
803
|
-
end
|
|
804
|
-
|
|
805
|
-
def unary?
|
|
806
|
-
@operands.size == 1
|
|
807
|
-
end
|
|
808
|
-
|
|
809
|
-
def to_s
|
|
810
|
-
return "#{@op}#{@operands.first}" if @operands.size == 1
|
|
811
|
-
|
|
812
|
-
@operands.join(" #{@op} ")
|
|
813
|
-
end
|
|
814
|
-
end
|
|
815
|
-
|
|
816
|
-
class Condition
|
|
817
|
-
attr_reader :if, :then, :else, :depth
|
|
818
|
-
|
|
819
|
-
def initialize(if_, then_, else_, depth: 1)
|
|
820
|
-
@if = if_
|
|
821
|
-
@then = then_
|
|
822
|
-
@else = else_
|
|
823
|
-
@depth = depth
|
|
824
|
-
end
|
|
825
|
-
|
|
826
|
-
def type
|
|
827
|
-
TYPES[:any]
|
|
828
|
-
end
|
|
829
|
-
|
|
830
|
-
def ==(other)
|
|
831
|
-
other.is_a?(Condition) && @if == other.if && @then == other.then && @else == other.else
|
|
832
|
-
end
|
|
833
|
-
end
|
|
834
6
|
end
|
|
7
|
+
|
|
8
|
+
require_relative "elements/group"
|
|
9
|
+
require_relative "elements/condition"
|
|
10
|
+
require_relative "elements/operation"
|
|
11
|
+
require_relative "elements/invoke"
|
|
12
|
+
require_relative "elements/message"
|
|
13
|
+
require_relative "elements/identifier"
|
|
14
|
+
require_relative "elements/function"
|
|
15
|
+
require_relative "elements/literal"
|
|
16
|
+
require_relative "elements/null"
|
|
17
|
+
require_relative "elements/number"
|
|
18
|
+
require_relative "elements/bool"
|
|
19
|
+
require_relative "elements/string"
|
|
20
|
+
require_relative "elements/bytes"
|
|
21
|
+
require_relative "elements/list"
|
|
22
|
+
require_relative "elements/map"
|
|
23
|
+
require_relative "elements/timestamp"
|
|
24
|
+
require_relative "elements/duration"
|