dentaku 3.4.2 → 3.5.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 +22 -0
- data/README.md +4 -5
- data/lib/dentaku/ast/access.rb +6 -0
- data/lib/dentaku/ast/arithmetic.rb +23 -18
- data/lib/dentaku/ast/array.rb +4 -0
- data/lib/dentaku/ast/bitwise.rb +30 -5
- data/lib/dentaku/ast/case/case_conditional.rb +4 -0
- data/lib/dentaku/ast/case/case_else.rb +6 -0
- data/lib/dentaku/ast/case/case_switch_variable.rb +6 -0
- data/lib/dentaku/ast/case/case_then.rb +6 -0
- data/lib/dentaku/ast/case/case_when.rb +10 -0
- data/lib/dentaku/ast/case.rb +6 -0
- data/lib/dentaku/ast/comparators.rb +35 -35
- data/lib/dentaku/ast/function.rb +6 -8
- data/lib/dentaku/ast/functions/all.rb +4 -17
- data/lib/dentaku/ast/functions/any.rb +4 -17
- data/lib/dentaku/ast/functions/duration.rb +2 -2
- data/lib/dentaku/ast/functions/enum.rb +37 -0
- data/lib/dentaku/ast/functions/filter.rb +4 -17
- data/lib/dentaku/ast/functions/if.rb +4 -0
- data/lib/dentaku/ast/functions/map.rb +3 -16
- data/lib/dentaku/ast/functions/pluck.rb +8 -7
- data/lib/dentaku/ast/functions/ruby_math.rb +3 -2
- data/lib/dentaku/ast/functions/xor.rb +44 -0
- data/lib/dentaku/ast/identifier.rb +8 -0
- data/lib/dentaku/ast/literal.rb +10 -0
- data/lib/dentaku/ast/negation.rb +4 -0
- data/lib/dentaku/ast/nil.rb +4 -0
- data/lib/dentaku/ast/node.rb +4 -0
- data/lib/dentaku/ast/operation.rb +9 -0
- data/lib/dentaku/ast/string.rb +7 -0
- data/lib/dentaku/ast.rb +2 -0
- data/lib/dentaku/bulk_expression_solver.rb +1 -5
- data/lib/dentaku/exceptions.rb +2 -2
- data/lib/dentaku/parser.rb +10 -3
- data/lib/dentaku/print_visitor.rb +101 -0
- data/lib/dentaku/token_scanner.rb +3 -3
- data/lib/dentaku/version.rb +1 -1
- data/lib/dentaku/visitor/infix.rb +82 -0
- data/spec/ast/all_spec.rb +25 -0
- data/spec/ast/any_spec.rb +23 -0
- data/spec/ast/arithmetic_spec.rb +7 -0
- data/spec/ast/comparator_spec.rb +14 -9
- data/spec/ast/filter_spec.rb +7 -0
- data/spec/ast/function_spec.rb +5 -0
- data/spec/ast/map_spec.rb +12 -0
- data/spec/ast/or_spec.rb +1 -1
- data/spec/ast/pluck_spec.rb +32 -0
- data/spec/ast/xor_spec.rb +35 -0
- data/spec/bulk_expression_solver_spec.rb +9 -0
- data/spec/calculator_spec.rb +71 -2
- data/spec/parser_spec.rb +18 -3
- data/spec/print_visitor_spec.rb +66 -0
- data/spec/tokenizer_spec.rb +18 -0
- data/spec/visitor/infix_spec.rb +31 -0
- data/spec/visitor_spec.rb +138 -0
- metadata +24 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5592654ee45adeb24167b584374fb2d69bc67d1db4d5f4ac99050130f187f8f
|
|
4
|
+
data.tar.gz: 31d3952b08887ae934661f4c0ecc5c298b2f36f0f8365cc1503495eb044eb558
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8e0d003f897e06173c91b200e62d9fed12ec3bacfe0f2ecc3f3705a1cbf914d1705948b79962f5ac6a5397138ff89c2123aa5c3753963f0046a88280b29825b
|
|
7
|
+
data.tar.gz: 5f48d3b8fef4e56ed308e717da88937bef508e47dfca4c3e16610aff7523f11405e53e2b55d00c53b5eca8efbe7be64df0d0d12e7ddafbc61099cde08bf92a53
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [v3.5.1]
|
|
4
|
+
- add bitwise shift left and shift right operators
|
|
5
|
+
- improve numeric conversions
|
|
6
|
+
- improve parse exceptions
|
|
7
|
+
- improve bitwise exceptions
|
|
8
|
+
- include variable name in bulk expression exceptions
|
|
9
|
+
|
|
10
|
+
## [v3.5.0]
|
|
11
|
+
- fix bug with function argument count
|
|
12
|
+
- add XOR operator
|
|
13
|
+
- make function args publicly accessible
|
|
14
|
+
- better argument handling for collection functions
|
|
15
|
+
- better dependency reporting for collection functions
|
|
16
|
+
- allow ruby math-backed functions to be serialized
|
|
17
|
+
- improve scientific notation handling
|
|
18
|
+
- improve comparator argument errors
|
|
19
|
+
- respect case sensitivity in nested case statments
|
|
20
|
+
- add visitor pattern
|
|
21
|
+
|
|
3
22
|
## [v3.4.2]
|
|
4
23
|
- add FILTER function
|
|
5
24
|
- add concurrent-ruby dependency to make global calculator object thread safe
|
|
@@ -212,6 +231,9 @@
|
|
|
212
231
|
## [v0.1.0] 2012-01-20
|
|
213
232
|
- initial release
|
|
214
233
|
|
|
234
|
+
[Unreleased]: https://github.com/rubysolo/dentaku/compare/v3.5.1...HEAD
|
|
235
|
+
[v3.5.1]: https://github.com/rubysolo/dentaku/compare/v3.5.0...v3.5.1
|
|
236
|
+
[v3.5.0]: https://github.com/rubysolo/dentaku/compare/v3.4.2...v3.5.0
|
|
215
237
|
[v3.4.2]: https://github.com/rubysolo/dentaku/compare/v3.4.1...v3.4.2
|
|
216
238
|
[v3.4.1]: https://github.com/rubysolo/dentaku/compare/v3.4.0...v3.4.1
|
|
217
239
|
[v3.4.0]: https://github.com/rubysolo/dentaku/compare/v3.3.4...v3.4.0
|
data/README.md
CHANGED
|
@@ -5,7 +5,6 @@ Dentaku
|
|
|
5
5
|
[](http://badge.fury.io/rb/dentaku)
|
|
6
6
|
[](https://travis-ci.org/rubysolo/dentaku)
|
|
7
7
|
[](https://codeclimate.com/github/rubysolo/dentaku)
|
|
8
|
-
[](https://hakiri.io/github/rubysolo/dentaku)
|
|
9
8
|
[](https://codecov.io/gh/rubysolo/dentaku)
|
|
10
9
|
|
|
11
10
|
|
|
@@ -138,17 +137,17 @@ application, AST caching will consume more memory with each new formula.
|
|
|
138
137
|
BUILT-IN OPERATORS AND FUNCTIONS
|
|
139
138
|
---------------------------------
|
|
140
139
|
|
|
141
|
-
Math: `+`, `-`, `*`, `/`, `%`, `^`, `|`,
|
|
140
|
+
Math: `+`, `-`, `*`, `/`, `%`, `^`, `|`, `&`, `<<`, `>>`
|
|
142
141
|
|
|
143
142
|
Also, all functions from Ruby's Math module, including `SIN`, `COS`, `TAN`, etc.
|
|
144
143
|
|
|
145
144
|
Comparison: `<`, `>`, `<=`, `>=`, `<>`, `!=`, `=`,
|
|
146
145
|
|
|
147
|
-
Logic: `IF`, `AND`, `OR`, `NOT`, `SWITCH`
|
|
146
|
+
Logic: `IF`, `AND`, `OR`, `XOR`, `NOT`, `SWITCH`
|
|
148
147
|
|
|
149
148
|
Numeric: `MIN`, `MAX`, `SUM`, `AVG`, `COUNT`, `ROUND`, `ROUNDDOWN`, `ROUNDUP`
|
|
150
149
|
|
|
151
|
-
Selections: `CASE` (syntax see [spec](https://github.com/rubysolo/dentaku/blob/master/spec/calculator_spec.rb#
|
|
150
|
+
Selections: `CASE` (syntax see [spec](https://github.com/rubysolo/dentaku/blob/master/spec/calculator_spec.rb#L593))
|
|
152
151
|
|
|
153
152
|
String: `LEFT`, `RIGHT`, `MID`, `LEN`, `FIND`, `SUBSTITUTE`, `CONCAT`, `CONTAINS`
|
|
154
153
|
|
|
@@ -323,7 +322,7 @@ LICENSE
|
|
|
323
322
|
|
|
324
323
|
(The MIT License)
|
|
325
324
|
|
|
326
|
-
Copyright © 2012-
|
|
325
|
+
Copyright © 2012-2022 Solomon White
|
|
327
326
|
|
|
328
327
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
329
328
|
this software and associated documentation files (the ‘Software’), to deal in
|
data/lib/dentaku/ast/access.rb
CHANGED
|
@@ -3,6 +3,8 @@ require_relative "./node"
|
|
|
3
3
|
module Dentaku
|
|
4
4
|
module AST
|
|
5
5
|
class Access < Node
|
|
6
|
+
attr_reader :structure, :index
|
|
7
|
+
|
|
6
8
|
def self.arity
|
|
7
9
|
2
|
|
8
10
|
end
|
|
@@ -36,6 +38,10 @@ module Dentaku
|
|
|
36
38
|
def type
|
|
37
39
|
nil
|
|
38
40
|
end
|
|
41
|
+
|
|
42
|
+
def accept(visitor)
|
|
43
|
+
visitor.visit_access(self)
|
|
44
|
+
end
|
|
39
45
|
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
@@ -31,33 +31,34 @@ module Dentaku
|
|
|
31
31
|
def value(context = {})
|
|
32
32
|
l = cast(left.value(context))
|
|
33
33
|
r = cast(right.value(context))
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end
|
|
34
|
+
|
|
35
|
+
l.public_send(operator, r)
|
|
36
|
+
rescue ::TypeError => e
|
|
37
|
+
# Right cannot be converted to a suitable type for left. e.g. [] + 1
|
|
38
|
+
raise Dentaku::ArgumentError.for(:incompatible_type, value: r, for: l.class), e.message
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
private
|
|
43
42
|
|
|
44
|
-
def cast(val
|
|
43
|
+
def cast(val)
|
|
45
44
|
validate_value(val)
|
|
46
|
-
numeric(val
|
|
45
|
+
numeric(val)
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
def numeric(val
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
def numeric(val)
|
|
49
|
+
case val.to_s
|
|
50
|
+
when /\A\d*\.\d+\z/ then decimal(val)
|
|
51
|
+
when /\A-?\d+\z/ then val.to_i
|
|
52
|
+
else val
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def decimal(val)
|
|
57
|
+
BigDecimal(val.to_s, Float::DIG + 1)
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def valid_node?(node)
|
|
60
|
-
node && (node.type == :numeric || node.dependencies.any?)
|
|
61
|
+
node && (node.type == :numeric || node.type == :integer || node.dependencies.any?)
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
def valid_left?
|
|
@@ -143,7 +144,7 @@ module Dentaku
|
|
|
143
144
|
end
|
|
144
145
|
|
|
145
146
|
def value(context = {})
|
|
146
|
-
r = cast(right.value(context)
|
|
147
|
+
r = decimal(cast(right.value(context)))
|
|
147
148
|
raise Dentaku::ZeroDivisionError if r.zero?
|
|
148
149
|
|
|
149
150
|
cast(cast(left.value(context)) / r)
|
|
@@ -220,6 +221,10 @@ module Dentaku
|
|
|
220
221
|
:**
|
|
221
222
|
end
|
|
222
223
|
|
|
224
|
+
def display_operator
|
|
225
|
+
"^"
|
|
226
|
+
end
|
|
227
|
+
|
|
223
228
|
def self.precedence
|
|
224
229
|
30
|
|
225
230
|
end
|
data/lib/dentaku/ast/array.rb
CHANGED
data/lib/dentaku/ast/bitwise.rb
CHANGED
|
@@ -2,15 +2,40 @@ require_relative './operation'
|
|
|
2
2
|
|
|
3
3
|
module Dentaku
|
|
4
4
|
module AST
|
|
5
|
-
class
|
|
5
|
+
class Bitwise < Operation
|
|
6
6
|
def value(context = {})
|
|
7
|
-
|
|
7
|
+
left_value = left.value(context)
|
|
8
|
+
right_value = right.value(context)
|
|
9
|
+
|
|
10
|
+
left_value.public_send(operator, right_value)
|
|
11
|
+
rescue NoMethodError => e
|
|
12
|
+
raise Dentaku::ArgumentError.for(:invalid_operator, value: left_value, for: left_value.class)
|
|
13
|
+
rescue TypeError => e
|
|
14
|
+
raise Dentaku::ArgumentError.for(:invalid_operator, value: right_value, for: right_value.class)
|
|
8
15
|
end
|
|
9
16
|
end
|
|
10
17
|
|
|
11
|
-
class
|
|
12
|
-
def
|
|
13
|
-
|
|
18
|
+
class BitwiseOr < Bitwise
|
|
19
|
+
def operator
|
|
20
|
+
:|
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class BitwiseAnd < Bitwise
|
|
25
|
+
def operator
|
|
26
|
+
:&
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class BitwiseShiftLeft < Bitwise
|
|
31
|
+
def operator
|
|
32
|
+
:<<
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class BitwiseShiftRight < Bitwise
|
|
37
|
+
def operator
|
|
38
|
+
:>>
|
|
14
39
|
end
|
|
15
40
|
end
|
|
16
41
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseElse < Node
|
|
4
|
+
attr_reader :node
|
|
5
|
+
|
|
4
6
|
def initialize(node)
|
|
5
7
|
@node = node
|
|
6
8
|
end
|
|
@@ -24,6 +26,10 @@ module Dentaku
|
|
|
24
26
|
def self.max_param_count
|
|
25
27
|
1
|
|
26
28
|
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_else(self)
|
|
32
|
+
end
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
35
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseSwitchVariable < Node
|
|
4
|
+
attr_reader :node
|
|
5
|
+
|
|
4
6
|
def initialize(node)
|
|
5
7
|
@node = node
|
|
6
8
|
end
|
|
@@ -24,6 +26,10 @@ module Dentaku
|
|
|
24
26
|
def self.max_param_count
|
|
25
27
|
1
|
|
26
28
|
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_switch(self)
|
|
32
|
+
end
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
35
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseThen < Node
|
|
4
|
+
attr_reader :node
|
|
5
|
+
|
|
4
6
|
def initialize(node)
|
|
5
7
|
@node = node
|
|
6
8
|
end
|
|
@@ -24,6 +26,10 @@ module Dentaku
|
|
|
24
26
|
def self.max_param_count
|
|
25
27
|
1
|
|
26
28
|
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_then(self)
|
|
32
|
+
end
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
35
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseWhen < Operation
|
|
4
|
+
attr_reader :node
|
|
5
|
+
|
|
4
6
|
def initialize(node)
|
|
5
7
|
@node = node
|
|
6
8
|
end
|
|
@@ -24,6 +26,14 @@ module Dentaku
|
|
|
24
26
|
def self.max_param_count
|
|
25
27
|
1
|
|
26
28
|
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_when(self)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
35
|
+
'WHEN'
|
|
36
|
+
end
|
|
27
37
|
end
|
|
28
38
|
end
|
|
29
39
|
end
|
data/lib/dentaku/ast/case.rb
CHANGED
|
@@ -8,6 +8,8 @@ require 'dentaku/exceptions'
|
|
|
8
8
|
module Dentaku
|
|
9
9
|
module AST
|
|
10
10
|
class Case < Node
|
|
11
|
+
attr_reader :switch, :conditions, :else
|
|
12
|
+
|
|
11
13
|
def self.min_param_count
|
|
12
14
|
2
|
|
13
15
|
end
|
|
@@ -57,6 +59,10 @@ module Dentaku
|
|
|
57
59
|
else_dependencies(context)
|
|
58
60
|
end
|
|
59
61
|
|
|
62
|
+
def accept(visitor)
|
|
63
|
+
visitor.visit_case(self)
|
|
64
|
+
end
|
|
65
|
+
|
|
60
66
|
private
|
|
61
67
|
|
|
62
68
|
def switch_dependencies(context = {})
|
|
@@ -15,74 +15,74 @@ module Dentaku
|
|
|
15
15
|
raise NotImplementedError
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def value(context = {})
|
|
19
|
+
l = validate_value(cast(left.value(context)))
|
|
20
|
+
r = validate_value(cast(right.value(context)))
|
|
21
|
+
|
|
22
|
+
l.public_send(operator, r)
|
|
23
|
+
rescue ::ArgumentError => e
|
|
24
|
+
raise Dentaku::ArgumentError.for(:incompatible_type, value: r, for: l.class), e.message
|
|
25
|
+
end
|
|
26
|
+
|
|
18
27
|
private
|
|
19
28
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
def cast(val)
|
|
30
|
+
return val unless val.is_a?(::String)
|
|
31
|
+
return val if val.empty?
|
|
32
|
+
return val unless val.match?(/\A-?\d*(\.\d+)?\z/)
|
|
33
|
+
|
|
34
|
+
v = BigDecimal(val, Float::DIG + 1)
|
|
35
|
+
v = v.to_i if v.frac.zero?
|
|
36
|
+
v
|
|
26
37
|
end
|
|
27
|
-
end
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
def validate_value(value)
|
|
40
|
+
unless value.respond_to?(operator)
|
|
41
|
+
raise Dentaku::ArgumentError.for(:invalid_operator, operation: self.class, operator: operator),
|
|
42
|
+
"#{ self.class } requires operands that respond to #{operator}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
value
|
|
32
46
|
end
|
|
47
|
+
end
|
|
33
48
|
|
|
49
|
+
class LessThan < Comparator
|
|
34
50
|
def operator
|
|
35
|
-
|
|
51
|
+
:<
|
|
36
52
|
end
|
|
37
53
|
end
|
|
38
54
|
|
|
39
55
|
class LessThanOrEqual < Comparator
|
|
40
|
-
def value(context = {})
|
|
41
|
-
super() { left.value(context) <= right.value(context) }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
56
|
def operator
|
|
45
|
-
|
|
57
|
+
:<=
|
|
46
58
|
end
|
|
47
59
|
end
|
|
48
60
|
|
|
49
61
|
class GreaterThan < Comparator
|
|
50
|
-
def value(context = {})
|
|
51
|
-
super() { left.value(context) > right.value(context) }
|
|
52
|
-
end
|
|
53
|
-
|
|
54
62
|
def operator
|
|
55
|
-
|
|
63
|
+
:>
|
|
56
64
|
end
|
|
57
65
|
end
|
|
58
66
|
|
|
59
67
|
class GreaterThanOrEqual < Comparator
|
|
60
|
-
def value(context = {})
|
|
61
|
-
super() { left.value(context) >= right.value(context) }
|
|
62
|
-
end
|
|
63
|
-
|
|
64
68
|
def operator
|
|
65
|
-
|
|
69
|
+
:>=
|
|
66
70
|
end
|
|
67
71
|
end
|
|
68
72
|
|
|
69
73
|
class NotEqual < Comparator
|
|
70
|
-
def value(context = {})
|
|
71
|
-
super() { left.value(context) != right.value(context) }
|
|
72
|
-
end
|
|
73
|
-
|
|
74
74
|
def operator
|
|
75
|
-
|
|
75
|
+
:!=
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
class Equal < Comparator
|
|
80
|
-
def
|
|
81
|
-
|
|
80
|
+
def operator
|
|
81
|
+
:==
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
def
|
|
85
|
-
|
|
84
|
+
def display_operator
|
|
85
|
+
"="
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
end
|
data/lib/dentaku/ast/function.rb
CHANGED
|
@@ -4,6 +4,8 @@ require_relative 'function_registry'
|
|
|
4
4
|
module Dentaku
|
|
5
5
|
module AST
|
|
6
6
|
class Function < Node
|
|
7
|
+
attr_reader :args
|
|
8
|
+
|
|
7
9
|
# @return [Integer] with the number of significant decimal digits to use.
|
|
8
10
|
DIG = Float::DIG + 1
|
|
9
11
|
|
|
@@ -11,19 +13,15 @@ module Dentaku
|
|
|
11
13
|
@args = args
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
def accept(visitor)
|
|
17
|
+
visitor.visit_function(self)
|
|
18
|
+
end
|
|
19
|
+
|
|
14
20
|
def dependencies(context = {})
|
|
15
|
-
deferred = deferred_args
|
|
16
21
|
@args.each_with_index
|
|
17
|
-
.reject { |_, i| deferred.include? i }
|
|
18
22
|
.flat_map { |a, _| a.dependencies(context) }
|
|
19
23
|
end
|
|
20
24
|
|
|
21
|
-
# override if your function implementation needs to defer evaluation of
|
|
22
|
-
# any arguments
|
|
23
|
-
def deferred_args
|
|
24
|
-
[]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
25
|
def self.get(name)
|
|
28
26
|
registry.get(name)
|
|
29
27
|
end
|
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
require_relative '
|
|
2
|
-
require_relative '../../exceptions'
|
|
1
|
+
require_relative './enum'
|
|
3
2
|
|
|
4
3
|
module Dentaku
|
|
5
4
|
module AST
|
|
6
|
-
class All <
|
|
7
|
-
def self.min_param_count
|
|
8
|
-
3
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def self.max_param_count
|
|
12
|
-
3
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def deferred_args
|
|
16
|
-
[1, 2]
|
|
17
|
-
end
|
|
18
|
-
|
|
5
|
+
class All < Enum
|
|
19
6
|
def value(context = {})
|
|
20
|
-
collection = @args[0].value(context)
|
|
7
|
+
collection = Array(@args[0].value(context))
|
|
21
8
|
item_identifier = @args[1].identifier
|
|
22
9
|
expression = @args[2]
|
|
23
10
|
|
|
24
|
-
|
|
11
|
+
collection.all? do |item_value|
|
|
25
12
|
expression.value(
|
|
26
13
|
context.merge(
|
|
27
14
|
FlatHash.from_hash_with_intermediates(item_identifier => item_value)
|
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
require_relative '
|
|
2
|
-
require_relative '../../exceptions'
|
|
1
|
+
require_relative './enum'
|
|
3
2
|
|
|
4
3
|
module Dentaku
|
|
5
4
|
module AST
|
|
6
|
-
class Any <
|
|
7
|
-
def self.min_param_count
|
|
8
|
-
3
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def self.max_param_count
|
|
12
|
-
3
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def deferred_args
|
|
16
|
-
[1, 2]
|
|
17
|
-
end
|
|
18
|
-
|
|
5
|
+
class Any < Enum
|
|
19
6
|
def value(context = {})
|
|
20
|
-
collection = @args[0].value(context)
|
|
7
|
+
collection = Array(@args[0].value(context))
|
|
21
8
|
item_identifier = @args[1].identifier
|
|
22
9
|
expression = @args[2]
|
|
23
10
|
|
|
24
|
-
|
|
11
|
+
collection.any? do |item_value|
|
|
25
12
|
expression.value(
|
|
26
13
|
context.merge(
|
|
27
14
|
FlatHash.from_hash_with_intermediates(item_identifier => item_value)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require_relative '../function'
|
|
2
|
+
require_relative '../../exceptions'
|
|
3
|
+
|
|
4
|
+
module Dentaku
|
|
5
|
+
module AST
|
|
6
|
+
class Enum < Function
|
|
7
|
+
def self.min_param_count
|
|
8
|
+
3
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.max_param_count
|
|
12
|
+
3
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def dependencies(context = {})
|
|
16
|
+
validate_identifier(@args[1])
|
|
17
|
+
|
|
18
|
+
collection = @args[0]
|
|
19
|
+
item_identifier = @args[1].identifier
|
|
20
|
+
expression = @args[2]
|
|
21
|
+
|
|
22
|
+
collection_deps = collection.dependencies(context)
|
|
23
|
+
expression_deps = (expression&.dependencies(context) || []).reject do |i|
|
|
24
|
+
i == item_identifier || i.start_with?("#{item_identifier}.")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
collection_deps + expression_deps
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate_identifier(arg, message = "#{name}() requires second argument to be an identifier")
|
|
31
|
+
unless arg.is_a?(Identifier)
|
|
32
|
+
raise ArgumentError.for(:incompatible_type, value: arg, for: Identifier), message
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
require_relative '
|
|
2
|
-
require_relative '../../exceptions'
|
|
1
|
+
require_relative './enum'
|
|
3
2
|
|
|
4
3
|
module Dentaku
|
|
5
4
|
module AST
|
|
6
|
-
class Filter <
|
|
7
|
-
def self.min_param_count
|
|
8
|
-
3
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def self.max_param_count
|
|
12
|
-
3
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def deferred_args
|
|
16
|
-
[1, 2]
|
|
17
|
-
end
|
|
18
|
-
|
|
5
|
+
class Filter < Enum
|
|
19
6
|
def value(context = {})
|
|
20
|
-
collection = @args[0].value(context)
|
|
7
|
+
collection = Array(@args[0].value(context))
|
|
21
8
|
item_identifier = @args[1].identifier
|
|
22
9
|
expression = @args[2]
|
|
23
10
|
|
|
24
|
-
|
|
11
|
+
collection.select do |item_value|
|
|
25
12
|
expression.value(
|
|
26
13
|
context.merge(
|
|
27
14
|
FlatHash.from_hash_with_intermediates(item_identifier => item_value)
|