dentaku 3.3.4 → 3.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -7
- data/.travis.yml +3 -4
- data/CHANGELOG.md +18 -0
- data/dentaku.gemspec +0 -2
- data/lib/dentaku.rb +14 -4
- data/lib/dentaku/ast.rb +4 -0
- data/lib/dentaku/ast/access.rb +3 -1
- data/lib/dentaku/ast/arithmetic.rb +7 -2
- data/lib/dentaku/ast/array.rb +3 -1
- data/lib/dentaku/ast/function.rb +10 -1
- data/lib/dentaku/ast/functions/all.rb +36 -0
- data/lib/dentaku/ast/functions/any.rb +36 -0
- data/lib/dentaku/ast/functions/avg.rb +2 -2
- data/lib/dentaku/ast/functions/map.rb +36 -0
- data/lib/dentaku/ast/functions/mul.rb +3 -2
- data/lib/dentaku/ast/functions/pluck.rb +29 -0
- data/lib/dentaku/ast/functions/round.rb +1 -1
- data/lib/dentaku/ast/functions/rounddown.rb +1 -1
- data/lib/dentaku/ast/functions/roundup.rb +1 -1
- data/lib/dentaku/ast/functions/ruby_math.rb +47 -3
- data/lib/dentaku/ast/functions/string_functions.rb +4 -4
- data/lib/dentaku/ast/functions/sum.rb +3 -2
- data/lib/dentaku/ast/grouping.rb +3 -1
- data/lib/dentaku/ast/identifier.rb +3 -1
- data/lib/dentaku/bulk_expression_solver.rb +36 -25
- data/lib/dentaku/calculator.rb +13 -5
- data/lib/dentaku/date_arithmetic.rb +1 -1
- data/lib/dentaku/exceptions.rb +3 -3
- data/lib/dentaku/flat_hash.rb +7 -0
- data/lib/dentaku/parser.rb +2 -1
- data/lib/dentaku/tokenizer.rb +1 -1
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/arithmetic_spec.rb +19 -5
- data/spec/ast/avg_spec.rb +4 -0
- data/spec/ast/mul_spec.rb +4 -0
- data/spec/ast/negation_spec.rb +18 -2
- data/spec/ast/round_spec.rb +10 -0
- data/spec/ast/rounddown_spec.rb +10 -0
- data/spec/ast/roundup_spec.rb +10 -0
- data/spec/ast/string_functions_spec.rb +35 -0
- data/spec/ast/sum_spec.rb +4 -0
- data/spec/bulk_expression_solver_spec.rb +27 -0
- data/spec/calculator_spec.rb +112 -0
- data/spec/dentaku_spec.rb +14 -8
- data/spec/parser_spec.rb +13 -0
- data/spec/tokenizer_spec.rb +24 -5
- metadata +7 -3
@@ -32,7 +32,7 @@ module Dentaku
|
|
32
32
|
|
33
33
|
def value(context = {})
|
34
34
|
string = @string.value(context).to_s
|
35
|
-
length = @length.value(context)
|
35
|
+
length = Dentaku::AST::Function.numeric(@length.value(context)).to_i
|
36
36
|
negative_argument_failure('LEFT') if length < 0
|
37
37
|
string[0, length]
|
38
38
|
end
|
@@ -54,7 +54,7 @@ module Dentaku
|
|
54
54
|
|
55
55
|
def value(context = {})
|
56
56
|
string = @string.value(context).to_s
|
57
|
-
length = @length.value(context)
|
57
|
+
length = Dentaku::AST::Function.numeric(@length.value(context)).to_i
|
58
58
|
negative_argument_failure('RIGHT') if length < 0
|
59
59
|
string[length * -1, length] || string
|
60
60
|
end
|
@@ -76,9 +76,9 @@ module Dentaku
|
|
76
76
|
|
77
77
|
def value(context = {})
|
78
78
|
string = @string.value(context).to_s
|
79
|
-
offset = @offset.value(context)
|
79
|
+
offset = Dentaku::AST::Function.numeric(@offset.value(context)).to_i
|
80
80
|
negative_argument_failure('MID', 'offset') if offset < 0
|
81
|
-
length = @length.value(context)
|
81
|
+
length = Dentaku::AST::Function.numeric(@length.value(context)).to_i
|
82
82
|
negative_argument_failure('MID') if length < 0
|
83
83
|
string[offset - 1, length].to_s
|
84
84
|
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
require_relative '../function'
|
2
2
|
|
3
3
|
Dentaku::AST::Function.register(:sum, :numeric, ->(*args) {
|
4
|
-
|
4
|
+
flatten_args = args.flatten
|
5
|
+
if flatten_args.empty?
|
5
6
|
raise Dentaku::ArgumentError.for(
|
6
7
|
:too_few_arguments,
|
7
8
|
function_name: 'SUM()', at_least: 1, given: 0
|
8
9
|
), 'SUM() requires at least one argument'
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
+
flatten_args.map { |arg| Dentaku::AST::Function.numeric(arg) }.reduce(0, :+)
|
12
13
|
})
|
data/lib/dentaku/ast/grouping.rb
CHANGED
@@ -53,40 +53,55 @@ module Dentaku
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def expression_with_exception_handler(&block)
|
56
|
-
->(
|
56
|
+
->(_expr, ex) { block.call(ex) }
|
57
57
|
end
|
58
58
|
|
59
59
|
def load_results(&block)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
60
|
+
facts, _formulas = expressions.transform_keys(&:downcase)
|
61
|
+
.transform_values { |v| calculator.ast(v) }
|
62
|
+
.partition { |_, v| calculator.dependencies(v, nil).empty? }
|
63
|
+
|
64
|
+
evaluated_facts = facts.to_h.each_with_object({}) do |(var_name, ast), h|
|
65
|
+
with_rescues(var_name, h, block) do
|
66
|
+
h[var_name] = ast.is_a?(Array) ? ast.map(&:value) : ast.value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context = calculator.memory.merge(evaluated_facts)
|
71
|
+
|
72
|
+
variables_in_resolve_order.each_with_object({}) do |var_name, results|
|
73
|
+
next if expressions[var_name].nil?
|
74
|
+
|
75
|
+
with_rescues(var_name, results, block) do
|
76
|
+
results[var_name] = evaluated_facts[var_name] || calculator.evaluate!(
|
72
77
|
expressions[var_name],
|
73
|
-
|
78
|
+
context.merge(results),
|
74
79
|
&expression_with_exception_handler(&block)
|
75
80
|
)
|
76
|
-
|
77
|
-
r[var_name] = value
|
78
|
-
rescue UnboundVariableError, Dentaku::ZeroDivisionError => ex
|
79
|
-
ex.recipient_variable = var_name
|
80
|
-
r[var_name] = block.call(ex)
|
81
|
-
rescue Dentaku::ArgumentError => ex
|
82
|
-
r[var_name] = block.call(ex)
|
83
81
|
end
|
84
82
|
end
|
83
|
+
|
85
84
|
rescue TSort::Cyclic => ex
|
86
85
|
block.call(ex)
|
87
86
|
{}
|
88
87
|
end
|
89
88
|
|
89
|
+
def with_rescues(var_name, results, block)
|
90
|
+
yield
|
91
|
+
|
92
|
+
rescue UnboundVariableError, Dentaku::ZeroDivisionError => ex
|
93
|
+
ex.recipient_variable = var_name
|
94
|
+
results[var_name] = block.call(ex)
|
95
|
+
|
96
|
+
rescue Dentaku::ArgumentError => ex
|
97
|
+
results[var_name] = block.call(ex)
|
98
|
+
|
99
|
+
ensure
|
100
|
+
if results[var_name] == :undefined && calculator.memory.has_key?(var_name.downcase)
|
101
|
+
results[var_name] = calculator.memory[var_name.downcase]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
90
105
|
def expressions
|
91
106
|
@expressions ||= Hash[expression_hash.map { |k, v| [k.to_s, v] }]
|
92
107
|
end
|
@@ -113,9 +128,5 @@ module Dentaku
|
|
113
128
|
end
|
114
129
|
}
|
115
130
|
end
|
116
|
-
|
117
|
-
def evaluate!(expression, results, &block)
|
118
|
-
calculator.evaluate!(expression, results, &block)
|
119
|
-
end
|
120
131
|
end
|
121
132
|
end
|
data/lib/dentaku/calculator.rb
CHANGED
@@ -62,7 +62,7 @@ module Dentaku
|
|
62
62
|
unbound = node.dependencies - memory.keys
|
63
63
|
unless unbound.empty?
|
64
64
|
raise UnboundVariableError.new(unbound),
|
65
|
-
"no value provided for variables: #{unbound.join(', ')}"
|
65
|
+
"no value provided for variables: #{unbound.uniq.join(', ')}"
|
66
66
|
end
|
67
67
|
node.value(memory)
|
68
68
|
end
|
@@ -77,13 +77,21 @@ module Dentaku
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def dependencies(expression, context = {})
|
80
|
-
|
81
|
-
|
80
|
+
test_context = context.nil? ? {} : store(context) { memory }
|
81
|
+
|
82
|
+
case expression
|
83
|
+
when Dentaku::AST::Node
|
84
|
+
expression.dependencies(test_context)
|
85
|
+
when Array
|
86
|
+
expression.flat_map { |e| dependencies(e, context) }
|
87
|
+
else
|
88
|
+
ast(expression).dependencies(test_context)
|
82
89
|
end
|
83
|
-
store(context) { ast(expression).dependencies(memory) }
|
84
90
|
end
|
85
91
|
|
86
92
|
def ast(expression)
|
93
|
+
return expression.map { |e| ast(e) } if expression.is_a? Array
|
94
|
+
|
87
95
|
@ast_cache.fetch(expression) {
|
88
96
|
options = {
|
89
97
|
case_sensitive: case_sensitive,
|
@@ -119,7 +127,7 @@ module Dentaku
|
|
119
127
|
restore = Hash[memory]
|
120
128
|
|
121
129
|
if value.nil?
|
122
|
-
key_or_hash = FlatHash.
|
130
|
+
key_or_hash = FlatHash.from_hash_with_intermediates(key_or_hash) if nested_data_support
|
123
131
|
key_or_hash.each do |key, val|
|
124
132
|
memory[standardize_case(key.to_s)] = val
|
125
133
|
end
|
data/lib/dentaku/exceptions.rb
CHANGED
@@ -44,7 +44,7 @@ module Dentaku
|
|
44
44
|
raise ::ArgumentError, "Unhandled #{reason}"
|
45
45
|
end
|
46
46
|
|
47
|
-
new
|
47
|
+
new(reason, **meta)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -68,7 +68,7 @@ module Dentaku
|
|
68
68
|
raise ::ArgumentError, "Unhandled #{reason}"
|
69
69
|
end
|
70
70
|
|
71
|
-
new
|
71
|
+
new(reason, **meta)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -92,7 +92,7 @@ module Dentaku
|
|
92
92
|
raise ::ArgumentError, "Unhandled #{reason}"
|
93
93
|
end
|
94
94
|
|
95
|
-
new
|
95
|
+
new(reason, **meta)
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
data/lib/dentaku/flat_hash.rb
CHANGED
@@ -6,6 +6,13 @@ module Dentaku
|
|
6
6
|
flatten_keys(acc)
|
7
7
|
end
|
8
8
|
|
9
|
+
def self.from_hash_with_intermediates(h, key = [], acc = {})
|
10
|
+
acc.update(key => h) unless key.empty?
|
11
|
+
return unless h.is_a? Hash
|
12
|
+
h.each { |k, v| from_hash_with_intermediates(v, key + [k], acc) }
|
13
|
+
flatten_keys(acc)
|
14
|
+
end
|
15
|
+
|
9
16
|
def self.flatten_keys(hash)
|
10
17
|
hash.each_with_object({}) do |(k, v), h|
|
11
18
|
h[flatten_key(k)] = v
|
data/lib/dentaku/parser.rb
CHANGED
@@ -51,6 +51,7 @@ module Dentaku
|
|
51
51
|
fail! :too_many_operands, operator: operator, expect: max_size, actual: output.length
|
52
52
|
end
|
53
53
|
|
54
|
+
fail! :invalid_statement if output.size < args_size
|
54
55
|
args = Array.new(args_size) { output.pop }.reverse
|
55
56
|
|
56
57
|
output.push operator.new(*args)
|
@@ -335,7 +336,7 @@ module Dentaku
|
|
335
336
|
raise ::ArgumentError, "Unhandled #{reason}"
|
336
337
|
end
|
337
338
|
|
338
|
-
raise ParseError.for(reason, meta), message
|
339
|
+
raise ParseError.for(reason, **meta), message
|
339
340
|
end
|
340
341
|
end
|
341
342
|
end
|
data/lib/dentaku/tokenizer.rb
CHANGED
data/lib/dentaku/version.rb
CHANGED
data/spec/ast/arithmetic_spec.rb
CHANGED
@@ -4,11 +4,12 @@ require 'dentaku/ast/arithmetic'
|
|
4
4
|
require 'dentaku/token'
|
5
5
|
|
6
6
|
describe Dentaku::AST::Arithmetic do
|
7
|
-
let(:one)
|
8
|
-
let(:two)
|
9
|
-
let(:x)
|
10
|
-
let(:y)
|
11
|
-
let(:ctx)
|
7
|
+
let(:one) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 1) }
|
8
|
+
let(:two) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 2) }
|
9
|
+
let(:x) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'x') }
|
10
|
+
let(:y) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'y') }
|
11
|
+
let(:ctx) { {'x' => 1, 'y' => 2} }
|
12
|
+
let(:date) { Dentaku::AST::DateTime.new Dentaku::Token.new(:datetime, DateTime.new(2020, 4, 16)) }
|
12
13
|
|
13
14
|
it 'performs an arithmetic operation with numeric operands' do
|
14
15
|
expect(add(one, two)).to eq(3)
|
@@ -46,6 +47,19 @@ describe Dentaku::AST::Arithmetic do
|
|
46
47
|
expect { add(x, one, 'x' => '') }.to raise_error(Dentaku::ArgumentError)
|
47
48
|
end
|
48
49
|
|
50
|
+
it 'performs arithmetic on arrays' do
|
51
|
+
expect(add(x, y, 'x' => [1], 'y' => [2])).to eq([1, 2])
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'performs date arithmetic' do
|
55
|
+
expect(add(date, one)).to eq(DateTime.new(2020, 4, 17))
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'raises ArgumentError if given individually valid but incompatible arguments' do
|
59
|
+
expect { add(one, date) }.to raise_error(Dentaku::ArgumentError)
|
60
|
+
expect { add(x, one, 'x' => [1]) }.to raise_error(Dentaku::ArgumentError)
|
61
|
+
end
|
62
|
+
|
49
63
|
private
|
50
64
|
|
51
65
|
def add(left, right, context = ctx)
|
data/spec/ast/avg_spec.rb
CHANGED
@@ -29,5 +29,9 @@ describe 'Dentaku::AST::Function::Avg' do
|
|
29
29
|
it 'raises an error if no arguments are passed' do
|
30
30
|
expect { calculator.evaluate!('AVG()') }.to raise_error(Dentaku::ArgumentError)
|
31
31
|
end
|
32
|
+
|
33
|
+
it 'raises an error if an empty array is passed' do
|
34
|
+
expect { calculator.evaluate!('AVG(x)', x: []) }.to raise_error(Dentaku::ArgumentError)
|
35
|
+
end
|
32
36
|
end
|
33
37
|
end
|
data/spec/ast/mul_spec.rb
CHANGED
@@ -34,5 +34,9 @@ describe 'Dentaku::AST::Function::Mul' do
|
|
34
34
|
it 'raises an error if no arguments are passed' do
|
35
35
|
expect { calculator.evaluate!('MUL()') }.to raise_error(Dentaku::ArgumentError)
|
36
36
|
end
|
37
|
+
|
38
|
+
it 'raises an error if an empty array is passed' do
|
39
|
+
expect { calculator.evaluate!('MUL(x)', x: []) }.to raise_error(Dentaku::ArgumentError)
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
data/spec/ast/negation_spec.rb
CHANGED
@@ -4,8 +4,9 @@ require 'dentaku/ast/arithmetic'
|
|
4
4
|
require 'dentaku/token'
|
5
5
|
|
6
6
|
describe Dentaku::AST::Negation do
|
7
|
-
let(:five) { Dentaku::AST::
|
8
|
-
let(:t) { Dentaku::AST::
|
7
|
+
let(:five) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 5) }
|
8
|
+
let(:t) { Dentaku::AST::Logical.new Dentaku::Token.new(:logical, true) }
|
9
|
+
let(:x) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'x') }
|
9
10
|
|
10
11
|
it 'allows access to its sub-node' do
|
11
12
|
node = described_class.new(five)
|
@@ -29,4 +30,19 @@ describe Dentaku::AST::Negation do
|
|
29
30
|
described_class.new(group)
|
30
31
|
}.not_to raise_error
|
31
32
|
end
|
33
|
+
|
34
|
+
it 'correctly parses string operands to numeric values' do
|
35
|
+
node = described_class.new(x)
|
36
|
+
expect(node.value('x' => '5')).to eq(-5)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'raises error if input string is not coercible to numeric' do
|
40
|
+
node = described_class.new(x)
|
41
|
+
expect { node.value('x' => 'invalid') }.to raise_error(Dentaku::ArgumentError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'raises error if given a non-numeric argument' do
|
45
|
+
node = described_class.new(x)
|
46
|
+
expect { node.value('x' => true) }.to raise_error(Dentaku::ArgumentError)
|
47
|
+
end
|
32
48
|
end
|
data/spec/ast/round_spec.rb
CHANGED
@@ -22,4 +22,14 @@ describe 'Dentaku::AST::Function::Round' do
|
|
22
22
|
result = Dentaku('ROUND(x, y)', x: '1.8453', y: nil)
|
23
23
|
expect(result).to eq(2)
|
24
24
|
end
|
25
|
+
|
26
|
+
context 'checking errors' do
|
27
|
+
it 'raises an error if first argument is not numeric' do
|
28
|
+
expect { Dentaku!("ROUND(2020-1-1, 0)") }.to raise_error(Dentaku::ArgumentError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises an error if places is not numeric' do
|
32
|
+
expect { Dentaku!("ROUND(1.8, 2020-1-1)") }.to raise_error(Dentaku::ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
25
35
|
end
|
data/spec/ast/rounddown_spec.rb
CHANGED
@@ -22,4 +22,14 @@ describe 'Dentaku::AST::Function::Round' do
|
|
22
22
|
result = Dentaku('ROUNDDOWN(x, y)', x: '1.8453', y: nil)
|
23
23
|
expect(result).to eq(1)
|
24
24
|
end
|
25
|
+
|
26
|
+
context 'checking errors' do
|
27
|
+
it 'raises an error if first argument is not numeric' do
|
28
|
+
expect { Dentaku!("ROUND(2020-1-1, 0)") }.to raise_error(Dentaku::ArgumentError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises an error if places is not numeric' do
|
32
|
+
expect { Dentaku!("ROUND(1.8, 2020-1-1)") }.to raise_error(Dentaku::ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
25
35
|
end
|
data/spec/ast/roundup_spec.rb
CHANGED
@@ -22,4 +22,14 @@ describe 'Dentaku::AST::Function::Round' do
|
|
22
22
|
result = Dentaku('ROUNDUP(x, y)', x: '1.8453', y: nil)
|
23
23
|
expect(result).to eq(2)
|
24
24
|
end
|
25
|
+
|
26
|
+
context 'checking errors' do
|
27
|
+
it 'raises an error if first argument is not numeric' do
|
28
|
+
expect { Dentaku!("ROUND(2020-1-1, 0)") }.to raise_error(Dentaku::ArgumentError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises an error if places is not numeric' do
|
32
|
+
expect { Dentaku!("ROUND(1.8, 2020-1-1)") }.to raise_error(Dentaku::ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
25
35
|
end
|
@@ -26,6 +26,10 @@ describe Dentaku::AST::StringFunctions::Left do
|
|
26
26
|
expect(subject.value('string' => 'abcdefg', 'length' => 40)).to eq 'abcdefg'
|
27
27
|
end
|
28
28
|
|
29
|
+
it 'accepts strings as length if they can be parsed to a number' do
|
30
|
+
expect(subject.value('string' => 'ABCDEFG', 'length' => '4')).to eq 'ABCD'
|
31
|
+
end
|
32
|
+
|
29
33
|
it 'has the proper type' do
|
30
34
|
expect(subject.type).to eq(:string)
|
31
35
|
end
|
@@ -35,6 +39,12 @@ describe Dentaku::AST::StringFunctions::Left do
|
|
35
39
|
subject.value('string' => 'abcdefg', 'length' => -2)
|
36
40
|
}.to raise_error(Dentaku::ArgumentError, /LEFT\(\) requires length to be positive/)
|
37
41
|
end
|
42
|
+
|
43
|
+
it 'raises an error when given a junk length' do
|
44
|
+
expect {
|
45
|
+
subject.value('string' => 'abcdefg', 'length' => 'junk')
|
46
|
+
}.to raise_error(Dentaku::ArgumentError, "'junk' is not coercible to numeric")
|
47
|
+
end
|
38
48
|
end
|
39
49
|
|
40
50
|
describe Dentaku::AST::StringFunctions::Right do
|
@@ -53,9 +63,19 @@ describe Dentaku::AST::StringFunctions::Right do
|
|
53
63
|
expect(subject.value).to eq 'abcdefg'
|
54
64
|
end
|
55
65
|
|
66
|
+
it 'accepts strings as length if they can be parsed to a number' do
|
67
|
+
subject = described_class.new(literal('ABCDEFG'), literal('4'))
|
68
|
+
expect(subject.value).to eq 'DEFG'
|
69
|
+
end
|
70
|
+
|
56
71
|
it 'has the proper type' do
|
57
72
|
expect(subject.type).to eq(:string)
|
58
73
|
end
|
74
|
+
|
75
|
+
it 'raises an error when given a junk length' do
|
76
|
+
subject = described_class.new(literal('abcdefg'), literal('junk'))
|
77
|
+
expect { subject.value }.to raise_error(Dentaku::ArgumentError, "'junk' is not coercible to numeric")
|
78
|
+
end
|
59
79
|
end
|
60
80
|
|
61
81
|
describe Dentaku::AST::StringFunctions::Mid do
|
@@ -79,9 +99,24 @@ describe Dentaku::AST::StringFunctions::Mid do
|
|
79
99
|
expect(subject.value).to eq 'defg'
|
80
100
|
end
|
81
101
|
|
102
|
+
it 'accepts strings as offset and length if they can be parsed to a number' do
|
103
|
+
subject = described_class.new(literal('ABCDEFG'), literal('4'), literal('2'))
|
104
|
+
expect(subject.value).to eq 'DE'
|
105
|
+
end
|
106
|
+
|
82
107
|
it 'has the proper type' do
|
83
108
|
expect(subject.type).to eq(:string)
|
84
109
|
end
|
110
|
+
|
111
|
+
it 'raises an error when given a junk offset' do
|
112
|
+
subject = described_class.new(literal('abcdefg'), literal('junk offset'), literal(2))
|
113
|
+
expect { subject.value }.to raise_error(Dentaku::ArgumentError, "'junk offset' is not coercible to numeric")
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'raises an error when given a junk length' do
|
117
|
+
subject = described_class.new(literal('abcdefg'), literal(4), literal('junk'))
|
118
|
+
expect { subject.value }.to raise_error(Dentaku::ArgumentError, "'junk' is not coercible to numeric")
|
119
|
+
end
|
85
120
|
end
|
86
121
|
|
87
122
|
describe Dentaku::AST::StringFunctions::Len do
|