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