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
@@ -231,43 +231,62 @@ describe Dentaku::Tokenizer do
231
231
  ])
232
232
  end
233
233
 
234
- describe 'functions' do
235
- it 'include IF' do
234
+ describe 'tokenizing function calls' do
235
+ it 'handles IF' do
236
236
  tokens = tokenizer.tokenize('if(x < 10, y, z)')
237
237
  expect(tokens.length).to eq(10)
238
238
  expect(tokens.map(&:category)).to eq([:function, :grouping, :identifier, :comparator, :numeric, :grouping, :identifier, :grouping, :identifier, :grouping])
239
239
  expect(tokens.map(&:value)).to eq([:if, :open, 'x', :lt, 10, :comma, 'y', :comma, 'z', :close])
240
240
  end
241
241
 
242
- it 'include ROUND/UP/DOWN' do
242
+ it 'handles ROUND/UP/DOWN' do
243
243
  tokens = tokenizer.tokenize('round(8.2)')
244
244
  expect(tokens.length).to eq(4)
245
245
  expect(tokens.map(&:category)).to eq([:function, :grouping, :numeric, :grouping])
246
- expect(tokens.map(&:value)).to eq([:round, :open, BigDecimal.new('8.2'), :close])
246
+ expect(tokens.map(&:value)).to eq([:round, :open, BigDecimal('8.2'), :close])
247
247
 
248
248
  tokens = tokenizer.tokenize('round(8.75, 1)')
249
249
  expect(tokens.length).to eq(6)
250
250
  expect(tokens.map(&:category)).to eq([:function, :grouping, :numeric, :grouping, :numeric, :grouping])
251
- expect(tokens.map(&:value)).to eq([:round, :open, BigDecimal.new('8.75'), :comma, 1, :close])
251
+ expect(tokens.map(&:value)).to eq([:round, :open, BigDecimal('8.75'), :comma, 1, :close])
252
252
 
253
253
  tokens = tokenizer.tokenize('ROUNDUP(8.2)')
254
254
  expect(tokens.length).to eq(4)
255
255
  expect(tokens.map(&:category)).to eq([:function, :grouping, :numeric, :grouping])
256
- expect(tokens.map(&:value)).to eq([:roundup, :open, BigDecimal.new('8.2'), :close])
256
+ expect(tokens.map(&:value)).to eq([:roundup, :open, BigDecimal('8.2'), :close])
257
257
 
258
258
  tokens = tokenizer.tokenize('RoundDown(8.2)')
259
259
  expect(tokens.length).to eq(4)
260
260
  expect(tokens.map(&:category)).to eq([:function, :grouping, :numeric, :grouping])
261
- expect(tokens.map(&:value)).to eq([:rounddown, :open, BigDecimal.new('8.2'), :close])
261
+ expect(tokens.map(&:value)).to eq([:rounddown, :open, BigDecimal('8.2'), :close])
262
262
  end
263
263
 
264
- it 'include NOT' do
264
+ it 'handles NOT' do
265
265
  tokens = tokenizer.tokenize('not(8 < 5)')
266
266
  expect(tokens.length).to eq(6)
267
267
  expect(tokens.map(&:category)).to eq([:function, :grouping, :numeric, :comparator, :numeric, :grouping])
268
268
  expect(tokens.map(&:value)).to eq([:not, :open, 8, :lt, 5, :close])
269
269
  end
270
270
 
271
+ it 'handles ANY/ALL' do
272
+ %i( any all ).each do |fn|
273
+ tokens = tokenizer.tokenize("#{fn}(users, u, u.age > 18)")
274
+ expect(tokens.length).to eq(10)
275
+ expect(tokens.map { |t| [t.category, t.value] }).to eq([
276
+ [:function, fn ], # function call (any/all)
277
+ [:grouping, :open ], # (
278
+ [:identifier, "users"], # users
279
+ [:grouping, :comma ], # ,
280
+ [:identifier, "u" ], # u
281
+ [:grouping, :comma ], # ,
282
+ [:identifier, "u.age"], # u.age
283
+ [:comparator, :gt ], # >
284
+ [:numeric, 18 ], # 18
285
+ [:grouping, :close ] # )
286
+ ])
287
+ end
288
+ end
289
+
271
290
  it 'handles whitespace after function name' do
272
291
  tokens = tokenizer.tokenize('not (8 < 5)')
273
292
  expect(tokens.length).to eq(6)
@@ -275,7 +294,7 @@ describe Dentaku::Tokenizer do
275
294
  expect(tokens.map(&:value)).to eq([:not, :open, 8, :lt, 5, :close])
276
295
  end
277
296
 
278
- it 'can end with a bang' do
297
+ it 'handles when function ends with a bang' do
279
298
  tokens = tokenizer.tokenize('exp!(5 * 3)')
280
299
  expect(tokens.length).to eq(6)
281
300
  expect(tokens.map(&:category)).to eq([:function, :grouping, :numeric, :operator, :numeric, :grouping])
@@ -298,25 +317,25 @@ describe Dentaku::Tokenizer do
298
317
  it 'replaced with function name' do
299
318
  input = 'rrrrround!(8.2) + minimo(4,6,2)'
300
319
  tokenizer.tokenize(input, aliases: aliases)
301
- expect(tokenizer.replace_aliases(input)).to eq 'round(8.2) + min(4,6,2)'
320
+ expect(tokenizer.replace_aliases(input)).to eq('round(8.2) + min(4,6,2)')
302
321
  end
303
322
 
304
323
  it 'case insensitive' do
305
324
  input = 'MinImO(4,6,2)'
306
325
  tokenizer.tokenize(input, aliases: aliases)
307
- expect(tokenizer.replace_aliases(input)).to eq 'min(4,6,2)'
326
+ expect(tokenizer.replace_aliases(input)).to eq('min(4,6,2)')
308
327
  end
309
328
 
310
329
  it 'replace only whole aliases without word parts' do
311
330
  input = 'maximo(2,minimoooo())' # `minimoooo` doesn't match `minimo`
312
331
  tokenizer.tokenize(input, aliases: aliases)
313
- expect(tokenizer.replace_aliases(input)).to eq 'max(2,minimoooo())'
332
+ expect(tokenizer.replace_aliases(input)).to eq('max(2,minimoooo())')
314
333
  end
315
334
 
316
335
  it 'work with non-latin symbols' do
317
336
  input = '如果(1,2,3)'
318
337
  tokenizer.tokenize(input, aliases: aliases)
319
- expect(tokenizer.replace_aliases(input)).to eq 'if(1,2,3)'
338
+ expect(tokenizer.replace_aliases(input)).to eq('if(1,2,3)')
320
339
  end
321
340
  end
322
341
  end
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.3.0
4
+ version: 3.4.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-12-04 00:00:00.000000000 Z
11
+ date: 2020-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codecov
@@ -156,15 +156,20 @@ files:
156
156
  - lib/dentaku/ast/datetime.rb
157
157
  - lib/dentaku/ast/function.rb
158
158
  - lib/dentaku/ast/function_registry.rb
159
+ - lib/dentaku/ast/functions/all.rb
159
160
  - lib/dentaku/ast/functions/and.rb
161
+ - lib/dentaku/ast/functions/any.rb
160
162
  - lib/dentaku/ast/functions/avg.rb
161
163
  - lib/dentaku/ast/functions/count.rb
164
+ - lib/dentaku/ast/functions/duration.rb
162
165
  - lib/dentaku/ast/functions/if.rb
166
+ - lib/dentaku/ast/functions/map.rb
163
167
  - lib/dentaku/ast/functions/max.rb
164
168
  - lib/dentaku/ast/functions/min.rb
165
169
  - lib/dentaku/ast/functions/mul.rb
166
170
  - lib/dentaku/ast/functions/not.rb
167
171
  - lib/dentaku/ast/functions/or.rb
172
+ - lib/dentaku/ast/functions/pluck.rb
168
173
  - lib/dentaku/ast/functions/round.rb
169
174
  - lib/dentaku/ast/functions/rounddown.rb
170
175
  - lib/dentaku/ast/functions/roundup.rb
@@ -184,6 +189,7 @@ files:
184
189
  - lib/dentaku/ast/string.rb
185
190
  - lib/dentaku/bulk_expression_solver.rb
186
191
  - lib/dentaku/calculator.rb
192
+ - lib/dentaku/date_arithmetic.rb
187
193
  - lib/dentaku/dependency_resolver.rb
188
194
  - lib/dentaku/exceptions.rb
189
195
  - lib/dentaku/flat_hash.rb
@@ -208,6 +214,7 @@ files:
208
214
  - spec/ast/max_spec.rb
209
215
  - spec/ast/min_spec.rb
210
216
  - spec/ast/mul_spec.rb
217
+ - spec/ast/negation_spec.rb
211
218
  - spec/ast/node_spec.rb
212
219
  - spec/ast/numeric_spec.rb
213
220
  - spec/ast/or_spec.rb
@@ -248,8 +255,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
248
255
  - !ruby/object:Gem::Version
249
256
  version: '0'
250
257
  requirements: []
251
- rubyforge_project: dentaku
252
- rubygems_version: 2.7.6
258
+ rubygems_version: 3.1.4
253
259
  signing_key:
254
260
  specification_version: 4
255
261
  summary: A formula language parser and evaluator
@@ -267,6 +273,7 @@ test_files:
267
273
  - spec/ast/max_spec.rb
268
274
  - spec/ast/min_spec.rb
269
275
  - spec/ast/mul_spec.rb
276
+ - spec/ast/negation_spec.rb
270
277
  - spec/ast/node_spec.rb
271
278
  - spec/ast/numeric_spec.rb
272
279
  - spec/ast/or_spec.rb