dentaku 3.3.0 → 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/.travis.yml +4 -6
- data/CHANGELOG.md +7 -1
- data/lib/dentaku/ast/arithmetic.rb +1 -1
- data/lib/dentaku/ast/case.rb +5 -3
- data/lib/dentaku/ast/case/case_conditional.rb +5 -2
- data/lib/dentaku/ast/function.rb +1 -1
- data/lib/dentaku/flat_hash.rb +2 -2
- data/lib/dentaku/parser.rb +17 -11
- data/lib/dentaku/token_matcher.rb +1 -1
- data/lib/dentaku/token_scanner.rb +1 -1
- data/lib/dentaku/tokenizer.rb +6 -1
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/addition_spec.rb +1 -1
- data/spec/ast/and_function_spec.rb +6 -6
- data/spec/ast/and_spec.rb +1 -1
- data/spec/ast/arithmetic_spec.rb +17 -17
- data/spec/ast/avg_spec.rb +5 -5
- data/spec/ast/count_spec.rb +7 -7
- data/spec/ast/division_spec.rb +1 -1
- data/spec/ast/function_spec.rb +8 -8
- data/spec/ast/max_spec.rb +3 -3
- data/spec/ast/min_spec.rb +3 -3
- data/spec/ast/mul_spec.rb +6 -6
- data/spec/ast/node_spec.rb +8 -8
- data/spec/ast/numeric_spec.rb +1 -1
- data/spec/ast/or_spec.rb +6 -6
- data/spec/ast/round_spec.rb +4 -4
- data/spec/ast/rounddown_spec.rb +4 -4
- data/spec/ast/roundup_spec.rb +4 -4
- data/spec/ast/sum_spec.rb +6 -6
- data/spec/ast/switch_spec.rb +5 -5
- data/spec/bulk_expression_solver_spec.rb +1 -1
- data/spec/calculator_spec.rb +30 -26
- data/spec/external_function_spec.rb +3 -3
- data/spec/parser_spec.rb +72 -123
- data/spec/spec_helper.rb +6 -4
- data/spec/token_matcher_spec.rb +8 -8
- data/spec/token_scanner_spec.rb +4 -4
- data/spec/tokenizer_spec.rb +8 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b760fc43a6d5745ea9c95827fb0ce03e8fef8453c2264ee1073e5754c2b8d402
|
4
|
+
data.tar.gz: 04e03adf0729e170babe62c8f91cc88ca8aa46932dacb694ccfc564696eed765
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 006ec24c2c61758a1b7f5921e181ed49e2d54bf479091f9600ed31019fedd0a9411d744932e7dbf973b2e8fea098ff7299e58fd97851e5b2382a81e05043f8e4
|
7
|
+
data.tar.gz: 7f9d3735fc20e918d808158e8c618e11a0021762f102d9d5b32eec7d24f6a28845c01912fbc63c4cb835438f7f8d6d494fe811a5b01e373a5a6296a087c4edda
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
-
TargetRubyVersion: 2.
|
2
|
+
TargetRubyVersion: 2.6
|
3
3
|
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
|
4
4
|
# to ignore them, so only the ones explicitly set in this file are enabled.
|
5
5
|
DisabledByDefault: true
|
@@ -110,7 +110,7 @@ Style/UnneededPercentQ:
|
|
110
110
|
|
111
111
|
# Align `end` with the matching keyword or starting expression except for
|
112
112
|
# assignments, where it should be aligned with the LHS.
|
113
|
-
|
113
|
+
Layout/EndAlignment:
|
114
114
|
Enabled: true
|
115
115
|
EnforcedStyleAlignWith: variable
|
116
116
|
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v3.3.1] 2019-03-26
|
4
|
+
- better errors for parse failures and exceptions in internal functions
|
5
|
+
- fix Ruby 2.6.0 deprecation warnings
|
6
|
+
- fix issue with functions in nested case statements
|
7
|
+
|
3
8
|
## [v3.3.0] 2018-12-04
|
4
9
|
- add array literal syntax
|
5
10
|
- return correct type from string function AST nodes
|
@@ -166,7 +171,8 @@
|
|
166
171
|
## [v0.1.0] 2012-01-20
|
167
172
|
- initial release
|
168
173
|
|
169
|
-
[HEAD]: https://github.com/rubysolo/dentaku/compare/v3.3.
|
174
|
+
[HEAD]: https://github.com/rubysolo/dentaku/compare/v3.3.1...HEAD
|
175
|
+
[v3.3.1]: https://github.com/rubysolo/dentaku/compare/v3.3.0...v3.3.1
|
170
176
|
[v3.3.0]: https://github.com/rubysolo/dentaku/compare/v3.2.1...v3.3.0
|
171
177
|
[v3.2.1]: https://github.com/rubysolo/dentaku/compare/v3.2.0...v3.2.1
|
172
178
|
[v3.2.0]: https://github.com/rubysolo/dentaku/compare/v3.1.0...v3.2.0
|
data/lib/dentaku/ast/case.rb
CHANGED
@@ -3,6 +3,7 @@ require_relative './case/case_when'
|
|
3
3
|
require_relative './case/case_then'
|
4
4
|
require_relative './case/case_switch_variable'
|
5
5
|
require_relative './case/case_else'
|
6
|
+
require 'dentaku/exceptions'
|
6
7
|
|
7
8
|
module Dentaku
|
8
9
|
module AST
|
@@ -11,16 +12,17 @@ module Dentaku
|
|
11
12
|
@switch = nodes.shift
|
12
13
|
|
13
14
|
unless @switch.is_a?(AST::CaseSwitchVariable)
|
14
|
-
raise 'Case missing switch variable'
|
15
|
+
raise ParseError.for(:node_invalid), 'Case missing switch variable'
|
15
16
|
end
|
16
17
|
|
17
18
|
@conditions = nodes
|
18
19
|
|
20
|
+
@else = nil
|
19
21
|
@else = @conditions.pop if @conditions.last.is_a?(AST::CaseElse)
|
20
22
|
|
21
23
|
@conditions.each do |condition|
|
22
24
|
unless condition.is_a?(AST::CaseConditional)
|
23
|
-
raise "#{condition} is not a CaseConditional"
|
25
|
+
raise ParseError.for(:node_invalid), "#{condition} is not a CaseConditional"
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -36,7 +38,7 @@ module Dentaku
|
|
36
38
|
if @else
|
37
39
|
return @else.value(context)
|
38
40
|
else
|
39
|
-
raise "No block matched the switch value '#{switch_value}'"
|
41
|
+
raise ArgumentError.for(:invalid_value), "No block matched the switch value '#{switch_value}'"
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'dentaku/exceptions'
|
2
|
+
|
1
3
|
module Dentaku
|
2
4
|
module AST
|
3
5
|
class CaseConditional < Node
|
@@ -7,11 +9,12 @@ module Dentaku
|
|
7
9
|
def initialize(when_statement, then_statement)
|
8
10
|
@when = when_statement
|
9
11
|
unless @when.is_a?(AST::CaseWhen)
|
10
|
-
raise 'Expected first argument to be a CaseWhen'
|
12
|
+
raise ParseError.for(:node_invalid), 'Expected first argument to be a CaseWhen'
|
11
13
|
end
|
14
|
+
|
12
15
|
@then = then_statement
|
13
16
|
unless @then.is_a?(AST::CaseThen)
|
14
|
-
raise 'Expected second argument to be a CaseThen'
|
17
|
+
raise ParseError.for(:node_invalid), 'Expected second argument to be a CaseThen'
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
data/lib/dentaku/ast/function.rb
CHANGED
@@ -38,7 +38,7 @@ module Dentaku
|
|
38
38
|
|
39
39
|
if value.is_a?(::String)
|
40
40
|
number = value[/\A-?\d*\.?\d+\z/]
|
41
|
-
return number.include?('.') ?
|
41
|
+
return number.include?('.') ? BigDecimal(number, DIG) : number.to_i if number
|
42
42
|
end
|
43
43
|
|
44
44
|
raise Dentaku::ArgumentError.for(:incompatible_type, value: value, for: Numeric),
|
data/lib/dentaku/flat_hash.rb
CHANGED
@@ -19,8 +19,8 @@ module Dentaku
|
|
19
19
|
key
|
20
20
|
end
|
21
21
|
|
22
|
-
def self.expand(
|
23
|
-
|
22
|
+
def self.expand(hash)
|
23
|
+
hash.each_with_object({}) do |(k, v), r|
|
24
24
|
hash_levels = k.to_s.split('.')
|
25
25
|
hash_levels = hash_levels.map(&:to_sym) if k.is_a?(Symbol)
|
26
26
|
child_hash = hash_levels[0...-1].reduce(r) { |h, n| h[n] ||= {} }
|
data/lib/dentaku/parser.rb
CHANGED
@@ -46,6 +46,8 @@ module Dentaku
|
|
46
46
|
args = Array.new(args_size) { output.pop }.reverse
|
47
47
|
|
48
48
|
output.push operator.new(*args)
|
49
|
+
rescue ::ArgumentError => e
|
50
|
+
raise Dentaku::ArgumentError, e.message
|
49
51
|
rescue NodeError => e
|
50
52
|
fail! :node_invalid, operator: operator, child: e.child, expect: e.expect, actual: e.actual
|
51
53
|
end
|
@@ -111,17 +113,19 @@ module Dentaku
|
|
111
113
|
open_cases = 0
|
112
114
|
case_end_index = nil
|
113
115
|
|
114
|
-
input.each_with_index do |
|
115
|
-
if
|
116
|
-
|
117
|
-
|
116
|
+
input.each_with_index do |input_token, index|
|
117
|
+
if input_token.category == :case
|
118
|
+
if input_token.value == :open
|
119
|
+
open_cases += 1
|
120
|
+
end
|
118
121
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
122
|
+
if input_token.value == :close
|
123
|
+
if open_cases > 0
|
124
|
+
open_cases -= 1
|
125
|
+
else
|
126
|
+
case_end_index = index
|
127
|
+
break
|
128
|
+
end
|
125
129
|
end
|
126
130
|
end
|
127
131
|
end
|
@@ -129,7 +133,8 @@ module Dentaku
|
|
129
133
|
subparser = Parser.new(
|
130
134
|
inner_case_inputs,
|
131
135
|
operations: [AST::Case],
|
132
|
-
arities: [0]
|
136
|
+
arities: [0],
|
137
|
+
function_registry: @function_registry
|
133
138
|
)
|
134
139
|
subparser.parse
|
135
140
|
output.concat(subparser.output)
|
@@ -253,6 +258,7 @@ module Dentaku
|
|
253
258
|
end
|
254
259
|
|
255
260
|
when :comma
|
261
|
+
fail! :invalid_statement if arities.empty?
|
256
262
|
arities[-1] += 1
|
257
263
|
while operations.any? && operations.last != AST::Grouping && operations.last != AST::Array
|
258
264
|
consume
|
data/lib/dentaku/tokenizer.rb
CHANGED
@@ -12,7 +12,7 @@ module Dentaku
|
|
12
12
|
def tokenize(string, options = {})
|
13
13
|
@nesting = 0
|
14
14
|
@tokens = []
|
15
|
-
@aliases = options.fetch(:aliases,
|
15
|
+
@aliases = options.fetch(:aliases, global_aliases)
|
16
16
|
input = strip_comments(string.to_s.dup)
|
17
17
|
input = replace_aliases(input)
|
18
18
|
@case_sensitive = options.fetch(:case_sensitive, false)
|
@@ -84,6 +84,11 @@ module Dentaku
|
|
84
84
|
|
85
85
|
private
|
86
86
|
|
87
|
+
def global_aliases
|
88
|
+
return {} unless Dentaku.respond_to?(:aliases)
|
89
|
+
Dentaku.aliases
|
90
|
+
end
|
91
|
+
|
87
92
|
def fail!(reason, **meta)
|
88
93
|
message =
|
89
94
|
case reason
|
data/lib/dentaku/version.rb
CHANGED
data/spec/ast/addition_spec.rb
CHANGED
@@ -7,29 +7,29 @@ describe 'Dentaku::AST::And' do
|
|
7
7
|
|
8
8
|
it 'returns false if any of the arguments is false' do
|
9
9
|
result = Dentaku('AND(1 = 1, 0 = 1)')
|
10
|
-
expect(result).to eq
|
10
|
+
expect(result).to eq(false)
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'supports nested expressions' do
|
14
14
|
result = Dentaku('AND(y = 1, x = 1)', x: 1, y: 2)
|
15
|
-
expect(result).to eq
|
15
|
+
expect(result).to eq(false)
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'returns true if all of the arguments are true' do
|
19
19
|
result = Dentaku('AND(1 = 1, "2" = "2", true = true, true)')
|
20
|
-
expect(result).to eq
|
20
|
+
expect(result).to eq(true)
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'returns true if all nested AND functions return true' do
|
24
24
|
result = Dentaku('AND(AND(1 = 1), AND(true != false, AND(true)))')
|
25
|
-
expect(result).to eq
|
25
|
+
expect(result).to eq(true)
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'raises an error if no arguments are passed' do
|
29
|
-
expect { calculator.evaluate!('AND()') }.to raise_error(ArgumentError)
|
29
|
+
expect { calculator.evaluate!('AND()') }.to raise_error(Dentaku::ArgumentError)
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'raises an error if a non logical argument is passed' do
|
33
|
-
expect { calculator.evaluate!('AND("r")') }.to raise_error(ArgumentError)
|
33
|
+
expect { calculator.evaluate!('AND("r")') }.to raise_error(Dentaku::ArgumentError)
|
34
34
|
end
|
35
35
|
end
|
data/spec/ast/and_spec.rb
CHANGED
data/spec/ast/arithmetic_spec.rb
CHANGED
@@ -11,29 +11,29 @@ describe Dentaku::AST::Arithmetic do
|
|
11
11
|
let(:ctx) { {'x' => 1, 'y' => 2} }
|
12
12
|
|
13
13
|
it 'performs an arithmetic operation with numeric operands' do
|
14
|
-
expect(add(one, two)).to eq
|
15
|
-
expect(sub(one, two)).to eq
|
16
|
-
expect(mul(one, two)).to eq
|
17
|
-
expect(div(one, two)).to eq
|
14
|
+
expect(add(one, two)).to eq(3)
|
15
|
+
expect(sub(one, two)).to eq(-1)
|
16
|
+
expect(mul(one, two)).to eq(2)
|
17
|
+
expect(div(one, two)).to eq(0.5)
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'performs an arithmetic operation with one numeric operand and one string operand' do
|
21
|
-
expect(add(one, x)).to eq
|
22
|
-
expect(sub(one, x)).to eq
|
23
|
-
expect(mul(one, x)).to eq
|
24
|
-
expect(div(one, x)).to eq
|
25
|
-
|
26
|
-
expect(add(y, two)).to eq
|
27
|
-
expect(sub(y, two)).to eq
|
28
|
-
expect(mul(y, two)).to eq
|
29
|
-
expect(div(y, two)).to eq
|
21
|
+
expect(add(one, x)).to eq(2)
|
22
|
+
expect(sub(one, x)).to eq(0)
|
23
|
+
expect(mul(one, x)).to eq(1)
|
24
|
+
expect(div(one, x)).to eq(1)
|
25
|
+
|
26
|
+
expect(add(y, two)).to eq(4)
|
27
|
+
expect(sub(y, two)).to eq(0)
|
28
|
+
expect(mul(y, two)).to eq(4)
|
29
|
+
expect(div(y, two)).to eq(1)
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'performs an arithmetic operation with string operands' do
|
33
|
-
expect(add(x, y)).to eq
|
34
|
-
expect(sub(x, y)).to eq
|
35
|
-
expect(mul(x, y)).to eq
|
36
|
-
expect(div(x, y)).to eq
|
33
|
+
expect(add(x, y)).to eq(3)
|
34
|
+
expect(sub(x, y)).to eq(-1)
|
35
|
+
expect(mul(x, y)).to eq(2)
|
36
|
+
expect(div(x, y)).to eq(0.5)
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
data/spec/ast/avg_spec.rb
CHANGED
@@ -5,29 +5,29 @@ require 'dentaku'
|
|
5
5
|
describe 'Dentaku::AST::Function::Avg' do
|
6
6
|
it 'returns the average of an array of Numeric values' do
|
7
7
|
result = Dentaku('AVG(1, x, 1.8)', x: 2.3)
|
8
|
-
expect(result).to eq
|
8
|
+
expect(result).to eq(1.7)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'returns the average of a single entry array of a Numeric value' do
|
12
12
|
result = Dentaku('AVG(x)', x: 2.3)
|
13
|
-
expect(result).to eq
|
13
|
+
expect(result).to eq(2.3)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'returns the average even if a String is passed' do
|
17
17
|
result = Dentaku('AVG(1, x, 1.8)', x: '2.3')
|
18
|
-
expect(result).to eq
|
18
|
+
expect(result).to eq(1.7)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'returns the average even if an array is passed' do
|
22
22
|
result = Dentaku('AVG(1, x, 2.3)', x: [4, 5])
|
23
|
-
expect(result).to eq
|
23
|
+
expect(result).to eq(3.075)
|
24
24
|
end
|
25
25
|
|
26
26
|
context 'checking errors' do
|
27
27
|
let(:calculator) { Dentaku::Calculator.new }
|
28
28
|
|
29
29
|
it 'raises an error if no arguments are passed' do
|
30
|
-
expect { calculator.evaluate!('AVG()') }.to raise_error(ArgumentError)
|
30
|
+
expect { calculator.evaluate!('AVG()') }.to raise_error(Dentaku::ArgumentError)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/spec/ast/count_spec.rb
CHANGED
@@ -5,36 +5,36 @@ require 'dentaku'
|
|
5
5
|
describe 'Dentaku::AST::Count' do
|
6
6
|
it 'returns the length of an array' do
|
7
7
|
result = Dentaku('COUNT(1, x, 1.8)', x: 2.3)
|
8
|
-
expect(result).to eq
|
8
|
+
expect(result).to eq(3)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'returns the length of a single number object' do
|
12
12
|
result = Dentaku('COUNT(x)', x: 2.3)
|
13
|
-
expect(result).to eq
|
13
|
+
expect(result).to eq(1)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'returns the length if a single String is passed' do
|
17
17
|
result = Dentaku('COUNT(x)', x: 'dentaku')
|
18
|
-
expect(result).to eq
|
18
|
+
expect(result).to eq(7)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'returns the length if an array is passed' do
|
22
22
|
result = Dentaku('COUNT(x)', x: [4, 5])
|
23
|
-
expect(result).to eq
|
23
|
+
expect(result).to eq(2)
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'returns the length if an array with one element is passed' do
|
27
27
|
result = Dentaku('COUNT(x)', x: [4])
|
28
|
-
expect(result).to eq
|
28
|
+
expect(result).to eq(1)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'returns the length if an array even if it has nested array' do
|
32
32
|
result = Dentaku('COUNT(1, x, 3)', x: [4, 5])
|
33
|
-
expect(result).to eq
|
33
|
+
expect(result).to eq(3)
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'returns the length if an array is passed' do
|
37
37
|
result = Dentaku('COUNT()')
|
38
|
-
expect(result).to eq
|
38
|
+
expect(result).to eq(0)
|
39
39
|
end
|
40
40
|
end
|
data/spec/ast/division_spec.rb
CHANGED