diff --git a/crates/calc/src/eval.rs b/crates/calc/src/eval.rs index c4c9746..9141691 100644 --- a/crates/calc/src/eval.rs +++ b/crates/calc/src/eval.rs @@ -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) } } } diff --git a/crates/calc/src/tests.rs b/crates/calc/src/tests.rs index b7a5850..e8ec305 100644 --- a/crates/calc/src/tests.rs +++ b/crates/calc/src/tests.rs @@ -596,6 +596,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();