dentaku 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +119 -0
  3. data/.travis.yml +8 -9
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile +0 -5
  6. data/LICENSE +21 -0
  7. data/README.md +44 -3
  8. data/Rakefile +4 -1
  9. data/dentaku.gemspec +8 -4
  10. data/lib/dentaku.rb +15 -2
  11. data/lib/dentaku/ast.rb +1 -0
  12. data/lib/dentaku/ast/access.rb +2 -2
  13. data/lib/dentaku/ast/arithmetic.rb +7 -7
  14. data/lib/dentaku/ast/bitwise.rb +2 -2
  15. data/lib/dentaku/ast/case.rb +5 -5
  16. data/lib/dentaku/ast/case/case_conditional.rb +1 -1
  17. data/lib/dentaku/ast/case/case_else.rb +2 -2
  18. data/lib/dentaku/ast/case/case_switch_variable.rb +2 -2
  19. data/lib/dentaku/ast/case/case_then.rb +2 -2
  20. data/lib/dentaku/ast/case/case_when.rb +2 -2
  21. data/lib/dentaku/ast/combinators.rb +10 -2
  22. data/lib/dentaku/ast/comparators.rb +34 -6
  23. data/lib/dentaku/ast/function.rb +1 -1
  24. data/lib/dentaku/ast/function_registry.rb +1 -1
  25. data/lib/dentaku/ast/functions/if.rb +6 -2
  26. data/lib/dentaku/ast/functions/max.rb +1 -1
  27. data/lib/dentaku/ast/functions/min.rb +1 -1
  28. data/lib/dentaku/ast/functions/ruby_math.rb +1 -1
  29. data/lib/dentaku/ast/functions/string_functions.rb +8 -8
  30. data/lib/dentaku/ast/functions/sum.rb +12 -0
  31. data/lib/dentaku/ast/grouping.rb +2 -2
  32. data/lib/dentaku/ast/identifier.rb +8 -5
  33. data/lib/dentaku/ast/negation.rb +2 -2
  34. data/lib/dentaku/ast/node.rb +1 -1
  35. data/lib/dentaku/ast/operation.rb +1 -1
  36. data/lib/dentaku/bulk_expression_solver.rb +39 -20
  37. data/lib/dentaku/calculator.rb +38 -28
  38. data/lib/dentaku/dependency_resolver.rb +1 -1
  39. data/lib/dentaku/flat_hash.rb +31 -0
  40. data/lib/dentaku/parser.rb +7 -6
  41. data/lib/dentaku/string_casing.rb +7 -0
  42. data/lib/dentaku/token.rb +1 -1
  43. data/lib/dentaku/token_matcher.rb +4 -4
  44. data/lib/dentaku/token_scanner.rb +18 -7
  45. data/lib/dentaku/tokenizer.rb +26 -2
  46. data/lib/dentaku/version.rb +1 -1
  47. data/spec/ast/arithmetic_spec.rb +2 -2
  48. data/spec/ast/comparator_spec.rb +57 -0
  49. data/spec/ast/function_spec.rb +1 -1
  50. data/spec/ast/max_spec.rb +5 -0
  51. data/spec/ast/min_spec.rb +5 -0
  52. data/spec/ast/sum_spec.rb +38 -0
  53. data/spec/benchmark.rb +2 -2
  54. data/spec/bulk_expression_solver_spec.rb +89 -1
  55. data/spec/calculator_spec.rb +40 -7
  56. data/spec/dentaku_spec.rb +11 -0
  57. data/spec/external_function_spec.rb +7 -7
  58. data/spec/parser_spec.rb +11 -11
  59. data/spec/spec_helper.rb +21 -3
  60. data/spec/token_matcher_spec.rb +0 -1
  61. data/spec/token_spec.rb +6 -0
  62. data/spec/tokenizer_spec.rb +37 -0
  63. metadata +70 -5
@@ -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
@@ -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
@@ -15,7 +15,7 @@ module Dentaku
15
15
  end
16
16
  end
17
17
 
18
- def dependencies(context={})
18
+ def dependencies(context = {})
19
19
  @when.dependencies(context) + @then.dependencies(context)
20
20
  end
21
21
  end
@@ -9,11 +9,11 @@ module Dentaku
9
9
  @node = node
10
10
  end
11
11
 
12
- def value(context={})
12
+ def value(context = {})
13
13
  @node.value(context)
14
14
  end
15
15
 
16
- def dependencies(context={})
16
+ def dependencies(context = {})
17
17
  @node.dependencies(context)
18
18
  end
19
19
  end
@@ -5,11 +5,11 @@ 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
 
12
- def dependencies(context={})
12
+ def dependencies(context = {})
13
13
  @node.dependencies(context)
14
14
  end
15
15
 
@@ -9,11 +9,11 @@ module Dentaku
9
9
  @node = node
10
10
  end
11
11
 
12
- def value(context={})
12
+ def value(context = {})
13
13
  @node.value(context)
14
14
  end
15
15
 
16
- def dependencies(context={})
16
+ def dependencies(context = {})
17
17
  @node.dependencies(context)
18
18
  end
19
19
  end
@@ -9,11 +9,11 @@ module Dentaku
9
9
  @node = node
10
10
  end
11
11
 
12
- def value(context={})
12
+ def value(context = {})
13
13
  @node.value(context)
14
14
  end
15
15
 
16
- def dependencies(context={})
16
+ def dependencies(context = {})
17
17
  @node.dependencies(context)
18
18
  end
19
19
  end
@@ -28,13 +28,21 @@ module Dentaku
28
28
  end
29
29
 
30
30
  class And < Combinator
31
- def value(context={})
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 value(context={})
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
@@ -11,7 +11,7 @@ module Dentaku
11
11
  @args = args
12
12
  end
13
13
 
14
- def dependencies(context={})
14
+ def dependencies(context = {})
15
15
  @args.flat_map { |a| a.dependencies(context) }
16
16
  end
17
17
 
@@ -38,7 +38,7 @@ module Dentaku
38
38
  @implementation.arity < 0 ? nil : @implementation.arity
39
39
  end
40
40
 
41
- def value(context={})
41
+ def value(context = {})
42
42
  args = @args.map { |a| a.value(context) }
43
43
  self.class.implementation.call(*args)
44
44
  end
@@ -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
@@ -1,5 +1,5 @@
1
1
  require_relative '../function'
2
2
 
3
3
  Dentaku::AST::Function.register(:max, :numeric, ->(*args) {
4
- args.map { |arg| Dentaku::AST::Function.numeric(arg) }.max
4
+ args.flatten.map { |arg| Dentaku::AST::Function.numeric(arg) }.max
5
5
  })
@@ -1,5 +1,5 @@
1
1
  require_relative '../function'
2
2
 
3
3
  Dentaku::AST::Function.register(:min, :numeric, ->(*args) {
4
- args.map { |arg| Dentaku::AST::Function.numeric(arg) }.min
4
+ args.flatten.map { |arg| Dentaku::AST::Function.numeric(arg) }.min
5
5
  })
@@ -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
+ })
@@ -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
- attr_reader :identifier
7
+ include StringCasing
8
+ attr_reader :identifier, :case_sensitive
7
9
 
8
- def initialize(token)
9
- @identifier = token.value.downcase
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
 
@@ -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
 
@@ -12,7 +12,7 @@ module Dentaku
12
12
  def self.peek(*)
13
13
  end
14
14
 
15
- def dependencies(context={})
15
+ def dependencies(context = {})
16
16
  []
17
17
  end
18
18
  end