dentaku 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|