dentaku 1.2.1 → 1.2.2
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 +7 -0
- data/README.md +1 -0
- data/lib/dentaku/calculator.rb +14 -10
- data/lib/dentaku/dependency_resolver.rb +3 -3
- data/lib/dentaku/evaluator.rb +2 -0
- data/lib/dentaku/expression.rb +7 -6
- data/lib/dentaku/external_function.rb +1 -1
- data/lib/dentaku/rules.rb +4 -1
- data/lib/dentaku/token_matcher.rb +10 -0
- data/lib/dentaku/token_scanner.rb +1 -1
- data/lib/dentaku/tokenizer.rb +1 -1
- data/lib/dentaku/version.rb +1 -1
- data/spec/calculator_spec.rb +34 -4
- data/spec/dentaku_spec.rb +7 -0
- data/spec/evaluator_spec.rb +19 -0
- data/spec/tokenizer_spec.rb +29 -11
- metadata +15 -28
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c588f75b508adbbda4b56b6b3286144aff133c01
|
4
|
+
data.tar.gz: b444936b8e75de4aa64af4a39b0d2e9d667b85f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 33f894376d9098500762e3a5aab7e6be70ce95a3da9fb0c7dd09186bbb30db0471b8bcc750dd906bb3dfdc585f37f5bfe59002dbe4f93a4a65f5784b7e9b2d02
|
7
|
+
data.tar.gz: 0fbf3567fd3b9b5e8e5db7df0647ddc687842f9e6b2bdce9e685d1c2a45fdfa2b1ad9ca80c7c5a1ac4fc2e5a910faf167205520d7f52a40ae1ea4de7c573c32e
|
data/README.md
CHANGED
@@ -255,6 +255,7 @@ contributors:
|
|
255
255
|
* [jasonhutchens](https://github.com/jasonhutchens)
|
256
256
|
* [jmangs](https://github.com/jmangs)
|
257
257
|
* [mvbrocato](https://github.com/mvbrocato)
|
258
|
+
* [schneidmaster](https://github.com/schneidmaster)
|
258
259
|
* [thbar](https://github.com/thbar) / [BoxCar](https://www.boxcar.io)
|
259
260
|
|
260
261
|
|
data/lib/dentaku/calculator.rb
CHANGED
@@ -39,20 +39,24 @@ module Dentaku
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def solve!(expression_hash)
|
42
|
+
expressions = Hash[expression_hash.map { |k,v| [k.to_s, v] }]
|
43
|
+
|
42
44
|
# expression_hash: { variable_name: "string expression" }
|
43
45
|
# TSort thru the expressions' dependencies, then evaluate all
|
44
|
-
expression_dependencies = Hash[
|
46
|
+
expression_dependencies = Hash[expressions.map do |var, expr|
|
45
47
|
[var, dependencies(expr)]
|
46
48
|
end]
|
49
|
+
|
47
50
|
variables_in_resolve_order = DependencyResolver::find_resolve_order(
|
48
51
|
expression_dependencies)
|
49
52
|
|
50
|
-
results = {}
|
51
|
-
|
52
|
-
results[var_name] = evaluate!(expression_hash[var_name], results)
|
53
|
+
results = variables_in_resolve_order.each_with_object({}) do |var_name, r|
|
54
|
+
r[var_name] = evaluate!(expressions[var_name], r)
|
53
55
|
end
|
54
56
|
|
55
|
-
|
57
|
+
expression_hash.each_with_object({}) do |(k, _), r|
|
58
|
+
r[k] = results[k.to_s]
|
59
|
+
end
|
56
60
|
end
|
57
61
|
|
58
62
|
def dependencies(expression)
|
@@ -62,12 +66,12 @@ module Dentaku
|
|
62
66
|
def store(key_or_hash, value=nil)
|
63
67
|
restore = @memory.dup
|
64
68
|
|
65
|
-
if
|
66
|
-
|
67
|
-
|
68
|
-
key_or_hash.each do |key, value|
|
69
|
-
@memory[key.to_sym] = value
|
69
|
+
if value.nil?
|
70
|
+
key_or_hash.each do |key, val|
|
71
|
+
@memory[key.downcase.to_s] = val
|
70
72
|
end
|
73
|
+
else
|
74
|
+
@memory[key_or_hash.to_s] = value
|
71
75
|
end
|
72
76
|
|
73
77
|
if block_given?
|
@@ -9,8 +9,8 @@ module Dentaku
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(vars_to_dependencies_hash)
|
12
|
-
# ensure variables are
|
13
|
-
@vars_to_deps = Hash[vars_to_dependencies_hash.map { |k, v| [k.
|
12
|
+
# ensure variables are strings
|
13
|
+
@vars_to_deps = Hash[vars_to_dependencies_hash.map { |k, v| [k.to_s, v]}]
|
14
14
|
end
|
15
15
|
|
16
16
|
def tsort_each_node(&block)
|
@@ -18,7 +18,7 @@ module Dentaku
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def tsort_each_child(node, &block)
|
21
|
-
@vars_to_deps[node.
|
21
|
+
@vars_to_deps[node.to_s].each(&block)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
data/lib/dentaku/evaluator.rb
CHANGED
@@ -47,10 +47,12 @@ module Dentaku
|
|
47
47
|
pattern.each do |matcher|
|
48
48
|
_matched, match = matcher.match(token_stream, position + matches.length)
|
49
49
|
matched &&= _matched
|
50
|
+
break unless matched
|
50
51
|
matches += match
|
51
52
|
end
|
52
53
|
|
53
54
|
return position, matches if matched
|
55
|
+
return if pattern.first.caret?
|
54
56
|
position += 1
|
55
57
|
end
|
56
58
|
|
data/lib/dentaku/expression.rb
CHANGED
@@ -2,13 +2,14 @@ require 'dentaku/tokenizer'
|
|
2
2
|
|
3
3
|
module Dentaku
|
4
4
|
class Expression
|
5
|
-
attr_reader :tokens
|
5
|
+
attr_reader :tokens, :variables
|
6
6
|
|
7
7
|
def initialize(string, variables={})
|
8
8
|
@raw = string
|
9
9
|
@tokenizer ||= Tokenizer.new
|
10
10
|
@tokens = @tokenizer.tokenize(@raw)
|
11
|
-
|
11
|
+
@variables = Hash[variables.map { |k,v| [k.to_s, v] }]
|
12
|
+
replace_identifiers_with_values
|
12
13
|
end
|
13
14
|
|
14
15
|
def identifiers
|
@@ -21,18 +22,18 @@ module Dentaku
|
|
21
22
|
|
22
23
|
private
|
23
24
|
|
24
|
-
def replace_identifiers_with_values
|
25
|
+
def replace_identifiers_with_values
|
25
26
|
@tokens.map! do |token|
|
26
27
|
if token.is?(:identifier)
|
27
|
-
replace_identifier_with_value(token
|
28
|
+
replace_identifier_with_value(token)
|
28
29
|
else
|
29
30
|
token
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
|
-
def replace_identifier_with_value(token
|
35
|
-
key = token.value.
|
35
|
+
def replace_identifier_with_value(token)
|
36
|
+
key = token.value.to_s
|
36
37
|
|
37
38
|
if variables.key? key
|
38
39
|
value = variables[key]
|
data/lib/dentaku/rules.rb
CHANGED
@@ -13,6 +13,7 @@ module Dentaku
|
|
13
13
|
[ p(:not), :not ],
|
14
14
|
|
15
15
|
[ p(:group), :evaluate_group ],
|
16
|
+
[ p(:start_neg), :negate ],
|
16
17
|
[ p(:math_pow), :apply ],
|
17
18
|
[ p(:math_mod), :apply ],
|
18
19
|
[ p(:math_mul), :apply ],
|
@@ -66,7 +67,8 @@ module Dentaku
|
|
66
67
|
:numeric, :string, :addsub, :subtract, :muldiv, :pow, :mod,
|
67
68
|
:comparator, :comp_gt, :comp_lt, :fopen, :open, :close, :comma,
|
68
69
|
:non_close_plus, :non_group, :non_group_star, :arguments,
|
69
|
-
:logical, :combinator, :if, :round, :roundup, :rounddown, :not
|
70
|
+
:logical, :combinator, :if, :round, :roundup, :rounddown, :not,
|
71
|
+
:anchored_minus
|
70
72
|
].each_with_object({}) do |name, matchers|
|
71
73
|
matchers[name] = TokenMatcher.send(name)
|
72
74
|
end
|
@@ -80,6 +82,7 @@ module Dentaku
|
|
80
82
|
math_pow: pattern(:numeric, :pow, :numeric),
|
81
83
|
math_mod: pattern(:numeric, :mod, :numeric),
|
82
84
|
negation: pattern(:subtract, :numeric),
|
85
|
+
start_neg: pattern(:anchored_minus, :numeric),
|
83
86
|
percentage: pattern(:numeric, :mod),
|
84
87
|
range_asc: pattern(:numeric, :comp_lt, :numeric, :comp_lt, :numeric),
|
85
88
|
range_desc: pattern(:numeric, :comp_gt, :numeric, :comp_gt, :numeric),
|
@@ -44,6 +44,15 @@ module Dentaku
|
|
44
44
|
[matched, matched_tokens]
|
45
45
|
end
|
46
46
|
|
47
|
+
def caret
|
48
|
+
@caret = true
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def caret?
|
53
|
+
@caret
|
54
|
+
end
|
55
|
+
|
47
56
|
def star
|
48
57
|
@min = 0
|
49
58
|
@max = Float::INFINITY
|
@@ -93,6 +102,7 @@ module Dentaku
|
|
93
102
|
|
94
103
|
def self.addsub; new(:operator, [:add, :subtract]); end
|
95
104
|
def self.subtract; new(:operator, :subtract); end
|
105
|
+
def self.anchored_minus; new(:operator, :subtract).caret; end
|
96
106
|
def self.muldiv; new(:operator, [:multiply, :divide]); end
|
97
107
|
def self.pow; new(:operator, :pow); end
|
98
108
|
def self.mod; new(:operator, :mod); end
|
data/lib/dentaku/tokenizer.rb
CHANGED
data/lib/dentaku/version.rb
CHANGED
data/spec/calculator_spec.rb
CHANGED
@@ -6,6 +6,22 @@ describe Dentaku::Calculator do
|
|
6
6
|
|
7
7
|
it 'evaluates an expression' do
|
8
8
|
expect(calculator.evaluate('7+3')).to eq(10)
|
9
|
+
expect(calculator.evaluate('2 -1')).to eq(1)
|
10
|
+
expect(calculator.evaluate('-1 + 2')).to eq(1)
|
11
|
+
expect(calculator.evaluate('1 - 2')).to eq(-1)
|
12
|
+
expect(calculator.evaluate('1 - - 2')).to eq(3)
|
13
|
+
expect(calculator.evaluate('-1 - - 2')).to eq(1)
|
14
|
+
expect(calculator.evaluate('1 - - - 2')).to eq(-1)
|
15
|
+
expect(calculator.evaluate('(-1 + 2)')).to eq(1)
|
16
|
+
expect(calculator.evaluate('-(1 + 2)')).to eq(-3)
|
17
|
+
expect(calculator.evaluate('2 ^ - 1')).to eq(0.5)
|
18
|
+
expect(calculator.evaluate('2 ^ -(3 - 2)')).to eq(0.5)
|
19
|
+
expect(calculator.evaluate('(2 + 3) - 1')).to eq(4)
|
20
|
+
expect(calculator.evaluate('(-2 + 3) - 1')).to eq(0)
|
21
|
+
expect(calculator.evaluate('(-2 - 3) - 1')).to eq(-6)
|
22
|
+
expect(calculator.evaluate('1 + -2 ^ 2')).to eq(-3)
|
23
|
+
expect(calculator.evaluate('3 + -num', :num => 2)).to eq(1)
|
24
|
+
expect(calculator.evaluate('-num + 3', :num => 2)).to eq(1)
|
9
25
|
end
|
10
26
|
|
11
27
|
describe 'memory' do
|
@@ -32,10 +48,10 @@ describe Dentaku::Calculator do
|
|
32
48
|
|
33
49
|
describe 'dependencies' do
|
34
50
|
it "finds dependencies in a generic statement" do
|
35
|
-
expect(calculator.dependencies("bob + dole / 3")).to eq([
|
51
|
+
expect(calculator.dependencies("bob + dole / 3")).to eq(['bob', 'dole'])
|
36
52
|
end
|
37
53
|
it "doesn't consider variables in memory as dependencies" do
|
38
|
-
expect(with_memory.dependencies("apples + oranges")).to eq([
|
54
|
+
expect(with_memory.dependencies("apples + oranges")).to eq(['oranges'])
|
39
55
|
end
|
40
56
|
end
|
41
57
|
|
@@ -48,6 +64,14 @@ describe Dentaku::Calculator do
|
|
48
64
|
)).to eq(pear: 1, weekly_apple_budget: 21, weekly_fruit_budget: 25)
|
49
65
|
end
|
50
66
|
|
67
|
+
it "preserves hash keys" do
|
68
|
+
expect(calculator.solve!(
|
69
|
+
'meaning_of_life' => 'age + kids',
|
70
|
+
'age' => 40,
|
71
|
+
'kids' => 2
|
72
|
+
)).to eq('age' => 40, 'kids' => 2, 'meaning_of_life' => 42)
|
73
|
+
end
|
74
|
+
|
51
75
|
it "lets you know about a cycle if one occurs" do
|
52
76
|
expect do
|
53
77
|
calculator.solve!(health: "happiness", happiness: "health")
|
@@ -64,7 +88,7 @@ describe Dentaku::Calculator do
|
|
64
88
|
unbound = 'foo * 1.5'
|
65
89
|
expect { calculator.evaluate!(unbound) }.to raise_error(Dentaku::UnboundVariableError)
|
66
90
|
expect { calculator.evaluate!(unbound) }.to raise_error do |error|
|
67
|
-
expect(error.unbound_variables).to eq [
|
91
|
+
expect(error.unbound_variables).to eq ['foo']
|
68
92
|
end
|
69
93
|
expect(calculator.evaluate(unbound)).to be_nil
|
70
94
|
expect(calculator.evaluate(unbound) { :bar }).to eq :bar
|
@@ -112,7 +136,6 @@ describe Dentaku::Calculator do
|
|
112
136
|
expect(calculator.evaluate('some_boolean OR 7 > 5', :some_boolean => true)).to be_truthy
|
113
137
|
expect(calculator.evaluate('some_boolean OR 7 < 5', :some_boolean => true)).to be_truthy
|
114
138
|
expect(calculator.evaluate('some_boolean OR 7 < 5', :some_boolean => false)).to be_falsey
|
115
|
-
|
116
139
|
end
|
117
140
|
|
118
141
|
describe 'functions' do
|
@@ -138,5 +161,12 @@ describe Dentaku::Calculator do
|
|
138
161
|
expect(calculator.evaluate('NOT(some_boolean) AND 7 > 5', :some_boolean => true)).to be_falsey
|
139
162
|
expect(calculator.evaluate('NOT(some_boolean) OR 7 < 5', :some_boolean => false)).to be_truthy
|
140
163
|
end
|
164
|
+
|
165
|
+
it 'evaluates functions with negative numbers' do
|
166
|
+
expect(calculator.evaluate('if (-1 < 5, -1, 5)')).to eq(-1)
|
167
|
+
expect(calculator.evaluate('if (-1 = -1, -1, 5)')).to eq(-1)
|
168
|
+
expect(calculator.evaluate('round(-1.23, 1)')).to eq(BigDecimal.new('-1.2'))
|
169
|
+
expect(calculator.evaluate('NOT(some_boolean) AND -1 > 3', :some_boolean => true)).to be_falsey
|
170
|
+
end
|
141
171
|
end
|
142
172
|
end
|
data/spec/dentaku_spec.rb
CHANGED
@@ -12,4 +12,11 @@ describe Dentaku do
|
|
12
12
|
it 'evaulates a nested function' do
|
13
13
|
expect(Dentaku('roundup(roundup(3 * cherries) + raspberries)', cherries: 1.5, raspberries: 0.9)).to eql(6)
|
14
14
|
end
|
15
|
+
|
16
|
+
it 'treats variables as case-insensitive' do
|
17
|
+
expect(Dentaku('40 + N', 'n' => 2)).to eql(42)
|
18
|
+
expect(Dentaku('40 + N', 'N' => 2)).to eql(42)
|
19
|
+
expect(Dentaku('40 + n', 'N' => 2)).to eql(42)
|
20
|
+
expect(Dentaku('40 + n', 'n' => 2)).to eql(42)
|
21
|
+
end
|
15
22
|
end
|
data/spec/evaluator_spec.rb
CHANGED
@@ -44,6 +44,8 @@ describe Dentaku::Evaluator do
|
|
44
44
|
it 'supports unary minus' do
|
45
45
|
expect(evaluator.evaluate(token_stream(:subtract, 1))).to eq(-1)
|
46
46
|
expect(evaluator.evaluate(token_stream(1, :subtract, :subtract, 1))).to eq(2)
|
47
|
+
expect(evaluator.evaluate(token_stream(1, :subtract, :subtract, :subtract, 1))).to eq(0)
|
48
|
+
expect(evaluator.evaluate(token_stream(:subtract, 1, :add, 1))).to eq(0)
|
47
49
|
end
|
48
50
|
|
49
51
|
it 'supports unary percentage' do
|
@@ -77,6 +79,23 @@ describe Dentaku::Evaluator do
|
|
77
79
|
expect(position).to eq 0
|
78
80
|
expect(tokens.length).to eq 8
|
79
81
|
end
|
82
|
+
|
83
|
+
describe 'with start-anchored token' do
|
84
|
+
let(:number) { [Dentaku::TokenMatcher.new(:numeric).caret] }
|
85
|
+
let(:string) { [Dentaku::TokenMatcher.new(:string).caret] }
|
86
|
+
let(:stream) { token_stream(1, 'foo') }
|
87
|
+
|
88
|
+
it 'matches anchored to the beginning of the token stream' do
|
89
|
+
position, tokens = evaluator.find_rule_match(number, stream)
|
90
|
+
expect(position).to eq 0
|
91
|
+
expect(tokens.length).to eq 1
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'does not match later in the stream' do
|
95
|
+
position, _tokens = evaluator.find_rule_match(string, stream)
|
96
|
+
expect(position).to be_nil
|
97
|
+
end
|
98
|
+
end
|
80
99
|
end
|
81
100
|
|
82
101
|
describe 'functions' do
|
data/spec/tokenizer_spec.rb
CHANGED
@@ -16,19 +16,19 @@ describe Dentaku::Tokenizer do
|
|
16
16
|
it 'tokenizes comparison with =' do
|
17
17
|
tokens = tokenizer.tokenize('number = 5')
|
18
18
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric])
|
19
|
-
expect(tokens.map(&:value)).to eq([
|
19
|
+
expect(tokens.map(&:value)).to eq(['number', :eq, 5])
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'tokenizes comparison with =' do
|
23
23
|
tokens = tokenizer.tokenize('number = 5')
|
24
24
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric])
|
25
|
-
expect(tokens.map(&:value)).to eq([
|
25
|
+
expect(tokens.map(&:value)).to eq(['number', :eq, 5])
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'tokenizes comparison with alternate ==' do
|
29
29
|
tokens = tokenizer.tokenize('number == 5')
|
30
30
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric])
|
31
|
-
expect(tokens.map(&:value)).to eq([
|
31
|
+
expect(tokens.map(&:value)).to eq(['number', :eq, 5])
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'ignores whitespace' do
|
@@ -52,37 +52,55 @@ describe Dentaku::Tokenizer do
|
|
52
52
|
it 'accepts arbitrary identifiers' do
|
53
53
|
tokens = tokenizer.tokenize('monkeys > 1500')
|
54
54
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric])
|
55
|
-
expect(tokens.map(&:value)).to eq([
|
55
|
+
expect(tokens.map(&:value)).to eq(['monkeys', :gt, 1500])
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'recognizes double-quoted strings' do
|
59
59
|
tokens = tokenizer.tokenize('animal = "giraffe"')
|
60
60
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :string])
|
61
|
-
expect(tokens.map(&:value)).to eq([
|
61
|
+
expect(tokens.map(&:value)).to eq(['animal', :eq, 'giraffe'])
|
62
62
|
end
|
63
63
|
|
64
64
|
it 'recognizes single-quoted strings' do
|
65
65
|
tokens = tokenizer.tokenize("animal = 'giraffe'")
|
66
66
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :string])
|
67
|
-
expect(tokens.map(&:value)).to eq([
|
67
|
+
expect(tokens.map(&:value)).to eq(['animal', :eq, 'giraffe'])
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'recognizes binary minus operator' do
|
71
|
+
tokens = tokenizer.tokenize('2 - 3')
|
72
|
+
expect(tokens.map(&:category)).to eq([:numeric, :operator, :numeric])
|
73
|
+
expect(tokens.map(&:value)).to eq([2, :subtract, 3])
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'recognizes unary minus operator' do
|
77
|
+
tokens = tokenizer.tokenize('-2 + 3')
|
78
|
+
expect(tokens.map(&:category)).to eq([:operator, :numeric, :operator, :numeric])
|
79
|
+
expect(tokens.map(&:value)).to eq([:subtract, 2, :add, 3])
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'recognizes unary minus operator' do
|
83
|
+
tokens = tokenizer.tokenize('2 - -3')
|
84
|
+
expect(tokens.map(&:category)).to eq([:numeric, :operator, :operator, :numeric])
|
85
|
+
expect(tokens.map(&:value)).to eq([2, :subtract, :subtract, 3])
|
68
86
|
end
|
69
87
|
|
70
88
|
it 'matches "<=" before "<"' do
|
71
89
|
tokens = tokenizer.tokenize('perimeter <= 7500')
|
72
90
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric])
|
73
|
-
expect(tokens.map(&:value)).to eq([
|
91
|
+
expect(tokens.map(&:value)).to eq(['perimeter', :le, 7500])
|
74
92
|
end
|
75
93
|
|
76
94
|
it 'matches "and" for logical expressions' do
|
77
95
|
tokens = tokenizer.tokenize('octopi <= 7500 AND sharks > 1500')
|
78
96
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric, :combinator, :identifier, :comparator, :numeric])
|
79
|
-
expect(tokens.map(&:value)).to eq([
|
97
|
+
expect(tokens.map(&:value)).to eq(['octopi', :le, 7500, :and, 'sharks', :gt, 1500])
|
80
98
|
end
|
81
99
|
|
82
100
|
it 'matches "or" for logical expressions' do
|
83
101
|
tokens = tokenizer.tokenize('size < 3 or admin = 1')
|
84
102
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric, :combinator, :identifier, :comparator, :numeric])
|
85
|
-
expect(tokens.map(&:value)).to eq([
|
103
|
+
expect(tokens.map(&:value)).to eq(['size', :lt, 3, :or, 'admin', :eq, 1])
|
86
104
|
end
|
87
105
|
|
88
106
|
it 'detects unbalanced parentheses' do
|
@@ -94,7 +112,7 @@ describe Dentaku::Tokenizer do
|
|
94
112
|
tokens = tokenizer.tokenize('andover < 10')
|
95
113
|
expect(tokens.length).to eq(3)
|
96
114
|
expect(tokens.map(&:category)).to eq([:identifier, :comparator, :numeric])
|
97
|
-
expect(tokens.map(&:value)).to eq([
|
115
|
+
expect(tokens.map(&:value)).to eq(['andover', :lt, 10])
|
98
116
|
end
|
99
117
|
|
100
118
|
describe 'functions' do
|
@@ -102,7 +120,7 @@ describe Dentaku::Tokenizer do
|
|
102
120
|
tokens = tokenizer.tokenize('if(x < 10, y, z)')
|
103
121
|
expect(tokens.length).to eq(10)
|
104
122
|
expect(tokens.map(&:category)).to eq([:function, :grouping, :identifier, :comparator, :numeric, :grouping, :identifier, :grouping, :identifier, :grouping])
|
105
|
-
expect(tokens.map(&:value)).to eq([:if, :fopen,
|
123
|
+
expect(tokens.map(&:value)).to eq([:if, :fopen, 'x', :lt, 10, :comma, 'y', :comma, 'z', :close])
|
106
124
|
end
|
107
125
|
|
108
126
|
it 'include ROUND/UP/DOWN' do
|
metadata
CHANGED
@@ -1,59 +1,53 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dentaku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
5
|
-
prerelease:
|
4
|
+
version: 1.2.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Solomon White
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-
|
11
|
+
date: 2014-12-19 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rake
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rspec
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
|
-
description:
|
47
|
-
|
48
|
-
'
|
41
|
+
description: |2
|
42
|
+
Dentaku is a parser and evaluator for mathematical formulas
|
49
43
|
email:
|
50
44
|
- rubysolo@gmail.com
|
51
45
|
executables: []
|
52
46
|
extensions: []
|
53
47
|
extra_rdoc_files: []
|
54
48
|
files:
|
55
|
-
- .gitignore
|
56
|
-
- .travis.yml
|
49
|
+
- ".gitignore"
|
50
|
+
- ".travis.yml"
|
57
51
|
- Gemfile
|
58
52
|
- README.md
|
59
53
|
- Rakefile
|
@@ -86,33 +80,26 @@ files:
|
|
86
80
|
homepage: http://github.com/rubysolo/dentaku
|
87
81
|
licenses:
|
88
82
|
- MIT
|
83
|
+
metadata: {}
|
89
84
|
post_install_message:
|
90
85
|
rdoc_options: []
|
91
86
|
require_paths:
|
92
87
|
- lib
|
93
88
|
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
-
none: false
|
95
89
|
requirements:
|
96
|
-
- -
|
90
|
+
- - ">="
|
97
91
|
- !ruby/object:Gem::Version
|
98
92
|
version: '0'
|
99
|
-
segments:
|
100
|
-
- 0
|
101
|
-
hash: -1375666282720046081
|
102
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
-
none: false
|
104
94
|
requirements:
|
105
|
-
- -
|
95
|
+
- - ">="
|
106
96
|
- !ruby/object:Gem::Version
|
107
97
|
version: '0'
|
108
|
-
segments:
|
109
|
-
- 0
|
110
|
-
hash: -1375666282720046081
|
111
98
|
requirements: []
|
112
99
|
rubyforge_project: dentaku
|
113
|
-
rubygems_version:
|
100
|
+
rubygems_version: 2.2.2
|
114
101
|
signing_key:
|
115
|
-
specification_version:
|
102
|
+
specification_version: 4
|
116
103
|
summary: A formula language parser and evaluator
|
117
104
|
test_files:
|
118
105
|
- spec/binary_operation_spec.rb
|