dentaku 3.4.0 → 3.5.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/.travis.yml +4 -3
- data/CHANGELOG.md +28 -0
- data/README.md +5 -4
- data/dentaku.gemspec +2 -0
- data/lib/dentaku/ast/access.rb +6 -0
- data/lib/dentaku/ast/arithmetic.rb +5 -1
- data/lib/dentaku/ast/array.rb +4 -0
- data/lib/dentaku/ast/bitwise.rb +8 -0
- 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 +25 -35
- data/lib/dentaku/ast/function.rb +6 -8
- data/lib/dentaku/ast/functions/all.rb +6 -19
- data/lib/dentaku/ast/functions/any.rb +6 -19
- 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 +23 -0
- data/lib/dentaku/ast/functions/if.rb +4 -0
- data/lib/dentaku/ast/functions/map.rb +5 -18
- data/lib/dentaku/ast/functions/mul.rb +2 -3
- data/lib/dentaku/ast/functions/pluck.rb +8 -7
- data/lib/dentaku/ast/functions/ruby_math.rb +6 -3
- data/lib/dentaku/ast/functions/sum.rb +2 -3
- data/lib/dentaku/ast/functions/xor.rb +44 -0
- data/lib/dentaku/ast/identifier.rb +11 -3
- 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 +5 -3
- data/lib/dentaku/calculator.rb +1 -1
- data/lib/dentaku/parser.rb +5 -3
- data/lib/dentaku/print_visitor.rb +101 -0
- data/lib/dentaku/token_scanner.rb +1 -1
- data/lib/dentaku/version.rb +1 -1
- data/lib/dentaku/visitor/infix.rb +82 -0
- data/lib/dentaku.rb +4 -3
- data/spec/ast/all_spec.rb +25 -0
- data/spec/ast/any_spec.rb +23 -0
- data/spec/ast/comparator_spec.rb +6 -9
- data/spec/ast/filter_spec.rb +25 -0
- data/spec/ast/function_spec.rb +5 -0
- data/spec/ast/map_spec.rb +27 -0
- data/spec/ast/max_spec.rb +13 -0
- data/spec/ast/min_spec.rb +13 -0
- data/spec/ast/mul_spec.rb +3 -2
- data/spec/ast/pluck_spec.rb +32 -0
- data/spec/ast/sum_spec.rb +3 -2
- data/spec/ast/xor_spec.rb +35 -0
- data/spec/bulk_expression_solver_spec.rb +10 -0
- data/spec/calculator_spec.rb +66 -2
- data/spec/parser_spec.rb +18 -3
- data/spec/print_visitor_spec.rb +66 -0
- data/spec/tokenizer_spec.rb +6 -0
- data/spec/visitor/infix_spec.rb +31 -0
- data/spec/visitor_spec.rb +137 -0
- metadata +43 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9a0b093e29b178197c0f92c1f63ec56342716c1fa84a4d12a73a7d355d42bc76
|
|
4
|
+
data.tar.gz: 480ccf9248568006227518363a0ef6350843007389f2e94ac5610ae62028e87e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 904292b2d2fd834701fd18900d689b9125579d58421fc58aaad03cd75c0fb556eeaeb5635dcd034a3f29e9f70f5c8fe0370e605acefd599c3dd75eae64436fdd
|
|
7
|
+
data.tar.gz: 3b6ed8763b9241e55e85f1a1e5a09d2652ec91eee21c080ca825029e04867b8fe65b128ddfc557cab3ef4fae76abb30b936f5971326355ea763081f90ba66638
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [v3.5.0]
|
|
4
|
+
- fix bug with function argument count
|
|
5
|
+
- add XOR operator
|
|
6
|
+
- make function args publicly accessible
|
|
7
|
+
- better argument handling for collection functions
|
|
8
|
+
- better dependency reporting for collection functions
|
|
9
|
+
- allow ruby math-backed functions to be serialized
|
|
10
|
+
- improve scientific notation handling
|
|
11
|
+
- improve comparator argument errors
|
|
12
|
+
- respect case sensitivity in nested case statments
|
|
13
|
+
- add visitor pattern
|
|
14
|
+
|
|
15
|
+
## [v3.4.2]
|
|
16
|
+
- add FILTER function
|
|
17
|
+
- add concurrent-ruby dependency to make global calculator object thread safe
|
|
18
|
+
- add Ruby 3 support
|
|
19
|
+
- allow formulas to access intermediate context values
|
|
20
|
+
- fix incorrect Ruby Math function return type
|
|
21
|
+
- fix context mutation bug
|
|
22
|
+
- fix dependency resolution bug
|
|
23
|
+
|
|
24
|
+
## [v3.4.1] 2020-12-12
|
|
25
|
+
- prevent extra evaluations in bulk expression solver
|
|
26
|
+
|
|
3
27
|
## [v3.4.0] 2020-12-07
|
|
4
28
|
- allow access to intermediate values of flattened hashes
|
|
5
29
|
- catch invalid array syntax in the parse phase
|
|
@@ -12,6 +36,7 @@
|
|
|
12
36
|
- add enum functions `ANY`, `ALL`, `MAP` and `PLUCK`
|
|
13
37
|
- allow self-referential formulas in bulk expression solver
|
|
14
38
|
- misc internal fixes and enhancements
|
|
39
|
+
|
|
15
40
|
## [v3.3.4] 2019-11-21
|
|
16
41
|
- bugfix release
|
|
17
42
|
|
|
@@ -199,6 +224,9 @@
|
|
|
199
224
|
## [v0.1.0] 2012-01-20
|
|
200
225
|
- initial release
|
|
201
226
|
|
|
227
|
+
[v3.5.0]: https://github.com/rubysolo/dentaku/compare/v3.4.2...v3.5.0
|
|
228
|
+
[v3.4.2]: https://github.com/rubysolo/dentaku/compare/v3.4.1...v3.4.2
|
|
229
|
+
[v3.4.1]: https://github.com/rubysolo/dentaku/compare/v3.4.0...v3.4.1
|
|
202
230
|
[v3.4.0]: https://github.com/rubysolo/dentaku/compare/v3.3.4...v3.4.0
|
|
203
231
|
[v3.3.4]: https://github.com/rubysolo/dentaku/compare/v3.3.3...v3.3.4
|
|
204
232
|
[v3.3.3]: https://github.com/rubysolo/dentaku/compare/v3.3.2...v3.3.3
|
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
|
|
|
@@ -144,14 +143,16 @@ 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
|
|
|
154
|
+
Collection: `MAP`, `FILTER`, `ALL`, `ANY`, `PLUCK`
|
|
155
|
+
|
|
155
156
|
RESOLVING DEPENDENCIES
|
|
156
157
|
----------------------
|
|
157
158
|
|
|
@@ -321,7 +322,7 @@ LICENSE
|
|
|
321
322
|
|
|
322
323
|
(The MIT License)
|
|
323
324
|
|
|
324
|
-
Copyright © 2012-
|
|
325
|
+
Copyright © 2012-2022 Solomon White
|
|
325
326
|
|
|
326
327
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
327
328
|
this software and associated documentation files (the ‘Software’), to deal in
|
data/dentaku.gemspec
CHANGED
|
@@ -14,6 +14,8 @@ Gem::Specification.new do |s|
|
|
|
14
14
|
Dentaku is a parser and evaluator for mathematical formulas
|
|
15
15
|
DESC
|
|
16
16
|
|
|
17
|
+
s.add_dependency('concurrent-ruby')
|
|
18
|
+
|
|
17
19
|
s.add_development_dependency('codecov')
|
|
18
20
|
s.add_development_dependency('pry')
|
|
19
21
|
s.add_development_dependency('pry-byebug')
|
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
|
|
@@ -57,7 +57,7 @@ module Dentaku
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def valid_node?(node)
|
|
60
|
-
node && (node.type == :numeric || node.dependencies.any?)
|
|
60
|
+
node && (node.type == :numeric || node.type == :integer || node.dependencies.any?)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def valid_left?
|
|
@@ -220,6 +220,10 @@ module Dentaku
|
|
|
220
220
|
:**
|
|
221
221
|
end
|
|
222
222
|
|
|
223
|
+
def display_operator
|
|
224
|
+
"^"
|
|
225
|
+
end
|
|
226
|
+
|
|
223
227
|
def self.precedence
|
|
224
228
|
30
|
|
225
229
|
end
|
data/lib/dentaku/ast/array.rb
CHANGED
data/lib/dentaku/ast/bitwise.rb
CHANGED
|
@@ -6,12 +6,20 @@ module Dentaku
|
|
|
6
6
|
def value(context = {})
|
|
7
7
|
left.value(context) | right.value(context)
|
|
8
8
|
end
|
|
9
|
+
|
|
10
|
+
def operator
|
|
11
|
+
:|
|
|
12
|
+
end
|
|
9
13
|
end
|
|
10
14
|
|
|
11
15
|
class BitwiseAnd < Operation
|
|
12
16
|
def value(context = {})
|
|
13
17
|
left.value(context) & right.value(context)
|
|
14
18
|
end
|
|
19
|
+
|
|
20
|
+
def operator
|
|
21
|
+
:&
|
|
22
|
+
end
|
|
15
23
|
end
|
|
16
24
|
end
|
|
17
25
|
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,64 @@ module Dentaku
|
|
|
15
15
|
raise NotImplementedError
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def value(context = {})
|
|
19
|
+
l = validate_value(left.value(context))
|
|
20
|
+
r = validate_value(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 value
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
def validate_value(value)
|
|
30
|
+
unless value.respond_to?(operator)
|
|
31
|
+
raise Dentaku::ArgumentError.for(:invalid_operator, operation: self.class, operator: operator),
|
|
32
|
+
"#{ self.class } requires operands that respond to #{operator}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
value
|
|
26
36
|
end
|
|
27
37
|
end
|
|
28
38
|
|
|
29
39
|
class LessThan < Comparator
|
|
30
|
-
def value(context = {})
|
|
31
|
-
super() { left.value(context) < right.value(context) }
|
|
32
|
-
end
|
|
33
|
-
|
|
34
40
|
def operator
|
|
35
|
-
|
|
41
|
+
:<
|
|
36
42
|
end
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
class LessThanOrEqual < Comparator
|
|
40
|
-
def value(context = {})
|
|
41
|
-
super() { left.value(context) <= right.value(context) }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
46
|
def operator
|
|
45
|
-
|
|
47
|
+
:<=
|
|
46
48
|
end
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
class GreaterThan < Comparator
|
|
50
|
-
def value(context = {})
|
|
51
|
-
super() { left.value(context) > right.value(context) }
|
|
52
|
-
end
|
|
53
|
-
|
|
54
52
|
def operator
|
|
55
|
-
|
|
53
|
+
:>
|
|
56
54
|
end
|
|
57
55
|
end
|
|
58
56
|
|
|
59
57
|
class GreaterThanOrEqual < Comparator
|
|
60
|
-
def value(context = {})
|
|
61
|
-
super() { left.value(context) >= right.value(context) }
|
|
62
|
-
end
|
|
63
|
-
|
|
64
58
|
def operator
|
|
65
|
-
|
|
59
|
+
:>=
|
|
66
60
|
end
|
|
67
61
|
end
|
|
68
62
|
|
|
69
63
|
class NotEqual < Comparator
|
|
70
|
-
def value(context = {})
|
|
71
|
-
super() { left.value(context) != right.value(context) }
|
|
72
|
-
end
|
|
73
|
-
|
|
74
64
|
def operator
|
|
75
|
-
|
|
65
|
+
:!=
|
|
76
66
|
end
|
|
77
67
|
end
|
|
78
68
|
|
|
79
69
|
class Equal < Comparator
|
|
80
|
-
def
|
|
81
|
-
|
|
70
|
+
def operator
|
|
71
|
+
:==
|
|
82
72
|
end
|
|
83
73
|
|
|
84
|
-
def
|
|
85
|
-
|
|
74
|
+
def display_operator
|
|
75
|
+
"="
|
|
86
76
|
end
|
|
87
77
|
end
|
|
88
78
|
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,30 +1,17 @@
|
|
|
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
|
-
context.
|
|
27
|
-
FlatHash.
|
|
13
|
+
context.merge(
|
|
14
|
+
FlatHash.from_hash_with_intermediates(item_identifier => item_value)
|
|
28
15
|
)
|
|
29
16
|
)
|
|
30
17
|
end
|
|
@@ -1,30 +1,17 @@
|
|
|
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
|
-
context.
|
|
27
|
-
FlatHash.
|
|
13
|
+
context.merge(
|
|
14
|
+
FlatHash.from_hash_with_intermediates(item_identifier => item_value)
|
|
28
15
|
)
|
|
29
16
|
)
|
|
30
17
|
end
|
|
@@ -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
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require_relative './enum'
|
|
2
|
+
|
|
3
|
+
module Dentaku
|
|
4
|
+
module AST
|
|
5
|
+
class Filter < Enum
|
|
6
|
+
def value(context = {})
|
|
7
|
+
collection = Array(@args[0].value(context))
|
|
8
|
+
item_identifier = @args[1].identifier
|
|
9
|
+
expression = @args[2]
|
|
10
|
+
|
|
11
|
+
collection.select do |item_value|
|
|
12
|
+
expression.value(
|
|
13
|
+
context.merge(
|
|
14
|
+
FlatHash.from_hash_with_intermediates(item_identifier => item_value)
|
|
15
|
+
)
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Dentaku::AST::Function.register_class(:filter, Dentaku::AST::Filter)
|
|
@@ -1,30 +1,17 @@
|
|
|
1
|
-
require_relative '
|
|
2
|
-
require_relative '../../exceptions'
|
|
1
|
+
require_relative './enum'
|
|
3
2
|
|
|
4
3
|
module Dentaku
|
|
5
4
|
module AST
|
|
6
|
-
class Map <
|
|
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 Map < 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.map do |item_value|
|
|
25
12
|
expression.value(
|
|
26
|
-
context.
|
|
27
|
-
FlatHash.
|
|
13
|
+
context.merge(
|
|
14
|
+
FlatHash.from_hash_with_intermediates(item_identifier => item_value)
|
|
28
15
|
)
|
|
29
16
|
)
|
|
30
17
|
end
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
require_relative '../function'
|
|
2
2
|
|
|
3
3
|
Dentaku::AST::Function.register(:mul, :numeric, ->(*args) {
|
|
4
|
-
|
|
5
|
-
if flatten_args.empty?
|
|
4
|
+
if args.empty?
|
|
6
5
|
raise Dentaku::ArgumentError.for(
|
|
7
6
|
:too_few_arguments,
|
|
8
7
|
function_name: 'MUL()', at_least: 1, given: 0
|
|
9
8
|
), 'MUL() requires at least one argument'
|
|
10
9
|
end
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
args.flatten.map { |arg| Dentaku::AST::Function.numeric(arg) }.reduce(1, :*)
|
|
13
12
|
})
|