1use crate::{
2 CellRef,
3 broadcast::{broadcast_shape, project_index},
4 coercion,
5 traits::{ArgumentHandle, DefaultFunctionContext, EvaluationContext},
6};
7use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
8use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
9pub struct Interpreter<'a> {
12 pub context: &'a dyn EvaluationContext,
13 current_sheet: &'a str,
14 current_cell: Option<crate::CellRef>,
15}
16
17impl<'a> Interpreter<'a> {
18 pub fn new(context: &'a dyn EvaluationContext, current_sheet: &'a str) -> Self {
19 Self {
20 context,
21 current_sheet,
22 current_cell: None,
23 }
24 }
25
26 pub fn new_with_cell(
27 context: &'a dyn EvaluationContext,
28 current_sheet: &'a str,
29 cell: crate::CellRef,
30 ) -> Self {
31 Self {
32 context,
33 current_sheet,
34 current_cell: Some(cell),
35 }
36 }
37
38 pub fn current_sheet(&self) -> &'a str {
39 self.current_sheet
40 }
41
42 pub fn resolve_range_view<'c>(
43 &'c self,
44 reference: &ReferenceType,
45 current_sheet: &str,
46 ) -> Result<crate::engine::range_view::RangeView<'c>, ExcelError> {
47 self.context.resolve_range_view(reference, current_sheet)
48 }
49
50 pub fn evaluate_ast_as_reference(&self, node: &ASTNode) -> Result<ReferenceType, ExcelError> {
55 match &node.node_type {
56 ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
57 ASTNodeType::Function { name, args } => {
58 if let Some(fun) = self.context.get_function("", name) {
59 let handles: Vec<ArgumentHandle> =
61 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
62 let fctx = DefaultFunctionContext::new(self.context, None);
63 if let Some(res) = fun.eval_reference(&handles, &fctx) {
64 res
65 } else {
66 Err(ExcelError::new(ExcelErrorKind::Ref)
67 .with_message("Function does not return a reference"))
68 }
69 } else {
70 Err(ExcelError::new(ExcelErrorKind::Name)
71 .with_message(format!("Unknown function: {name}")))
72 }
73 }
74 ASTNodeType::Array(_)
75 | ASTNodeType::UnaryOp { .. }
76 | ASTNodeType::BinaryOp { .. }
77 | ASTNodeType::Literal(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
78 .with_message("Expression cannot be used as a reference")),
79 }
80 }
81
82 pub fn evaluate_ast(&self, node: &ASTNode) -> Result<LiteralValue, ExcelError> {
84 self.evaluate_ast_uncached(node)
85 }
86
87 fn evaluate_ast_uncached(&self, node: &ASTNode) -> Result<LiteralValue, ExcelError> {
88 let current_sheet = self.current_sheet.to_string();
92 let range_probe = |reference: &ReferenceType| -> Option<(u32, u32)> {
93 use formualizer_parse::parser::ReferenceType as RT;
95 match reference {
96 RT::Range {
97 sheet,
98 start_row,
99 start_col,
100 end_row,
101 end_col,
102 } => {
103 let sheet_name = sheet.as_deref().unwrap_or(¤t_sheet);
104 let mut sr = *start_row;
106 let mut sc = *start_col;
107 let mut er = *end_row;
108 let mut ec = *end_col;
109
110 if sr.is_none() && er.is_none() {
112 let scv = sc.unwrap_or(1);
114 let ecv = ec.unwrap_or(scv);
115 sr = Some(1);
116 if let Some((_, max_r)) =
117 self.context.used_rows_for_columns(sheet_name, scv, ecv)
118 {
119 er = Some(max_r);
120 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
121 er = Some(max_rows);
122 }
123 }
124
125 if sc.is_none() && ec.is_none() {
127 let srv = sr.unwrap_or(1);
129 let erv = er.unwrap_or(srv);
130 sc = Some(1);
131 if let Some((_, max_c)) =
132 self.context.used_cols_for_rows(sheet_name, srv, erv)
133 {
134 ec = Some(max_c);
135 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
136 ec = Some(max_cols);
137 }
138 }
139
140 if sr.is_some() && er.is_none() {
142 let scv = sc.unwrap_or(1);
143 let ecv = ec.unwrap_or(scv);
144 if let Some((_, max_r)) =
145 self.context.used_rows_for_columns(sheet_name, scv, ecv)
146 {
147 er = Some(max_r);
148 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
149 er = Some(max_rows);
150 }
151 }
152 if er.is_some() && sr.is_none() {
153 sr = Some(1);
155 }
156 if sc.is_some() && ec.is_none() {
157 let srv = sr.unwrap_or(1);
158 let erv = er.unwrap_or(srv);
159 if let Some((_, max_c)) =
160 self.context.used_cols_for_rows(sheet_name, srv, erv)
161 {
162 ec = Some(max_c);
163 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
164 ec = Some(max_cols);
165 }
166 }
167 if ec.is_some() && sc.is_none() {
168 sc = Some(1);
170 }
171
172 let sr = sr.unwrap_or(1);
173 let sc = sc.unwrap_or(1);
174 let er = er.unwrap_or(sr.saturating_sub(1));
175 let ec = ec.unwrap_or(sc.saturating_sub(1));
176 if er < sr || ec < sc {
177 return Some((0, 0));
178 }
179 Some((er.saturating_sub(sr) + 1, ec.saturating_sub(sc) + 1))
180 }
181 RT::Cell { .. } => Some((1, 1)),
182 _ => None,
183 }
184 };
185 let fn_lookup = |ns: &str, name: &str| self.context.get_function(ns, name);
186
187 let mut planner = crate::planner::Planner::new(crate::planner::PlanConfig::default())
188 .with_range_probe(&range_probe)
189 .with_function_lookup(&fn_lookup);
190 let plan = planner.plan(node);
191 self.eval_with_plan(node, &plan.root)
192 }
193
194 fn eval_with_plan(
195 &self,
196 node: &ASTNode,
197 plan_node: &crate::planner::PlanNode,
198 ) -> Result<LiteralValue, ExcelError> {
199 match &node.node_type {
200 ASTNodeType::Literal(v) => Ok(v.clone()),
201 ASTNodeType::Reference { reference, .. } => self.eval_reference(reference),
202 ASTNodeType::UnaryOp { op, expr } => {
203 self.eval_unary(op, expr)
206 }
207 ASTNodeType::BinaryOp { op, left, right } => self.eval_binary(op, left, right),
208 ASTNodeType::Function { name, args } => {
209 let strategy = plan_node.strategy;
210 if let Some(fun) = self.context.get_function("", name) {
211 use crate::function::FnCaps;
212 use crate::planner::ExecStrategy;
213 let caps = fun.caps();
214
215 if caps.contains(FnCaps::SHORT_CIRCUIT) || caps.contains(FnCaps::VOLATILE) {
217 return self.eval_function(name, args);
218 }
219
220 if matches!(strategy, ExecStrategy::ChunkedReduce)
222 && caps.contains(FnCaps::WINDOWED)
223 {
224 let handles: Vec<ArgumentHandle> =
225 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
226 let fctx = DefaultFunctionContext::new(self.context, self.current_cell);
227 let mut w = crate::window_ctx::SimpleWindowCtx::new(
228 &handles,
229 &fctx,
230 crate::window_ctx::WindowSpec::default(),
231 );
232 if let Some(res) = fun.eval_window(&mut w) {
233 return res;
234 }
235 return self.eval_function(name, args);
237 }
238
239 if matches!(strategy, ExecStrategy::ArgParallel)
241 && caps.contains(FnCaps::PARALLEL_ARGS)
242 {
243 for arg in args {
245 match &arg.node_type {
246 ASTNodeType::Reference { reference, .. } => {
247 let _ = self
248 .context
249 .resolve_range_view(reference, self.current_sheet);
250 }
251 _ => {
252 let _ = self.evaluate_ast(arg);
253 }
254 }
255 }
256 return self.eval_function(name, args);
257 }
258
259 return self.eval_function(name, args);
261 }
262 self.eval_function(name, args)
263 }
264 ASTNodeType::Array(rows) => self.eval_array_literal(rows),
265 }
266 }
267
268 fn eval_reference(&self, reference: &ReferenceType) -> Result<LiteralValue, ExcelError> {
270 let view = self
271 .context
272 .resolve_range_view(reference, self.current_sheet)?;
273
274 match reference {
275 ReferenceType::Cell { .. } => {
276 Ok(view.as_1x1().unwrap_or(LiteralValue::Empty))
278 }
279 _ => {
280 let (rows, cols) = view.dims();
282 let mut data = Vec::with_capacity(rows);
283
284 view.for_each_row(&mut |row| {
285 let row_data: Vec<LiteralValue> = (0..cols)
286 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
287 .collect();
288 data.push(row_data);
289 Ok(())
290 })?;
291
292 if data.len() == 1 && data[0].len() == 1 {
293 Ok(data[0][0].clone())
294 } else {
295 Ok(LiteralValue::Array(data))
296 }
297 }
298 }
299 }
300
301 fn eval_unary(&self, op: &str, expr: &ASTNode) -> Result<LiteralValue, ExcelError> {
303 let v = self.evaluate_ast(expr)?;
304 match v {
305 LiteralValue::Array(arr) => {
306 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
307 }
308 other => self.eval_unary_scalar(op, other),
309 }
310 }
311
312 fn eval_unary_scalar(&self, op: &str, v: LiteralValue) -> Result<LiteralValue, ExcelError> {
313 match op {
314 "+" => self.apply_number_unary(v, |n| n),
315 "-" => self.apply_number_unary(v, |n| -n),
316 "%" => self.apply_number_unary(v, |n| n / 100.0),
317 _ => {
318 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
319 }
320 }
321 }
322
323 fn apply_number_unary<F>(&self, v: LiteralValue, f: F) -> Result<LiteralValue, ExcelError>
324 where
325 F: Fn(f64) -> f64,
326 {
327 match crate::coercion::to_number_lenient_with_locale(&v, &self.context.locale()) {
328 Ok(n) => match crate::coercion::sanitize_numeric(f(n)) {
329 Ok(n2) => Ok(LiteralValue::Number(n2)),
330 Err(e) => Ok(LiteralValue::Error(e)),
331 },
332 Err(e) => Ok(LiteralValue::Error(e)),
333 }
334 }
335
336 fn eval_binary(
338 &self,
339 op: &str,
340 left: &ASTNode,
341 right: &ASTNode,
342 ) -> Result<LiteralValue, ExcelError> {
343 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
345 let l = self.evaluate_ast(left)?;
346 let r = self.evaluate_ast(right)?;
347 return self.compare(op, l, r);
348 }
349
350 let l_val = self.evaluate_ast(left)?;
351 let r_val = self.evaluate_ast(right)?;
352
353 match op {
354 "+" => self.numeric_binary(l_val, r_val, |a, b| a + b),
355 "-" => self.numeric_binary(l_val, r_val, |a, b| a - b),
356 "*" => self.numeric_binary(l_val, r_val, |a, b| a * b),
357 "/" => self.divide(l_val, r_val),
358 "^" => self.power(l_val, r_val),
359 "&" => Ok(LiteralValue::Text(format!(
360 "{}{}",
361 crate::coercion::to_text_invariant(&l_val),
362 crate::coercion::to_text_invariant(&r_val)
363 ))),
364 ":" => {
365 let lref = self.evaluate_ast_as_reference(left)?;
367 let rref = self.evaluate_ast_as_reference(right)?;
368 match crate::reference::combine_references(&lref, &rref) {
369 Ok(_r) => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
370 "Reference produced by ':' cannot be used directly as a value",
371 )),
372 Err(e) => Ok(LiteralValue::Error(e)),
373 }
374 }
375 _ => {
376 Err(ExcelError::new(ExcelErrorKind::NImpl)
377 .with_message(format!("Binary op '{op}'")))
378 }
379 }
380 }
381
382 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
384 if let Some(fun) = self.context.get_function("", name) {
385 let handles: Vec<ArgumentHandle> =
386 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
387 let fctx = DefaultFunctionContext::new(self.context, self.current_cell);
389 fun.dispatch(&handles, &fctx)
390 } else {
391 Ok(LiteralValue::Error(
393 ExcelError::new(ExcelErrorKind::Name)
394 .with_message(format!("Unknown function: {name}")),
395 ))
396 }
397 }
398
399 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
400 DefaultFunctionContext::new(self.context, cell_ref.cloned())
401 }
402
403 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
408 let mut out = Vec::with_capacity(rows.len());
409 for row in rows {
410 let mut r = Vec::with_capacity(row.len());
411 for cell in row {
412 r.push(self.evaluate_ast(cell)?);
413 }
414 out.push(r);
415 }
416 Ok(LiteralValue::Array(out))
417 }
418
419 fn numeric_binary<F>(
421 &self,
422 left: LiteralValue,
423 right: LiteralValue,
424 f: F,
425 ) -> Result<LiteralValue, ExcelError>
426 where
427 F: Fn(f64, f64) -> f64 + Copy,
428 {
429 self.broadcast_apply(left, right, |l, r| {
430 let a = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
431 let b = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
432 match (a, b) {
433 (Ok(a), Ok(b)) => match crate::coercion::sanitize_numeric(f(a, b)) {
434 Ok(n2) => Ok(LiteralValue::Number(n2)),
435 Err(e) => Ok(LiteralValue::Error(e)),
436 },
437 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
438 }
439 })
440 }
441
442 fn divide(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
443 self.broadcast_apply(left, right, |l, r| {
444 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
445 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
446 let (a, b) = match (ln, rn) {
447 (Ok(a), Ok(b)) => (a, b),
448 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
449 };
450 if b == 0.0 {
451 return Ok(LiteralValue::Error(ExcelError::from_error_string(
452 "#DIV/0!",
453 )));
454 }
455 match crate::coercion::sanitize_numeric(a / b) {
456 Ok(n) => Ok(LiteralValue::Number(n)),
457 Err(e) => Ok(LiteralValue::Error(e)),
458 }
459 })
460 }
461
462 fn power(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
463 self.broadcast_apply(left, right, |l, r| {
464 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
465 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
466 let (a, b) = match (ln, rn) {
467 (Ok(a), Ok(b)) => (a, b),
468 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
469 };
470 if a < 0.0 && b.fract() != 0.0 {
472 return Ok(LiteralValue::Error(ExcelError::new_num()));
473 }
474 match crate::coercion::sanitize_numeric(a.powf(b)) {
475 Ok(n) => Ok(LiteralValue::Number(n)),
476 Err(e) => Ok(LiteralValue::Error(e)),
477 }
478 })
479 }
480
481 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
482 where
483 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
484 {
485 let mut out = Vec::with_capacity(arr.len());
486 for row in arr {
487 let mut new_row = Vec::with_capacity(row.len());
488 for cell in row {
489 new_row.push(match f(cell) {
490 Ok(v) => v,
491 Err(e) => LiteralValue::Error(e),
492 });
493 }
494 out.push(new_row);
495 }
496 Ok(LiteralValue::Array(out))
497 }
498
499 fn combine_arrays<F>(
500 &self,
501 l: Vec<Vec<LiteralValue>>,
502 r: Vec<Vec<LiteralValue>>,
503 f: F,
504 ) -> Result<LiteralValue, ExcelError>
505 where
506 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
507 {
508 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
510 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
511 let target = match broadcast_shape(&[l_shape, r_shape]) {
512 Ok(s) => s,
513 Err(e) => return Ok(LiteralValue::Error(e)),
514 };
515
516 let mut out = Vec::with_capacity(target.0);
517 for i in 0..target.0 {
518 let mut row = Vec::with_capacity(target.1);
519 for j in 0..target.1 {
520 let (li, lj) = project_index((i, j), l_shape);
521 let (ri, rj) = project_index((i, j), r_shape);
522 let lv = l
523 .get(li)
524 .and_then(|r| r.get(lj))
525 .cloned()
526 .unwrap_or(LiteralValue::Empty);
527 let rv = r
528 .get(ri)
529 .and_then(|r| r.get(rj))
530 .cloned()
531 .unwrap_or(LiteralValue::Empty);
532 row.push(match f(lv, rv) {
533 Ok(v) => v,
534 Err(e) => LiteralValue::Error(e),
535 });
536 }
537 out.push(row);
538 }
539 Ok(LiteralValue::Array(out))
540 }
541
542 fn broadcast_apply<F>(
543 &self,
544 left: LiteralValue,
545 right: LiteralValue,
546 f: F,
547 ) -> Result<LiteralValue, ExcelError>
548 where
549 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
550 {
551 use LiteralValue::*;
552 match (left, right) {
553 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
554 (Array(arr), v) => {
555 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
556 let shape_r = (1usize, 1usize);
557 let target = match broadcast_shape(&[shape_l, shape_r]) {
558 Ok(s) => s,
559 Err(e) => return Ok(LiteralValue::Error(e)),
560 };
561 let mut out = Vec::with_capacity(target.0);
562 for i in 0..target.0 {
563 let mut row = Vec::with_capacity(target.1);
564 for j in 0..target.1 {
565 let (li, lj) = project_index((i, j), shape_l);
566 let lv = arr
567 .get(li)
568 .and_then(|r| r.get(lj))
569 .cloned()
570 .unwrap_or(LiteralValue::Empty);
571 row.push(match f(lv, v.clone()) {
572 Ok(vv) => vv,
573 Err(e) => LiteralValue::Error(e),
574 });
575 }
576 out.push(row);
577 }
578 Ok(LiteralValue::Array(out))
579 }
580 (v, Array(arr)) => {
581 let shape_l = (1usize, 1usize);
582 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
583 let target = match broadcast_shape(&[shape_l, shape_r]) {
584 Ok(s) => s,
585 Err(e) => return Ok(LiteralValue::Error(e)),
586 };
587 let mut out = Vec::with_capacity(target.0);
588 for i in 0..target.0 {
589 let mut row = Vec::with_capacity(target.1);
590 for j in 0..target.1 {
591 let (ri, rj) = project_index((i, j), shape_r);
592 let rv = arr
593 .get(ri)
594 .and_then(|r| r.get(rj))
595 .cloned()
596 .unwrap_or(LiteralValue::Empty);
597 row.push(match f(v.clone(), rv) {
598 Ok(vv) => vv,
599 Err(e) => LiteralValue::Error(e),
600 });
601 }
602 out.push(row);
603 }
604 Ok(LiteralValue::Array(out))
605 }
606 (l, r) => f(l, r),
607 }
608 }
609
610 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
612 coercion::to_number_lenient(v)
613 }
614
615 fn coerce_text(&self, v: &LiteralValue) -> String {
616 coercion::to_text_invariant(v)
617 }
618
619 fn compare(
621 &self,
622 op: &str,
623 left: LiteralValue,
624 right: LiteralValue,
625 ) -> Result<LiteralValue, ExcelError> {
626 use LiteralValue::*;
627 if matches!(left, Error(_)) {
628 return Ok(left);
629 }
630 if matches!(right, Error(_)) {
631 return Ok(right);
632 }
633
634 match (left, right) {
636 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
637 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
638 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
639 (l, r) => {
640 let res = match (l, r) {
641 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
642 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
643 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
644 (Boolean(a), Boolean(b)) => {
645 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
646 }
647 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
648 (a, b) => {
649 let an = crate::coercion::to_number_lenient_with_locale(
651 &a,
652 &self.context.locale(),
653 )
654 .ok();
655 let bn = crate::coercion::to_number_lenient_with_locale(
656 &b,
657 &self.context.locale(),
658 )
659 .ok();
660 if let (Some(a), Some(b)) = (an, bn) {
661 self.cmp_f64(a, b, op)
662 } else {
663 self.cmp_text(
664 &crate::coercion::to_text_invariant(&a),
665 &crate::coercion::to_text_invariant(&b),
666 op,
667 )
668 }
669 }
670 };
671 Ok(LiteralValue::Boolean(res))
672 }
673 }
674 }
675
676 fn cmp_f64(&self, a: f64, b: f64, op: &str) -> bool {
677 match op {
678 "=" => a == b,
679 "<>" => a != b,
680 ">" => a > b,
681 "<" => a < b,
682 ">=" => a >= b,
683 "<=" => a <= b,
684 _ => unreachable!(),
685 }
686 }
687 fn cmp_text(&self, a: &str, b: &str, op: &str) -> bool {
688 let loc = self.context.locale();
689 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
690 self.cmp_f64(
691 a.cmp(&b) as i32 as f64,
692 0.0,
693 match op {
694 "=" => "=",
695 "<>" => "<>",
696 ">" => ">",
697 "<" => "<",
698 ">=" => ">=",
699 "<=" => "<=",
700 _ => unreachable!(),
701 },
702 )
703 }
704}