dentaku 2.0.9 → 2.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +6 -0
- data/lib/dentaku/ast/case.rb +2 -3
- data/lib/dentaku/ast/function.rb +1 -1
- data/lib/dentaku/ast/functions/string_functions.rb +21 -22
- data/lib/dentaku/calculator.rb +2 -2
- data/lib/dentaku/parser.rb +14 -5
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/case_spec.rb +6 -2
- data/spec/ast/function_spec.rb +6 -0
- data/spec/calculator_spec.rb +34 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2b19bb69372e3700ffb09ca14b5f3fbcb630b06
|
4
|
+
data.tar.gz: 4a67960044c94875d8a0ab2487e9464c820af5ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 017f2d92d2b8110151d51ea2c93abe8964a82a662b40ce1f38a8133b75dea64b51ba774f64d06ffb24353024046eeb59457d239527560a57eeb0671b6dff8cc1
|
7
|
+
data.tar.gz: cd39bb1acee73e4b05d65940310749601bf758c8797c2cca5ed4e45f6850b59a0637b132a8c4eafd0b60ac52a8714ad5b6933ca38fe3dcc0e60ef9588d2565c0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v2.0.10] 2016-12-30
|
4
|
+
- fix string function initialization bug
|
5
|
+
- fix issues with CASE statements
|
6
|
+
- allow injecting AST cache
|
7
|
+
|
3
8
|
## [v2.0.9] 2016-09-19
|
4
9
|
- namespace tokenization errors
|
5
10
|
- automatically coerce arguments to string functions as strings
|
@@ -111,6 +116,7 @@
|
|
111
116
|
## [v0.1.0] 2012-01-20
|
112
117
|
- initial release
|
113
118
|
|
119
|
+
[HEAD]: https://github.com/rubysolo/dentaku/compare/v2.0.9...HEAD
|
114
120
|
[v2.0.9]: https://github.com/rubysolo/dentaku/compare/v2.0.8...v2.0.9
|
115
121
|
[v2.0.8]: https://github.com/rubysolo/dentaku/compare/v2.0.7...v2.0.8
|
116
122
|
[v2.0.7]: https://github.com/rubysolo/dentaku/compare/v2.0.6...v2.0.7
|
data/lib/dentaku/ast/case.rb
CHANGED
@@ -43,9 +43,8 @@ module Dentaku
|
|
43
43
|
def dependencies(context={})
|
44
44
|
# TODO: should short-circuit
|
45
45
|
@switch.dependencies(context) +
|
46
|
-
@conditions.flat_map
|
47
|
-
|
48
|
-
end
|
46
|
+
@conditions.flat_map { |condition| condition.dependencies(context) } +
|
47
|
+
@else.dependencies(context)
|
49
48
|
end
|
50
49
|
end
|
51
50
|
end
|
data/lib/dentaku/ast/function.rb
CHANGED
@@ -46,7 +46,7 @@ module Dentaku
|
|
46
46
|
end
|
47
47
|
|
48
48
|
function_class = name.to_s.capitalize
|
49
|
-
Dentaku::AST.send(:remove_const, function_class) if Dentaku::AST.const_defined?(function_class)
|
49
|
+
Dentaku::AST.send(:remove_const, function_class) if Dentaku::AST.const_defined?(function_class, false)
|
50
50
|
Dentaku::AST.const_set(function_class, function)
|
51
51
|
|
52
52
|
function.implementation = implementation
|
@@ -4,9 +4,9 @@ module Dentaku
|
|
4
4
|
module AST
|
5
5
|
module StringFunctions
|
6
6
|
class Left < Function
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
@length =
|
7
|
+
def initialize(*args)
|
8
|
+
super
|
9
|
+
@string, @length = *@args
|
10
10
|
end
|
11
11
|
|
12
12
|
def value(context={})
|
@@ -17,9 +17,9 @@ module Dentaku
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class Right < Function
|
20
|
-
def initialize(
|
21
|
-
|
22
|
-
@length =
|
20
|
+
def initialize(*args)
|
21
|
+
super
|
22
|
+
@string, @length = *@args
|
23
23
|
end
|
24
24
|
|
25
25
|
def value(context={})
|
@@ -30,10 +30,9 @@ module Dentaku
|
|
30
30
|
end
|
31
31
|
|
32
32
|
class Mid < Function
|
33
|
-
def initialize(
|
34
|
-
|
35
|
-
@offset =
|
36
|
-
@length = length
|
33
|
+
def initialize(*args)
|
34
|
+
super
|
35
|
+
@string, @offset, @length = *@args
|
37
36
|
end
|
38
37
|
|
39
38
|
def value(context={})
|
@@ -45,8 +44,9 @@ module Dentaku
|
|
45
44
|
end
|
46
45
|
|
47
46
|
class Len < Function
|
48
|
-
def initialize(
|
49
|
-
|
47
|
+
def initialize(*args)
|
48
|
+
super
|
49
|
+
@string = @args[0]
|
50
50
|
end
|
51
51
|
|
52
52
|
def value(context={})
|
@@ -56,9 +56,9 @@ module Dentaku
|
|
56
56
|
end
|
57
57
|
|
58
58
|
class Find < Function
|
59
|
-
def initialize(
|
60
|
-
|
61
|
-
@haystack =
|
59
|
+
def initialize(*args)
|
60
|
+
super
|
61
|
+
@needle, @haystack = *@args
|
62
62
|
end
|
63
63
|
|
64
64
|
def value(context={})
|
@@ -71,10 +71,9 @@ module Dentaku
|
|
71
71
|
end
|
72
72
|
|
73
73
|
class Substitute < Function
|
74
|
-
def initialize(
|
75
|
-
|
76
|
-
@search =
|
77
|
-
@replacement = replacement
|
74
|
+
def initialize(*args)
|
75
|
+
super
|
76
|
+
@original, @search, @replacement = *@args
|
78
77
|
end
|
79
78
|
|
80
79
|
def value(context={})
|
@@ -87,9 +86,9 @@ module Dentaku
|
|
87
86
|
end
|
88
87
|
|
89
88
|
class Concat < Function
|
90
|
-
def initialize(
|
91
|
-
|
92
|
-
@right =
|
89
|
+
def initialize(*args)
|
90
|
+
super
|
91
|
+
@left, @right = *@args
|
93
92
|
end
|
94
93
|
|
95
94
|
def value(context={})
|
data/lib/dentaku/calculator.rb
CHANGED
@@ -8,10 +8,10 @@ module Dentaku
|
|
8
8
|
class Calculator
|
9
9
|
attr_reader :result, :memory, :tokenizer
|
10
10
|
|
11
|
-
def initialize
|
11
|
+
def initialize(ast_cache={})
|
12
12
|
clear
|
13
13
|
@tokenizer = Tokenizer.new
|
14
|
-
@ast_cache =
|
14
|
+
@ast_cache = ast_cache
|
15
15
|
@disable_ast_cache = false
|
16
16
|
end
|
17
17
|
|
data/lib/dentaku/parser.rb
CHANGED
@@ -67,15 +67,24 @@ module Dentaku
|
|
67
67
|
# special handling for case nesting: strip out inner case
|
68
68
|
# statements and parse their AST segments recursively
|
69
69
|
if operations.include?(AST::Case)
|
70
|
-
|
71
|
-
|
70
|
+
open_cases = 0
|
71
|
+
case_end_index = nil
|
72
|
+
|
72
73
|
input.each_with_index do |token, index|
|
73
|
-
|
74
|
+
if token.category == :case && token.value == :open
|
75
|
+
open_cases += 1
|
76
|
+
end
|
77
|
+
|
74
78
|
if token.category == :case && token.value == :close
|
75
|
-
|
79
|
+
if open_cases > 0
|
80
|
+
open_cases -= 1
|
81
|
+
else
|
82
|
+
case_end_index = index
|
83
|
+
break
|
84
|
+
end
|
76
85
|
end
|
77
86
|
end
|
78
|
-
inner_case_inputs = input.slice!(0..
|
87
|
+
inner_case_inputs = input.slice!(0..case_end_index)
|
79
88
|
subparser = Parser.new(
|
80
89
|
inner_case_inputs,
|
81
90
|
operations: [AST::Case],
|
data/lib/dentaku/version.rb
CHANGED
data/spec/ast/case_spec.rb
CHANGED
@@ -67,14 +67,18 @@ describe Dentaku::AST::Case do
|
|
67
67
|
let!(:tax) do
|
68
68
|
Dentaku::AST::Identifier.new(Dentaku::Token.new(:identifier, :tax))
|
69
69
|
end
|
70
|
+
let!(:fallback) do
|
71
|
+
Dentaku::AST::Identifier.new(Dentaku::Token.new(:identifier, :fallback))
|
72
|
+
end
|
70
73
|
let!(:addition) { Dentaku::AST::Addition.new(two, tax) }
|
71
74
|
let!(:when2) { Dentaku::AST::CaseWhen.new(banana) }
|
72
75
|
let!(:then2) { Dentaku::AST::CaseThen.new(addition) }
|
76
|
+
let!(:else2) { Dentaku::AST::CaseElse.new(fallback) }
|
73
77
|
let!(:conditional2) { Dentaku::AST::CaseConditional.new(when2, then2) }
|
74
78
|
|
75
79
|
it 'gathers dependencies from switch and conditionals' do
|
76
|
-
node = described_class.new(switch, conditional1, conditional2)
|
77
|
-
expect(node.dependencies).to eq([:fruit, :tax])
|
80
|
+
node = described_class.new(switch, conditional1, conditional2, else2)
|
81
|
+
expect(node.dependencies).to eq([:fruit, :tax, :fallback])
|
78
82
|
end
|
79
83
|
end
|
80
84
|
end
|
data/spec/ast/function_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'dentaku/ast/function'
|
3
3
|
|
4
|
+
class Clazz; end
|
5
|
+
|
4
6
|
describe Dentaku::AST::Function do
|
5
7
|
it 'maintains a function registry' do
|
6
8
|
expect(described_class).to respond_to(:get)
|
@@ -18,4 +20,8 @@ describe Dentaku::AST::Function do
|
|
18
20
|
function = described_class.get("flarble").new
|
19
21
|
expect(function.value).to eq "flarble"
|
20
22
|
end
|
23
|
+
|
24
|
+
it 'does not throw an error when registering a function with a name that matches a currently defined constant' do
|
25
|
+
expect { described_class.register("clazz", :string, -> { "clazzified" }) }.not_to raise_error
|
26
|
+
end
|
21
27
|
end
|
data/spec/calculator_spec.rb
CHANGED
@@ -66,6 +66,12 @@ describe Dentaku::Calculator do
|
|
66
66
|
expect(calculator.dependencies("bob + dole / 3")).to eq(['bob', 'dole'])
|
67
67
|
end
|
68
68
|
|
69
|
+
it "finds dependencies in formula arguments" do
|
70
|
+
allow(Dentaku).to receive(:cache_ast?) { true }
|
71
|
+
|
72
|
+
expect(calculator.dependencies("CONCAT(bob, dole)")).to eq(['bob', 'dole'])
|
73
|
+
end
|
74
|
+
|
69
75
|
it "doesn't consider variables in memory as dependencies" do
|
70
76
|
expect(with_memory.dependencies("apples + oranges")).to eq(['oranges'])
|
71
77
|
end
|
@@ -379,6 +385,34 @@ describe Dentaku::Calculator do
|
|
379
385
|
fruit: 'banana')
|
380
386
|
expect(value).to eq(5)
|
381
387
|
end
|
388
|
+
|
389
|
+
it 'handles multiple nested case statements' do
|
390
|
+
formula = <<-FORMULA
|
391
|
+
CASE fruit
|
392
|
+
WHEN 'apple'
|
393
|
+
THEN
|
394
|
+
CASE quantity
|
395
|
+
WHEN 2 THEN 3
|
396
|
+
END
|
397
|
+
WHEN 'banana'
|
398
|
+
THEN
|
399
|
+
CASE quantity
|
400
|
+
WHEN 1 THEN 2
|
401
|
+
END
|
402
|
+
END
|
403
|
+
FORMULA
|
404
|
+
value = calculator.evaluate(
|
405
|
+
formula,
|
406
|
+
quantity: 1,
|
407
|
+
fruit: 'banana')
|
408
|
+
expect(value).to eq(2)
|
409
|
+
|
410
|
+
value = calculator.evaluate(
|
411
|
+
formula,
|
412
|
+
quantity: 2,
|
413
|
+
fruit: 'apple')
|
414
|
+
expect(value).to eq(3)
|
415
|
+
end
|
382
416
|
end
|
383
417
|
|
384
418
|
describe 'math functions' do
|
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: 2.0.
|
4
|
+
version: 2.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Solomon White
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|