hayadentaku 3.5.7

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.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rspec.yml +26 -0
  3. data/.github/workflows/rubocop.yml +14 -0
  4. data/.gitignore +14 -0
  5. data/.pryrc +2 -0
  6. data/.rubocop.yml +114 -0
  7. data/.travis.yml +10 -0
  8. data/CHANGELOG.md +328 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE +21 -0
  11. data/README.md +352 -0
  12. data/Rakefile +31 -0
  13. data/hayadentaku.gemspec +35 -0
  14. data/lib/dentaku/ast/access.rb +44 -0
  15. data/lib/dentaku/ast/arithmetic.rb +292 -0
  16. data/lib/dentaku/ast/array.rb +38 -0
  17. data/lib/dentaku/ast/bitwise.rb +42 -0
  18. data/lib/dentaku/ast/case/case_conditional.rb +38 -0
  19. data/lib/dentaku/ast/case/case_else.rb +35 -0
  20. data/lib/dentaku/ast/case/case_switch_variable.rb +35 -0
  21. data/lib/dentaku/ast/case/case_then.rb +35 -0
  22. data/lib/dentaku/ast/case/case_when.rb +39 -0
  23. data/lib/dentaku/ast/case.rb +93 -0
  24. data/lib/dentaku/ast/combinators.rb +50 -0
  25. data/lib/dentaku/ast/comparators.rb +88 -0
  26. data/lib/dentaku/ast/datetime.rb +8 -0
  27. data/lib/dentaku/ast/function.rb +56 -0
  28. data/lib/dentaku/ast/function_registry.rb +107 -0
  29. data/lib/dentaku/ast/functions/abs.rb +5 -0
  30. data/lib/dentaku/ast/functions/all.rb +19 -0
  31. data/lib/dentaku/ast/functions/and.rb +25 -0
  32. data/lib/dentaku/ast/functions/any.rb +19 -0
  33. data/lib/dentaku/ast/functions/avg.rb +13 -0
  34. data/lib/dentaku/ast/functions/count.rb +26 -0
  35. data/lib/dentaku/ast/functions/duration.rb +51 -0
  36. data/lib/dentaku/ast/functions/enum.rb +54 -0
  37. data/lib/dentaku/ast/functions/filter.rb +21 -0
  38. data/lib/dentaku/ast/functions/if.rb +47 -0
  39. data/lib/dentaku/ast/functions/intercept.rb +33 -0
  40. data/lib/dentaku/ast/functions/map.rb +19 -0
  41. data/lib/dentaku/ast/functions/max.rb +5 -0
  42. data/lib/dentaku/ast/functions/min.rb +5 -0
  43. data/lib/dentaku/ast/functions/mul.rb +12 -0
  44. data/lib/dentaku/ast/functions/not.rb +5 -0
  45. data/lib/dentaku/ast/functions/or.rb +25 -0
  46. data/lib/dentaku/ast/functions/pluck.rb +34 -0
  47. data/lib/dentaku/ast/functions/reduce.rb +60 -0
  48. data/lib/dentaku/ast/functions/round.rb +5 -0
  49. data/lib/dentaku/ast/functions/rounddown.rb +8 -0
  50. data/lib/dentaku/ast/functions/roundup.rb +8 -0
  51. data/lib/dentaku/ast/functions/ruby_math.rb +57 -0
  52. data/lib/dentaku/ast/functions/string_functions.rb +212 -0
  53. data/lib/dentaku/ast/functions/sum.rb +12 -0
  54. data/lib/dentaku/ast/functions/switch.rb +8 -0
  55. data/lib/dentaku/ast/functions/xor.rb +44 -0
  56. data/lib/dentaku/ast/grouping.rb +23 -0
  57. data/lib/dentaku/ast/identifier.rb +52 -0
  58. data/lib/dentaku/ast/literal.rb +30 -0
  59. data/lib/dentaku/ast/logical.rb +8 -0
  60. data/lib/dentaku/ast/negation.rb +54 -0
  61. data/lib/dentaku/ast/nil.rb +13 -0
  62. data/lib/dentaku/ast/node.rb +29 -0
  63. data/lib/dentaku/ast/numeric.rb +8 -0
  64. data/lib/dentaku/ast/operation.rb +44 -0
  65. data/lib/dentaku/ast/string.rb +15 -0
  66. data/lib/dentaku/ast.rb +42 -0
  67. data/lib/dentaku/bulk_expression_solver.rb +158 -0
  68. data/lib/dentaku/calculator.rb +192 -0
  69. data/lib/dentaku/date_arithmetic.rb +60 -0
  70. data/lib/dentaku/dependency_resolver.rb +29 -0
  71. data/lib/dentaku/exceptions.rb +116 -0
  72. data/lib/dentaku/flat_hash.rb +161 -0
  73. data/lib/dentaku/parser.rb +318 -0
  74. data/lib/dentaku/print_visitor.rb +112 -0
  75. data/lib/dentaku/string_casing.rb +7 -0
  76. data/lib/dentaku/token.rb +48 -0
  77. data/lib/dentaku/token_matcher.rb +138 -0
  78. data/lib/dentaku/token_matchers.rb +29 -0
  79. data/lib/dentaku/token_scanner.rb +240 -0
  80. data/lib/dentaku/tokenizer.rb +127 -0
  81. data/lib/dentaku/version.rb +3 -0
  82. data/lib/dentaku/visitor/infix.rb +86 -0
  83. data/lib/dentaku.rb +69 -0
  84. data/spec/ast/abs_spec.rb +26 -0
  85. data/spec/ast/addition_spec.rb +67 -0
  86. data/spec/ast/all_spec.rb +38 -0
  87. data/spec/ast/and_function_spec.rb +35 -0
  88. data/spec/ast/and_spec.rb +32 -0
  89. data/spec/ast/any_spec.rb +36 -0
  90. data/spec/ast/arithmetic_spec.rb +147 -0
  91. data/spec/ast/avg_spec.rb +42 -0
  92. data/spec/ast/case_spec.rb +84 -0
  93. data/spec/ast/comparator_spec.rb +87 -0
  94. data/spec/ast/count_spec.rb +40 -0
  95. data/spec/ast/division_spec.rb +64 -0
  96. data/spec/ast/filter_spec.rb +25 -0
  97. data/spec/ast/function_spec.rb +69 -0
  98. data/spec/ast/intercept_spec.rb +30 -0
  99. data/spec/ast/map_spec.rb +40 -0
  100. data/spec/ast/max_spec.rb +33 -0
  101. data/spec/ast/min_spec.rb +33 -0
  102. data/spec/ast/mul_spec.rb +43 -0
  103. data/spec/ast/negation_spec.rb +48 -0
  104. data/spec/ast/node_spec.rb +43 -0
  105. data/spec/ast/numeric_spec.rb +16 -0
  106. data/spec/ast/or_spec.rb +35 -0
  107. data/spec/ast/pluck_spec.rb +49 -0
  108. data/spec/ast/reduce_spec.rb +22 -0
  109. data/spec/ast/round_spec.rb +35 -0
  110. data/spec/ast/rounddown_spec.rb +35 -0
  111. data/spec/ast/roundup_spec.rb +35 -0
  112. data/spec/ast/string_functions_spec.rb +217 -0
  113. data/spec/ast/sum_spec.rb +43 -0
  114. data/spec/ast/switch_spec.rb +30 -0
  115. data/spec/ast/xor_spec.rb +35 -0
  116. data/spec/benchmark.rb +70 -0
  117. data/spec/bulk_expression_solver_spec.rb +241 -0
  118. data/spec/calculator_spec.rb +1003 -0
  119. data/spec/dentaku_spec.rb +52 -0
  120. data/spec/dependency_resolver_spec.rb +18 -0
  121. data/spec/exceptions_spec.rb +9 -0
  122. data/spec/external_function_spec.rb +177 -0
  123. data/spec/parser_spec.rb +183 -0
  124. data/spec/print_visitor_spec.rb +77 -0
  125. data/spec/spec_helper.rb +69 -0
  126. data/spec/token_matcher_spec.rb +134 -0
  127. data/spec/token_scanner_spec.rb +49 -0
  128. data/spec/token_spec.rb +16 -0
  129. data/spec/tokenizer_spec.rb +375 -0
  130. data/spec/visitor/infix_spec.rb +52 -0
  131. data/spec/visitor_spec.rb +139 -0
  132. metadata +353 -0
@@ -0,0 +1,34 @@
1
+ require_relative './enum'
2
+ require_relative '../../exceptions'
3
+
4
+ module Dentaku
5
+ module AST
6
+ class Pluck < Enum
7
+ def self.min_param_count
8
+ 2
9
+ end
10
+
11
+ def self.max_param_count
12
+ 3
13
+ end
14
+
15
+ def value(context = {})
16
+ collection = Array(@args[0].value(context))
17
+
18
+ unless collection.all? { |elem| elem.is_a?(Hash) }
19
+ raise ArgumentError.for(:incompatible_type, value: collection),
20
+ 'PLUCK() requires first argument to be an array of hashes'
21
+ end
22
+
23
+ pluck_path = @args[1].identifier
24
+ default = @args[2]
25
+
26
+ collection.map { |h|
27
+ h.transform_keys(&:to_s).fetch(pluck_path, default&.value(context))
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ Dentaku::AST::Function.register_class(:pluck, Dentaku::AST::Pluck)
@@ -0,0 +1,60 @@
1
+ require_relative '../function'
2
+ require_relative '../../exceptions'
3
+
4
+ module Dentaku
5
+ module AST
6
+ class Reduce < Function
7
+ def self.min_param_count
8
+ 4
9
+ end
10
+
11
+ def self.max_param_count
12
+ 5
13
+ end
14
+
15
+ def initialize(*args)
16
+ super
17
+
18
+ validate_identifier(@args[1], 'second')
19
+ validate_identifier(@args[2], 'third')
20
+ end
21
+
22
+ def dependencies(context = {})
23
+ collection = @args[0]
24
+ memo_identifier = @args[1].identifier
25
+ item_identifier = @args[2].identifier
26
+ expression = @args[3]
27
+
28
+ collection_deps = collection.dependencies(context)
29
+ expression_deps = expression.dependencies(context).reject do |i|
30
+ i == memo_identifier || i.start_with?("#{memo_identifier}.") ||
31
+ i == item_identifier || i.start_with?("#{item_identifier}.")
32
+ end
33
+ inital_value_deps = @args[4] ? @args[4].dependencies(context) : []
34
+
35
+ collection_deps + expression_deps + inital_value_deps
36
+ end
37
+
38
+ def value(context = {})
39
+ collection = Array(@args[0].value(context))
40
+ memo_identifier = @args[1].identifier
41
+ item_identifier = @args[2].identifier
42
+ expression = @args[3]
43
+ initial_value = @args[4] && @args[4].value(context)
44
+
45
+ collection.reduce(initial_value) do |memo, item|
46
+ scratch = context.dup
47
+ FlatHash.write_pair_with_intermediates!(memo_identifier, memo, scratch)
48
+ FlatHash.write_pair_with_intermediates!(item_identifier, item, scratch)
49
+ expression.value(scratch)
50
+ end
51
+ end
52
+
53
+ def validate_identifier(arg, position, message = "#{name}() requires #{position} argument to be an identifier")
54
+ raise ParseError.for(:node_invalid), message unless arg.is_a?(Identifier)
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ Dentaku::AST::Function.register_class(:reduce, Dentaku::AST::Reduce)
@@ -0,0 +1,5 @@
1
+ require_relative '../function'
2
+
3
+ Dentaku::AST::Function.register(:round, :numeric, lambda { |numeric, places = 0|
4
+ Dentaku::AST::Function.numeric(numeric).round(Dentaku::AST::Function.numeric(places || 0).to_i)
5
+ })
@@ -0,0 +1,8 @@
1
+ require_relative '../function'
2
+
3
+ Dentaku::AST::Function.register(:rounddown, :numeric, lambda { |numeric, precision = 0|
4
+ precision = Dentaku::AST::Function.numeric(precision || 0).to_i
5
+ tens = 10.0**precision
6
+ result = (Dentaku::AST::Function.numeric(numeric) * tens).floor / tens
7
+ precision <= 0 ? result.to_i : result
8
+ })
@@ -0,0 +1,8 @@
1
+ require_relative '../function'
2
+
3
+ Dentaku::AST::Function.register(:roundup, :numeric, lambda { |numeric, precision = 0|
4
+ precision = Dentaku::AST::Function.numeric(precision || 0).to_i
5
+ tens = 10.0**precision
6
+ result = (Dentaku::AST::Function.numeric(numeric) * tens).ceil / tens
7
+ precision <= 0 ? result.to_i : result
8
+ })
@@ -0,0 +1,57 @@
1
+ # import all functions from Ruby's Math module
2
+ require_relative '../function'
3
+
4
+ module Dentaku
5
+ module AST
6
+ class RubyMath < Function
7
+ def self.[](method)
8
+ klass_name = method.to_s.capitalize
9
+ klass = const_set(klass_name , Class.new(self))
10
+ klass.implement(method)
11
+ const_get(klass_name)
12
+ end
13
+
14
+ def self.implement(method)
15
+ @name = method
16
+ @implementation = Math.method(method)
17
+ end
18
+
19
+ def self.name
20
+ @name
21
+ end
22
+
23
+ def self.arity
24
+ @implementation.arity < 0 ? nil : @implementation.arity
25
+ end
26
+
27
+ def self.min_param_count
28
+ @implementation.parameters.select { |type, _name| type == :req }.count
29
+ end
30
+
31
+ def self.max_param_count
32
+ @implementation.parameters.select { |type, _name| type == :rest }.any? ? Float::INFINITY : @implementation.parameters.count
33
+ end
34
+
35
+ def self.call(*args)
36
+ @implementation.call(*args)
37
+ rescue Math::DomainError => _e
38
+ raise Dentaku::MathDomainError.new(name, args)
39
+ end
40
+
41
+ def value(context = {})
42
+ args = @args.flatten.map { |a| Dentaku::AST::Function.numeric(a.value(context)) }
43
+ self.class.call(*args)
44
+ end
45
+
46
+ ARRAY_RETURN_TYPES = [:frexp, :lgamma].freeze
47
+
48
+ def type
49
+ ARRAY_RETURN_TYPES.include?(@name) ? :array : :numeric
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ Math.methods(false).each do |method|
56
+ Dentaku::AST::Function.register_class(method, Dentaku::AST::RubyMath[method])
57
+ end
@@ -0,0 +1,212 @@
1
+ require_relative '../function'
2
+
3
+ module Dentaku
4
+ module AST
5
+ module StringFunctions
6
+ class Base < Function
7
+ def type
8
+ :string
9
+ end
10
+
11
+ def negative_argument_failure(fun, arg = 'length')
12
+ raise Dentaku::ArgumentError.for(
13
+ :invalid_value,
14
+ function_name: "#{fun}()"
15
+ ), "#{fun}() requires #{arg} to be positive"
16
+ end
17
+ end
18
+
19
+ class Left < Base
20
+ def self.min_param_count
21
+ 2
22
+ end
23
+
24
+ def self.max_param_count
25
+ 2
26
+ end
27
+
28
+ def initialize(*args)
29
+ super
30
+ @string, @length = *@args
31
+ end
32
+
33
+ def value(context = {})
34
+ string = @string.value(context).to_s
35
+ length = Dentaku::AST::Function.numeric(@length.value(context)).to_i
36
+ negative_argument_failure('LEFT') if length < 0
37
+ string[0, length]
38
+ end
39
+ end
40
+
41
+ class Right < Base
42
+ def self.min_param_count
43
+ 2
44
+ end
45
+
46
+ def self.max_param_count
47
+ 2
48
+ end
49
+
50
+ def initialize(*args)
51
+ super
52
+ @string, @length = *@args
53
+ end
54
+
55
+ def value(context = {})
56
+ string = @string.value(context).to_s
57
+ length = Dentaku::AST::Function.numeric(@length.value(context)).to_i
58
+ negative_argument_failure('RIGHT') if length < 0
59
+ string[length * -1, length] || string
60
+ end
61
+ end
62
+
63
+ class Mid < Base
64
+ def self.min_param_count
65
+ 3
66
+ end
67
+
68
+ def self.max_param_count
69
+ 3
70
+ end
71
+
72
+ def initialize(*args)
73
+ super
74
+ @string, @offset, @length = *@args
75
+ end
76
+
77
+ def value(context = {})
78
+ string = @string.value(context).to_s
79
+ offset = Dentaku::AST::Function.numeric(@offset.value(context)).to_i
80
+ negative_argument_failure('MID', 'offset') if offset < 0
81
+ length = Dentaku::AST::Function.numeric(@length.value(context)).to_i
82
+ negative_argument_failure('MID') if length < 0
83
+ string[offset - 1, length].to_s
84
+ end
85
+ end
86
+
87
+ class Len < Base
88
+ def self.min_param_count
89
+ 1
90
+ end
91
+
92
+ def self.max_param_count
93
+ 1
94
+ end
95
+
96
+ def initialize(*args)
97
+ super
98
+ @string = @args[0]
99
+ end
100
+
101
+ def value(context = {})
102
+ string = @string.value(context).to_s
103
+ string.length
104
+ end
105
+
106
+ def type
107
+ :numeric
108
+ end
109
+ end
110
+
111
+ class Find < Base
112
+ def self.min_param_count
113
+ 2
114
+ end
115
+
116
+ def self.max_param_count
117
+ 2
118
+ end
119
+
120
+ def initialize(*args)
121
+ super
122
+ @needle, @haystack = *@args
123
+ end
124
+
125
+ def value(context = {})
126
+ needle = @needle.value(context)
127
+ needle = needle.to_s unless needle.is_a?(Regexp)
128
+ haystack = @haystack.value(context).to_s
129
+ pos = haystack.index(needle)
130
+ pos && pos + 1
131
+ end
132
+
133
+ def type
134
+ :numeric
135
+ end
136
+ end
137
+
138
+ class Substitute < Base
139
+ def self.min_param_count
140
+ 3
141
+ end
142
+
143
+ def self.max_param_count
144
+ 3
145
+ end
146
+
147
+ def initialize(*args)
148
+ super
149
+ @original, @search, @replacement = *@args
150
+ end
151
+
152
+ def value(context = {})
153
+ original = @original.value(context).to_s
154
+ search = @search.value(context)
155
+ search = search.to_s unless search.is_a?(Regexp)
156
+ replacement = @replacement.value(context).to_s
157
+ original.sub(search, replacement)
158
+ end
159
+ end
160
+
161
+ class Concat < Base
162
+ def self.min_param_count
163
+ 1
164
+ end
165
+
166
+ def self.max_param_count
167
+ Float::INFINITY
168
+ end
169
+
170
+ def initialize(*args)
171
+ super
172
+ end
173
+
174
+ def value(context = {})
175
+ @args.map { |arg| arg.value(context).to_s }.join
176
+ end
177
+ end
178
+
179
+ class Contains < Base
180
+ def self.min_param_count
181
+ 2
182
+ end
183
+
184
+ def self.max_param_count
185
+ 2
186
+ end
187
+
188
+ def initialize(*args)
189
+ super
190
+ @needle, @haystack = *args
191
+ end
192
+
193
+ def value(context = {})
194
+ @haystack.value(context).to_s.include? @needle.value(context).to_s
195
+ end
196
+
197
+ def type
198
+ :logical
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ Dentaku::AST::Function.register_class(:left, Dentaku::AST::StringFunctions::Left)
206
+ Dentaku::AST::Function.register_class(:right, Dentaku::AST::StringFunctions::Right)
207
+ Dentaku::AST::Function.register_class(:mid, Dentaku::AST::StringFunctions::Mid)
208
+ Dentaku::AST::Function.register_class(:len, Dentaku::AST::StringFunctions::Len)
209
+ Dentaku::AST::Function.register_class(:find, Dentaku::AST::StringFunctions::Find)
210
+ Dentaku::AST::Function.register_class(:substitute, Dentaku::AST::StringFunctions::Substitute)
211
+ Dentaku::AST::Function.register_class(:concat, Dentaku::AST::StringFunctions::Concat)
212
+ Dentaku::AST::Function.register_class(:contains, Dentaku::AST::StringFunctions::Contains)
@@ -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
+ })
@@ -0,0 +1,8 @@
1
+ require_relative '../function'
2
+
3
+ Dentaku::AST::Function.register(:switch, :logical, lambda { |*args|
4
+ value = args.shift
5
+ default = args.pop if args.size.odd?
6
+ match = args.find_index.each_with_index { |arg, index| index.even? && arg == value }
7
+ match ? args[match + 1] : default
8
+ })
@@ -0,0 +1,44 @@
1
+ require_relative '../function'
2
+ require_relative '../../exceptions'
3
+
4
+ module Dentaku
5
+ module AST
6
+ class Xor < Function
7
+ def self.min_param_count
8
+ 1
9
+ end
10
+
11
+ def self.max_param_count
12
+ Float::INFINITY
13
+ end
14
+
15
+ def value(context = {})
16
+ if @args.empty?
17
+ raise Dentaku::ArgumentError.for(
18
+ :too_few_arguments,
19
+ function_name: 'XOR()', at_least: 1, given: 0
20
+ ), 'XOR() requires at least one argument'
21
+ end
22
+
23
+ true_arg_count = 0
24
+ @args.each do |arg|
25
+ case arg.value(context)
26
+ when TrueClass
27
+ true_arg_count += 1
28
+ break if true_arg_count > 1
29
+ when FalseClass, nil
30
+ next
31
+ else
32
+ raise Dentaku::ArgumentError.for(
33
+ :incompatible_type,
34
+ function_name: 'XOR()', expect: :logical, actual: arg.class
35
+ ), 'XOR() requires arguments to be logical expressions'
36
+ end
37
+ end
38
+ true_arg_count == 1
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ Dentaku::AST::Function.register_class(:xor, Dentaku::AST::Xor)
@@ -0,0 +1,23 @@
1
+ require_relative "./node"
2
+
3
+ module Dentaku
4
+ module AST
5
+ class Grouping < Node
6
+ def initialize(node)
7
+ @node = node
8
+ end
9
+
10
+ def value(context = {})
11
+ @node.value(context)
12
+ end
13
+
14
+ def type
15
+ @node.type
16
+ end
17
+
18
+ def dependencies(context = {})
19
+ @node.dependencies(context)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ require_relative '../exceptions'
2
+ require 'dentaku/string_casing'
3
+
4
+ module Dentaku
5
+ module AST
6
+ class Identifier < Node
7
+ include StringCasing
8
+ attr_reader :identifier, :case_sensitive
9
+
10
+ def initialize(token, options = {})
11
+ @case_sensitive = options.fetch(:case_sensitive, false)
12
+ @identifier = standardize_case(token.value)
13
+ end
14
+
15
+ def value(context = {})
16
+ v = context.fetch(identifier) do
17
+ raise UnboundVariableError.new([identifier]),
18
+ "no value provided for variables: #{identifier}"
19
+ end
20
+
21
+ case v
22
+ when Node
23
+ value = v.value(context)
24
+ context[identifier] = value if Dentaku.cache_identifier?
25
+ value
26
+ when Proc
27
+ v.call
28
+ else
29
+ v
30
+ end
31
+ end
32
+
33
+ def dependencies(context = {})
34
+ context.key?(identifier) ? dependencies_of(context[identifier], context) : [identifier]
35
+ end
36
+
37
+ def accept(visitor)
38
+ visitor.visit_identifier(self)
39
+ end
40
+
41
+ def to_s
42
+ identifier.to_s
43
+ end
44
+
45
+ private
46
+
47
+ def dependencies_of(node, context)
48
+ node.respond_to?(:dependencies) ? node.dependencies(context) : []
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,30 @@
1
+ module Dentaku
2
+ module AST
3
+ class Literal < Node
4
+ attr_reader :type
5
+
6
+ def initialize(token)
7
+ @token = token
8
+ @value = token.value
9
+ @type = token.category
10
+ end
11
+
12
+ def value(*)
13
+ @value
14
+ end
15
+
16
+ def dependencies(*)
17
+ []
18
+ end
19
+
20
+ def accept(visitor)
21
+ visitor.visit_literal(self)
22
+ end
23
+
24
+ def quoted
25
+ @token.raw_value || value.to_s
26
+ end
27
+ alias_method :to_s, :quoted
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "./literal"
2
+
3
+ module Dentaku
4
+ module AST
5
+ class Logical < Literal
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,54 @@
1
+ module Dentaku
2
+ module AST
3
+ class Negation < Arithmetic
4
+ attr_reader :node
5
+
6
+ def initialize(node)
7
+ @node = node
8
+
9
+ unless valid_node?(node)
10
+ raise NodeError.new(:numeric, node.type, :node),
11
+ "#{self.class} requires numeric operands"
12
+ end
13
+ end
14
+
15
+ def operator
16
+ :*
17
+ end
18
+
19
+ def value(context = {})
20
+ cast(@node.value(context)) * -1
21
+ end
22
+
23
+ def type
24
+ :numeric
25
+ end
26
+
27
+ def self.arity
28
+ 1
29
+ end
30
+
31
+ def self.right_associative?
32
+ true
33
+ end
34
+
35
+ def self.precedence
36
+ 40
37
+ end
38
+
39
+ def dependencies(context = {})
40
+ @node.dependencies(context)
41
+ end
42
+
43
+ def accept(visitor)
44
+ visitor.visit_negation(self)
45
+ end
46
+
47
+ private
48
+
49
+ def valid_node?(node)
50
+ node && (node.dependencies.any? || node.type == :numeric)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,13 @@
1
+ module Dentaku
2
+ module AST
3
+ class Nil < Node
4
+ def value(*)
5
+ nil
6
+ end
7
+
8
+ def accept(visitor)
9
+ visitor.visit_nil(self)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,29 @@
1
+ module Dentaku
2
+ module AST
3
+ class Node
4
+ def self.precedence
5
+ 0
6
+ end
7
+
8
+ def self.arity
9
+ nil
10
+ end
11
+
12
+ def self.resolve_class(*)
13
+ self
14
+ end
15
+
16
+ def dependencies(context = {})
17
+ []
18
+ end
19
+
20
+ def type
21
+ nil
22
+ end
23
+
24
+ def name
25
+ self.class.name.to_s.split("::").last.upcase
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "./literal"
2
+
3
+ module Dentaku
4
+ module AST
5
+ class Numeric < Literal
6
+ end
7
+ end
8
+ end