dentaku 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -10
  3. data/.travis.yml +3 -6
  4. data/CHANGELOG.md +38 -1
  5. data/README.md +2 -2
  6. data/dentaku.gemspec +0 -2
  7. data/lib/dentaku.rb +14 -6
  8. data/lib/dentaku/ast.rb +5 -0
  9. data/lib/dentaku/ast/access.rb +15 -1
  10. data/lib/dentaku/ast/arithmetic.rb +29 -6
  11. data/lib/dentaku/ast/array.rb +15 -1
  12. data/lib/dentaku/ast/case.rb +13 -3
  13. data/lib/dentaku/ast/case/case_conditional.rb +13 -2
  14. data/lib/dentaku/ast/case/case_else.rb +12 -4
  15. data/lib/dentaku/ast/case/case_switch_variable.rb +8 -0
  16. data/lib/dentaku/ast/case/case_then.rb +12 -4
  17. data/lib/dentaku/ast/case/case_when.rb +12 -4
  18. data/lib/dentaku/ast/function.rb +11 -2
  19. data/lib/dentaku/ast/function_registry.rb +21 -0
  20. data/lib/dentaku/ast/functions/all.rb +36 -0
  21. data/lib/dentaku/ast/functions/any.rb +36 -0
  22. data/lib/dentaku/ast/functions/avg.rb +2 -2
  23. data/lib/dentaku/ast/functions/count.rb +8 -0
  24. data/lib/dentaku/ast/functions/duration.rb +51 -0
  25. data/lib/dentaku/ast/functions/if.rb +15 -2
  26. data/lib/dentaku/ast/functions/map.rb +36 -0
  27. data/lib/dentaku/ast/functions/mul.rb +3 -2
  28. data/lib/dentaku/ast/functions/pluck.rb +29 -0
  29. data/lib/dentaku/ast/functions/round.rb +1 -1
  30. data/lib/dentaku/ast/functions/rounddown.rb +1 -1
  31. data/lib/dentaku/ast/functions/roundup.rb +1 -1
  32. data/lib/dentaku/ast/functions/ruby_math.rb +47 -3
  33. data/lib/dentaku/ast/functions/string_functions.rb +68 -4
  34. data/lib/dentaku/ast/functions/sum.rb +3 -2
  35. data/lib/dentaku/ast/grouping.rb +3 -1
  36. data/lib/dentaku/ast/identifier.rb +5 -1
  37. data/lib/dentaku/ast/negation.rb +3 -1
  38. data/lib/dentaku/ast/node.rb +4 -0
  39. data/lib/dentaku/ast/operation.rb +8 -0
  40. data/lib/dentaku/bulk_expression_solver.rb +34 -25
  41. data/lib/dentaku/calculator.rb +19 -6
  42. data/lib/dentaku/date_arithmetic.rb +45 -0
  43. data/lib/dentaku/exceptions.rb +4 -4
  44. data/lib/dentaku/flat_hash.rb +9 -2
  45. data/lib/dentaku/parser.rb +31 -14
  46. data/lib/dentaku/token_matcher.rb +1 -1
  47. data/lib/dentaku/token_scanner.rb +1 -1
  48. data/lib/dentaku/tokenizer.rb +7 -2
  49. data/lib/dentaku/version.rb +1 -1
  50. data/spec/ast/addition_spec.rb +7 -1
  51. data/spec/ast/and_function_spec.rb +6 -6
  52. data/spec/ast/and_spec.rb +1 -1
  53. data/spec/ast/arithmetic_spec.rb +57 -29
  54. data/spec/ast/avg_spec.rb +9 -5
  55. data/spec/ast/count_spec.rb +7 -7
  56. data/spec/ast/division_spec.rb +7 -1
  57. data/spec/ast/function_spec.rb +9 -9
  58. data/spec/ast/max_spec.rb +3 -3
  59. data/spec/ast/min_spec.rb +3 -3
  60. data/spec/ast/mul_spec.rb +10 -6
  61. data/spec/ast/negation_spec.rb +48 -0
  62. data/spec/ast/node_spec.rb +11 -8
  63. data/spec/ast/numeric_spec.rb +1 -1
  64. data/spec/ast/or_spec.rb +6 -6
  65. data/spec/ast/round_spec.rb +14 -4
  66. data/spec/ast/rounddown_spec.rb +14 -4
  67. data/spec/ast/roundup_spec.rb +14 -4
  68. data/spec/ast/string_functions_spec.rb +35 -0
  69. data/spec/ast/sum_spec.rb +10 -6
  70. data/spec/ast/switch_spec.rb +5 -5
  71. data/spec/bulk_expression_solver_spec.rb +18 -1
  72. data/spec/calculator_spec.rb +173 -28
  73. data/spec/dentaku_spec.rb +18 -5
  74. data/spec/external_function_spec.rb +29 -5
  75. data/spec/parser_spec.rb +85 -123
  76. data/spec/spec_helper.rb +6 -4
  77. data/spec/token_matcher_spec.rb +8 -8
  78. data/spec/token_scanner_spec.rb +4 -4
  79. data/spec/tokenizer_spec.rb +32 -13
  80. metadata +11 -4
@@ -27,13 +27,26 @@ describe Dentaku do
27
27
  end
28
28
 
29
29
  it 'evaluates with class-level shortcut functions' do
30
- expect(Dentaku.evaluate('2+2')).to eq(4)
31
- expect(Dentaku.evaluate!('2+2')).to eq(4)
32
- expect { Dentaku.evaluate!('a+1') }.to raise_error(Dentaku::UnboundVariableError)
30
+ expect(described_class.evaluate('2+2')).to eq(4)
31
+ expect(described_class.evaluate!('2+2')).to eq(4)
32
+ expect { described_class.evaluate!('a+1') }.to raise_error(Dentaku::UnboundVariableError)
33
+ end
34
+
35
+ it 'accepts a block for custom handling of unbound variables' do
36
+ unbound = 'apples * 1.5'
37
+ expect(described_class.evaluate(unbound) { :bar }).to eq(:bar)
38
+ expect(described_class.evaluate(unbound) { |e| e }).to eq(unbound)
33
39
  end
34
40
 
35
41
  it 'evaluates with class-level aliases' do
36
- Dentaku.aliases = { roundup: ['roundupup'] }
37
- expect(Dentaku.evaluate('roundupup(6.1)')).to eq(7)
42
+ described_class.aliases = { roundup: ['roundupup'] }
43
+ expect(described_class.evaluate('roundupup(6.1)')).to eq(7)
44
+ end
45
+
46
+ it 'sets caching opt-in flags' do
47
+ expect {
48
+ described_class.enable_caching!
49
+ }.to change { described_class.cache_ast? }.from(false).to(true)
50
+ .and change { described_class.cache_dependency_order? }.from(false).to(true)
38
51
  end
39
52
  end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'dentaku'
2
3
  require 'dentaku/calculator'
3
4
 
4
5
  describe Dentaku::Calculator do
@@ -14,6 +15,7 @@ describe Dentaku::Calculator do
14
15
  [:pow, :numeric, ->(mantissa, exponent) { mantissa**exponent }],
15
16
  [:biggest, :numeric, ->(*args) { args.max }],
16
17
  [:smallest, :numeric, ->(*args) { args.min }],
18
+ [:optional, :numeric, ->(x, y, z = 0) { x + y + z }],
17
19
  ]
18
20
 
19
21
  c.add_functions(fns)
@@ -39,6 +41,13 @@ describe Dentaku::Calculator do
39
41
  expect(with_external_funcs.evaluate('SMALLEST(8,6,7,5,3,0,9)')).to eq(0)
40
42
  end
41
43
 
44
+ it 'includes OPTIONAL' do
45
+ expect(with_external_funcs.evaluate('OPTIONAL(1,2)')).to eq(3)
46
+ expect(with_external_funcs.evaluate('OPTIONAL(1,2,3)')).to eq(6)
47
+ expect { with_external_funcs.dependencies('OPTIONAL()') }.to raise_error(Dentaku::ParseError)
48
+ expect { with_external_funcs.dependencies('OPTIONAL(1,2,3,4)') }.to raise_error(Dentaku::ParseError)
49
+ end
50
+
42
51
  it 'supports array parameters' do
43
52
  calculator = described_class.new
44
53
  calculator.add_function(
@@ -59,6 +68,19 @@ describe Dentaku::Calculator do
59
68
  expect(calculator.evaluate("hey!()")).to eq("hey!")
60
69
  end
61
70
 
71
+ it 'defines for a given function a properly named class that represents it to support AST marshaling' do
72
+ calculator = described_class.new
73
+ expect {
74
+ calculator.add_function(:ho, :string, -> {})
75
+ }.to change {
76
+ Dentaku::AST::Function.const_defined?("Ho")
77
+ }.from(false).to(true)
78
+
79
+ expect {
80
+ Marshal.dump(calculator.ast('MAX(1, 2)'))
81
+ }.not_to raise_error
82
+ end
83
+
62
84
  it 'does not store functions across all calculators' do
63
85
  calculator1 = Dentaku::Calculator.new
64
86
  calculator1.add_function(:my_function, :numeric, ->(x) { 2 * x + 1 })
@@ -66,17 +88,19 @@ describe Dentaku::Calculator do
66
88
  calculator2 = Dentaku::Calculator.new
67
89
  calculator2.add_function(:my_function, :numeric, ->(x) { 4 * x + 3 })
68
90
 
69
- expect(calculator1.evaluate("1 + my_function(2)")). to eq (1 + 2 * 2 + 1)
70
- expect(calculator2.evaluate("1 + my_function(2)")). to eq (1 + 4 * 2 + 3)
91
+ expect(calculator1.evaluate!("1 + my_function(2)")). to eq(1 + 2 * 2 + 1)
92
+ expect(calculator2.evaluate!("1 + my_function(2)")). to eq(1 + 4 * 2 + 3)
71
93
 
72
94
  expect {
73
95
  Dentaku::Calculator.new.evaluate!("1 + my_function(2)")
74
96
  }.to raise_error(Dentaku::ParseError)
75
97
  end
76
98
 
77
- it 'self.add_function adds to default/global function registry' do
78
- Dentaku::Calculator.add_function(:global_function, :numeric, ->(x) { 10 + x**2 })
79
- expect(Dentaku::Calculator.new.evaluate("global_function(3) + 5")).to eq (10 + 3**2 + 5)
99
+ describe 'Dentaku::Calculator.add_function' do
100
+ it 'adds to default/global function registry' do
101
+ Dentaku::Calculator.add_function(:global_function, :numeric, ->(x) { 10 + x**2 })
102
+ expect(Dentaku::Calculator.new.evaluate("global_function(3) + 5")).to eq(10 + 3**2 + 5)
103
+ end
80
104
  end
81
105
  end
82
106
  end
@@ -1,189 +1,151 @@
1
1
  require 'spec_helper'
2
2
  require 'dentaku/token'
3
+ require 'dentaku/tokenizer'
3
4
  require 'dentaku/parser'
4
5
 
5
6
  describe Dentaku::Parser do
6
- it 'is constructed from a token' do
7
- token = Dentaku::Token.new(:numeric, 5)
8
- node = described_class.new([token]).parse
9
- expect(node.value).to eq 5
7
+ it 'parses an integer literal' do
8
+ node = parse('5')
9
+ expect(node.value).to eq(5)
10
10
  end
11
11
 
12
12
  it 'performs simple addition' do
13
- five = Dentaku::Token.new(:numeric, 5)
14
- plus = Dentaku::Token.new(:operator, :add)
15
- four = Dentaku::Token.new(:numeric, 4)
16
-
17
- node = described_class.new([five, plus, four]).parse
18
- expect(node.value).to eq 9
13
+ node = parse('5 + 4')
14
+ expect(node.value).to eq(9)
19
15
  end
20
16
 
21
17
  it 'compares two numbers' do
22
- five = Dentaku::Token.new(:numeric, 5)
23
- lt = Dentaku::Token.new(:comparator, :lt)
24
- four = Dentaku::Token.new(:numeric, 4)
25
-
26
- node = described_class.new([five, lt, four]).parse
27
- expect(node.value).to eq false
18
+ node = parse('5 < 4')
19
+ expect(node.value).to eq(false)
28
20
  end
29
21
 
30
22
  it 'calculates unary percentage' do
31
- five = Dentaku::Token.new(:numeric, 5)
32
- mod = Dentaku::Token.new(:operator, :mod)
33
-
34
- node = described_class.new([five, mod]).parse
35
- expect(node.value).to eq 0.05
23
+ node = parse('5%')
24
+ expect(node.value).to eq(0.05)
36
25
  end
37
26
 
38
27
  it 'calculates bitwise OR' do
39
- two = Dentaku::Token.new(:numeric, 2)
40
- bitor = Dentaku::Token.new(:operator, :bitor)
41
- three = Dentaku::Token.new(:numeric, 3)
42
-
43
- node = described_class.new([two, bitor, three]).parse
44
- expect(node.value).to eq 3
28
+ node = parse('2|3')
29
+ expect(node.value).to eq(3)
45
30
  end
46
31
 
47
32
  it 'performs multiple operations in one stream' do
48
- five = Dentaku::Token.new(:numeric, 5)
49
- plus = Dentaku::Token.new(:operator, :add)
50
- four = Dentaku::Token.new(:numeric, 4)
51
- times = Dentaku::Token.new(:operator, :multiply)
52
- three = Dentaku::Token.new(:numeric, 3)
53
-
54
- node = described_class.new([five, plus, four, times, three]).parse
55
- expect(node.value).to eq 17
33
+ node = parse('5 * 4 + 3')
34
+ expect(node.value).to eq(23)
56
35
  end
57
36
 
58
37
  it 'respects order of operations' do
59
- five = Dentaku::Token.new(:numeric, 5)
60
- times = Dentaku::Token.new(:operator, :multiply)
61
- four = Dentaku::Token.new(:numeric, 4)
62
- plus = Dentaku::Token.new(:operator, :add)
63
- three = Dentaku::Token.new(:numeric, 3)
64
-
65
- node = described_class.new([five, times, four, plus, three]).parse
66
- expect(node.value).to eq 23
38
+ node = parse('5 + 4*3')
39
+ expect(node.value).to eq(17)
67
40
  end
68
41
 
69
42
  it 'respects grouping by parenthesis' do
70
- lpar = Dentaku::Token.new(:grouping, :open)
71
- five = Dentaku::Token.new(:numeric, 5)
72
- plus = Dentaku::Token.new(:operator, :add)
73
- four = Dentaku::Token.new(:numeric, 4)
74
- rpar = Dentaku::Token.new(:grouping, :close)
75
- times = Dentaku::Token.new(:operator, :multiply)
76
- three = Dentaku::Token.new(:numeric, 3)
77
-
78
- node = described_class.new([lpar, five, plus, four, rpar, times, three]).parse
79
- expect(node.value).to eq 27
43
+ node = parse('(5 + 4) * 3')
44
+ expect(node.value).to eq(27)
80
45
  end
81
46
 
82
47
  it 'evaluates functions' do
83
- fn = Dentaku::Token.new(:function, :if)
84
- fopen = Dentaku::Token.new(:grouping, :open)
85
- five = Dentaku::Token.new(:numeric, 5)
86
- lt = Dentaku::Token.new(:comparator, :lt)
87
- four = Dentaku::Token.new(:numeric, 4)
88
- comma = Dentaku::Token.new(:grouping, :comma)
89
- three = Dentaku::Token.new(:numeric, 3)
90
- two = Dentaku::Token.new(:numeric, 2)
91
- rpar = Dentaku::Token.new(:grouping, :close)
92
-
93
- node = described_class.new([fn, fopen, five, lt, four, comma, three, comma, two, rpar]).parse
94
- expect(node.value).to eq 2
48
+ node = parse('IF(5 < 4, 3, 2)')
49
+ expect(node.value).to eq(2)
95
50
  end
96
51
 
97
52
  it 'represents formulas with variables' do
98
- five = Dentaku::Token.new(:numeric, 5)
99
- times = Dentaku::Token.new(:operator, :multiply)
100
- x = Dentaku::Token.new(:identifier, :x)
101
-
102
- node = described_class.new([five, times, x]).parse
53
+ node = parse('5 * x')
103
54
  expect { node.value }.to raise_error(Dentaku::UnboundVariableError)
104
- expect(node.value(x: 3)).to eq 15
55
+ expect(node.value("x" => 3)).to eq(15)
105
56
  end
106
57
 
107
58
  it 'evaluates access into data structures' do
108
- a = token(:a)
109
- lbracket = token(:lbracket)
110
- one = token(1)
111
- rbracket = token(:rbracket)
112
-
113
- node = described_class.new([a, lbracket, one, rbracket]).parse
59
+ node = parse('a[1]')
114
60
  expect { node.value }.to raise_error(Dentaku::UnboundVariableError)
115
- expect(node.value(a: [1, 2, 3])).to eq 2
61
+ expect(node.value("a" => [1, 2, 3])).to eq(2)
116
62
  end
117
63
 
118
64
  it 'evaluates boolean expressions' do
119
- d_true = Dentaku::Token.new(:logical, true)
120
- d_and = Dentaku::Token.new(:combinator, :and)
121
- d_false = Dentaku::Token.new(:logical, false)
122
-
123
- node = described_class.new([d_true, d_and, d_false]).parse
124
- expect(node.value).to eq false
65
+ node = parse('true AND false')
66
+ expect(node.value).to eq(false)
125
67
  end
126
68
 
127
69
  it 'evaluates a case statement' do
128
- case_start = Dentaku::Token.new(:case, :open)
129
- x = Dentaku::Token.new(:identifier, :x)
130
- case_when1 = Dentaku::Token.new(:case, :when)
131
- one = Dentaku::Token.new(:numeric, 1)
132
- case_then1 = Dentaku::Token.new(:case, :then)
133
- two = Dentaku::Token.new(:numeric, 2)
134
- case_when2 = Dentaku::Token.new(:case, :when)
135
- three = Dentaku::Token.new(:numeric, 3)
136
- case_then2 = Dentaku::Token.new(:case, :then)
137
- four = Dentaku::Token.new(:numeric, 4)
138
- case_close = Dentaku::Token.new(:case, :close)
139
-
140
- node = described_class.new(
141
- [case_start,
142
- x,
143
- case_when1,
144
- one,
145
- case_then1,
146
- two,
147
- case_when2,
148
- three,
149
- case_then2,
150
- four,
151
- case_close]).parse
152
- expect(node.value(x: 3)).to eq(4)
70
+ node = parse('CASE x WHEN 1 THEN 2 WHEN 3 THEN 4 END')
71
+ expect(node.value("x" => 3)).to eq(4)
72
+ end
73
+
74
+ it 'evaluates arrays' do
75
+ node = parse('{1, 2, 3}')
76
+ expect(node.value).to eq([1, 2, 3])
153
77
  end
154
78
 
155
79
  context 'invalid expression' do
156
80
  it 'raises a parse error for bad math' do
157
- five = Dentaku::Token.new(:numeric, 5)
158
- times = Dentaku::Token.new(:operator, :multiply)
159
- minus = Dentaku::Token.new(:operator, :subtract)
160
-
161
81
  expect {
162
- described_class.new([five, times, minus]).parse
82
+ parse("5 * -")
163
83
  }.to raise_error(Dentaku::ParseError)
164
84
  end
165
85
 
166
86
  it 'raises a parse error for bad logic' do
167
- this = Dentaku::Token.new(:logical, true)
168
- also = Dentaku::Token.new(:combinator, :and)
87
+ expect {
88
+ parse("TRUE AND")
89
+ }.to raise_error(Dentaku::ParseError)
90
+ end
91
+
92
+ it 'raises a parse error for bad grouping structure' do
93
+ expect {
94
+ parse(",")
95
+ }.to raise_error(Dentaku::ParseError)
96
+
97
+ expect {
98
+ parse("5, x")
99
+ described_class.new([five, comma, x]).parse
100
+ }.to raise_error(Dentaku::ParseError)
101
+
102
+ expect {
103
+ parse("5 + 5, x")
104
+ }.to raise_error(Dentaku::ParseError)
169
105
 
170
106
  expect {
171
- described_class.new([this, also]).parse
107
+ parse("{1, 2, }")
108
+ }.to raise_error(Dentaku::ParseError)
109
+
110
+ expect {
111
+ parse("CONCAT('1', '2', )")
172
112
  }.to raise_error(Dentaku::ParseError)
173
113
  end
174
114
 
175
- it 'raises an exception when trying to access an undefined function' do
176
- fn = Dentaku::Token.new(:function, 'non_exists_func')
115
+ it 'raises parse errors for malformed case statements' do
116
+ expect {
117
+ parse("CASE a when 'one' then 1")
118
+ }.to raise_error(Dentaku::ParseError)
119
+
120
+ expect {
121
+ parse("case a whend 'one' then 1 end")
122
+ }.to raise_error(Dentaku::ParseError)
123
+
124
+ expect {
125
+ parse("CASE a WHEN 'one' THEND 1 END")
126
+ }.to raise_error(Dentaku::ParseError)
177
127
 
178
128
  expect {
179
- described_class.new([fn]).parse
129
+ parse("CASE a when 'one' then end")
130
+ }.to raise_error(Dentaku::ParseError)
131
+ end
132
+
133
+ it 'raises a parse error when trying to access an undefined function' do
134
+ expect {
135
+ parse("undefined()")
180
136
  }.to raise_error(Dentaku::ParseError)
181
137
  end
182
138
  end
183
139
 
184
- it "evaluates explicit 'NULL' as a Nil" do
185
- null = Dentaku::Token.new(:null, nil)
186
- node = described_class.new([null]).parse
140
+ it "evaluates explicit 'NULL' as nil" do
141
+ node = parse("NULL")
187
142
  expect(node.value).to eq(nil)
188
143
  end
144
+
145
+ private
146
+
147
+ def parse(expr)
148
+ tokens = Dentaku::Tokenizer.new.tokenize(expr)
149
+ described_class.new(tokens).parse
150
+ end
189
151
  end
@@ -16,9 +16,11 @@ end
16
16
 
17
17
  RSpec.configure do |c|
18
18
  c.before(:all) {
19
- # add example for alias because we can set aliases just once
20
- # before `calculator` method called
21
- Dentaku.aliases = { roundup: ['roundupup'] }
19
+ if Dentaku.respond_to?(:aliases=)
20
+ # add example for alias because we can set aliases just once
21
+ # before `calculator` method called
22
+ Dentaku.aliases = { roundup: ['roundupup'] }
23
+ end
22
24
  }
23
25
  end
24
26
 
@@ -45,7 +47,7 @@ def type_for(value)
45
47
  :grouping
46
48
  when :lbracket, :rbracket
47
49
  :access
48
- when :le, :ge, :ne, :ne, :lt, :gt, :eq
50
+ when :le, :ge, :ne, :lt, :gt, :eq
49
51
  :comparator
50
52
  when :and, :or
51
53
  :combinator
@@ -82,8 +82,8 @@ describe Dentaku::TokenMatcher do
82
82
  it 'matches zero or more occurrences in a token stream' do
83
83
  matched, substream = standard.match(stream)
84
84
  expect(matched).to be_truthy
85
- expect(substream.length).to eq 1
86
- expect(substream.map(&:value)).to eq [5]
85
+ expect(substream.length).to eq(1)
86
+ expect(substream.map(&:value)).to eq([5])
87
87
 
88
88
  matched, substream = standard.match(stream, 4)
89
89
  expect(substream).to be_empty
@@ -97,8 +97,8 @@ describe Dentaku::TokenMatcher do
97
97
  it 'matches zero or more occurrences in a token stream' do
98
98
  matched, substream = star.match(stream)
99
99
  expect(matched).to be_truthy
100
- expect(substream.length).to eq 4
101
- expect(substream.map(&:value)).to eq [5, 11, 9, 24]
100
+ expect(substream.length).to eq(4)
101
+ expect(substream.map(&:value)).to eq([5, 11, 9, 24])
102
102
 
103
103
  matched, substream = star.match(stream, 4)
104
104
  expect(substream).to be_empty
@@ -112,8 +112,8 @@ describe Dentaku::TokenMatcher do
112
112
  it 'matches one or more occurrences in a token stream' do
113
113
  matched, substream = plus.match(stream)
114
114
  expect(matched).to be_truthy
115
- expect(substream.length).to eq 4
116
- expect(substream.map(&:value)).to eq [5, 11, 9, 24]
115
+ expect(substream.length).to eq(4)
116
+ expect(substream.map(&:value)).to eq([5, 11, 9, 24])
117
117
 
118
118
  matched, substream = plus.match(stream, 4)
119
119
  expect(substream).to be_empty
@@ -126,8 +126,8 @@ describe Dentaku::TokenMatcher do
126
126
  stream = token_stream(1, :comma, 2, :comma, true, :comma, 'olive', :comma, :'(')
127
127
  matched, substream = described_class.arguments.match(stream)
128
128
  expect(matched).to be_truthy
129
- expect(substream.length).to eq 8
130
- expect(substream.map(&:value)).to eq [1, :comma, 2, :comma, true, :comma, 'olive', :comma]
129
+ expect(substream.length).to eq(8)
130
+ expect(substream.map(&:value)).to eq([1, :comma, 2, :comma, true, :comma, 'olive', :comma])
131
131
  end
132
132
  end
133
133
  end
@@ -3,7 +3,7 @@ require 'dentaku/token_scanner'
3
3
  describe Dentaku::TokenScanner do
4
4
  let(:whitespace) { described_class.new(:whitespace, '\s') }
5
5
  let(:numeric) { described_class.new(:numeric, '(\d+(\.\d+)?|\.\d+)',
6
- ->(raw) { raw =~ /\./ ? BigDecimal.new(raw) : raw.to_i })
6
+ ->(raw) { raw =~ /\./ ? BigDecimal(raw) : raw.to_i })
7
7
  }
8
8
  let(:custom) { described_class.new(:identifier, '#\w+\b',
9
9
  ->(raw) { raw.gsub('#', '').to_sym })
@@ -29,18 +29,18 @@ describe Dentaku::TokenScanner do
29
29
 
30
30
  it 'allows customizing available scanners' do
31
31
  described_class.scanners = [:whitespace, :numeric]
32
- expect(described_class.scanners.length).to eq 2
32
+ expect(described_class.scanners.length).to eq(2)
33
33
  end
34
34
 
35
35
  it 'ignores invalid scanners' do
36
36
  described_class.scanners = [:whitespace, :numeric, :fake]
37
- expect(described_class.scanners.length).to eq 2
37
+ expect(described_class.scanners.length).to eq(2)
38
38
  end
39
39
 
40
40
  it 'uses a custom scanner' do
41
41
  described_class.scanners = [:whitespace, :numeric]
42
42
  described_class.register_scanner(:custom, custom)
43
- expect(described_class.scanners.length).to eq 3
43
+ expect(described_class.scanners.length).to eq(3)
44
44
 
45
45
  token = custom.scan('#apple + #pear').first
46
46
  expect(token.category).to eq(:identifier)