dentaku 3.0.0 → 3.1.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/.rubocop.yml +119 -0
- data/.travis.yml +8 -9
- data/CHANGELOG.md +9 -0
- data/Gemfile +0 -5
- data/LICENSE +21 -0
- data/README.md +44 -3
- data/Rakefile +4 -1
- data/dentaku.gemspec +8 -4
- data/lib/dentaku.rb +15 -2
- data/lib/dentaku/ast.rb +1 -0
- data/lib/dentaku/ast/access.rb +2 -2
- data/lib/dentaku/ast/arithmetic.rb +7 -7
- data/lib/dentaku/ast/bitwise.rb +2 -2
- data/lib/dentaku/ast/case.rb +5 -5
- data/lib/dentaku/ast/case/case_conditional.rb +1 -1
- data/lib/dentaku/ast/case/case_else.rb +2 -2
- data/lib/dentaku/ast/case/case_switch_variable.rb +2 -2
- data/lib/dentaku/ast/case/case_then.rb +2 -2
- data/lib/dentaku/ast/case/case_when.rb +2 -2
- data/lib/dentaku/ast/combinators.rb +10 -2
- data/lib/dentaku/ast/comparators.rb +34 -6
- data/lib/dentaku/ast/function.rb +1 -1
- data/lib/dentaku/ast/function_registry.rb +1 -1
- data/lib/dentaku/ast/functions/if.rb +6 -2
- data/lib/dentaku/ast/functions/max.rb +1 -1
- data/lib/dentaku/ast/functions/min.rb +1 -1
- data/lib/dentaku/ast/functions/ruby_math.rb +1 -1
- data/lib/dentaku/ast/functions/string_functions.rb +8 -8
- data/lib/dentaku/ast/functions/sum.rb +12 -0
- data/lib/dentaku/ast/grouping.rb +2 -2
- data/lib/dentaku/ast/identifier.rb +8 -5
- data/lib/dentaku/ast/negation.rb +2 -2
- data/lib/dentaku/ast/node.rb +1 -1
- data/lib/dentaku/ast/operation.rb +1 -1
- data/lib/dentaku/bulk_expression_solver.rb +39 -20
- data/lib/dentaku/calculator.rb +38 -28
- data/lib/dentaku/dependency_resolver.rb +1 -1
- data/lib/dentaku/flat_hash.rb +31 -0
- data/lib/dentaku/parser.rb +7 -6
- data/lib/dentaku/string_casing.rb +7 -0
- data/lib/dentaku/token.rb +1 -1
- data/lib/dentaku/token_matcher.rb +4 -4
- data/lib/dentaku/token_scanner.rb +18 -7
- data/lib/dentaku/tokenizer.rb +26 -2
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/arithmetic_spec.rb +2 -2
- data/spec/ast/comparator_spec.rb +57 -0
- data/spec/ast/function_spec.rb +1 -1
- data/spec/ast/max_spec.rb +5 -0
- data/spec/ast/min_spec.rb +5 -0
- data/spec/ast/sum_spec.rb +38 -0
- data/spec/benchmark.rb +2 -2
- data/spec/bulk_expression_solver_spec.rb +89 -1
- data/spec/calculator_spec.rb +40 -7
- data/spec/dentaku_spec.rb +11 -0
- data/spec/external_function_spec.rb +7 -7
- data/spec/parser_spec.rb +11 -11
- data/spec/spec_helper.rb +21 -3
- data/spec/token_matcher_spec.rb +0 -1
- data/spec/token_spec.rb +6 -0
- data/spec/tokenizer_spec.rb +37 -0
- metadata +70 -5
data/lib/dentaku/ast/bitwise.rb
CHANGED
@@ -3,13 +3,13 @@ require_relative './operation'
|
|
3
3
|
module Dentaku
|
4
4
|
module AST
|
5
5
|
class BitwiseOr < Operation
|
6
|
-
def value(context={})
|
6
|
+
def value(context = {})
|
7
7
|
left.value(context) | right.value(context)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
class BitwiseAnd < Operation
|
12
|
-
def value(context={})
|
12
|
+
def value(context = {})
|
13
13
|
left.value(context) & right.value(context)
|
14
14
|
end
|
15
15
|
end
|
data/lib/dentaku/ast/case.rb
CHANGED
@@ -25,7 +25,7 @@ module Dentaku
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def value(context={})
|
28
|
+
def value(context = {})
|
29
29
|
switch_value = @switch.value(context)
|
30
30
|
@conditions.each do |condition|
|
31
31
|
if condition.when.value(context) == switch_value
|
@@ -40,7 +40,7 @@ module Dentaku
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
def dependencies(context={})
|
43
|
+
def dependencies(context = {})
|
44
44
|
# TODO: should short-circuit
|
45
45
|
switch_dependencies(context) +
|
46
46
|
condition_dependencies(context) +
|
@@ -49,15 +49,15 @@ module Dentaku
|
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
def switch_dependencies(context={})
|
52
|
+
def switch_dependencies(context = {})
|
53
53
|
@switch.dependencies(context)
|
54
54
|
end
|
55
55
|
|
56
|
-
def condition_dependencies(context={})
|
56
|
+
def condition_dependencies(context = {})
|
57
57
|
@conditions.flat_map { |condition| condition.dependencies(context) }
|
58
58
|
end
|
59
59
|
|
60
|
-
def else_dependencies(context={})
|
60
|
+
def else_dependencies(context = {})
|
61
61
|
@else ? @else.dependencies(context) : []
|
62
62
|
end
|
63
63
|
end
|
@@ -28,13 +28,21 @@ module Dentaku
|
|
28
28
|
end
|
29
29
|
|
30
30
|
class And < Combinator
|
31
|
-
def
|
31
|
+
def operator
|
32
|
+
:and
|
33
|
+
end
|
34
|
+
|
35
|
+
def value(context = {})
|
32
36
|
left.value(context) && right.value(context)
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
36
40
|
class Or < Combinator
|
37
|
-
def
|
41
|
+
def operator
|
42
|
+
:or
|
43
|
+
end
|
44
|
+
|
45
|
+
def value(context = {})
|
38
46
|
left.value(context) || right.value(context)
|
39
47
|
end
|
40
48
|
end
|
@@ -10,42 +10,70 @@ module Dentaku
|
|
10
10
|
def type
|
11
11
|
:logical
|
12
12
|
end
|
13
|
+
|
14
|
+
def operator
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
class LessThan < Comparator
|
16
|
-
def value(context={})
|
20
|
+
def value(context = {})
|
17
21
|
left.value(context) < right.value(context)
|
18
22
|
end
|
23
|
+
|
24
|
+
def operator
|
25
|
+
return :<
|
26
|
+
end
|
19
27
|
end
|
20
28
|
|
21
29
|
class LessThanOrEqual < Comparator
|
22
|
-
def value(context={})
|
30
|
+
def value(context = {})
|
23
31
|
left.value(context) <= right.value(context)
|
24
32
|
end
|
33
|
+
|
34
|
+
def operator
|
35
|
+
return :<=
|
36
|
+
end
|
25
37
|
end
|
26
38
|
|
27
39
|
class GreaterThan < Comparator
|
28
|
-
def value(context={})
|
40
|
+
def value(context = {})
|
29
41
|
left.value(context) > right.value(context)
|
30
42
|
end
|
43
|
+
|
44
|
+
def operator
|
45
|
+
return :>
|
46
|
+
end
|
31
47
|
end
|
32
48
|
|
33
49
|
class GreaterThanOrEqual < Comparator
|
34
|
-
def value(context={})
|
50
|
+
def value(context = {})
|
35
51
|
left.value(context) >= right.value(context)
|
36
52
|
end
|
53
|
+
|
54
|
+
def operator
|
55
|
+
return :>=
|
56
|
+
end
|
37
57
|
end
|
38
58
|
|
39
59
|
class NotEqual < Comparator
|
40
|
-
def value(context={})
|
60
|
+
def value(context = {})
|
41
61
|
left.value(context) != right.value(context)
|
42
62
|
end
|
63
|
+
|
64
|
+
def operator
|
65
|
+
return :!=
|
66
|
+
end
|
43
67
|
end
|
44
68
|
|
45
69
|
class Equal < Comparator
|
46
|
-
def value(context={})
|
70
|
+
def value(context = {})
|
47
71
|
left.value(context) == right.value(context)
|
48
72
|
end
|
73
|
+
|
74
|
+
def operator
|
75
|
+
return :==
|
76
|
+
end
|
49
77
|
end
|
50
78
|
end
|
51
79
|
end
|
data/lib/dentaku/ast/function.rb
CHANGED
@@ -11,15 +11,19 @@ module Dentaku
|
|
11
11
|
@right = right
|
12
12
|
end
|
13
13
|
|
14
|
-
def value(context={})
|
14
|
+
def value(context = {})
|
15
15
|
predicate.value(context) ? left.value(context) : right.value(context)
|
16
16
|
end
|
17
17
|
|
18
|
+
def node_type
|
19
|
+
:condition
|
20
|
+
end
|
21
|
+
|
18
22
|
def type
|
19
23
|
left.type
|
20
24
|
end
|
21
25
|
|
22
|
-
def dependencies(context={})
|
26
|
+
def dependencies(context = {})
|
23
27
|
# TODO : short-circuit?
|
24
28
|
(predicate.dependencies(context) + left.dependencies(context) + right.dependencies(context)).uniq
|
25
29
|
end
|
@@ -3,6 +3,6 @@ require_relative '../function'
|
|
3
3
|
|
4
4
|
Math.methods(false).each do |method|
|
5
5
|
Dentaku::AST::Function.register(method, :numeric, lambda { |*args|
|
6
|
-
Math.send(method, *args.map { |arg| Dentaku::AST::Function.numeric(arg) })
|
6
|
+
Math.send(method, *args.flatten.map { |arg| Dentaku::AST::Function.numeric(arg) })
|
7
7
|
})
|
8
8
|
end
|
@@ -9,7 +9,7 @@ module Dentaku
|
|
9
9
|
@string, @length = *@args
|
10
10
|
end
|
11
11
|
|
12
|
-
def value(context={})
|
12
|
+
def value(context = {})
|
13
13
|
string = @string.value(context).to_s
|
14
14
|
length = @length.value(context)
|
15
15
|
string[0, length]
|
@@ -22,7 +22,7 @@ module Dentaku
|
|
22
22
|
@string, @length = *@args
|
23
23
|
end
|
24
24
|
|
25
|
-
def value(context={})
|
25
|
+
def value(context = {})
|
26
26
|
string = @string.value(context).to_s
|
27
27
|
length = @length.value(context)
|
28
28
|
string[length * -1, length] || string
|
@@ -35,7 +35,7 @@ module Dentaku
|
|
35
35
|
@string, @offset, @length = *@args
|
36
36
|
end
|
37
37
|
|
38
|
-
def value(context={})
|
38
|
+
def value(context = {})
|
39
39
|
string = @string.value(context).to_s
|
40
40
|
offset = @offset.value(context)
|
41
41
|
length = @length.value(context)
|
@@ -49,7 +49,7 @@ module Dentaku
|
|
49
49
|
@string = @args[0]
|
50
50
|
end
|
51
51
|
|
52
|
-
def value(context={})
|
52
|
+
def value(context = {})
|
53
53
|
string = @string.value(context).to_s
|
54
54
|
string.length
|
55
55
|
end
|
@@ -61,7 +61,7 @@ module Dentaku
|
|
61
61
|
@needle, @haystack = *@args
|
62
62
|
end
|
63
63
|
|
64
|
-
def value(context={})
|
64
|
+
def value(context = {})
|
65
65
|
needle = @needle.value(context)
|
66
66
|
needle = needle.to_s unless needle.is_a?(Regexp)
|
67
67
|
haystack = @haystack.value(context).to_s
|
@@ -76,7 +76,7 @@ module Dentaku
|
|
76
76
|
@original, @search, @replacement = *@args
|
77
77
|
end
|
78
78
|
|
79
|
-
def value(context={})
|
79
|
+
def value(context = {})
|
80
80
|
original = @original.value(context).to_s
|
81
81
|
search = @search.value(context)
|
82
82
|
search = search.to_s unless search.is_a?(Regexp)
|
@@ -90,7 +90,7 @@ module Dentaku
|
|
90
90
|
super
|
91
91
|
end
|
92
92
|
|
93
|
-
def value(context={})
|
93
|
+
def value(context = {})
|
94
94
|
@args.map { |arg| arg.value(context).to_s }.join
|
95
95
|
end
|
96
96
|
end
|
@@ -101,7 +101,7 @@ module Dentaku
|
|
101
101
|
@needle, @haystack = *args
|
102
102
|
end
|
103
103
|
|
104
|
-
def value(context={})
|
104
|
+
def value(context = {})
|
105
105
|
@haystack.value(context).to_s.include? @needle.value(context).to_s
|
106
106
|
end
|
107
107
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative '../function'
|
2
|
+
|
3
|
+
Dentaku::AST::Function.register(:sum, :numeric, ->(*args) {
|
4
|
+
if args.empty?
|
5
|
+
raise Dentaku::ArgumentError.for(
|
6
|
+
:too_few_arguments,
|
7
|
+
function_name: 'SUM()', at_least: 1, given: 0
|
8
|
+
), 'SUM() requires at least one argument'
|
9
|
+
end
|
10
|
+
|
11
|
+
args.flatten.map { |arg| Dentaku::AST::Function.numeric(arg) }.reduce(0, :+)
|
12
|
+
})
|
data/lib/dentaku/ast/grouping.rb
CHANGED
@@ -5,7 +5,7 @@ module Dentaku
|
|
5
5
|
@node = node
|
6
6
|
end
|
7
7
|
|
8
|
-
def value(context={})
|
8
|
+
def value(context = {})
|
9
9
|
@node.value(context)
|
10
10
|
end
|
11
11
|
|
@@ -13,7 +13,7 @@ module Dentaku
|
|
13
13
|
@node.type
|
14
14
|
end
|
15
15
|
|
16
|
-
def dependencies(context={})
|
16
|
+
def dependencies(context = {})
|
17
17
|
@node.dependencies(context)
|
18
18
|
end
|
19
19
|
end
|
@@ -1,15 +1,18 @@
|
|
1
1
|
require_relative '../exceptions'
|
2
|
+
require 'dentaku/string_casing'
|
2
3
|
|
3
4
|
module Dentaku
|
4
5
|
module AST
|
5
6
|
class Identifier < Node
|
6
|
-
|
7
|
+
include StringCasing
|
8
|
+
attr_reader :identifier, :case_sensitive
|
7
9
|
|
8
|
-
def initialize(token)
|
9
|
-
@
|
10
|
+
def initialize(token, options = {})
|
11
|
+
@case_sensitive = options.fetch(:case_sensitive, false)
|
12
|
+
@identifier = standardize_case(token.value)
|
10
13
|
end
|
11
14
|
|
12
|
-
def value(context={})
|
15
|
+
def value(context = {})
|
13
16
|
v = context.fetch(identifier) do
|
14
17
|
raise UnboundVariableError.new([identifier]),
|
15
18
|
"no value provided for variables: #{identifier}"
|
@@ -23,7 +26,7 @@ module Dentaku
|
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
def dependencies(context={})
|
29
|
+
def dependencies(context = {})
|
27
30
|
context.key?(identifier) ? dependencies_of(context[identifier]) : [identifier]
|
28
31
|
end
|
29
32
|
|
data/lib/dentaku/ast/negation.rb
CHANGED
@@ -14,7 +14,7 @@ module Dentaku
|
|
14
14
|
:*
|
15
15
|
end
|
16
16
|
|
17
|
-
def value(context={})
|
17
|
+
def value(context = {})
|
18
18
|
cast(@node.value(context)) * -1
|
19
19
|
end
|
20
20
|
|
@@ -34,7 +34,7 @@ module Dentaku
|
|
34
34
|
40
|
35
35
|
end
|
36
36
|
|
37
|
-
def dependencies(context={})
|
37
|
+
def dependencies(context = {})
|
38
38
|
@node.dependencies(context)
|
39
39
|
end
|
40
40
|
|