1use std::sync::Arc;
8
9use crate::base::{Array, CellError, CellRef, MAX_COLS, MAX_ROWS, RangeRef, Value, compare};
10use crate::formula::{BinOp, Expr, Reference, SheetSel, StructItem, StructRef, UnOp};
11use crate::functions::{Arg, Ctx};
12use crate::model::Workbook;
13
14mod array;
15mod call;
16mod format;
17mod lambda;
18mod reference_fns;
19mod special;
20
21use array::map_unary;
22pub use array::{binary_values, zip2};
23pub use format::r1c1_to_a1;
24pub use special::{SPECIAL_FUNCTIONS, is_known_function, precedents, resized_sum_range};
25
26pub const MAX_CELLS: u64 = 16_000_000;
28const FULL_SIZE_CELLS: u64 = 16_384;
30const FULL_SIZE_BOUNDED_CELLS: u64 = 1_048_576;
36const MAX_DEPTH: usize = 400;
37const LAMBDA_FUEL: u64 = MAX_CELLS;
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
44pub struct Area {
45 pub sheet: usize,
46 pub range: RangeRef,
47}
48
49#[derive(Clone, Debug)]
50pub struct Lambda {
51 pub params: Vec<String>,
52 pub body: Expr,
53 pub env: Vec<(String, Ev)>,
54}
55
56#[derive(Clone, Debug)]
57pub enum Ev {
58 V(Value),
59 R(Vec<Area>),
60 L(Arc<Lambda>),
61}
62
63impl From<Value> for Ev {
64 fn from(v: Value) -> Self {
65 Ev::V(v)
66 }
67}
68
69fn err(e: CellError) -> Ev {
70 Ev::V(Value::Error(e))
71}
72
73const MAX_LAMBDA_CALLS: usize = 140;
77
78const STACK_BUDGET: usize = 896 * 1024;
83
84#[inline(always)]
86pub(crate) fn stack_position() -> usize {
87 let marker = 0u8;
88 std::hint::black_box(&marker) as *const u8 as usize
89}
90
91pub trait Host {
93 fn workbook(&self) -> &Workbook;
94 fn cell_value(&mut self, sheet: usize, c: CellRef) -> Value;
96 fn now_serial(&self) -> f64;
97 fn random(&mut self) -> f64;
98 fn note_volatile(&mut self) {}
100 fn spill_range(&mut self, sheet: usize, anchor: CellRef) -> Option<RangeRef> {
102 self.workbook().sheet(sheet)?.spill_ranges.get(&anchor).copied()
103 }
104 fn range_values(&mut self, sheet: usize, range: RangeRef) -> Vec<Value> {
106 range.iter().map(|c| self.cell_value(sheet, c)).collect()
107 }
108 fn used_range(&mut self, sheet: usize) -> Option<RangeRef> {
111 self.workbook().sheet(sheet).and_then(|s| s.used_range())
112 }
113 fn visit_range(&mut self, _sheet: usize, _range: RangeRef, _f: &mut dyn FnMut(&Value) -> bool) -> bool {
116 false
117 }
118 fn stack_top(&self) -> Option<usize> {
121 None
122 }
123 fn stack_exhausted(&mut self) {}
125}
126
127pub struct Evaluator<'h> {
128 pub host: &'h mut dyn Host,
129 pub sheet: usize,
130 pub at: CellRef,
131 env: Vec<(String, Ev)>,
132 depth: usize,
133 calls: usize,
135 stack_top: usize,
137 fuel: u64,
142}
143
144struct FnCtx<'a, 'h> {
145 ev: &'a mut Evaluator<'h>,
146 criteria: Option<Area>,
148 lambda: Option<Arc<Lambda>>,
150}
151
152impl Ctx for FnCtx<'_, '_> {
153 fn date_system(&self) -> crate::base::DateSystem {
154 self.ev.host.workbook().date_system
155 }
156 fn now_serial(&self) -> f64 {
157 self.ev.host.now_serial()
158 }
159 fn random(&mut self) -> f64 {
160 self.ev.host.random()
161 }
162 fn computed_criterion(&mut self, row: usize, col: usize, db: &Array, record: usize) -> Option<bool> {
169 let area = self.criteria?;
170 let at = area.range.start.offset(row as i64, col as i64).filter(|c| area.range.contains(*c))?;
171 let expr = self.ev.host.workbook().sheet(area.sheet)?.cell(at)?.formula.as_ref()?.expr_arc()?;
172 let shifted = crate::formula::adjust::shift_relative((*expr).clone(), record as i64 - 1, 0);
173 let env = (0..db.cols)
174 .filter_map(|c| match db.get(0, c) {
175 Some(Value::Text(t)) if !t.trim().is_empty() => {
176 Some((t.trim().to_string(), Ev::V(db.get(record, c).cloned().unwrap_or(Value::Empty))))
177 }
178 _ => None,
179 })
180 .collect();
181 let mut ev = Evaluator {
182 host: &mut *self.ev.host,
183 sheet: area.sheet,
184 at,
185 env,
186 depth: self.ev.depth,
187 calls: self.ev.calls,
188 stack_top: self.ev.stack_top,
189 fuel: self.ev.fuel,
190 };
191 let v = matches!(ev.value(&shifted).scalar().to_bool(), Ok(true));
192 self.ev.fuel = ev.fuel;
194 Some(v)
195 }
196 fn has_lambda(&self) -> bool {
197 self.lambda.is_some()
198 }
199 fn call_lambda(&mut self, arg: Value) -> Option<Value> {
201 let l = self.lambda.clone()?;
202 let [p] = l.params.as_slice() else { return Some(Value::Error(CellError::Value)) };
203 let r = self.ev.call_lambda_values(&l, vec![(p.clone(), Ev::V(arg))]);
204 Some(match self.ev.deref(r) {
205 Value::Array(a) if a.data.len() != 1 => Value::Error(CellError::Calc),
206 v => v.scalar(),
207 })
208 }
209}
210
211impl<'h> Evaluator<'h> {
212 pub fn new(host: &'h mut dyn Host, sheet: usize, at: CellRef) -> Self {
213 Evaluator { host, sheet, at, env: Vec::new(), depth: 0, calls: 0, stack_top: 0, fuel: LAMBDA_FUEL }
214 }
215
216 pub fn value(&mut self, e: &Expr) -> Value {
218 let ev = self.eval(e);
219 self.deref(ev)
220 }
221
222 fn resolve_sheet(&self, s: &SheetSel) -> Result<Vec<usize>, CellError> {
223 let wb = self.host.workbook();
224 match s {
225 SheetSel::Current => Ok(vec![self.sheet]),
226 SheetSel::Named(n) => wb.sheet_index(n).map(|i| vec![i]).ok_or(CellError::Ref),
227 SheetSel::Span(a, b) => {
228 let (i, j) = (wb.sheet_index(a).ok_or(CellError::Ref)?, wb.sheet_index(b).ok_or(CellError::Ref)?);
229 Ok((i.min(j)..=i.max(j)).collect())
230 }
231 }
232 }
233
234 pub fn reference_areas(&self, r: &Reference) -> Result<Vec<Area>, CellError> {
235 let range = r.range();
236 Ok(self.resolve_sheet(&r.sheet)?.into_iter().map(|sheet| Area { sheet, range }).collect())
237 }
238
239 pub fn deref(&mut self, ev: Ev) -> Value {
247 match ev {
248 Ev::V(v) => v,
249 Ev::L(_) => Value::Error(CellError::Calc),
250 Ev::R(areas) => match areas.as_slice() {
251 [a] => self.area_value(a),
252 _ => Value::Error(CellError::Value),
253 },
254 }
255 }
256
257 pub fn area_value(&mut self, a: &Area) -> Value {
258 if a.range.is_single() {
259 return self.host.cell_value(a.sheet, a.range.start);
260 }
261 let cells = u64::from(a.range.height()) * u64::from(a.range.width());
264 let to_edge = a.range.end.row == MAX_ROWS - 1 || a.range.end.col == MAX_COLS - 1;
267 let full_size = cells <= FULL_SIZE_CELLS || (!to_edge && cells <= FULL_SIZE_BOUNDED_CELLS);
268 let range = if full_size { a.range } else { self.trim(a) };
269 let (h, w) = (range.height() as usize, range.width() as usize);
270 if (h as u64) * (w as u64) > MAX_CELLS {
271 return Value::Error(CellError::Num);
272 }
273 let data = self.host.range_values(a.sheet, range);
274 Array::new(h, w, data).map(Value::from).unwrap_or(Value::Error(CellError::Value))
275 }
276
277 pub fn trim(&mut self, a: &Area) -> RangeRef {
280 if self.host.workbook().sheet(a.sheet).is_none() {
281 return a.range;
282 }
283 let used = self.host.used_range(a.sheet);
284 let (mut r1, mut c1) = (a.range.end.row, a.range.end.col);
285 match used {
286 Some(u) => {
287 r1 = r1.min(u.end.row.max(a.range.start.row));
288 c1 = c1.min(u.end.col.max(a.range.start.col));
289 }
290 None => {
291 r1 = a.range.start.row;
292 c1 = a.range.start.col;
293 }
294 }
295 RangeRef::new(a.range.start, CellRef::new(r1, c1))
296 }
297
298 pub fn eval(&mut self, e: &Expr) -> Ev {
299 let here = stack_position();
300 if self.depth == 0 {
301 self.stack_top = self.host.stack_top().unwrap_or(here);
302 }
303 if self.stack_top.abs_diff(here) > STACK_BUDGET {
304 self.host.stack_exhausted();
305 return err(CellError::Num);
306 }
307 self.depth += 1;
308 if self.depth > MAX_DEPTH {
309 self.depth -= 1;
310 return err(CellError::Num);
311 }
312 let r = self.eval_inner(e);
313 self.depth -= 1;
314 r
315 }
316
317 fn eval_inner(&mut self, e: &Expr) -> Ev {
318 match e {
319 Expr::Number(n) => Ev::V(Value::number(*n)),
320 Expr::Text(t) => Ev::V(Value::Text(t.clone())),
321 Expr::Bool(b) => Ev::V(Value::Bool(*b)),
322 Expr::Error(x) => err(*x),
323 Expr::Missing => Ev::V(Value::Empty),
324 Expr::Paren(x) => self.eval(x),
325 Expr::Array(rows) => {
326 let h = rows.len();
327 let w = rows.first().map_or(0, Vec::len);
328 if rows.iter().any(|row| row.len() != w) {
331 return err(CellError::Value);
332 }
333 let mut data = Vec::with_capacity(h * w);
334 for row in rows {
335 for el in row {
336 let v = self.value(el);
337 data.push(v);
338 }
339 }
340 Ev::V(Array::new(h, w, data).map(Value::from).unwrap_or(Value::Error(CellError::Value)))
341 }
342 Expr::Ref(r) => match self.reference_areas(r) {
343 Ok(a) => Ev::R(a),
344 Err(x) => err(x),
345 },
346 Expr::Name(n) => self.name(n),
347 Expr::Struct(s) => self.structured(s),
348 Expr::Unary(op, x) => self.unary(*op, x),
349 Expr::Binary(op, a, b) => self.binary(*op, a, b),
350 Expr::Call(name, args) => self.call(name, args),
351 Expr::Invoke(callee, args) => {
352 let f = self.eval(callee);
353 match f {
354 Ev::L(l) => self.apply_lambda(&l, args),
355 Ev::V(Value::Error(x)) => err(x),
356 _ => err(CellError::Value),
357 }
358 }
359 }
360 }
361
362 fn name(&mut self, n: &str) -> Ev {
363 if let Some((_, v)) = self.env.iter().rev().find(|(k, _)| crate::formula::same_name(k, n)) {
364 return v.clone();
365 }
366 let wb = self.host.workbook();
367 let (scope_sheet, bare) = match n.rsplit_once('!') {
370 Some((s, b)) => match wb.sheet_index(s) {
371 Some(i) => (i, b),
372 None => return err(CellError::Name),
373 },
374 None => (self.sheet, n),
375 };
376 if let Some(def) = wb.name(bare, scope_sheet) {
377 let text = def.formula.clone();
378 let scope = def.scope.unwrap_or(self.sheet);
379 return match crate::formula::parse_name(&text) {
380 Ok(expr) => {
381 let expr = if crate::formula::adjust::has_relative_refs(&expr) {
384 crate::formula::adjust::shift_relative_wrapping(expr, self.at.row as i64, self.at.col as i64)
385 } else {
386 expr
387 };
388 let saved = self.sheet;
389 self.sheet = scope;
391 let r = self.eval(&expr);
392 self.sheet = saved;
393 r
394 }
395 Err(_) => err(CellError::Name),
396 };
397 }
398 if let Some((si, ti)) = wb.table(bare)
400 && let Some(t) = wb.sheet(si).and_then(|s| s.tables.get(ti))
401 && let Some(d) = t.data_range()
402 {
403 return Ev::R(vec![Area { sheet: si, range: d }]);
404 }
405 err(CellError::Name)
406 }
407
408 fn structured(&mut self, s: &StructRef) -> Ev {
409 let wb = self.host.workbook();
410 let found = if s.table.is_empty() {
411 wb.sheet(self.sheet).and_then(|sh| sh.tables.iter().position(|t| t.range.contains(self.at)).map(|ti| (self.sheet, ti)))
412 } else {
413 wb.table(&s.table)
414 };
415 let Some((si, ti)) = found else { return err(CellError::Ref) };
416 let Some(t) = wb.sheet(si).and_then(|sh| sh.tables.get(ti)) else { return err(CellError::Ref) };
417 let mut r0 = t.range.start.row;
418 let mut r1 = t.range.end.row;
419 let data = t.data_range();
420 let items = &s.specifiers;
421 if items.is_empty() || items == &[StructItem::Data] {
422 match data {
423 Some(d) => {
424 r0 = d.start.row;
425 r1 = d.end.row;
426 }
427 None => return err(CellError::Ref),
428 }
429 } else if items.contains(&StructItem::ThisRow) {
430 if self.at.row < t.range.start.row || self.at.row > t.range.end.row {
431 return err(CellError::Value);
432 }
433 r0 = self.at.row;
434 r1 = self.at.row;
435 } else if !items.contains(&StructItem::All) {
436 let has = |i: StructItem| items.contains(&i);
437 let hdr = t.header_row.then_some(t.range.start.row);
438 let tot = t.totals_row.then_some(t.range.end.row);
439 let mut lo = u32::MAX;
440 let mut hi = 0;
441 let mut add = |a: u32, b: u32| {
442 lo = lo.min(a);
443 hi = hi.max(b);
444 };
445 if has(StructItem::Headers) {
446 match hdr {
447 Some(h) => add(h, h),
448 None => return err(CellError::Ref),
449 }
450 }
451 if has(StructItem::Data)
452 && let Some(d) = data
453 {
454 add(d.start.row, d.end.row);
455 }
456 if has(StructItem::Totals) {
457 match tot {
458 Some(h) => add(h, h),
459 None => return err(CellError::Ref),
460 }
461 }
462 if lo > hi {
463 return err(CellError::Ref);
464 }
465 r0 = lo;
466 r1 = hi;
467 }
468 let (mut c0, mut c1) = (t.range.start.col, t.range.end.col);
469 if let Some(a) = &s.col_start {
470 let Some(i) = t.column_index(a) else { return err(CellError::Ref) };
471 c0 = t.range.start.col + i as u32;
472 c1 = c0;
473 if let Some(b) = &s.col_end {
474 let Some(j) = t.column_index(b) else { return err(CellError::Ref) };
475 let cj = t.range.start.col + j as u32;
476 c0 = c0.min(cj);
477 c1 = c1.max(cj);
478 }
479 }
480 Ev::R(vec![Area { sheet: si, range: RangeRef::new(CellRef::new(r0, c0), CellRef::new(r1, c1)) }])
481 }
482
483 fn unary(&mut self, op: UnOp, x: &Expr) -> Ev {
484 if let UnOp::Trim(mode) = op {
485 return self.trim_ref(mode, x);
486 }
487 if op == UnOp::Spill {
488 return match self.eval(x) {
489 Ev::R(areas) => match areas.as_slice() {
490 [a] if a.range.is_single() => match self.host.spill_range(a.sheet, a.range.start) {
491 Some(range) => Ev::R(vec![Area { sheet: a.sheet, range }]),
492 None => err(CellError::Ref),
493 },
494 _ => err(CellError::Ref),
495 },
496 Ev::V(Value::Error(e)) => err(e),
497 _ => err(CellError::Ref),
498 };
499 }
500 if op == UnOp::At {
501 let ev = self.eval(x);
502 return match ev {
503 Ev::R(areas) => match areas.as_slice() {
504 [a] => match self.implicit_intersect(a) {
505 Some(c) => Ev::V(self.host.cell_value(a.sheet, c)),
506 None => err(CellError::Value),
507 },
508 _ => err(CellError::Value),
509 },
510 Ev::V(v) => Ev::V(v.scalar()),
511 l => l,
512 };
513 }
514 let v = self.value(x);
515 let sys = self.host.workbook().date_system;
516 Ev::V(map_unary(
517 &v,
518 &|n| match op {
519 UnOp::Neg => Value::number(-n),
520 UnOp::Plus => Value::number(n),
521 UnOp::Percent => Value::number(n / 100.0),
522 UnOp::At | UnOp::Spill | UnOp::Trim(_) => Value::number(n),
523 },
524 op == UnOp::Plus,
525 sys,
526 ))
527 }
528
529 fn implicit_intersect(&self, a: &Area) -> Option<CellRef> {
530 let r = a.range;
531 if r.is_single() {
532 return Some(r.start);
533 }
534 if r.width() == 1 && r.start.row <= self.at.row && self.at.row <= r.end.row {
535 return Some(CellRef::new(self.at.row, r.start.col));
536 }
537 if r.height() == 1 && r.start.col <= self.at.col && self.at.col <= r.end.col {
538 return Some(CellRef::new(r.start.row, self.at.col));
539 }
540 None
541 }
542
543 fn binary(&mut self, op: BinOp, a: &Expr, b: &Expr) -> Ev {
544 match op {
545 BinOp::Range => {
546 let (x, y) = (self.eval(a), self.eval(b));
547 let (Ev::R(xa), Ev::R(ya)) = (&x, &y) else {
548 if let Ev::V(Value::Error(e)) = x {
549 return err(e);
550 }
551 if let Ev::V(Value::Error(e)) = y {
552 return err(e);
553 }
554 return err(CellError::Value);
555 };
556 let all: Vec<&Area> = xa.iter().chain(ya.iter()).collect();
557 let Some(first) = all.first() else { return err(CellError::Ref) };
558 if all.iter().any(|ar| ar.sheet != first.sheet) {
559 return err(CellError::Value);
560 }
561 let mut r = first.range;
562 for ar in &all {
563 r = r.union(&ar.range);
564 }
565 Ev::R(vec![Area { sheet: first.sheet, range: r }])
566 }
567 BinOp::Union => {
568 let (x, y) = (self.eval(a), self.eval(b));
569 match (x, y) {
570 (Ev::R(mut xa), Ev::R(ya)) => {
571 xa.extend(ya);
572 Ev::R(xa)
573 }
574 (Ev::V(Value::Error(e)), _) | (_, Ev::V(Value::Error(e))) => err(e),
575 _ => err(CellError::Value),
576 }
577 }
578 BinOp::Intersect => {
579 let (x, y) = (self.eval(a), self.eval(b));
580 match (x, y) {
581 (Ev::R(xa), Ev::R(ya)) => {
582 let mut out = Vec::new();
583 for p in &xa {
584 for q in &ya {
585 if p.sheet == q.sheet
586 && let Some(i) = p.range.intersection(&q.range)
587 {
588 out.push(Area { sheet: p.sheet, range: i });
589 }
590 }
591 }
592 if out.is_empty() { err(CellError::Null) } else { Ev::R(out) }
593 }
594 (Ev::V(Value::Error(e)), _) | (_, Ev::V(Value::Error(e))) => err(e),
595 _ => err(CellError::Value),
596 }
597 }
598 _ => {
599 let mut rest = vec![(op, b)];
602 let mut left = a;
603 while let Expr::Binary(o, l, r) = left
604 && !matches!(o, BinOp::Range | BinOp::Union | BinOp::Intersect)
605 {
606 rest.push((*o, r.as_ref()));
607 left = l.as_ref();
608 }
609 let mut acc = self.value(left);
610 while let Some((o, r)) = rest.pop() {
611 let y = self.value(r);
612 acc = binary_values(o, &acc, &y, self.host.workbook().date_system);
613 }
614 Ev::V(acc)
615 }
616 }
617 }
618
619 fn arg_ev(&mut self, args: &[Expr], i: usize) -> Option<Ev> {
622 args.get(i).map(|e| self.eval(e))
623 }
624 fn arg_val(&mut self, args: &[Expr], i: usize) -> Option<Value> {
625 args.get(i).map(|e| self.value(e))
626 }
627 fn arg_num(&mut self, args: &[Expr], i: usize, default: f64) -> Result<f64, CellError> {
628 match args.get(i) {
629 None | Some(Expr::Missing) => Ok(default),
630 Some(e) => {
631 let v = self.value(e);
632 v.scalar().to_number()
633 }
634 }
635 }
636}
637
638trait Pipe: Sized {
639 fn pipe<T>(self, f: impl FnOnce(Self) -> T) -> T {
640 f(self)
641 }
642}
643impl<T> Pipe for T {}
644
645#[cfg(test)]
646mod fuel_tests {
647 use super::*;
648 use crate::model::Workbook;
649
650 struct Empty {
651 wb: Workbook,
652 }
653 impl Host for Empty {
654 fn workbook(&self) -> &Workbook {
655 &self.wb
656 }
657 fn cell_value(&mut self, _sheet: usize, _c: CellRef) -> Value {
658 Value::Empty
659 }
660 fn now_serial(&self) -> f64 {
661 0.0
662 }
663 fn random(&mut self) -> f64 {
664 0.0
665 }
666 }
667
668 fn eval_with(fuel: u64, src: &str) -> Value {
669 let mut host = Empty { wb: Workbook::default() };
670 let mut ev = Evaluator::new(&mut host, 0, CellRef::new(0, 0));
671 ev.fuel = fuel;
672 ev.value(&crate::formula::parser::parse(src).unwrap_or_else(|e| panic!("{e}")))
673 }
674
675 #[test]
676 fn lambda_calls_spend_fuel_and_run_out_quietly() {
677 let r = eval_with(3, "=LAMBDA(x,LAMBDA(y,LAMBDA(z,z+1)(3))(2))(1)");
681 assert_eq!(r, Value::number(4.0), "{r:?}");
682 let r = eval_with(2, "=LAMBDA(x,LAMBDA(y,LAMBDA(z,z+1)(3))(2))(1)");
684 assert!(matches!(r, Value::Error(CellError::Calc)), "{r:?}");
685 let r = eval_with(0, "=LAMBDA(x,x+1)(2)");
687 assert!(matches!(r, Value::Error(CellError::Calc)), "{r:?}");
688 let r = eval_with(1, "=LAMBDA(x,x+1)(2)");
689 assert_eq!(r, Value::number(3.0));
690 let r = eval_with(3, "=MAP({1,2,3},LAMBDA(x,x*2))");
692 assert!(matches!(r, Value::Array(_)) && !matches!(&r, Value::Array(a) if a.data.iter().any(|v| matches!(v, Value::Error(_)))), "{r:?}");
693 }
694}