dentaku 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|