dentaku 2.0.11 → 3.0.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/.gitignore +3 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.md +19 -0
- data/README.md +3 -2
- data/dentaku.gemspec +1 -0
- data/lib/dentaku/ast.rb +4 -0
- data/lib/dentaku/ast/access.rb +27 -0
- data/lib/dentaku/ast/arithmetic.rb +49 -7
- data/lib/dentaku/ast/case.rb +17 -3
- data/lib/dentaku/ast/combinators.rb +8 -2
- data/lib/dentaku/ast/function.rb +16 -0
- data/lib/dentaku/ast/function_registry.rb +15 -2
- data/lib/dentaku/ast/functions/and.rb +25 -0
- data/lib/dentaku/ast/functions/max.rb +1 -1
- data/lib/dentaku/ast/functions/min.rb +1 -1
- data/lib/dentaku/ast/functions/or.rb +25 -0
- data/lib/dentaku/ast/functions/round.rb +2 -2
- data/lib/dentaku/ast/functions/rounddown.rb +3 -2
- data/lib/dentaku/ast/functions/roundup.rb +3 -2
- data/lib/dentaku/ast/functions/ruby_math.rb +3 -3
- data/lib/dentaku/ast/functions/switch.rb +8 -0
- data/lib/dentaku/ast/identifier.rb +3 -2
- data/lib/dentaku/ast/negation.rb +5 -1
- data/lib/dentaku/ast/node.rb +3 -0
- data/lib/dentaku/bulk_expression_solver.rb +1 -2
- data/lib/dentaku/calculator.rb +7 -6
- data/lib/dentaku/exceptions.rb +75 -1
- data/lib/dentaku/parser.rb +73 -12
- data/lib/dentaku/token.rb +4 -0
- data/lib/dentaku/token_scanner.rb +20 -3
- data/lib/dentaku/tokenizer.rb +31 -4
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/addition_spec.rb +6 -6
- data/spec/ast/and_function_spec.rb +35 -0
- data/spec/ast/and_spec.rb +1 -1
- data/spec/ast/arithmetic_spec.rb +56 -0
- data/spec/ast/division_spec.rb +1 -1
- data/spec/ast/function_spec.rb +43 -6
- data/spec/ast/max_spec.rb +15 -0
- data/spec/ast/min_spec.rb +15 -0
- data/spec/ast/or_spec.rb +35 -0
- data/spec/ast/round_spec.rb +25 -0
- data/spec/ast/rounddown_spec.rb +25 -0
- data/spec/ast/roundup_spec.rb +25 -0
- data/spec/ast/switch_spec.rb +30 -0
- data/spec/calculator_spec.rb +26 -4
- data/spec/exceptions_spec.rb +1 -1
- data/spec/parser_spec.rb +22 -3
- data/spec/spec_helper.rb +12 -2
- data/spec/token_scanner_spec.rb +0 -4
- data/spec/tokenizer_spec.rb +40 -2
- metadata +39 -3
data/lib/dentaku/version.rb
CHANGED
data/spec/ast/addition_spec.rb
CHANGED
@@ -4,8 +4,8 @@ require 'dentaku/ast/arithmetic'
|
|
4
4
|
require 'dentaku/token'
|
5
5
|
|
6
6
|
describe Dentaku::AST::Addition do
|
7
|
-
let(:five) { Dentaku::AST::
|
8
|
-
let(:six) { Dentaku::AST::
|
7
|
+
let(:five) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 5) }
|
8
|
+
let(:six) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 6) }
|
9
9
|
|
10
10
|
let(:t) { Dentaku::AST::Numeric.new Dentaku::Token.new(:logical, true) }
|
11
11
|
|
@@ -17,7 +17,7 @@ describe Dentaku::AST::Addition do
|
|
17
17
|
it 'requires numeric operands' do
|
18
18
|
expect {
|
19
19
|
described_class.new(five, t)
|
20
|
-
}.to raise_error(Dentaku::
|
20
|
+
}.to raise_error(Dentaku::NodeError, /requires numeric operands/)
|
21
21
|
|
22
22
|
expression = Dentaku::AST::Multiplication.new(five, five)
|
23
23
|
group = Dentaku::AST::Grouping.new(expression)
|
@@ -29,7 +29,7 @@ describe Dentaku::AST::Addition do
|
|
29
29
|
|
30
30
|
it 'allows operands that respond to addition' do
|
31
31
|
# Sample struct that has a custom definition for addition
|
32
|
-
|
32
|
+
|
33
33
|
Operand = Struct.new(:value) do
|
34
34
|
def +(other)
|
35
35
|
case other
|
@@ -41,8 +41,8 @@ describe Dentaku::AST::Addition do
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
operand_five = Dentaku::AST::
|
45
|
-
operand_six = Dentaku::AST::
|
44
|
+
operand_five = Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, Operand.new(5))
|
45
|
+
operand_six = Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, Operand.new(6))
|
46
46
|
|
47
47
|
expect {
|
48
48
|
described_class.new(operand_five, operand_six)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku'
|
3
|
+
require 'dentaku/ast/functions/and'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::And' do
|
6
|
+
let(:calculator) { Dentaku::Calculator.new }
|
7
|
+
|
8
|
+
it 'returns false if any of the arguments is false' do
|
9
|
+
result = Dentaku('AND(1 = 1, 0 = 1)')
|
10
|
+
expect(result).to eq false
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'supports nested expressions' do
|
14
|
+
result = Dentaku('AND(y = 1, x = 1)', x: 1, y: 2)
|
15
|
+
expect(result).to eq false
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns true if all of the arguments are true' do
|
19
|
+
result = Dentaku('AND(1 = 1, "2" = "2", true = true, true)')
|
20
|
+
expect(result).to eq true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns true if all nested AND functions return true' do
|
24
|
+
result = Dentaku('AND(AND(1 = 1), AND(true != false, AND(true)))')
|
25
|
+
expect(result).to eq true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises an error if no arguments are passed' do
|
29
|
+
expect { calculator.evaluate!('AND()') }.to raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises an error if a non logical argument is passed' do
|
33
|
+
expect { calculator.evaluate!('AND("r")') }.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
end
|
data/spec/ast/and_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe Dentaku::AST::And do
|
|
17
17
|
it 'requires logical operands' do
|
18
18
|
expect {
|
19
19
|
described_class.new(t, five)
|
20
|
-
}.to raise_error(Dentaku::
|
20
|
+
}.to raise_error(Dentaku::NodeError, /requires logical operands/)
|
21
21
|
|
22
22
|
expression = Dentaku::AST::LessThanOrEqual.new(five, five)
|
23
23
|
expect {
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/arithmetic'
|
3
|
+
|
4
|
+
require 'dentaku/token'
|
5
|
+
|
6
|
+
describe Dentaku::AST::Arithmetic do
|
7
|
+
let(:one) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 1) }
|
8
|
+
let(:two) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 2) }
|
9
|
+
let(:x) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'x') }
|
10
|
+
let(:y) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'y') }
|
11
|
+
let(:ctx) {{'x' => 1, 'y' => 2}}
|
12
|
+
|
13
|
+
it 'performs an arithmetic operation with numeric operands' do
|
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
|
+
end
|
19
|
+
|
20
|
+
it 'performs an arithmetic operation with one numeric operand and one string operand' do
|
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
|
+
end
|
31
|
+
|
32
|
+
it 'performs an arithmetic operation with string operands' do
|
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
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def add(left, right)
|
42
|
+
Dentaku::AST::Addition.new(left, right).value(ctx)
|
43
|
+
end
|
44
|
+
|
45
|
+
def sub(left, right)
|
46
|
+
Dentaku::AST::Subtraction.new(left, right).value(ctx)
|
47
|
+
end
|
48
|
+
|
49
|
+
def mul(left, right)
|
50
|
+
Dentaku::AST::Multiplication.new(left, right).value(ctx)
|
51
|
+
end
|
52
|
+
|
53
|
+
def div(left, right)
|
54
|
+
Dentaku::AST::Division.new(left, right).value(ctx)
|
55
|
+
end
|
56
|
+
end
|
data/spec/ast/division_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe Dentaku::AST::Division do
|
|
17
17
|
it 'requires numeric operands' do
|
18
18
|
expect {
|
19
19
|
described_class.new(five, t)
|
20
|
-
}.to raise_error(Dentaku::
|
20
|
+
}.to raise_error(Dentaku::NodeError, /requires numeric operands/)
|
21
21
|
|
22
22
|
expression = Dentaku::AST::Multiplication.new(five, five)
|
23
23
|
group = Dentaku::AST::Grouping.new(expression)
|
data/spec/ast/function_spec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'bigdecimal'
|
1
2
|
require 'spec_helper'
|
2
3
|
require 'dentaku/ast/function'
|
4
|
+
require 'dentaku/exceptions'
|
3
5
|
|
4
6
|
class Clazz; end
|
5
7
|
|
@@ -8,12 +10,6 @@ describe Dentaku::AST::Function do
|
|
8
10
|
expect(described_class).to respond_to(:get)
|
9
11
|
end
|
10
12
|
|
11
|
-
it 'raises an exception when trying to access an undefined function' do
|
12
|
-
expect {
|
13
|
-
described_class.get("flarble")
|
14
|
-
}.to raise_error(Dentaku::ParseError, /undefined function/i)
|
15
|
-
end
|
16
|
-
|
17
13
|
it 'registers a custom function' do
|
18
14
|
described_class.register("flarble", :string, -> { "flarble" })
|
19
15
|
expect { described_class.get("flarble") }.not_to raise_error
|
@@ -24,4 +20,45 @@ describe Dentaku::AST::Function do
|
|
24
20
|
it 'does not throw an error when registering a function with a name that matches a currently defined constant' do
|
25
21
|
expect { described_class.register("clazz", :string, -> { "clazzified" }) }.not_to raise_error
|
26
22
|
end
|
23
|
+
|
24
|
+
describe "#arity" do
|
25
|
+
it "gives the correct arity for custom functions" do
|
26
|
+
zero = described_class.register("zero", :numeric, ->() { 0 })
|
27
|
+
expect(zero.arity).to eq 0
|
28
|
+
|
29
|
+
one = described_class.register("one", :numeric, ->(x) { x * 2 })
|
30
|
+
expect(one.arity).to eq 1
|
31
|
+
|
32
|
+
two = described_class.register("two", :numeric, ->(x,y) { x + y })
|
33
|
+
expect(two.arity).to eq 2
|
34
|
+
|
35
|
+
many = described_class.register("many", :numeric, ->(*args) { args.max })
|
36
|
+
expect(many.arity).to be_nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'casts a String to an Integer if possible' do
|
41
|
+
expect(described_class.numeric('3')).to eq 3
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'casts a String to a BigDecimal if possible and if Integer would loose information' do
|
45
|
+
expect(described_class.numeric('3.2')).to eq 3.2
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'casts a String to a BigDecimal with a negative number' do
|
49
|
+
expect(described_class.numeric('-3.2')).to eq -3.2
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'casts a String to a BigDecimal without a leading zero' do
|
53
|
+
expect(described_class.numeric('-.2')).to eq -0.2
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'raises an error if the value could not be cast to a Numeric' do
|
57
|
+
expect { described_class.numeric('flarble') }.to raise_error TypeError
|
58
|
+
expect { described_class.numeric('-') }.to raise_error TypeError
|
59
|
+
expect { described_class.numeric('') }.to raise_error TypeError
|
60
|
+
expect { described_class.numeric(nil) }.to raise_error TypeError
|
61
|
+
expect { described_class.numeric('7.') }.to raise_error TypeError
|
62
|
+
expect { described_class.numeric(true) }.to raise_error TypeError
|
63
|
+
end
|
27
64
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/functions/max'
|
3
|
+
require 'dentaku'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::Function::Max' do
|
6
|
+
it 'returns the largest numeric value in an array of Numeric values' do
|
7
|
+
result = Dentaku('MAX(1, x, 1.8)', x: 2.3)
|
8
|
+
expect(result).to eq 2.3
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns the largest value even if a String is passed' do
|
12
|
+
result = Dentaku('MAX(1, x, 1.8)', x: '2.3')
|
13
|
+
expect(result).to eq 2.3
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/functions/min'
|
3
|
+
require 'dentaku'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::Function::Min' do
|
6
|
+
it 'returns the smallest numeric value in an array of Numeric values' do
|
7
|
+
result = Dentaku('MIN(1, x, 1.8)', x: 2.3)
|
8
|
+
expect(result).to eq 1
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns the smallest value even if a String is passed' do
|
12
|
+
result = Dentaku('MIN(1, x, 1.8)', x: '0.3')
|
13
|
+
expect(result).to eq 0.3
|
14
|
+
end
|
15
|
+
end
|
data/spec/ast/or_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku'
|
3
|
+
require 'dentaku/ast/functions/or'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::Or' do
|
6
|
+
let(:calculator) { Dentaku::Calculator.new }
|
7
|
+
|
8
|
+
it 'returns false if all of the arguments are false' do
|
9
|
+
result = Dentaku('OR(1 = "1", 0 = 1)')
|
10
|
+
expect(result).to eq false
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'supports nested expressions' do
|
14
|
+
result = Dentaku('OR(y = 1, x = 1)', x: 1, y: 2)
|
15
|
+
expect(result).to eq true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns true if any of the arguments is true' do
|
19
|
+
result = Dentaku('OR(1 = "1", "2" = "2", true = false, false)')
|
20
|
+
expect(result).to eq true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns true if any nested OR function returns true' do
|
24
|
+
result = Dentaku('OR(OR(1 = 0), OR(true = false, OR(true)))')
|
25
|
+
expect(result).to eq true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises an error if no arguments are passed' do
|
29
|
+
expect { calculator.evaluate!('OR()') }.to raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises an error if a non logical argument is passed' do
|
33
|
+
expect { calculator.evaluate!('OR("r")') }.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/functions/round'
|
3
|
+
require 'dentaku'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::Function::Round' do
|
6
|
+
it 'returns the rounded down value' do
|
7
|
+
result = Dentaku('ROUND(1.8)')
|
8
|
+
expect(result).to eq 2
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns the rounded down value to the given precision' do
|
12
|
+
result = Dentaku('ROUND(x, y)', x: 1.8453, y: 3)
|
13
|
+
expect(result).to eq 1.845
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns the rounded down value to the given precision, also with strings' do
|
17
|
+
result = Dentaku('ROUND(x, y)', x: '1.8453', y: '3')
|
18
|
+
expect(result).to eq 1.845
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the rounded down value to the given precision, also with nil' do
|
22
|
+
result = Dentaku('ROUND(x, y)', x: '1.8453', y: nil)
|
23
|
+
expect(result).to eq 2
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/functions/rounddown'
|
3
|
+
require 'dentaku'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::Function::Round' do
|
6
|
+
it 'returns the rounded value' do
|
7
|
+
result = Dentaku('ROUNDDOWN(1.8)')
|
8
|
+
expect(result).to eq 1
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns the rounded value to the given precision' do
|
12
|
+
result = Dentaku('ROUNDDOWN(x, y)', x: 1.8453, y: 3)
|
13
|
+
expect(result).to eq 1.845
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns the rounded value to the given precision, also with strings' do
|
17
|
+
result = Dentaku('ROUNDDOWN(x, y)', x: '1.8453', y: '3')
|
18
|
+
expect(result).to eq 1.845
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the rounded value to the given precision, also with nil' do
|
22
|
+
result = Dentaku('ROUNDDOWN(x, y)', x: '1.8453', y: nil)
|
23
|
+
expect(result).to eq 1
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/functions/roundup'
|
3
|
+
require 'dentaku'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::Function::Round' do
|
6
|
+
it 'returns the rounded value' do
|
7
|
+
result = Dentaku('ROUNDUP(1.8)')
|
8
|
+
expect(result).to eq 2
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns the rounded value to the given precision' do
|
12
|
+
result = Dentaku('ROUNDUP(x, y)', x: 1.8453, y: 3)
|
13
|
+
expect(result).to eq 1.846
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns the rounded value to the given precision, also with strings' do
|
17
|
+
result = Dentaku('ROUNDUP(x, y)', x: '1.8453', y: '3')
|
18
|
+
expect(result).to eq 1.846
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the rounded value to the given precision, also with nil' do
|
22
|
+
result = Dentaku('ROUNDUP(x, y)', x: '1.8453', y: nil)
|
23
|
+
expect(result).to eq 2
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dentaku/ast/functions/switch'
|
3
|
+
require 'dentaku'
|
4
|
+
|
5
|
+
describe 'Dentaku::AST::Function::Switch' do
|
6
|
+
it 'returns the match if present in argumtents' do
|
7
|
+
result = Dentaku('SWITCH(1, 1, "one", 2, "two")')
|
8
|
+
expect(result).to eq 'one'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns nil if no match was found' do
|
12
|
+
result = Dentaku('SWITCH(3, 1, "one", 2, "two")')
|
13
|
+
expect(result).to eq nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns the default value if present and no match was found' do
|
17
|
+
result = Dentaku('SWITCH(3, 1, "one", 2, "two", "no match")')
|
18
|
+
expect(result).to eq 'no match'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the first match if multiple matches exist' do
|
22
|
+
result = Dentaku('SWITCH(1, 1, "one", 2, "two", 1, "three")')
|
23
|
+
expect(result).to eq 'one'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'does not return a match where a value matches the search value' do
|
27
|
+
result = Dentaku('SWITCH(1, "one", 1, 2, "two", 3)')
|
28
|
+
expect(result).to eq 3
|
29
|
+
end
|
30
|
+
end
|
data/spec/calculator_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'dentaku
|
2
|
+
require 'dentaku'
|
3
3
|
|
4
4
|
describe Dentaku::Calculator do
|
5
5
|
let(:calculator) { described_class.new }
|
@@ -32,6 +32,7 @@ describe Dentaku::Calculator do
|
|
32
32
|
expect(calculator.evaluate('0.253/0.253')).to eq(1)
|
33
33
|
expect(calculator.evaluate('0.253/d', d: 0.253)).to eq(1)
|
34
34
|
expect(calculator.evaluate('10 + x', x: 'abc')).to be_nil
|
35
|
+
expect(calculator.evaluate('a/b', a: '10', b: '2')).to eq(5)
|
35
36
|
expect(calculator.evaluate('t + 1*24*60*60', t: Time.local(2017, 1, 1))).to eq(Time.local(2017, 1, 2))
|
36
37
|
expect(calculator.evaluate("2 | 3 * 9")).to eq (27)
|
37
38
|
expect(calculator.evaluate("2 & 3 * 9")).to eq (2)
|
@@ -68,6 +69,13 @@ describe Dentaku::Calculator do
|
|
68
69
|
expect(calculator.evaluate!('a.basket.of')).to eq 'apples'
|
69
70
|
expect(calculator.evaluate!('b')).to eq 2
|
70
71
|
end
|
72
|
+
|
73
|
+
it 'stores arrays' do
|
74
|
+
calculator.store({a: [1, 2, 3]})
|
75
|
+
expect(calculator.evaluate!('a[0]')).to eq 1
|
76
|
+
expect(calculator.evaluate!('a[x]', x: 1)).to eq 2
|
77
|
+
expect(calculator.evaluate!('a[x+1]', x: 1)).to eq 3
|
78
|
+
end
|
71
79
|
end
|
72
80
|
|
73
81
|
describe 'dependencies' do
|
@@ -192,14 +200,20 @@ describe Dentaku::Calculator do
|
|
192
200
|
expect { calculator.evaluate!(unbound) }.to raise_error do |error|
|
193
201
|
expect(error.unbound_variables).to eq ['foo']
|
194
202
|
end
|
203
|
+
expect { calculator.evaluate!('a + b') }.to raise_error do |error|
|
204
|
+
expect(error.unbound_variables).to eq ['a', 'b']
|
205
|
+
end
|
195
206
|
expect(calculator.evaluate(unbound)).to be_nil
|
196
207
|
expect(calculator.evaluate(unbound) { :bar }).to eq :bar
|
197
208
|
expect(calculator.evaluate(unbound) { |e| e }).to eq unbound
|
198
209
|
end
|
199
210
|
|
200
211
|
it 'fails to evaluate incomplete statements' do
|
201
|
-
|
202
|
-
|
212
|
+
['true AND', 'a a ^&'].each do |statement|
|
213
|
+
expect {
|
214
|
+
calculator.evaluate!(statement)
|
215
|
+
}.to raise_error(Dentaku::ParseError)
|
216
|
+
end
|
203
217
|
end
|
204
218
|
|
205
219
|
it 'evaluates unbound statements given a binding in memory' do
|
@@ -455,7 +469,7 @@ describe Dentaku::Calculator do
|
|
455
469
|
Math.methods(false).each do |method|
|
456
470
|
it method do
|
457
471
|
if Math.method(method).arity == 2
|
458
|
-
expect(calculator.evaluate("#{method}(1,2)
|
472
|
+
expect(calculator.evaluate("#{method}(x,y)", x: 1, y: '2')).to eq Math.send(method, 1, 2)
|
459
473
|
else
|
460
474
|
expect(calculator.evaluate("#{method}(1)")).to eq Math.send(method, 1)
|
461
475
|
end
|
@@ -517,4 +531,12 @@ describe Dentaku::Calculator do
|
|
517
531
|
).to eq 'abcdef'
|
518
532
|
end
|
519
533
|
end
|
534
|
+
|
535
|
+
describe 'zero-arity functions' do
|
536
|
+
it 'can be used in formulas' do
|
537
|
+
calculator.add_function(:two, :numeric, -> { 2 })
|
538
|
+
expect(calculator.evaluate("max(two(), 1)")).to eq 2
|
539
|
+
expect(calculator.evaluate("max(1, two())")).to eq 2
|
540
|
+
end
|
541
|
+
end
|
520
542
|
end
|