dentaku 3.4.2 → 3.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![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
|
|
@@ -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)
|