cel 0.1.2 → 0.2.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.
data/lib/cel/ast/types.rb CHANGED
@@ -14,6 +14,8 @@ module Cel
14
14
  @type.to_s
15
15
  end
16
16
 
17
+ alias_method :to_s, :to_str
18
+
17
19
  def type
18
20
  TYPES[:type]
19
21
  end
@@ -32,6 +34,10 @@ module Cel
32
34
  Bytes.new(value.bytes)
33
35
  when :bool
34
36
  Bool.new(value)
37
+ when :timestamp
38
+ Timestamp.new(value)
39
+ when :duration
40
+ Duration.new(value)
35
41
  when :any
36
42
  value
37
43
  else
@@ -52,6 +58,14 @@ module Cel
52
58
  def get(idx)
53
59
  @type_list[idx]
54
60
  end
61
+
62
+ def ==(other)
63
+ other == :list || super
64
+ end
65
+
66
+ def cast(value)
67
+ List.new(value)
68
+ end
55
69
  end
56
70
 
57
71
  class MapType < Type
@@ -67,12 +81,45 @@ module Cel
67
81
  _, value = @type_map.find { |k, _| k == attrib.to_s }
68
82
  value
69
83
  end
84
+
85
+ def ==(other)
86
+ other == :map || super
87
+ end
88
+
89
+ def cast(value)
90
+ Map.new(value)
91
+ end
70
92
  end
71
93
 
72
94
  # Primitive Cel Types
73
95
 
74
- PRIMITIVE_TYPES = %i[int uint double bool string bytes list map null_type type].freeze
75
- TYPES = PRIMITIVE_TYPES.to_h { |typ| [typ, Type.new(typ)] }
96
+ PRIMITIVE_TYPES = %i[int uint double bool string bytes list map timestamp duration null_type type].freeze
97
+ COLTYPES = %i[list map].freeze
98
+ TYPES = (PRIMITIVE_TYPES - COLTYPES).to_h { |typ| [typ, Type.new(typ)] }
76
99
  TYPES[:type] = Type.new(:type)
77
100
  TYPES[:any] = Type.new(:any)
101
+
102
+ module CollectionTypeFetch
103
+ def [](*args)
104
+ col_type, elem_type = args
105
+
106
+ return super unless COLTYPES.include?(col_type)
107
+
108
+ return super if args.size > 2
109
+
110
+ elem_type ||= :any
111
+
112
+ type = case col_type
113
+ when :list
114
+ ListType.new([])
115
+ when :map
116
+ MapType.new({})
117
+ end
118
+
119
+ type.element_type = super(*elem_type)
120
+ type
121
+ end
122
+ end
123
+
124
+ TYPES.singleton_class.prepend(CollectionTypeFetch)
78
125
  end
data/lib/cel/checker.rb CHANGED
@@ -31,10 +31,9 @@ module Cel
31
31
  Checker.new(@declarations ? @declarations.merge(declarations) : declarations)
32
32
  end
33
33
 
34
- # TODO: add protobuf timestamp and duration
35
- LOGICAL_EXPECTED_TYPES = %i[bool int uint double string bytes].freeze
36
- ADD_EXPECTED_TYPES = %i[int uint double string bytes list].freeze
37
- SUB_EXPECTED_TYPES = %i[int uint double].freeze
34
+ LOGICAL_EXPECTED_TYPES = %i[bool int uint double string bytes timestamp duration].freeze
35
+ ADD_EXPECTED_TYPES = %i[int uint double string bytes list duration].freeze
36
+ SUB_EXPECTED_TYPES = %i[int uint double duration].freeze
38
37
  MULTIDIV_EXPECTED_TYPES = %i[int uint double].freeze
39
38
  REMAINDER_EXPECTED_TYPES = %i[int uint].freeze
40
39
 
@@ -73,26 +72,48 @@ module Cel
73
72
  else
74
73
 
75
74
  case op
76
- when "&&", "||", "==", "!=", "<", "<=", ">=", ">"
77
- return TYPES[:bool]
75
+ when "&&", "||", "<", "<=", ">=", ">"
76
+ return TYPES[:bool] if find_match_all_types(LOGICAL_EXPECTED_TYPES, values) || values.include?(:any)
77
+ when "!=", "=="
78
+ return TYPES[:bool] if values.uniq.size == 1 ||
79
+ values.all? { |v| v == :list } ||
80
+ values.all? { |v| v == :map } ||
81
+ values.include?(:any)
78
82
  when "in"
79
- return TYPES[:bool] if find_match_all_types(%i[list map], values.last)
83
+ return TYPES[:bool] if find_match_all_types(%i[list map any], values.last)
80
84
  when "+"
81
- if (type = find_match_all_types(ADD_EXPECTED_TYPES, values))
82
- return type
83
- end
85
+ return type if (type = find_match_all_types(ADD_EXPECTED_TYPES, values))
86
+
87
+ return TYPES[:timestamp] if %i[timestamp duration].any? { |typ| values.first == typ }
88
+
89
+ return values.last if values.first == :any
90
+
84
91
  when "-"
85
- if (type = find_match_all_types(SUB_EXPECTED_TYPES, values))
86
- return type
92
+ return type if (type = find_match_all_types(SUB_EXPECTED_TYPES, values))
93
+
94
+ case values.first
95
+ when TYPES[:timestamp]
96
+ return TYPES[:duration] if values.last == :timestamp
97
+
98
+ return TYPES[:timestamp] if values.last == :duration
99
+
100
+ return TYPES[:any] if values.last == :any
101
+
102
+ when TYPES[:any]
103
+ return values.last
87
104
  end
88
105
  when "*", "/"
89
- if (type = find_match_all_types(MULTIDIV_EXPECTED_TYPES, values))
90
- return type
91
- end
106
+ return type if (type = find_match_all_types(MULTIDIV_EXPECTED_TYPES, values))
107
+
108
+ values.include?(:any)
109
+ values.find { |typ| typ != :any } || TYPES[:any]
110
+
92
111
  when "%"
93
- if (type = find_match_all_types(REMAINDER_EXPECTED_TYPES, values))
94
- return type
95
- end
112
+ return type if (type = find_match_all_types(REMAINDER_EXPECTED_TYPES, values))
113
+
114
+ values.include?(:any)
115
+ values.find { |typ| typ != :any } || TYPES[:any]
116
+
96
117
  else
97
118
  unsupported_type(operation)
98
119
  end
@@ -100,6 +121,17 @@ module Cel
100
121
  unsupported_type(operation)
101
122
  end
102
123
 
124
+ def infer_variable_type(var)
125
+ case var
126
+ when Identifier
127
+ check_identifier(var)
128
+ when Invoke
129
+ check_invoke(var)
130
+ else
131
+ var.type
132
+ end
133
+ end
134
+
103
135
  def check_invoke(funcall, var_type = nil)
104
136
  var = funcall.var
105
137
  func = funcall.func
@@ -107,14 +139,7 @@ module Cel
107
139
 
108
140
  return check_standard_func(funcall) unless var
109
141
 
110
- var_type ||= case var
111
- when Identifier
112
- check_identifier(var)
113
- when Invoke
114
- check_invoke(var)
115
- else
116
- var.type
117
- end
142
+ var_type ||= infer_variable_type(var)
118
143
 
119
144
  case var_type
120
145
  when MapType
@@ -123,7 +148,7 @@ module Cel
123
148
  case func
124
149
  when :[]
125
150
  attribute = var_type.get(args)
126
- unsupported_operation(funcall) unless attribute
151
+ return TYPES[:any] unless attribute
127
152
  when :all, :exists, :exists_one
128
153
  check_arity(funcall, args, 2)
129
154
  identifier, predicate = args
@@ -137,7 +162,7 @@ module Cel
137
162
  return TYPES[:bool]
138
163
  else
139
164
  attribute = var_type.get(func)
140
- unsupported_operation(funcall) unless attribute
165
+ return TYPES[:any] unless attribute
141
166
  end
142
167
 
143
168
  call(attribute)
@@ -153,6 +178,8 @@ module Cel
153
178
 
154
179
  unsupported_type(funcall) unless identifier.is_a?(Identifier)
155
180
 
181
+ identifier.type = var_type.element_type
182
+
156
183
  element_checker = merge(identifier.to_sym => var_type.element_type)
157
184
 
158
185
  unsupported_type(funcall) if element_checker.check(predicate) != :bool
@@ -195,17 +222,38 @@ module Cel
195
222
  unsupported_type(funcall)
196
223
  end
197
224
  unsupported_operation(funcall)
225
+ when TYPES[:timestamp]
226
+ case func
227
+ when :getDate, :getDayOfMonth, :getDayOfWeek, :getDayOfYear, :getFullYear, :getHours,
228
+ :getMilliseconds, :getMinutes, :getMonth, :getSeconds
229
+ check_arity(func, args, 0..1)
230
+ return TYPES[:int] if args.empty? || (args.size.positive? && args[0] == :string)
231
+ else
232
+ unsupported_type(funcall)
233
+ end
234
+ unsupported_operation(funcall)
235
+ when TYPES[:duration]
236
+ case func
237
+ when :getMilliseconds, :getMinutes, :getHours, :getSeconds
238
+ check_arity(func, args, 0)
239
+ return TYPES[:int]
240
+ else
241
+ unsupported_type(funcall)
242
+ end
243
+ unsupported_operation(funcall)
198
244
  else
199
245
  TYPES[:any]
200
246
  end
201
247
  end
202
248
 
203
249
  CAST_ALLOWED_TYPES = {
204
- int: %i[uint double string], # TODO: enum, timestamp
250
+ int: %i[uint double string timestamp], # TODO: enum
205
251
  uint: %i[int double string],
206
- string: %i[int uint double bytes], # TODO: timestamp, duration
252
+ string: %i[int uint double bytes timestamp duration],
207
253
  double: %i[int uint string],
208
254
  bytes: %i[string],
255
+ duration: %i[string],
256
+ timestamp: %i[string],
209
257
  }.freeze
210
258
 
211
259
  def check_standard_func(funcall)
@@ -223,15 +271,18 @@ module Cel
223
271
  return TYPES[:bool]
224
272
  when :size
225
273
  check_arity(func, args, 1)
226
- return TYPES[:int] if find_match_all_types(%i[string bytes list map], call(args.first))
227
- when :int, :uint, :string, :double, :bytes # :duration, :timestamp
274
+
275
+ arg = call(args.first)
276
+ return TYPES[:int] if find_match_all_types(%i[string bytes list map], arg)
277
+ when *CAST_ALLOWED_TYPES.keys
228
278
  check_arity(func, args, 1)
229
279
  allowed_types = CAST_ALLOWED_TYPES[func]
230
280
 
231
- return TYPES[func] if find_match_all_types(allowed_types, call(args.first))
281
+ arg = call(args.first)
282
+ return TYPES[func] if find_match_all_types(allowed_types, arg)
232
283
  when :matches
233
284
  check_arity(func, args, 2)
234
- return TYPES[:bool] if find_match_all_types(%i[string], args.map { |arg| call(arg) })
285
+ return TYPES[:bool] if find_match_all_types(%i[string], args.map(&method(:call)))
235
286
  when :dyn
236
287
  check_arity(func, args, 1)
237
288
  arg_type = call(args.first)
@@ -241,14 +292,37 @@ module Cel
241
292
  end
242
293
  return arg_type
243
294
  else
295
+ return check_custom_func(@declarations[func], funcall) if @declarations.key?(func)
296
+
244
297
  unsupported_type(funcall)
245
298
  end
246
299
 
247
300
  unsupported_operation(funcall)
248
301
  end
249
302
 
303
+ def check_custom_func(func, funcall)
304
+ args = funcall.args
305
+
306
+ unless func.is_a?(Cel::Function)
307
+ raise CheckError, "#{func} must respond to #call" unless func.respond_to?(:call)
308
+
309
+ func = Cel::Function(&func)
310
+ end
311
+
312
+ unless func.types.empty?
313
+ unsupported_type(funcall) unless func.types.zip(args.map(&method(:call)))
314
+ .all? do |expected_type, type|
315
+ expected_type == :any || expected_type == type
316
+ end
317
+
318
+ return func.type
319
+ end
320
+
321
+ unsupported_operation(funcall)
322
+ end
323
+
250
324
  def check_identifier(identifier)
251
- return unless identifier.type == :any
325
+ return identifier.type unless identifier.type == :any
252
326
 
253
327
  return TYPES[:type] if Cel::PRIMITIVE_TYPES.include?(identifier.to_sym)
254
328
 
@@ -262,6 +336,10 @@ module Cel
262
336
  end
263
337
 
264
338
  def check_condition(condition)
339
+ if_type = call(condition.if)
340
+
341
+ raise CheckError, "`#{condition.if}` must evaluate to a bool" unless if_type == :bool
342
+
265
343
  then_type = call(condition.then)
266
344
  else_type = call(condition.else)
267
345
 
@@ -286,7 +364,7 @@ module Cel
286
364
  case typ
287
365
  when Symbol
288
366
  TYPES[typ] or
289
- raise CheckError, "#{typ} is not aa valid type"
367
+ raise CheckError, "#{typ} is not a valid type"
290
368
  else
291
369
  typ
292
370
  end
@@ -303,11 +381,11 @@ module Cel
303
381
  end
304
382
  end
305
383
 
306
- TYPES[type]
384
+ type && types.is_a?(Type) ? types : TYPES[type]
307
385
  end
308
386
 
309
387
  def check_arity(func, args, arity)
310
- return if args.size == arity
388
+ return if arity === args.size # rubocop:disable Style/CaseEquality
311
389
 
312
390
  raise CheckError, "`#{func}` invoked with wrong number of arguments (should be #{arity})"
313
391
  end
data/lib/cel/context.rb CHANGED
@@ -2,13 +2,18 @@
2
2
 
3
3
  module Cel
4
4
  class Context
5
- def initialize(bindings)
5
+ attr_reader :declarations
6
+
7
+ def initialize(declarations, bindings)
8
+ @declarations = declarations
6
9
  @bindings = bindings.dup
7
10
 
8
11
  return unless @bindings
9
12
 
10
13
  @bindings.each do |k, v|
11
- @bindings[k] = to_cel_type(v)
14
+ val = to_cel_type(v)
15
+ val = TYPES[@declarations[k]].cast(val) if @declarations && @declarations.key?(k)
16
+ @bindings[k] = val
12
17
  end
13
18
  end
14
19
 
@@ -24,7 +29,7 @@ module Cel
24
29
  end
25
30
 
26
31
  def merge(bindings)
27
- Context.new(@bindings ? @bindings.merge(bindings) : bindings)
32
+ Context.new(@declarations, @bindings ? @bindings.merge(bindings) : bindings)
28
33
  end
29
34
 
30
35
  private
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cel
4
+ module Encoder
5
+ module_function
6
+
7
+ def encode(expr)
8
+ case expr
9
+ when Group
10
+ ["group", encode(expr.value)]
11
+ when Invoke
12
+ enc = [
13
+ "inv",
14
+ (encode(expr.var) if expr.var),
15
+ expr.func.to_s,
16
+ *(if expr.args
17
+ expr.func == :[] ? [encode(expr.args)] : expr.args.map(&method(:encode))
18
+ end),
19
+ ]
20
+ enc.compact!
21
+ enc
22
+ when Operation
23
+ [
24
+ "op",
25
+ expr.op.to_s,
26
+ *expr.operands.map(&method(:encode)),
27
+ ]
28
+ when Condition
29
+ [
30
+ "cond",
31
+ encode(expr.if),
32
+ encode(expr.then),
33
+ encode(expr.else),
34
+ ]
35
+ when Identifier
36
+ [
37
+ "id",
38
+ expr.type.to_s,
39
+ expr.id.to_s,
40
+ ]
41
+ when List
42
+ ["lit",
43
+ expr.type.to_s,
44
+ *expr.value.map(&method(:encode))]
45
+ when Map
46
+ ["lit",
47
+ expr.type.to_s,
48
+ *expr.value.map { |kv| kv.map(&method(:encode)) }]
49
+ when Number, Bool, String, Bytes
50
+ ["lit", expr.type.to_s, expr.value]
51
+ when Null
52
+ %w[lit null]
53
+ when Type
54
+ ["lit", "type", expr.to_s]
55
+ end
56
+ end
57
+
58
+ def decode(enc)
59
+ case enc
60
+ in ["group", stmt]
61
+ Group.new(decode(stmt))
62
+ in ["inv", var, ::String => func, *args]
63
+ args = if func == "[]" && args.size == 1
64
+ decode(args.first)
65
+ elsif args.empty?
66
+ nil
67
+ else
68
+ args.map(&method(:decode))
69
+ end
70
+ Invoke.new(func: func, var: decode(var), args: args)
71
+ in ["inv", ::String => func, *args]
72
+ args = nil if args.empty?
73
+ Invoke.new(func: func, args: args.map(&method(:decode)))
74
+ in ["op", ::String => op, *args]
75
+ Operation.new(op, args.map(&method(:decode)))
76
+ in ["cond", f, th, el]
77
+ Condition.new(decode(f), decode(th), decode(el))
78
+ in ["id", ::String => type, ::String => val]
79
+ id = Identifier.new(val)
80
+ id.type = TYPES[type.to_sym]
81
+ id
82
+ in ["lit", "list", *items]
83
+ list = List.new(items.map(&method(:decode)))
84
+ list
85
+ in ["lit", "map", items]
86
+ Map.new(items.map(&method(:decode)).each_slice(2))
87
+ in ["lit", /\Aint|uint|double\z/ => type, Integer => val]
88
+ Number.new(type.to_sym, val)
89
+ in ["lit", "bool", val]
90
+ Bool.new(val)
91
+ in ["lit", "string", val]
92
+ String.new(val)
93
+ in ["lit", "bytes", val]
94
+ Bytes.new(val)
95
+ in ["lit", "null"]
96
+ Null.new
97
+ in ["lit", "type", type]
98
+ TYPES[type.to_sym]
99
+ end
100
+ end
101
+ end
102
+ end
@@ -14,19 +14,31 @@ module Cel
14
14
  ast
15
15
  end
16
16
 
17
+ def encode(expr)
18
+ Encoder.encode(compile(expr))
19
+ end
20
+
21
+ def decode(encoded_expr)
22
+ ast = Encoder.decode(encoded_expr)
23
+ @checker.check(ast)
24
+ ast
25
+ end
26
+
17
27
  def check(expr)
18
28
  ast = @parser.parse(expr)
19
29
  @checker.check(ast)
20
30
  end
21
31
 
22
32
  def program(expr)
23
- expr = compile(expr) if expr.is_a?(::String)
24
- Runner.new(expr)
33
+ expr = @parser.parse(expr) if expr.is_a?(::String)
34
+ @checker.check(expr)
35
+ Runner.new(@declarations, expr)
25
36
  end
26
37
 
27
38
  def evaluate(expr, bindings = nil)
28
- context = Context.new(bindings)
29
- expr = compile(expr) if expr.is_a?(::String)
39
+ context = Context.new(@declarations, bindings)
40
+ expr = @parser.parse(expr) if expr.is_a?(::String)
41
+ @checker.check(expr)
30
42
  Program.new(context).evaluate(expr)
31
43
  end
32
44
 
@@ -36,12 +48,13 @@ module Cel
36
48
  end
37
49
 
38
50
  class Runner
39
- def initialize(ast)
51
+ def initialize(declarations, ast)
52
+ @declarations = declarations
40
53
  @ast = ast
41
54
  end
42
55
 
43
56
  def evaluate(bindings = nil)
44
- context = Context.new(bindings)
57
+ context = Context.new(@declarations, bindings)
45
58
  Program.new(context).evaluate(@ast)
46
59
  end
47
60
  end
data/lib/cel/parser.rb CHANGED
@@ -63,9 +63,18 @@ def parse(str)
63
63
  tokenize(str)
64
64
  do_parse
65
65
  rescue Racc::ParseError => err
66
- ex = Cel::ParseError.new(err.message)
67
- ex.set_backtrace(err.backtrace)
68
- raise ex
66
+ raise parse_error(err)
67
+ end
68
+
69
+ def parse_error(error)
70
+ parse_error = case error.message
71
+ when /parse error on value "([^"]+)" \(tRESERVED\)/
72
+ Cel::ParseError.new("invalid usage of the reserved word \"#{$1}\"")
73
+ else
74
+ Cel::ParseError.new(error.message)
75
+ end
76
+ parse_error.set_backtrace(error.backtrace)
77
+ parse_error
69
78
  end
70
79
 
71
80
  def tokenize(str)
@@ -83,10 +92,6 @@ def tokenize(str)
83
92
  # skip comment lines
84
93
  when scanner.scan(NUMBER_REGEX)
85
94
  @q << convert_to_number(scanner)
86
- when scanner.scan(/true|false/)
87
- @q << [:tBOOL, scanner.matched == "true"]
88
- when scanner.scan(/null/)
89
- @q << [:tNULL, nil]
90
95
  when scanner.scan(/[bB]?[rR]?#{STRING_LIT_REGEX}/) # string
91
96
  # s = scanner.matched.yield_self {|s| s[1, s.length - 2] }
92
97
  # .gsub(DBL_QUOTE_STR_ESCAPE_SEQUENCES_RE) do |match|
@@ -115,7 +120,13 @@ def tokenize(str)
115
120
  end
116
121
  when scanner.scan(IDENTIFIER_REGEX)
117
122
  word = scanner.matched
118
- if RESERVED.include?(word)
123
+ if word == "null"
124
+ @q << [:tNULL, nil]
125
+ elsif word == "true"
126
+ @q << [:tBOOL, true]
127
+ elsif word == "false"
128
+ @q << [:tBOOL, false]
129
+ elsif RESERVED.include?(word)
119
130
  @q << [:tRESERVED, scanner.matched]
120
131
  elsif word == "in"
121
132
  @q << [OPERATORS[scanner.matched], scanner.matched]
@@ -470,7 +481,7 @@ racc_reduce_table = [
470
481
  1, 49, :_reduce_42,
471
482
  0, 45, :_reduce_43,
472
483
  1, 45, :_reduce_none,
473
- 5, 51, :_reduce_none,
484
+ 5, 51, :_reduce_45,
474
485
  3, 51, :_reduce_46,
475
486
  5, 50, :_reduce_47,
476
487
  3, 50, :_reduce_48,
@@ -738,7 +749,7 @@ module_eval(<<'.,.,', 'parser.ry', 54)
738
749
 
739
750
  module_eval(<<'.,.,', 'parser.ry', 55)
740
751
  def _reduce_30(val, _values, result)
741
- result = Cel::Invoke.new(func: val[0], args: [val[2]].flatten(1))
752
+ result = Cel::Invoke.new(func: val[0], args: [val[2]].flatten(1))
742
753
  result
743
754
  end
744
755
  .,.,
@@ -774,7 +785,7 @@ module_eval(<<'.,.,', 'parser.ry', 58)
774
785
 
775
786
  module_eval(<<'.,.,', 'parser.ry', 65)
776
787
  def _reduce_38(val, _values, result)
777
- result = nil
788
+ result = []
778
789
  result
779
790
  end
780
791
  .,.,
@@ -811,7 +822,12 @@ module_eval(<<'.,.,', 'parser.ry', 73)
811
822
 
812
823
  # reduce 44 omitted
813
824
 
814
- # reduce 45 omitted
825
+ module_eval(<<'.,.,', 'parser.ry', 76)
826
+ def _reduce_45(val, _values, result)
827
+ result = val[0].merge(Cel::Identifier.new(val[2]) => val[4])
828
+ result
829
+ end
830
+ .,.,
815
831
 
816
832
  module_eval(<<'.,.,', 'parser.ry', 77)
817
833
  def _reduce_46(val, _values, result)
data/lib/cel/program.rb CHANGED
@@ -40,7 +40,7 @@ module Cel
40
40
  def evaluate_literal(val)
41
41
  case val
42
42
  when List
43
- List.new(val.value.map { |y| call(y) })
43
+ List.new(val.value.map(&method(:call)))
44
44
  else
45
45
  val
46
46
  end
@@ -93,7 +93,7 @@ module Cel
93
93
  when String
94
94
  raise EvaluateError, "#{invoke} is not supported" unless String.method_defined?(func, false)
95
95
 
96
- var.public_send(func, *args)
96
+ var.public_send(func, *args.map(&method(:call)))
97
97
  when Message
98
98
  # If e evaluates to a message and f is not declared in this message, the
99
99
  # runtime error no_such_field is raised.
@@ -110,6 +110,10 @@ module Cel
110
110
  args ?
111
111
  var.public_send(func, *args) :
112
112
  var.public_send(func)
113
+ when Timestamp, Duration
114
+ raise EvaluateError, "#{invoke} is not supported" unless var.class.method_defined?(func, false)
115
+
116
+ var.public_send(func, *args)
113
117
  else
114
118
  raise EvaluateError, "#{invoke} is not supported"
115
119
  end
@@ -130,18 +134,28 @@ module Cel
130
134
 
131
135
  val.class
132
136
  # MACROS
133
- when :has, :size
134
- Macro.__send__(func, *args)
137
+ when :has
138
+ Bool.new(Macro.__send__(func, *args))
139
+ when :size
140
+ Cel::Number.new(:int, Macro.__send__(func, *args))
135
141
  when :matches
136
- Macro.__send__(func, *args.map { |arg| call(arg) })
137
- when :int, :uint, :string, :double, :bytes # :duration, :timestamp
142
+ Bool.new(Macro.__send__(func, *args.map(&method(:call))))
143
+ when :int, :uint, :string, :double, :bytes, :duration, :timestamp
138
144
  type = TYPES[func]
139
145
  type.cast(call(args.first))
140
146
  when :dyn
141
147
  call(args.first)
142
148
  else
149
+ return evaluate_custom_func(@context.declarations[func], funcall) if @context.declarations.key?(func)
150
+
143
151
  raise EvaluateError, "#{funcall} is not supported"
144
152
  end
145
153
  end
154
+
155
+ def evaluate_custom_func(func, funcall)
156
+ args = funcall.args
157
+
158
+ func.call(*args.map(&method(:call)).map(&:to_ruby_type))
159
+ end
146
160
  end
147
161
  end
data/lib/cel/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Cel
4
4
  module Ruby
5
- VERSION = "0.1.2"
5
+ VERSION = "0.2.1"
6
6
  end
7
7
  end