dentaku 3.4.0 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/dentaku.png)](http://badge.fury.io/rb/dentaku)
|
6
6
|
[![Build Status](https://travis-ci.org/rubysolo/dentaku.png?branch=master)](https://travis-ci.org/rubysolo/dentaku)
|
7
7
|
[![Code Climate](https://codeclimate.com/github/rubysolo/dentaku.png)](https://codeclimate.com/github/rubysolo/dentaku)
|
8
|
-
[![Hakiri](https://hakiri.io/github/rubysolo/dentaku/master.svg)](https://hakiri.io/github/rubysolo/dentaku)
|
9
8
|
[![Coverage](https://codecov.io/gh/rubysolo/dentaku/branch/master/graph/badge.svg)](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
|
})
|