dentaku 4.0.1 → 4.0.2
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/dentaku/ast/arithmetic.rb +22 -1
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/arithmetic_spec.rb +15 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 498d10748b47ba3ab1930e2d3df6685d8d557e1e4f7b95a0887952507557325a
|
|
4
|
+
data.tar.gz: a47a69280b8c298a299edbba9758b0dc132fdcceecb055eaa4008c0427d785de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0466598da5b7018075787a018873cc72f4185faa40eabe3596b1666646625d70d29c199a8ac91d5fe0c4436d5b960f5cc9c95efbfe6ff2ea91a4855928074288'
|
|
7
|
+
data.tar.gz: '0440877441b431ffb55da23343fe1fff2271afd6ef4868f86a860927ba26f6e37ffa586514dee83a3c018cea4506615377e092ead2135b1cf39e440006a3a933'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [v4.0.2] 2026-08-02
|
|
4
|
+
- the 4.0.1 guard against arithmetic that rejects a well-typed operand only
|
|
5
|
+
worked on Ruby 3.4+. Ruby 3.2 and 3.3 do not raise for an oversized
|
|
6
|
+
exponent: `Integer#**` warns and returns `Float::INFINITY`, so there was
|
|
7
|
+
nothing to rescue and `Infinity` still leaked out of the non-bang
|
|
8
|
+
`Calculator#evaluate` (#332). Arithmetic now also rejects a non-finite
|
|
9
|
+
result computed from finite operands, so `evaluate` returns `nil` and
|
|
10
|
+
`evaluate!` raises `Dentaku::ArgumentError` on every supported Ruby.
|
|
11
|
+
Operands that are already infinite are passed through unchanged.
|
|
12
|
+
|
|
3
13
|
## [v4.0.1] 2026-08-02
|
|
4
14
|
- parsing no longer executes user functions. The three parse-time operand
|
|
5
15
|
validators (`Arithmetic`, `Negation`, and the logical combinators) asked
|
|
@@ -38,7 +38,7 @@ module Dentaku
|
|
|
38
38
|
l = cast(left_value)
|
|
39
39
|
r = cast(right_value)
|
|
40
40
|
|
|
41
|
-
begin
|
|
41
|
+
result = begin
|
|
42
42
|
l.public_send(operator, r)
|
|
43
43
|
rescue ::TypeError => e
|
|
44
44
|
# Right cannot be converted to a suitable type for left. e.g. [] + 1
|
|
@@ -53,6 +53,27 @@ module Dentaku
|
|
|
53
53
|
# promises to return nil rather than raise.
|
|
54
54
|
raise Dentaku::ArgumentError.for(:invalid_value, actual: r), e.message
|
|
55
55
|
end
|
|
56
|
+
|
|
57
|
+
validate_result(result, l, r)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Not every Ruby rejects an operation it cannot carry out. Before 3.4,
|
|
61
|
+
# `Integer#**` with an oversized exponent warns and returns
|
|
62
|
+
# Float::INFINITY where 3.4+ raises ArgumentError, and overflowing float
|
|
63
|
+
# arithmetic is silently infinite on every version. Finite operands that
|
|
64
|
+
# produce a non-finite result mean the operation overflowed, so treat it
|
|
65
|
+
# as the same rejection newer Rubies raise -- otherwise Infinity leaks
|
|
66
|
+
# out of Calculator#evaluate, which promises nil on failure (#332).
|
|
67
|
+
def validate_result(result, l, r)
|
|
68
|
+
return result unless nonfinite?(result)
|
|
69
|
+
return result if nonfinite?(l) || nonfinite?(r)
|
|
70
|
+
|
|
71
|
+
raise Dentaku::ArgumentError.for(:invalid_value, actual: r),
|
|
72
|
+
"Result of #{l} #{operator} #{r} is not a finite number"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def nonfinite?(val)
|
|
76
|
+
val.is_a?(::Numeric) && val.respond_to?(:finite?) && !val.finite?
|
|
56
77
|
end
|
|
57
78
|
|
|
58
79
|
def cast(val)
|
data/lib/dentaku/version.rb
CHANGED
data/spec/ast/arithmetic_spec.rb
CHANGED
|
@@ -177,9 +177,12 @@ describe Dentaku::AST::Arithmetic do
|
|
|
177
177
|
let(:oversized) { "999999999999999 ^ 99999999999999" }
|
|
178
178
|
|
|
179
179
|
it 'wraps the raw Ruby error so it is a Dentaku::Error' do
|
|
180
|
+
# Ruby 3.4+ raises ArgumentError('exponent is too large') and we pass its
|
|
181
|
+
# message through; older Rubies only warn and return Infinity, which the
|
|
182
|
+
# non-finite result guard turns into the equivalent Dentaku error.
|
|
180
183
|
expect {
|
|
181
184
|
Dentaku::Calculator.new.evaluate!(oversized)
|
|
182
|
-
}.to raise_error(Dentaku::ArgumentError, /exponent is too large/)
|
|
185
|
+
}.to raise_error(Dentaku::ArgumentError, /exponent is too large|is not a finite number/)
|
|
183
186
|
end
|
|
184
187
|
|
|
185
188
|
it 'is rescuable as Dentaku::Error' do
|
|
@@ -209,6 +212,17 @@ describe Dentaku::AST::Arithmetic do
|
|
|
209
212
|
expect(Dentaku::Calculator.new.evaluate!("2 ^ 10")).to eq(1024)
|
|
210
213
|
end
|
|
211
214
|
|
|
215
|
+
it 'still evaluates large results that BigDecimal can represent' do
|
|
216
|
+
expect(Dentaku::Calculator.new.evaluate!("1e308 * 10")).to eq(BigDecimal("1e309"))
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'leaves arithmetic alone when an operand is already infinite' do
|
|
220
|
+
calculator = Dentaku::Calculator.new
|
|
221
|
+
|
|
222
|
+
expect(calculator.evaluate!("a + 1", a: Float::INFINITY)).to be_infinite
|
|
223
|
+
expect(calculator.evaluate!("a * 2", a: Float::INFINITY)).to be_infinite
|
|
224
|
+
end
|
|
225
|
+
|
|
212
226
|
it 'does not swallow Dentaku errors raised by the operation itself' do
|
|
213
227
|
expect {
|
|
214
228
|
Dentaku::Calculator.new.evaluate!("1 / 0")
|