Long operator chains: evaluate them without the depth limit
A formula such as =A1+A2+…+A500 nests one level per operator, and the evaluator recursed once per level, so past its depth limit (about 400 operators) the result was #CALC!. Excel allows such formulas up to its 8,192-character limit. Chains of arithmetic, comparison and & operators are now folded in a loop.
This commit is contained in:
parent
fc45b7f6d8
commit
6b6aa82be1
|
|
@ -465,9 +465,22 @@ impl<'h> Evaluator<'h> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
let x = self.value(a);
|
||||
let y = self.value(b);
|
||||
Ev::V(binary_values(op, &x, &y))
|
||||
// A long chain such as A1+A2+…+A500 nests to the left: fold it in a loop instead
|
||||
// of one level of recursion per operator (past the depth limit it was #CALC!).
|
||||
let mut rest = vec![(op, b)];
|
||||
let mut left = a;
|
||||
while let Expr::Binary(o, l, r) = left
|
||||
&& !matches!(o, BinOp::Range | BinOp::Union | BinOp::Intersect)
|
||||
{
|
||||
rest.push((*o, r.as_ref()));
|
||||
left = l.as_ref();
|
||||
}
|
||||
let mut acc = self.value(left);
|
||||
while let Some((o, r)) = rest.pop() {
|
||||
let y = self.value(r);
|
||||
acc = binary_values(o, &acc, &y);
|
||||
}
|
||||
Ev::V(acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -549,6 +549,17 @@ fn text_function() {
|
|||
assert_eq!(t.get("A1"), Value::from("1,234.50"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_operator_chains() {
|
||||
let mut t = T::new();
|
||||
t.set("A1", "1");
|
||||
// 1,000 additions: a formula of 3,000 characters, well within Excel's 8,192.
|
||||
t.set("B1", &format!("={}", vec!["A1"; 1000].join("+")));
|
||||
assert_eq!(t.num("B1"), 1000.0);
|
||||
t.set("B2", &format!("=5{}", "+1-1".repeat(600)));
|
||||
assert_eq!(t.num("B2"), 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtotal_skips_hidden() {
|
||||
let mut t = T::new();
|
||||
|
|
|
|||
Loading…
Reference in New Issue