dentaku 3.2.1 → 3.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc147a87584da15958dfa99d62dcbc680333b21d2f9a35758ab11b4d7327787b
4
- data.tar.gz: a3138608c305adc7ff88168e5379bc5f7da7fa0712b60d9f5d4afb8c63bfa2c3
3
+ metadata.gz: d7a7a3fa41f933a1f88a76e736c32e71dcf87f391a7a4ba09d37b2abad8269b2
4
+ data.tar.gz: 3045bd6715d486669c2fe015165a2bef31928fccdf4e6a07f59a67257b9f4867
5
5
  SHA512:
6
- metadata.gz: 24da7dff044e4909cc1a6f900fb91c1aa30064e940ddf57e1c68112b0d61331b66fa066173012daeb302f757d10ea7ccec1cc505c95d6a8f32c019b2d7d219ff
7
- data.tar.gz: 5745ef2e7896a3e0570d236e0b04bdd627659b15da2234855f81adde950c18c3b04d73eaeb34ae836756bfeb37f54bff9886d8730912a32c9e2700161807ac10
6
+ metadata.gz: 2f8b4a5d727ec64a0308a57570fc51ff09864174fd0669956acb6b26b0d6b5694d6a0ec563eb107b577db79dd37ddd383fc36ec47bd17ebb11008981e2d5c729
7
+ data.tar.gz: 1cb16ed5d1bbad1c896d3907761206178093531ef02fe27eabaa19e1b5187f66f1d9d6586d410107133c08c520596dd7b9a6d5b47f23decb47e80ed2cbdd0e28
@@ -1,5 +1,9 @@
1
1
  # Change Log
2
2
 
3
+ ## [v3.3.0] 2018-12-04
4
+ - add array literal syntax
5
+ - return correct type from string function AST nodes
6
+
3
7
  ## [v3.2.1] 2018-10-24
4
8
  - make `evaluate` rescue more exceptions
5
9
 
@@ -162,7 +166,8 @@
162
166
  ## [v0.1.0] 2012-01-20
163
167
  - initial release
164
168
 
165
- [HEAD]: https://github.com/rubysolo/dentaku/compare/v3.2.1...HEAD
169
+ [HEAD]: https://github.com/rubysolo/dentaku/compare/v3.3.0...HEAD
170
+ [v3.3.0]: https://github.com/rubysolo/dentaku/compare/v3.2.1...v3.3.0
166
171
  [v3.2.1]: https://github.com/rubysolo/dentaku/compare/v3.2.0...v3.2.1
167
172
  [v3.2.0]: https://github.com/rubysolo/dentaku/compare/v3.1.0...v3.2.0
168
173
  [v3.1.0]: https://github.com/rubysolo/dentaku/compare/v3.0.0...v3.1.0
@@ -11,6 +11,7 @@ require_relative './ast/negation'
11
11
  require_relative './ast/comparators'
12
12
  require_relative './ast/combinators'
13
13
  require_relative './ast/access'
14
+ require_relative './ast/array'
14
15
  require_relative './ast/grouping'
15
16
  require_relative './ast/case'
16
17
  require_relative './ast/function_registry'
@@ -0,0 +1,23 @@
1
+ module Dentaku
2
+ module AST
3
+ class Array
4
+ def self.arity
5
+ end
6
+
7
+ def self.peek(*)
8
+ end
9
+
10
+ def initialize(*elements)
11
+ @elements = *elements
12
+ end
13
+
14
+ def value(context = {})
15
+ @elements.map { |el| el.value(context) }
16
+ end
17
+
18
+ def dependencies(context = {})
19
+ @elements.flat_map { |el| el.dependencies(context) }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -3,7 +3,20 @@ require_relative '../function'
3
3
  module Dentaku
4
4
  module AST
5
5
  module StringFunctions
6
- class Left < Function
6
+ class Base < Function
7
+ def type
8
+ :string
9
+ end
10
+
11
+ def negative_argument_failure(fun, arg = 'length')
12
+ raise Dentaku::ArgumentError.for(
13
+ :invalid_value,
14
+ function_name: "#{fun}()"
15
+ ), "#{fun}() requires #{arg} to be positive"
16
+ end
17
+ end
18
+
19
+ class Left < Base
7
20
  def initialize(*args)
8
21
  super
9
22
  @string, @length = *@args
@@ -12,11 +25,12 @@ module Dentaku
12
25
  def value(context = {})
13
26
  string = @string.value(context).to_s
14
27
  length = @length.value(context)
28
+ negative_argument_failure('LEFT') if length < 0
15
29
  string[0, length]
16
30
  end
17
31
  end
18
32
 
19
- class Right < Function
33
+ class Right < Base
20
34
  def initialize(*args)
21
35
  super
22
36
  @string, @length = *@args
@@ -25,11 +39,12 @@ module Dentaku
25
39
  def value(context = {})
26
40
  string = @string.value(context).to_s
27
41
  length = @length.value(context)
42
+ negative_argument_failure('RIGHT') if length < 0
28
43
  string[length * -1, length] || string
29
44
  end
30
45
  end
31
46
 
32
- class Mid < Function
47
+ class Mid < Base
33
48
  def initialize(*args)
34
49
  super
35
50
  @string, @offset, @length = *@args
@@ -38,12 +53,14 @@ module Dentaku
38
53
  def value(context = {})
39
54
  string = @string.value(context).to_s
40
55
  offset = @offset.value(context)
56
+ negative_argument_failure('MID', 'offset') if offset < 0
41
57
  length = @length.value(context)
58
+ negative_argument_failure('MID') if length < 0
42
59
  string[offset - 1, length].to_s
43
60
  end
44
61
  end
45
62
 
46
- class Len < Function
63
+ class Len < Base
47
64
  def initialize(*args)
48
65
  super
49
66
  @string = @args[0]
@@ -53,9 +70,13 @@ module Dentaku
53
70
  string = @string.value(context).to_s
54
71
  string.length
55
72
  end
73
+
74
+ def type
75
+ :numeric
76
+ end
56
77
  end
57
78
 
58
- class Find < Function
79
+ class Find < Base
59
80
  def initialize(*args)
60
81
  super
61
82
  @needle, @haystack = *@args
@@ -68,9 +89,13 @@ module Dentaku
68
89
  pos = haystack.index(needle)
69
90
  pos && pos + 1
70
91
  end
92
+
93
+ def type
94
+ :numeric
95
+ end
71
96
  end
72
97
 
73
- class Substitute < Function
98
+ class Substitute < Base
74
99
  def initialize(*args)
75
100
  super
76
101
  @original, @search, @replacement = *@args
@@ -85,7 +110,7 @@ module Dentaku
85
110
  end
86
111
  end
87
112
 
88
- class Concat < Function
113
+ class Concat < Base
89
114
  def initialize(*args)
90
115
  super
91
116
  end
@@ -95,7 +120,7 @@ module Dentaku
95
120
  end
96
121
  end
97
122
 
98
- class Contains < Function
123
+ class Contains < Base
99
124
  def initialize(*args)
100
125
  super
101
126
  @needle, @haystack = *args
@@ -104,6 +129,10 @@ module Dentaku
104
129
  def value(context = {})
105
130
  @haystack.value(context).to_s.include? @needle.value(context).to_s
106
131
  end
132
+
133
+ def type
134
+ :logical
135
+ end
107
136
  end
108
137
  end
109
138
  end
@@ -205,11 +205,28 @@ module Dentaku
205
205
  end
206
206
 
207
207
  unless operations.last == AST::Access
208
- fail! :unbalanced_bracket, token
208
+ fail! :unbalanced_bracket, token: token
209
209
  end
210
210
  consume
211
211
  end
212
212
 
213
+ when :array
214
+ case token.value
215
+ when :array_start
216
+ operations.push AST::Array
217
+ arities.push 0
218
+ when :array_end
219
+ while operations.any? && operations.last != AST::Array
220
+ consume
221
+ end
222
+
223
+ unless operations.last == AST::Array
224
+ fail! :unbalanced_bracket, token: token
225
+ end
226
+
227
+ consume(arities.pop.succ)
228
+ end
229
+
213
230
  when :grouping
214
231
  case token.value
215
232
  when :open
@@ -237,7 +254,7 @@ module Dentaku
237
254
 
238
255
  when :comma
239
256
  arities[-1] += 1
240
- while operations.any? && operations.last != AST::Grouping
257
+ while operations.any? && operations.last != AST::Grouping && operations.last != AST::Array
241
258
  consume
242
259
  end
243
260
 
@@ -43,6 +43,7 @@ module Dentaku
43
43
  :combinator,
44
44
  :operator,
45
45
  :grouping,
46
+ :array,
46
47
  :access,
47
48
  :case_statement,
48
49
  :comparator,
@@ -129,6 +130,11 @@ module Dentaku
129
130
  new(:grouping, '\(|\)|,', lambda { |raw| names[raw] })
130
131
  end
131
132
 
133
+ def array
134
+ names = { array_start: '{', array_end: '}', }.invert
135
+ new(:array, '\{|\}|,', lambda { |raw| names[raw] })
136
+ end
137
+
132
138
  def access
133
139
  names = { lbracket: '[', rbracket: ']' }.invert
134
140
  new(:access, '\[|\]', lambda { |raw| names[raw] })
@@ -1,3 +1,3 @@
1
1
  module Dentaku
2
- VERSION = "3.2.1"
2
+ VERSION = "3.3.0"
3
3
  end
@@ -25,6 +25,16 @@ describe Dentaku::AST::StringFunctions::Left do
25
25
  it 'handles size greater than input string length correctly' do
26
26
  expect(subject.value('string' => 'abcdefg', 'length' => 40)).to eq 'abcdefg'
27
27
  end
28
+
29
+ it 'has the proper type' do
30
+ expect(subject.type).to eq(:string)
31
+ end
32
+
33
+ it 'raises an error if given invalid length' do
34
+ expect {
35
+ subject.value('string' => 'abcdefg', 'length' => -2)
36
+ }.to raise_error(Dentaku::ArgumentError, /LEFT\(\) requires length to be positive/)
37
+ end
28
38
  end
29
39
 
30
40
  describe Dentaku::AST::StringFunctions::Right do
@@ -42,6 +52,10 @@ describe Dentaku::AST::StringFunctions::Right do
42
52
  subject = described_class.new(literal('abcdefg'), literal(40))
43
53
  expect(subject.value).to eq 'abcdefg'
44
54
  end
55
+
56
+ it 'has the proper type' do
57
+ expect(subject.type).to eq(:string)
58
+ end
45
59
  end
46
60
 
47
61
  describe Dentaku::AST::StringFunctions::Mid do
@@ -64,6 +78,10 @@ describe Dentaku::AST::StringFunctions::Mid do
64
78
  subject = described_class.new(literal('abcdefg'), literal(4), literal(40))
65
79
  expect(subject.value).to eq 'defg'
66
80
  end
81
+
82
+ it 'has the proper type' do
83
+ expect(subject.type).to eq(:string)
84
+ end
67
85
  end
68
86
 
69
87
  describe Dentaku::AST::StringFunctions::Len do
@@ -76,6 +94,10 @@ describe Dentaku::AST::StringFunctions::Len do
76
94
  subject = described_class.new(literal(''))
77
95
  expect(subject.value).to eq 0
78
96
  end
97
+
98
+ it 'has the proper type' do
99
+ expect(subject.type).to eq(:numeric)
100
+ end
79
101
  end
80
102
 
81
103
  describe Dentaku::AST::StringFunctions::Find do
@@ -93,6 +115,10 @@ describe Dentaku::AST::StringFunctions::Find do
93
115
  subject = described_class.new(literal('DE'), literal(''))
94
116
  expect(subject.value).to be_nil
95
117
  end
118
+
119
+ it 'has the proper type' do
120
+ expect(subject.type).to eq(:numeric)
121
+ end
96
122
  end
97
123
 
98
124
  describe Dentaku::AST::StringFunctions::Substitute do
@@ -110,6 +136,10 @@ describe Dentaku::AST::StringFunctions::Substitute do
110
136
  subject = described_class.new(literal('ABCDEFG'), literal('DE'), literal(''))
111
137
  expect(subject.value).to eq 'ABCFG'
112
138
  end
139
+
140
+ it 'has the proper type' do
141
+ expect(subject.type).to eq(:string)
142
+ end
113
143
  end
114
144
 
115
145
  describe Dentaku::AST::StringFunctions::Concat do
@@ -132,6 +162,10 @@ describe Dentaku::AST::StringFunctions::Concat do
132
162
  subject = described_class.new(literal(''), literal(''))
133
163
  expect(subject.value).to eq ''
134
164
  end
165
+
166
+ it 'has the proper type' do
167
+ expect(subject.type).to eq(:string)
168
+ end
135
169
  end
136
170
 
137
171
  describe Dentaku::AST::StringFunctions::Contains do
@@ -141,4 +175,8 @@ describe Dentaku::AST::StringFunctions::Contains do
141
175
  subject = described_class.new(literal('app'), literal('orange'))
142
176
  expect(subject.value).to be_falsy
143
177
  end
178
+
179
+ it 'has the proper type' do
180
+ expect(subject.type).to eq(:logical)
181
+ end
144
182
  end
@@ -79,6 +79,7 @@ describe Dentaku::Calculator do
79
79
  describe 'evaluate!' do
80
80
  it 'raises exception when formula has error' do
81
81
  expect { calculator.evaluate!('1 + + 1') }.to raise_error(Dentaku::ParseError)
82
+ expect { calculator.evaluate!('(1 > 5) OR LEFT("abc", 1)') }.to raise_error(Dentaku::ParseError)
82
83
  end
83
84
 
84
85
  it 'raises unbound variable errors' do
@@ -149,6 +150,7 @@ describe Dentaku::Calculator do
149
150
 
150
151
  it 'evaluates arrays' do
151
152
  expect(calculator.evaluate([1, 2, 3])).to eq([1, 2, 3])
153
+ expect(calculator.evaluate!('{1,2,3}')).to eq([1, 2, 3])
152
154
  end
153
155
  end
154
156
 
@@ -179,6 +179,12 @@ describe Dentaku::Tokenizer do
179
179
  expect(tokens.map(&:value)).to eq(['size', :lt, 3, :or, 'admin', :eq, 1])
180
180
  end
181
181
 
182
+ it 'tokenizes curly brackets for array literals' do
183
+ tokens = tokenizer.tokenize('{}')
184
+ expect(tokens.map(&:category)).to eq(%i(array array))
185
+ expect(tokens.map(&:value)).to eq(%i(array_start array_end))
186
+ end
187
+
182
188
  it 'tokenizes square brackets for data structure access' do
183
189
  tokens = tokenizer.tokenize('a[1]')
184
190
  expect(tokens.map(&:category)).to eq(%i(identifier access numeric access))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dentaku
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Solomon White
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-25 00:00:00.000000000 Z
11
+ date: 2018-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codecov
@@ -143,6 +143,7 @@ files:
143
143
  - lib/dentaku/ast.rb
144
144
  - lib/dentaku/ast/access.rb
145
145
  - lib/dentaku/ast/arithmetic.rb
146
+ - lib/dentaku/ast/array.rb
146
147
  - lib/dentaku/ast/bitwise.rb
147
148
  - lib/dentaku/ast/case.rb
148
149
  - lib/dentaku/ast/case/case_conditional.rb