cel 0.2.3 → 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 +17 -0
- data/README.md +23 -1
- data/lib/cel/ast/elements/protobuf.rb +411 -66
- data/lib/cel/ast/elements.rb +339 -113
- 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 +428 -396
- data/lib/cel/program.rb +255 -48
- data/lib/cel/version.rb +1 -1
- data/lib/cel.rb +232 -1
- metadata +18 -9
- 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,78 @@ 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
|
48
41
|
|
49
|
-
|
42
|
+
def initialize(name, struct, package: nil)
|
43
|
+
@struct = struct ? struct.transform_keys(&:to_sym) : EMPTY_STRUCT
|
44
|
+
@name = name
|
45
|
+
@package = package
|
50
46
|
end
|
51
47
|
|
52
|
-
def
|
53
|
-
@
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
58
54
|
end
|
59
55
|
|
60
|
-
def
|
61
|
-
|
56
|
+
def message_type
|
57
|
+
@message_type ||= proto_type || container
|
62
58
|
end
|
63
59
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
60
|
+
def proto_type
|
61
|
+
@proto_type ||= Protobuf.convert_to_proto_type(@name.to_s, @package)
|
62
|
+
end
|
63
|
+
|
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
|
71
71
|
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
|
-
]
|
72
|
+
message_type.to_s
|
78
73
|
end
|
79
74
|
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def container
|
79
|
+
@container ||= Cel.message_container(@name, @struct)
|
80
|
+
end
|
80
81
|
end
|
81
82
|
|
82
83
|
class Invoke
|
83
|
-
|
84
|
+
attr_accessor :var
|
85
|
+
|
86
|
+
attr_reader :func, :args
|
84
87
|
|
85
|
-
def self.new(func:, var: nil, args: nil)
|
88
|
+
def self.new(func:, var: nil, args: nil, **rest)
|
86
89
|
Protobuf.try_invoke_from(var, func, args) || super
|
87
90
|
end
|
88
91
|
|
89
|
-
def
|
92
|
+
def type
|
93
|
+
TYPES[:any]
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize(func:, var: nil, args: nil, package: nil)
|
90
97
|
@var = var
|
91
98
|
@func = func.to_sym
|
92
99
|
@args = args
|
100
|
+
@package = package
|
93
101
|
end
|
94
102
|
|
95
103
|
def ==(other)
|
@@ -114,12 +122,24 @@ module Cel
|
|
114
122
|
"#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
|
115
123
|
end
|
116
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
|
117
137
|
end
|
118
138
|
|
119
139
|
class Function
|
120
|
-
attr_reader :types, :type
|
140
|
+
attr_reader :label, :types, :type
|
121
141
|
|
122
|
-
def initialize(*types, return_type: nil, &func)
|
142
|
+
def initialize(*types, label: nil, return_type: nil, &func)
|
123
143
|
unless func.nil?
|
124
144
|
types = Array.new(func.arity) { TYPES[:any] } if types.empty?
|
125
145
|
raise(Error, "number of arg types does not match number of yielded args") unless types.size == func.arity
|
@@ -131,6 +151,7 @@ module Cel
|
|
131
151
|
return_type.is_a?(Type) ? return_type : TYPES[return_type]
|
132
152
|
end
|
133
153
|
@func = func
|
154
|
+
@label = label
|
134
155
|
end
|
135
156
|
|
136
157
|
def call(*args)
|
@@ -154,17 +175,28 @@ module Cel
|
|
154
175
|
end
|
155
176
|
|
156
177
|
def ==(other)
|
157
|
-
|
158
|
-
|
178
|
+
case other
|
179
|
+
when Literal
|
180
|
+
@type == other.type && @value == other.value
|
181
|
+
else
|
182
|
+
@value == other
|
183
|
+
end
|
159
184
|
end
|
160
185
|
|
161
186
|
def self.to_cel_type(val)
|
162
|
-
|
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
|
163
193
|
|
164
194
|
case val
|
165
|
-
when Literal, Identifier
|
195
|
+
when Literal, Identifier, Invoke, Operation, Condition, Group, # already cel
|
196
|
+
Protobuf.enum_class # already a usable class a la protobuf
|
166
197
|
val
|
167
|
-
|
198
|
+
when Protobuf.map_class
|
199
|
+
Map.new(val.to_h)
|
168
200
|
when ::String
|
169
201
|
String.new(val)
|
170
202
|
when ::Symbol
|
@@ -178,9 +210,9 @@ module Cel
|
|
178
210
|
when ::Array
|
179
211
|
List.new(val)
|
180
212
|
when true, false
|
181
|
-
Bool.
|
213
|
+
Bool.cast(val)
|
182
214
|
when nil
|
183
|
-
Null
|
215
|
+
Null::INSTANCE
|
184
216
|
when Time
|
185
217
|
Timestamp.new(val)
|
186
218
|
else
|
@@ -188,9 +220,7 @@ module Cel
|
|
188
220
|
end
|
189
221
|
end
|
190
222
|
|
191
|
-
|
192
|
-
@value
|
193
|
-
end
|
223
|
+
alias_method :to_ruby_type, :value
|
194
224
|
|
195
225
|
private
|
196
226
|
|
@@ -198,21 +228,62 @@ module Cel
|
|
198
228
|
end
|
199
229
|
|
200
230
|
class Number < Literal
|
201
|
-
|
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|
|
202
241
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
203
242
|
def #{op}(other)
|
204
|
-
|
243
|
+
value = super
|
244
|
+
check_overflow(value)
|
245
|
+
Number.new(@type, value)
|
205
246
|
end
|
206
247
|
OUT
|
207
248
|
end
|
208
249
|
|
209
|
-
|
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|
|
210
267
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
211
|
-
def #{op}
|
212
|
-
|
268
|
+
def #{op}@
|
269
|
+
raise EvaluateError, "no such overload" if @type == TYPES[:uint]
|
270
|
+
value = super
|
271
|
+
Number.new(@type, value)
|
213
272
|
end
|
214
273
|
OUT
|
215
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
|
216
287
|
end
|
217
288
|
|
218
289
|
class Bool < Literal
|
@@ -220,16 +291,27 @@ module Cel
|
|
220
291
|
super(:bool, value)
|
221
292
|
end
|
222
293
|
|
223
|
-
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
294
|
+
(LOGICAL_OPERATORS - %w[== != in]).each do |op|
|
224
295
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
225
296
|
def #{op}(other)
|
226
|
-
|
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)
|
227
302
|
end
|
228
303
|
OUT
|
229
304
|
end
|
230
305
|
|
231
306
|
def !
|
232
|
-
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
|
233
315
|
end
|
234
316
|
end
|
235
317
|
|
@@ -237,6 +319,8 @@ module Cel
|
|
237
319
|
def initialize
|
238
320
|
super(:null_type, nil)
|
239
321
|
end
|
322
|
+
|
323
|
+
INSTANCE = new.freeze
|
240
324
|
end
|
241
325
|
|
242
326
|
class String < Literal
|
@@ -244,18 +328,23 @@ module Cel
|
|
244
328
|
super(:string, value)
|
245
329
|
end
|
246
330
|
|
331
|
+
# override base String#%
|
332
|
+
def %(_other)
|
333
|
+
raise NoMethodError, "undefined method '%' for #{self}"
|
334
|
+
end
|
335
|
+
|
247
336
|
# CEL string functions
|
248
337
|
|
249
338
|
def contains(string)
|
250
|
-
Bool.
|
339
|
+
Bool.cast(@value.include?(string))
|
251
340
|
end
|
252
341
|
|
253
342
|
def endsWith(string)
|
254
|
-
Bool.
|
343
|
+
Bool.cast(@value.end_with?(string))
|
255
344
|
end
|
256
345
|
|
257
346
|
def startsWith(string)
|
258
|
-
Bool.
|
347
|
+
Bool.cast(@value.start_with?(string))
|
259
348
|
end
|
260
349
|
|
261
350
|
def matches(pattern)
|
@@ -272,18 +361,23 @@ module Cel
|
|
272
361
|
end
|
273
362
|
|
274
363
|
class Bytes < Literal
|
364
|
+
attr_reader :string
|
365
|
+
|
275
366
|
def initialize(value)
|
276
367
|
super(:bytes, value)
|
368
|
+
@string = value.pack("C*")
|
277
369
|
end
|
278
370
|
|
279
371
|
def to_ary
|
280
372
|
[self]
|
281
373
|
end
|
282
374
|
|
375
|
+
alias_method :to_ruby_type, :string
|
376
|
+
|
283
377
|
(LOGICAL_OPERATORS - %w[==]).each do |op|
|
284
378
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
285
379
|
def #{op}(other)
|
286
|
-
Bool.
|
380
|
+
other.is_a?(Cel::Bytes) ? Bool.cast(@string.__send__(__method__, other.string)) : super
|
287
381
|
end
|
288
382
|
OUT
|
289
383
|
end
|
@@ -291,7 +385,7 @@ module Cel
|
|
291
385
|
%i[+ -].each do |op|
|
292
386
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
293
387
|
def #{op}(other)
|
294
|
-
|
388
|
+
Bytes.new(@value + other.value)
|
295
389
|
end
|
296
390
|
OUT
|
297
391
|
end
|
@@ -302,7 +396,20 @@ module Cel
|
|
302
396
|
value = value.map do |v|
|
303
397
|
Literal.to_cel_type(v)
|
304
398
|
end
|
305
|
-
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
|
306
413
|
end
|
307
414
|
|
308
415
|
def to_ary
|
@@ -312,65 +419,133 @@ module Cel
|
|
312
419
|
def to_ruby_type
|
313
420
|
value.map(&:to_ruby_type)
|
314
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
|
315
452
|
end
|
316
453
|
|
317
454
|
class Map < Literal
|
455
|
+
ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
|
318
456
|
def initialize(value)
|
319
|
-
|
457
|
+
# store array internally to support repeated keys
|
458
|
+
value = value.map do |k, v|
|
320
459
|
[Literal.to_cel_type(k), Literal.to_cel_type(v)]
|
321
460
|
end
|
322
|
-
super(
|
461
|
+
super(TYPES[:map], value)
|
323
462
|
check
|
324
463
|
end
|
325
464
|
|
326
465
|
def ==(other)
|
327
|
-
|
328
|
-
|
329
|
-
@value.
|
330
|
-
|
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
|
331
479
|
end
|
332
480
|
|
333
|
-
|
334
|
-
|
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
|
335
503
|
end
|
336
504
|
|
337
505
|
def to_ruby_type
|
338
|
-
value.to_h {
|
506
|
+
value.to_h { |args| args.map(&:to_ruby_type) }
|
339
507
|
end
|
340
508
|
|
341
509
|
def respond_to_missing?(meth, *args)
|
342
|
-
super || (@value && @value.
|
510
|
+
super || (@value && @value.any? { |k, _| k.to_s == meth.to_s })
|
343
511
|
end
|
344
512
|
|
345
513
|
def method_missing(meth, *args)
|
346
514
|
return super unless @value
|
347
515
|
|
348
|
-
|
516
|
+
values = @value.filter_map { |k, v| v if k.to_s == meth.to_s }
|
349
517
|
|
350
|
-
|
518
|
+
return super if values.empty?
|
519
|
+
|
520
|
+
values.first
|
351
521
|
end
|
352
522
|
|
353
523
|
private
|
354
524
|
|
355
|
-
ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
|
356
|
-
|
357
525
|
# For a map, the entry keys are sub-expressions that must evaluate to values
|
358
526
|
# of an allowed type (int, uint, bool, or string)
|
359
527
|
def check
|
360
|
-
return if @value.
|
528
|
+
return if @value.all? do |key, _|
|
529
|
+
key.is_a?(Identifier) || ALLOWED_TYPES.include?(key.type)
|
530
|
+
end
|
361
531
|
|
362
532
|
raise CheckError, "#{self} is invalid (keys must be of an allowed type (int, uint, bool, or string)"
|
363
533
|
end
|
364
534
|
end
|
365
535
|
|
366
536
|
class Timestamp < Literal
|
537
|
+
TIMESTAMP_RANGE = (Time.parse("0001-01-01T00:00:00Z")..Time.parse("9999-12-31T23:59:59.999999999Z"))
|
538
|
+
|
367
539
|
def initialize(value)
|
368
540
|
value = case value
|
369
541
|
when ::String then Time.parse(value)
|
370
542
|
when Numeric then Time.at(value)
|
371
543
|
else value
|
372
544
|
end
|
373
|
-
|
545
|
+
|
546
|
+
raise EvaluateError, "out of range" unless TIMESTAMP_RANGE.include?(value)
|
547
|
+
|
548
|
+
super(:timestamp, value.round(9))
|
374
549
|
end
|
375
550
|
|
376
551
|
def +(other)
|
@@ -389,7 +564,7 @@ module Cel
|
|
389
564
|
LOGICAL_OPERATORS.each do |op|
|
390
565
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
391
566
|
def #{op}(other)
|
392
|
-
other.is_a?(Cel::Literal) ? Bool.
|
567
|
+
other.is_a?(Cel::Literal) ? Bool.cast(super) : super
|
393
568
|
end
|
394
569
|
OUT
|
395
570
|
end
|
@@ -397,7 +572,7 @@ module Cel
|
|
397
572
|
# Cel Functions
|
398
573
|
|
399
574
|
def getDate(tz = nil)
|
400
|
-
to_local_time(tz).day
|
575
|
+
Number.new(:int, to_local_time(tz).day)
|
401
576
|
end
|
402
577
|
|
403
578
|
def getDayOfMonth(tz = nil)
|
@@ -405,35 +580,39 @@ module Cel
|
|
405
580
|
end
|
406
581
|
|
407
582
|
def getDayOfWeek(tz = nil)
|
408
|
-
to_local_time(tz).wday
|
583
|
+
Number.new(:int, to_local_time(tz).wday)
|
409
584
|
end
|
410
585
|
|
411
586
|
def getDayOfYear(tz = nil)
|
412
|
-
to_local_time(tz).yday - 1
|
587
|
+
Number.new(:int, to_local_time(tz).yday - 1)
|
413
588
|
end
|
414
589
|
|
415
590
|
def getMonth(tz = nil)
|
416
|
-
to_local_time(tz).month - 1
|
591
|
+
Number.new(:int, to_local_time(tz).month - 1)
|
417
592
|
end
|
418
593
|
|
419
594
|
def getFullYear(tz = nil)
|
420
|
-
to_local_time(tz).year
|
595
|
+
Number.new(:int, to_local_time(tz).year)
|
421
596
|
end
|
422
597
|
|
423
598
|
def getHours(tz = nil)
|
424
|
-
to_local_time(tz).hour
|
599
|
+
Number.new(:int, to_local_time(tz).hour)
|
425
600
|
end
|
426
601
|
|
427
602
|
def getMinutes(tz = nil)
|
428
|
-
to_local_time(tz).min
|
603
|
+
Number.new(:int, to_local_time(tz).min)
|
429
604
|
end
|
430
605
|
|
431
606
|
def getSeconds(tz = nil)
|
432
|
-
to_local_time(tz).sec
|
607
|
+
Number.new(:int, to_local_time(tz).sec)
|
433
608
|
end
|
434
609
|
|
435
610
|
def getMilliseconds(tz = nil)
|
436
|
-
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)
|
437
616
|
end
|
438
617
|
|
439
618
|
private
|
@@ -441,7 +620,12 @@ module Cel
|
|
441
620
|
def to_local_time(tz = nil)
|
442
621
|
time = @value
|
443
622
|
if tz
|
444
|
-
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
|
445
629
|
time = time.getlocal(tz)
|
446
630
|
end
|
447
631
|
time
|
@@ -450,26 +634,54 @@ module Cel
|
|
450
634
|
|
451
635
|
class Duration < Literal
|
452
636
|
def initialize(value)
|
453
|
-
value =
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
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
|
+
|
464
652
|
super(:duration, value)
|
465
653
|
end
|
466
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
|
+
|
467
679
|
LOGICAL_OPERATORS.each do |op|
|
468
680
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
469
681
|
def #{op}(other)
|
470
682
|
case other
|
471
683
|
when Cel::Literal
|
472
|
-
Bool.
|
684
|
+
Bool.cast(super)
|
473
685
|
when Numeric
|
474
686
|
@value == other
|
475
687
|
|
@@ -483,19 +695,23 @@ module Cel
|
|
483
695
|
# Cel Functions
|
484
696
|
|
485
697
|
def getHours
|
486
|
-
(getMinutes / 60).to_i
|
698
|
+
Cel::Number.new(:int, (getMinutes / 60).to_i)
|
487
699
|
end
|
488
700
|
|
489
701
|
def getMinutes
|
490
|
-
(getSeconds / 60).to_i
|
702
|
+
Cel::Number.new(:int, (getSeconds / 60).to_i)
|
491
703
|
end
|
492
704
|
|
493
705
|
def getSeconds
|
494
|
-
@value.divmod(1).first
|
706
|
+
Cel::Number.new(:int, @value.divmod(1).first)
|
495
707
|
end
|
496
708
|
|
497
709
|
def getMilliseconds
|
498
|
-
(@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)
|
499
715
|
end
|
500
716
|
|
501
717
|
private
|
@@ -521,7 +737,9 @@ module Cel
|
|
521
737
|
raise EvaluateError, "#{units} is unsupported"
|
522
738
|
end
|
523
739
|
end
|
524
|
-
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
|
525
743
|
end
|
526
744
|
end
|
527
745
|
|
@@ -532,6 +750,10 @@ module Cel
|
|
532
750
|
@value = value
|
533
751
|
end
|
534
752
|
|
753
|
+
def type
|
754
|
+
TYPES[:any]
|
755
|
+
end
|
756
|
+
|
535
757
|
def ==(other)
|
536
758
|
other.is_a?(Group) && @value == other.value
|
537
759
|
end
|
@@ -581,6 +803,10 @@ module Cel
|
|
581
803
|
@else = else_
|
582
804
|
end
|
583
805
|
|
806
|
+
def type
|
807
|
+
TYPES[:any]
|
808
|
+
end
|
809
|
+
|
584
810
|
def ==(other)
|
585
811
|
other.is_a?(Condition) && @if == other.if && @then == other.then && @else == other.else
|
586
812
|
end
|