cel 0.2.3 → 0.3.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 +28 -0
- data/README.md +62 -8
- data/lib/cel/ast/elements/protobuf.rb +438 -90
- data/lib/cel/ast/elements.rb +367 -121
- data/lib/cel/ast/types.rb +153 -17
- data/lib/cel/checker.rb +133 -30
- data/lib/cel/context.rb +60 -12
- data/lib/cel/encoder.rb +31 -13
- data/lib/cel/environment.rb +63 -13
- data/lib/cel/errors.rb +22 -1
- data/lib/cel/macro.rb +94 -28
- data/lib/cel/parser.rb +603 -387
- data/lib/cel/program.rb +262 -48
- data/lib/cel/version.rb +1 -1
- data/lib/cel.rb +232 -1
- metadata +19 -10
- data/lib/cel/parser.tab.rb +0 -1023
data/lib/cel/ast/elements.rb
CHANGED
@@ -4,18 +4,6 @@ require "time"
|
|
4
4
|
require "delegate"
|
5
5
|
|
6
6
|
module Cel
|
7
|
-
begin
|
8
|
-
require_relative "elements/protobuf"
|
9
|
-
rescue LoadError
|
10
|
-
module Protobuf
|
11
|
-
module_function
|
12
|
-
|
13
|
-
def method_missing(*) # rubocop:disable Style/MissingRespondToMissing
|
14
|
-
raise Error, "\"google/protobuf\" is required in order to use this feature"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
7
|
LOGICAL_OPERATORS = %w[<= >= < > == != in].freeze
|
20
8
|
MULTI_OPERATORS = %w[* / %].freeze
|
21
9
|
|
@@ -24,9 +12,10 @@ module Cel
|
|
24
12
|
|
25
13
|
attr_accessor :type
|
26
14
|
|
27
|
-
def initialize(identifier)
|
15
|
+
def initialize(identifier, package = nil)
|
28
16
|
@id = identifier
|
29
17
|
@type = TYPES[:any]
|
18
|
+
@package = package
|
30
19
|
super(@id)
|
31
20
|
end
|
32
21
|
|
@@ -37,59 +26,80 @@ module Cel
|
|
37
26
|
def to_s
|
38
27
|
@id.to_s
|
39
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
|
40
35
|
end
|
41
36
|
|
42
|
-
class Message
|
43
|
-
|
37
|
+
class Message
|
38
|
+
EMPTY_STRUCT = {}.freeze
|
44
39
|
|
45
|
-
|
46
|
-
value = convert_from_type(type, struct)
|
47
|
-
return value if value.is_a?(Null) || value != struct
|
40
|
+
attr_reader :name, :struct, :package, :depth
|
48
41
|
|
49
|
-
|
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
|
50
47
|
end
|
51
48
|
|
52
|
-
def
|
53
|
-
@
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
58
55
|
end
|
59
56
|
|
60
|
-
def
|
61
|
-
|
57
|
+
def message_type
|
58
|
+
@message_type ||= proto_type || container
|
62
59
|
end
|
63
60
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
71
72
|
else
|
72
|
-
|
73
|
-
MapType.new(struct.to_h do |k, v|
|
74
|
-
[Literal.to_cel_type(k), Literal.to_cel_type(v)]
|
75
|
-
end),
|
76
|
-
Struct.new(*struct.keys.map(&:to_sym)).new(*struct.values),
|
77
|
-
]
|
73
|
+
message_type.to_s
|
78
74
|
end
|
79
75
|
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def container
|
80
|
+
@container ||= Cel.message_container(@name, @struct)
|
81
|
+
end
|
80
82
|
end
|
81
83
|
|
82
84
|
class Invoke
|
83
|
-
|
85
|
+
attr_accessor :var
|
86
|
+
|
87
|
+
attr_reader :func, :args, :depth
|
84
88
|
|
85
|
-
def self.new(func:, var: nil, args: nil)
|
89
|
+
def self.new(func:, var: nil, args: nil, **rest)
|
86
90
|
Protobuf.try_invoke_from(var, func, args) || super
|
87
91
|
end
|
88
92
|
|
89
|
-
def
|
93
|
+
def type
|
94
|
+
TYPES[:any]
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(func:, var: nil, args: nil, depth: 1, package: nil)
|
90
98
|
@var = var
|
91
99
|
@func = func.to_sym
|
92
100
|
@args = args
|
101
|
+
@package = package
|
102
|
+
@depth = depth
|
93
103
|
end
|
94
104
|
|
95
105
|
def ==(other)
|
@@ -105,7 +115,7 @@ module Cel
|
|
105
115
|
|
106
116
|
def to_s
|
107
117
|
if var
|
108
|
-
if
|
118
|
+
if indexing?
|
109
119
|
"#{var}[#{args}]"
|
110
120
|
else
|
111
121
|
"#{var}.#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
|
@@ -114,12 +124,28 @@ module Cel
|
|
114
124
|
"#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
|
115
125
|
end
|
116
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
|
117
143
|
end
|
118
144
|
|
119
145
|
class Function
|
120
|
-
attr_reader :types, :type
|
146
|
+
attr_reader :label, :types, :type
|
121
147
|
|
122
|
-
def initialize(*types, return_type: nil, &func)
|
148
|
+
def initialize(*types, label: nil, return_type: nil, &func)
|
123
149
|
unless func.nil?
|
124
150
|
types = Array.new(func.arity) { TYPES[:any] } if types.empty?
|
125
151
|
raise(Error, "number of arg types does not match number of yielded args") unless types.size == func.arity
|
@@ -131,6 +157,7 @@ module Cel
|
|
131
157
|
return_type.is_a?(Type) ? return_type : TYPES[return_type]
|
132
158
|
end
|
133
159
|
@func = func
|
160
|
+
@label = label
|
134
161
|
end
|
135
162
|
|
136
163
|
def call(*args)
|
@@ -154,17 +181,28 @@ module Cel
|
|
154
181
|
end
|
155
182
|
|
156
183
|
def ==(other)
|
157
|
-
|
158
|
-
|
184
|
+
case other
|
185
|
+
when Literal
|
186
|
+
@type == other.type && @value == other.value
|
187
|
+
else
|
188
|
+
@value == other
|
189
|
+
end
|
159
190
|
end
|
160
191
|
|
161
192
|
def self.to_cel_type(val)
|
162
|
-
|
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
|
163
199
|
|
164
200
|
case val
|
165
|
-
when Literal, Identifier
|
201
|
+
when Literal, Identifier, Invoke, Operation, Condition, Group, # already cel
|
202
|
+
Protobuf.enum_class # already a usable class a la protobuf
|
166
203
|
val
|
167
|
-
|
204
|
+
when Protobuf.map_class
|
205
|
+
Map.new(val.to_h)
|
168
206
|
when ::String
|
169
207
|
String.new(val)
|
170
208
|
when ::Symbol
|
@@ -178,9 +216,9 @@ module Cel
|
|
178
216
|
when ::Array
|
179
217
|
List.new(val)
|
180
218
|
when true, false
|
181
|
-
Bool.
|
219
|
+
Bool.cast(val)
|
182
220
|
when nil
|
183
|
-
Null
|
221
|
+
Null::INSTANCE
|
184
222
|
when Time
|
185
223
|
Timestamp.new(val)
|
186
224
|
else
|
@@ -188,9 +226,7 @@ module Cel
|
|
188
226
|
end
|
189
227
|
end
|
190
228
|
|
191
|
-
|
192
|
-
@value
|
193
|
-
end
|
229
|
+
alias_method :to_ruby_type, :value
|
194
230
|
|
195
231
|
private
|
196
232
|
|
@@ -198,21 +234,62 @@ module Cel
|
|
198
234
|
end
|
199
235
|
|
200
236
|
class Number < Literal
|
201
|
-
|
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|
|
202
247
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
203
248
|
def #{op}(other)
|
204
|
-
|
249
|
+
value = super
|
250
|
+
check_overflow(value)
|
251
|
+
Number.new(@type, value)
|
205
252
|
end
|
206
253
|
OUT
|
207
254
|
end
|
208
255
|
|
209
|
-
|
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|
|
210
273
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
211
|
-
def #{op}
|
212
|
-
|
274
|
+
def #{op}@
|
275
|
+
raise EvaluateError, "no such overload" if @type == TYPES[:uint]
|
276
|
+
value = super
|
277
|
+
Number.new(@type, value)
|
213
278
|
end
|
214
279
|
OUT
|
215
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
|
216
293
|
end
|
217
294
|
|
218
295
|
class Bool < Literal
|
@@ -220,16 +297,27 @@ module Cel
|
|
220
297
|
super(:bool, value)
|
221
298
|
end
|
222
299
|
|
223
|
-
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
300
|
+
(LOGICAL_OPERATORS - %w[== != in]).each do |op|
|
224
301
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
225
302
|
def #{op}(other)
|
226
|
-
|
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)
|
227
308
|
end
|
228
309
|
OUT
|
229
310
|
end
|
230
311
|
|
231
312
|
def !
|
232
|
-
Bool.
|
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
|
233
321
|
end
|
234
322
|
end
|
235
323
|
|
@@ -237,6 +325,8 @@ module Cel
|
|
237
325
|
def initialize
|
238
326
|
super(:null_type, nil)
|
239
327
|
end
|
328
|
+
|
329
|
+
INSTANCE = new.freeze
|
240
330
|
end
|
241
331
|
|
242
332
|
class String < Literal
|
@@ -244,18 +334,23 @@ module Cel
|
|
244
334
|
super(:string, value)
|
245
335
|
end
|
246
336
|
|
337
|
+
# override base String#%
|
338
|
+
def %(_other)
|
339
|
+
raise NoMethodError, "undefined method '%' for #{self}"
|
340
|
+
end
|
341
|
+
|
247
342
|
# CEL string functions
|
248
343
|
|
249
344
|
def contains(string)
|
250
|
-
Bool.
|
345
|
+
Bool.cast(@value.include?(string))
|
251
346
|
end
|
252
347
|
|
253
348
|
def endsWith(string)
|
254
|
-
Bool.
|
349
|
+
Bool.cast(@value.end_with?(string))
|
255
350
|
end
|
256
351
|
|
257
352
|
def startsWith(string)
|
258
|
-
Bool.
|
353
|
+
Bool.cast(@value.start_with?(string))
|
259
354
|
end
|
260
355
|
|
261
356
|
def matches(pattern)
|
@@ -272,18 +367,23 @@ module Cel
|
|
272
367
|
end
|
273
368
|
|
274
369
|
class Bytes < Literal
|
370
|
+
attr_reader :string
|
371
|
+
|
275
372
|
def initialize(value)
|
276
373
|
super(:bytes, value)
|
374
|
+
@string = value.pack("C*")
|
277
375
|
end
|
278
376
|
|
279
377
|
def to_ary
|
280
378
|
[self]
|
281
379
|
end
|
282
380
|
|
381
|
+
alias_method :to_ruby_type, :string
|
382
|
+
|
283
383
|
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
284
384
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
285
385
|
def #{op}(other)
|
286
|
-
Bool.
|
386
|
+
other.is_a?(Cel::Bytes) ? Bool.cast(@string.__send__(__method__, other.string)) : super
|
287
387
|
end
|
288
388
|
OUT
|
289
389
|
end
|
@@ -291,18 +391,34 @@ module Cel
|
|
291
391
|
%i[+ -].each do |op|
|
292
392
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
293
393
|
def #{op}(other)
|
294
|
-
|
394
|
+
Bytes.new(@value + other.value)
|
295
395
|
end
|
296
396
|
OUT
|
297
397
|
end
|
298
398
|
end
|
299
399
|
|
300
400
|
class List < Literal
|
301
|
-
|
401
|
+
attr_reader :depth
|
402
|
+
|
403
|
+
def initialize(value, depth: 1)
|
302
404
|
value = value.map do |v|
|
303
405
|
Literal.to_cel_type(v)
|
304
406
|
end
|
305
|
-
super(
|
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
|
306
422
|
end
|
307
423
|
|
308
424
|
def to_ary
|
@@ -312,65 +428,142 @@ module Cel
|
|
312
428
|
def to_ruby_type
|
313
429
|
value.map(&:to_ruby_type)
|
314
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
|
315
461
|
end
|
316
462
|
|
317
463
|
class Map < Literal
|
318
|
-
|
319
|
-
|
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|
|
320
471
|
[Literal.to_cel_type(k), Literal.to_cel_type(v)]
|
321
472
|
end
|
322
|
-
super(
|
323
|
-
|
473
|
+
super(TYPES[:map], value)
|
474
|
+
@depth = depth
|
324
475
|
end
|
325
476
|
|
326
477
|
def ==(other)
|
327
|
-
|
328
|
-
|
329
|
-
@value.
|
330
|
-
|
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
|
331
491
|
end
|
332
492
|
|
333
|
-
|
334
|
-
|
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
|
335
515
|
end
|
336
516
|
|
337
517
|
def to_ruby_type
|
338
|
-
value.to_h {
|
518
|
+
value.to_h { |args| args.map(&:to_ruby_type) }
|
339
519
|
end
|
340
520
|
|
341
521
|
def respond_to_missing?(meth, *args)
|
342
|
-
|
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 }
|
343
528
|
end
|
344
529
|
|
345
530
|
def method_missing(meth, *args)
|
346
531
|
return super unless @value
|
347
532
|
|
348
|
-
|
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?
|
349
537
|
|
350
|
-
|
538
|
+
values.first
|
351
539
|
end
|
352
540
|
|
353
541
|
private
|
354
542
|
|
355
|
-
ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
|
356
|
-
|
357
543
|
# For a map, the entry keys are sub-expressions that must evaluate to values
|
358
544
|
# of an allowed type (int, uint, bool, or string)
|
359
545
|
def check
|
360
|
-
return if @value.
|
546
|
+
return if @value.all? do |key, _|
|
547
|
+
key.is_a?(Identifier) || ALLOWED_TYPES.include?(key.type)
|
548
|
+
end
|
361
549
|
|
362
550
|
raise CheckError, "#{self} is invalid (keys must be of an allowed type (int, uint, bool, or string)"
|
363
551
|
end
|
364
552
|
end
|
365
553
|
|
366
554
|
class Timestamp < Literal
|
555
|
+
TIMESTAMP_RANGE = (Time.parse("0001-01-01T00:00:00Z")..Time.parse("9999-12-31T23:59:59.999999999Z"))
|
556
|
+
|
367
557
|
def initialize(value)
|
368
558
|
value = case value
|
369
559
|
when ::String then Time.parse(value)
|
370
560
|
when Numeric then Time.at(value)
|
371
561
|
else value
|
372
562
|
end
|
373
|
-
|
563
|
+
|
564
|
+
raise EvaluateError, "out of range" unless TIMESTAMP_RANGE.include?(value)
|
565
|
+
|
566
|
+
super(:timestamp, value.round(9))
|
374
567
|
end
|
375
568
|
|
376
569
|
def +(other)
|
@@ -389,7 +582,7 @@ module Cel
|
|
389
582
|
LOGICAL_OPERATORS.each do |op|
|
390
583
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
391
584
|
def #{op}(other)
|
392
|
-
other.is_a?(Cel::Literal) ? Bool.
|
585
|
+
other.is_a?(Cel::Literal) ? Bool.cast(super) : super
|
393
586
|
end
|
394
587
|
OUT
|
395
588
|
end
|
@@ -397,7 +590,7 @@ module Cel
|
|
397
590
|
# Cel Functions
|
398
591
|
|
399
592
|
def getDate(tz = nil)
|
400
|
-
to_local_time(tz).day
|
593
|
+
Number.new(:int, to_local_time(tz).day)
|
401
594
|
end
|
402
595
|
|
403
596
|
def getDayOfMonth(tz = nil)
|
@@ -405,35 +598,39 @@ module Cel
|
|
405
598
|
end
|
406
599
|
|
407
600
|
def getDayOfWeek(tz = nil)
|
408
|
-
to_local_time(tz).wday
|
601
|
+
Number.new(:int, to_local_time(tz).wday)
|
409
602
|
end
|
410
603
|
|
411
604
|
def getDayOfYear(tz = nil)
|
412
|
-
to_local_time(tz).yday - 1
|
605
|
+
Number.new(:int, to_local_time(tz).yday - 1)
|
413
606
|
end
|
414
607
|
|
415
608
|
def getMonth(tz = nil)
|
416
|
-
to_local_time(tz).month - 1
|
609
|
+
Number.new(:int, to_local_time(tz).month - 1)
|
417
610
|
end
|
418
611
|
|
419
612
|
def getFullYear(tz = nil)
|
420
|
-
to_local_time(tz).year
|
613
|
+
Number.new(:int, to_local_time(tz).year)
|
421
614
|
end
|
422
615
|
|
423
616
|
def getHours(tz = nil)
|
424
|
-
to_local_time(tz).hour
|
617
|
+
Number.new(:int, to_local_time(tz).hour)
|
425
618
|
end
|
426
619
|
|
427
620
|
def getMinutes(tz = nil)
|
428
|
-
to_local_time(tz).min
|
621
|
+
Number.new(:int, to_local_time(tz).min)
|
429
622
|
end
|
430
623
|
|
431
624
|
def getSeconds(tz = nil)
|
432
|
-
to_local_time(tz).sec
|
625
|
+
Number.new(:int, to_local_time(tz).sec)
|
433
626
|
end
|
434
627
|
|
435
628
|
def getMilliseconds(tz = nil)
|
436
|
-
to_local_time(tz).nsec / 1_000_000
|
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)
|
437
634
|
end
|
438
635
|
|
439
636
|
private
|
@@ -441,7 +638,12 @@ module Cel
|
|
441
638
|
def to_local_time(tz = nil)
|
442
639
|
time = @value
|
443
640
|
if tz
|
444
|
-
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
|
445
647
|
time = time.getlocal(tz)
|
446
648
|
end
|
447
649
|
time
|
@@ -450,26 +652,54 @@ module Cel
|
|
450
652
|
|
451
653
|
class Duration < Literal
|
452
654
|
def initialize(value)
|
453
|
-
value =
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
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
|
+
|
464
670
|
super(:duration, value)
|
465
671
|
end
|
466
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
|
+
|
467
697
|
LOGICAL_OPERATORS.each do |op|
|
468
698
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
469
699
|
def #{op}(other)
|
470
700
|
case other
|
471
701
|
when Cel::Literal
|
472
|
-
Bool.
|
702
|
+
Bool.cast(super)
|
473
703
|
when Numeric
|
474
704
|
@value == other
|
475
705
|
|
@@ -483,19 +713,23 @@ module Cel
|
|
483
713
|
# Cel Functions
|
484
714
|
|
485
715
|
def getHours
|
486
|
-
(getMinutes / 60).to_i
|
716
|
+
Cel::Number.new(:int, (getMinutes / 60).to_i)
|
487
717
|
end
|
488
718
|
|
489
719
|
def getMinutes
|
490
|
-
(getSeconds / 60).to_i
|
720
|
+
Cel::Number.new(:int, (getSeconds / 60).to_i)
|
491
721
|
end
|
492
722
|
|
493
723
|
def getSeconds
|
494
|
-
@value.divmod(1).first
|
724
|
+
Cel::Number.new(:int, @value.divmod(1).first)
|
495
725
|
end
|
496
726
|
|
497
727
|
def getMilliseconds
|
498
|
-
(@value.divmod(1).last * 1000).round
|
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)
|
499
733
|
end
|
500
734
|
|
501
735
|
private
|
@@ -521,7 +755,9 @@ module Cel
|
|
521
755
|
raise EvaluateError, "#{units} is unsupported"
|
522
756
|
end
|
523
757
|
end
|
524
|
-
seconds + (nanos / 1_000_000_000.0)
|
758
|
+
duration = seconds + (nanos / 1_000_000_000.0)
|
759
|
+
duration = -duration if value.start_with?("-")
|
760
|
+
duration
|
525
761
|
end
|
526
762
|
end
|
527
763
|
|
@@ -532,20 +768,25 @@ module Cel
|
|
532
768
|
@value = value
|
533
769
|
end
|
534
770
|
|
771
|
+
def type
|
772
|
+
TYPES[:any]
|
773
|
+
end
|
774
|
+
|
535
775
|
def ==(other)
|
536
776
|
other.is_a?(Group) && @value == other.value
|
537
777
|
end
|
538
778
|
end
|
539
779
|
|
540
780
|
class Operation
|
541
|
-
attr_reader :op, :operands
|
781
|
+
attr_reader :op, :operands, :depth
|
542
782
|
|
543
783
|
attr_accessor :type
|
544
784
|
|
545
|
-
def initialize(op, operands)
|
785
|
+
def initialize(op, operands, depth: 1)
|
546
786
|
@op = op
|
547
787
|
@operands = operands
|
548
788
|
@type = TYPES[:any]
|
789
|
+
@depth = depth
|
549
790
|
end
|
550
791
|
|
551
792
|
def ==(other)
|
@@ -573,12 +814,17 @@ module Cel
|
|
573
814
|
end
|
574
815
|
|
575
816
|
class Condition
|
576
|
-
attr_reader :if, :then, :else
|
817
|
+
attr_reader :if, :then, :else, :depth
|
577
818
|
|
578
|
-
def initialize(if_, then_, else_)
|
819
|
+
def initialize(if_, then_, else_, depth: 1)
|
579
820
|
@if = if_
|
580
821
|
@then = then_
|
581
822
|
@else = else_
|
823
|
+
@depth = depth
|
824
|
+
end
|
825
|
+
|
826
|
+
def type
|
827
|
+
TYPES[:any]
|
582
828
|
end
|
583
829
|
|
584
830
|
def ==(other)
|