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};
9
10use crate::engine::arena::{AstNodeData, AstNodeId, DataStore};
11use crate::engine::sheet_registry::SheetRegistry;
12
13pub struct Interpreter<'a> {
16 pub context: &'a dyn EvaluationContext,
17 current_sheet: &'a str,
18 current_cell: Option<crate::CellRef>,
19}
20
21impl<'a> Interpreter<'a> {
22 pub fn new(context: &'a dyn EvaluationContext, current_sheet: &'a str) -> Self {
23 Self {
24 context,
25 current_sheet,
26 current_cell: None,
27 }
28 }
29
30 pub fn new_with_cell(
31 context: &'a dyn EvaluationContext,
32 current_sheet: &'a str,
33 cell: crate::CellRef,
34 ) -> Self {
35 Self {
36 context,
37 current_sheet,
38 current_cell: Some(cell),
39 }
40 }
41
42 pub fn current_sheet(&self) -> &'a str {
43 self.current_sheet
44 }
45
46 pub fn resolve_range_view<'c>(
47 &'c self,
48 reference: &ReferenceType,
49 current_sheet: &str,
50 ) -> Result<crate::engine::range_view::RangeView<'c>, ExcelError> {
51 self.context.resolve_range_view(reference, current_sheet)
52 }
53
54 pub fn evaluate_ast_as_reference(&self, node: &ASTNode) -> Result<ReferenceType, ExcelError> {
59 match &node.node_type {
60 ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
61 ASTNodeType::Function { name, args } => {
62 if let Some(fun) = self.context.get_function("", name) {
63 let handles: Vec<ArgumentHandle> =
65 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
66 let fctx = DefaultFunctionContext::new_with_sheet(
67 self.context,
68 None,
69 self.current_sheet,
70 );
71 if let Some(res) = fun.eval_reference(&handles, &fctx) {
72 res
73 } else {
74 Err(ExcelError::new(ExcelErrorKind::Ref)
75 .with_message("Function does not return a reference"))
76 }
77 } else {
78 Err(ExcelError::new(ExcelErrorKind::Name)
79 .with_message(format!("Unknown function: {name}")))
80 }
81 }
82 ASTNodeType::BinaryOp { op, left, right } if op == ":" => {
83 let lref = self.evaluate_ast_as_reference(left)?;
84 let rref = self.evaluate_ast_as_reference(right)?;
85 crate::reference::combine_references(&lref, &rref)
86 }
87 ASTNodeType::Array(_)
88 | ASTNodeType::UnaryOp { .. }
89 | ASTNodeType::BinaryOp { .. }
90 | ASTNodeType::Literal(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
91 .with_message("Expression cannot be used as a reference")),
92 }
93 }
94
95 pub(crate) fn evaluate_arena_ast_as_reference(
96 &self,
97 node_id: AstNodeId,
98 data_store: &DataStore,
99 sheet_registry: &SheetRegistry,
100 ) -> Result<ReferenceType, ExcelError> {
101 let node = data_store.get_node(node_id).ok_or_else(|| {
102 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
103 })?;
104
105 match node {
106 AstNodeData::Reference { ref_type, .. } => {
107 Ok(data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry))
108 }
109 AstNodeData::Function { name_id, .. } => {
110 let name = data_store.resolve_ast_string(*name_id);
111 let fun = self.context.get_function("", name).ok_or_else(|| {
112 ExcelError::new(ExcelErrorKind::Name)
113 .with_message(format!("Unknown function: {name}"))
114 })?;
115
116 let args = data_store.get_args(node_id).ok_or_else(|| {
117 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
118 })?;
119
120 let handles: Vec<ArgumentHandle> = args
121 .iter()
122 .copied()
123 .map(|arg_id| {
124 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
125 })
126 .collect();
127
128 let fctx =
129 DefaultFunctionContext::new_with_sheet(self.context, None, self.current_sheet);
130
131 fun.eval_reference(&handles, &fctx).ok_or_else(|| {
132 ExcelError::new(ExcelErrorKind::Ref)
133 .with_message("Function does not return a reference")
134 })?
135 }
136 AstNodeData::BinaryOp {
137 op_id,
138 left_id,
139 right_id,
140 } => {
141 let op = data_store.resolve_ast_string(*op_id);
142 if op != ":" {
143 return Err(ExcelError::new(ExcelErrorKind::Ref)
144 .with_message("Expression cannot be used as a reference"));
145 }
146 let lref =
147 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
148 let rref =
149 self.evaluate_arena_ast_as_reference(*right_id, data_store, sheet_registry)?;
150 crate::reference::combine_references(&lref, &rref)
151 }
152 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
153 .with_message("Expression cannot be used as a reference")),
154 }
155 }
156
157 pub fn evaluate_ast(&self, node: &ASTNode) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
159 self.evaluate_ast_uncached(node)
160 }
161
162 pub(crate) fn evaluate_arena_ast(
163 &self,
164 node_id: AstNodeId,
165 data_store: &DataStore,
166 sheet_registry: &SheetRegistry,
167 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
168 let node = data_store.get_node(node_id).ok_or_else(|| {
169 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
170 })?;
171
172 match node {
173 AstNodeData::Literal(vref) => Ok(crate::traits::CalcValue::Scalar(
174 data_store.retrieve_value(*vref),
175 )),
176 AstNodeData::Reference { ref_type, .. } => {
177 let reference =
178 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
179 self.eval_reference_to_calc(&reference)
180 }
181 AstNodeData::UnaryOp { op_id, expr_id } => {
182 let expr = self.evaluate_arena_ast(*expr_id, data_store, sheet_registry)?;
183
184 let op = data_store.resolve_ast_string(*op_id);
185 if op == "@" {
186 if let Some(AstNodeData::Reference { ref_type, .. }) =
189 data_store.get_node(*expr_id)
190 {
191 let reference = data_store
192 .reconstruct_reference_type_for_eval(ref_type, sheet_registry);
193 let v = self.implicit_intersection_from_reference(&reference);
194 return Ok(crate::traits::CalcValue::Scalar(v));
195 }
196
197 let v = self.eval_implicit_intersection_calc(expr);
198 return Ok(crate::traits::CalcValue::Scalar(v));
199 }
200 let v = expr.into_literal();
202 match v {
203 LiteralValue::Array(arr) => self
204 .map_array(arr, |cell| self.eval_unary_scalar(op, cell))
205 .map(crate::traits::CalcValue::Scalar),
206 other => self
207 .eval_unary_scalar(op, other)
208 .map(crate::traits::CalcValue::Scalar),
209 }
210 }
211 AstNodeData::BinaryOp {
212 op_id,
213 left_id,
214 right_id,
215 } => {
216 let op = data_store.resolve_ast_string(*op_id);
217 if op == ":" {
218 let lref =
219 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
220 let rref = self.evaluate_arena_ast_as_reference(
221 *right_id,
222 data_store,
223 sheet_registry,
224 )?;
225 return match crate::reference::combine_references(&lref, &rref) {
226 Ok(_r) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
227 ExcelError::new(ExcelErrorKind::Ref).with_message(
228 "Reference produced by ':' cannot be used directly as a value",
229 ),
230 ))),
231 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
232 };
233 }
234
235 let left = self
236 .evaluate_arena_ast(*left_id, data_store, sheet_registry)?
237 .into_literal();
238 let right = self
239 .evaluate_arena_ast(*right_id, data_store, sheet_registry)?
240 .into_literal();
241
242 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
243 return self
244 .compare(op, left, right)
245 .map(crate::traits::CalcValue::Scalar);
246 }
247
248 match op {
249 "+" => self
250 .add_sub_date_aware('+', left, right)
251 .map(crate::traits::CalcValue::Scalar),
252 "-" => self
253 .add_sub_date_aware('-', left, right)
254 .map(crate::traits::CalcValue::Scalar),
255 "*" => self
256 .numeric_binary(left, right, |a, b| a * b)
257 .map(crate::traits::CalcValue::Scalar),
258 "/" => self
259 .divide(left, right)
260 .map(crate::traits::CalcValue::Scalar),
261 "^" => self
262 .power(left, right)
263 .map(crate::traits::CalcValue::Scalar),
264 "&" => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
265 format!(
266 "{}{}",
267 crate::coercion::to_text_invariant(&left),
268 crate::coercion::to_text_invariant(&right)
269 ),
270 ))),
271 _ => Err(ExcelError::new(ExcelErrorKind::NImpl)
272 .with_message(format!("Binary op '{op}'"))),
273 }
274 }
275 AstNodeData::Array { .. } => {
276 let (rows, cols, elements) =
277 data_store.get_array_elems(node_id).ok_or_else(|| {
278 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
279 })?;
280
281 let rows_usize = rows as usize;
282 let cols_usize = cols as usize;
283 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
284 for r in 0..rows_usize {
285 let mut row = Vec::with_capacity(cols_usize);
286 for c in 0..cols_usize {
287 let idx = r * cols_usize + c;
288 if let Some(&elem_id) = elements.get(idx) {
289 row.push(
290 self.evaluate_arena_ast(elem_id, data_store, sheet_registry)?
291 .into_literal(),
292 );
293 }
294 }
295 out.push(row);
296 }
297
298 Ok(crate::traits::CalcValue::Range(
299 crate::engine::range_view::RangeView::from_owned_rows(
300 out,
301 self.context.date_system(),
302 ),
303 ))
304 }
305 AstNodeData::Function { name_id, .. } => {
306 let name = data_store.resolve_ast_string(*name_id);
307 let fun = self.context.get_function("", name).ok_or_else(|| {
308 ExcelError::new(ExcelErrorKind::Name)
309 .with_message(format!("Unknown function: {name}"))
310 })?;
311
312 let args = data_store.get_args(node_id).ok_or_else(|| {
313 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
314 })?;
315
316 let handles: Vec<ArgumentHandle> = args
317 .iter()
318 .copied()
319 .map(|arg_id| {
320 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
321 })
322 .collect();
323
324 let fctx = DefaultFunctionContext::new_with_sheet(
325 self.context,
326 self.current_cell,
327 self.current_sheet,
328 );
329
330 fun.dispatch(&handles, &fctx)
331 }
332 }
333 }
334
335 fn evaluate_ast_uncached(
336 &self,
337 node: &ASTNode,
338 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
339 let current_sheet = self.current_sheet.to_string();
343 let range_probe = |reference: &ReferenceType| -> Option<(u32, u32)> {
344 use formualizer_parse::parser::ReferenceType as RT;
346 match reference {
347 RT::Range {
348 sheet,
349 start_row,
350 start_col,
351 end_row,
352 end_col,
353 ..
354 } => {
355 let sheet_name = sheet.as_deref().unwrap_or(¤t_sheet);
356 let mut sr = *start_row;
358 let mut sc = *start_col;
359 let mut er = *end_row;
360 let mut ec = *end_col;
361
362 if sr.is_none() && er.is_none() {
364 let scv = sc.unwrap_or(1);
366 let ecv = ec.unwrap_or(scv);
367 sr = Some(1);
368 if let Some((_, max_r)) =
369 self.context.used_rows_for_columns(sheet_name, scv, ecv)
370 {
371 er = Some(max_r);
372 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
373 er = Some(max_rows);
374 }
375 }
376
377 if sc.is_none() && ec.is_none() {
379 let srv = sr.unwrap_or(1);
381 let erv = er.unwrap_or(srv);
382 sc = Some(1);
383 if let Some((_, max_c)) =
384 self.context.used_cols_for_rows(sheet_name, srv, erv)
385 {
386 ec = Some(max_c);
387 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
388 ec = Some(max_cols);
389 }
390 }
391
392 if sr.is_some() && er.is_none() {
394 let scv = sc.unwrap_or(1);
395 let ecv = ec.unwrap_or(scv);
396 if let Some((_, max_r)) =
397 self.context.used_rows_for_columns(sheet_name, scv, ecv)
398 {
399 er = Some(max_r);
400 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
401 er = Some(max_rows);
402 }
403 }
404 if er.is_some() && sr.is_none() {
405 sr = Some(1);
407 }
408 if sc.is_some() && ec.is_none() {
409 let srv = sr.unwrap_or(1);
410 let erv = er.unwrap_or(srv);
411 if let Some((_, max_c)) =
412 self.context.used_cols_for_rows(sheet_name, srv, erv)
413 {
414 ec = Some(max_c);
415 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
416 ec = Some(max_cols);
417 }
418 }
419 if ec.is_some() && sc.is_none() {
420 sc = Some(1);
422 }
423
424 let sr = sr.unwrap_or(1);
425 let sc = sc.unwrap_or(1);
426 let er = er.unwrap_or(sr.saturating_sub(1));
427 let ec = ec.unwrap_or(sc.saturating_sub(1));
428 if er < sr || ec < sc {
429 return Some((0, 0));
430 }
431 Some((er.saturating_sub(sr) + 1, ec.saturating_sub(sc) + 1))
432 }
433 RT::Cell { .. } => Some((1, 1)),
434 _ => None,
435 }
436 };
437 let fn_lookup = |ns: &str, name: &str| self.context.get_function(ns, name);
438
439 let mut planner = crate::planner::Planner::new(crate::planner::PlanConfig::default())
440 .with_range_probe(&range_probe)
441 .with_function_lookup(&fn_lookup);
442 let plan = planner.plan(node);
443 self.eval_with_plan(node, &plan.root)
444 }
445
446 fn eval_with_plan(
447 &self,
448 node: &ASTNode,
449 plan_node: &crate::planner::PlanNode,
450 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
451 match &node.node_type {
452 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
453 ASTNodeType::Reference { reference, .. } => self.eval_reference_to_calc(reference),
454 ASTNodeType::UnaryOp { op, expr } => {
455 self.eval_unary(op, expr)
458 .map(crate::traits::CalcValue::Scalar)
459 }
460 ASTNodeType::BinaryOp { op, left, right } => self
461 .eval_binary(op, left, right)
462 .map(crate::traits::CalcValue::Scalar),
463 ASTNodeType::Function { name, args } => {
464 let strategy = plan_node.strategy;
465 if let Some(fun) = self.context.get_function("", name) {
466 use crate::function::FnCaps;
467 use crate::planner::ExecStrategy;
468 let caps = fun.caps();
469
470 if caps.contains(FnCaps::SHORT_CIRCUIT) || caps.contains(FnCaps::VOLATILE) {
472 return self.eval_function_to_calc(name, args);
473 }
474
475 if matches!(strategy, ExecStrategy::ArgParallel)
479 && caps.contains(FnCaps::PARALLEL_ARGS)
480 {
481 for arg in args {
483 match &arg.node_type {
484 ASTNodeType::Reference { reference, .. } => {
485 let _ = self
486 .context
487 .resolve_range_view(reference, self.current_sheet);
488 }
489 _ => {
490 let _ = self.evaluate_ast(arg);
491 }
492 }
493 }
494 return self.eval_function_to_calc(name, args);
495 }
496
497 return self.eval_function_to_calc(name, args);
499 }
500 self.eval_function_to_calc(name, args)
501 }
502 ASTNodeType::Array(rows) => self.eval_array_literal_to_calc(rows),
503 }
504 }
505
506 fn eval_reference_to_calc(
508 &self,
509 reference: &ReferenceType,
510 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
511 let view = self
512 .context
513 .resolve_range_view(reference, self.current_sheet)?
514 .with_cancel_token(self.context.cancellation_token());
515
516 match reference {
517 ReferenceType::Cell { .. } => {
518 Ok(crate::traits::CalcValue::Scalar(
520 view.as_1x1().unwrap_or(LiteralValue::Empty),
521 ))
522 }
523 _ => Ok(crate::traits::CalcValue::Range(view)),
524 }
525 }
526
527 fn eval_reference(&self, reference: &ReferenceType) -> Result<LiteralValue, ExcelError> {
528 self.eval_reference_to_calc(reference)
529 .map(|cv| cv.into_literal())
530 }
531
532 fn eval_unary(&self, op: &str, expr: &ASTNode) -> Result<LiteralValue, ExcelError> {
534 if op == "@" {
535 if let ASTNodeType::Reference { reference, .. } = &expr.node_type {
536 return Ok(self.implicit_intersection_from_reference(reference));
537 }
538
539 let cv = self.evaluate_ast(expr)?;
540 return Ok(self.eval_implicit_intersection_calc(cv));
541 }
542
543 let v = self.evaluate_ast(expr)?.into_literal();
544 match v {
545 LiteralValue::Array(arr) => {
546 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
547 }
548 other => self.eval_unary_scalar(op, other),
549 }
550 }
551
552 fn eval_unary_scalar(&self, op: &str, v: LiteralValue) -> Result<LiteralValue, ExcelError> {
553 match op {
554 "+" => self.apply_number_unary(v, |n| n),
555 "-" => self.apply_number_unary(v, |n| -n),
556 "%" => self.apply_number_unary(v, |n| n / 100.0),
557 _ => {
558 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
559 }
560 }
561 }
562
563 fn eval_implicit_intersection_calc(&self, cv: crate::traits::CalcValue<'a>) -> LiteralValue {
564 let (cur_r0, cur_c0) = match self.current_cell {
565 Some(cell) => (cell.coord.row() as usize, cell.coord.col() as usize),
566 None => (0usize, 0usize),
567 };
568
569 match cv {
570 crate::traits::CalcValue::Scalar(v) => match v {
571 LiteralValue::Array(arr) => {
572 if arr.is_empty() || arr.first().map(|r| r.is_empty()).unwrap_or(true) {
573 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
574 }
575 arr[0][0].clone()
576 }
577 other => other,
578 },
579 crate::traits::CalcValue::Range(rv) => {
580 if rv.is_empty() {
581 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
582 }
583
584 if rv.sheet_name() == "__tmp" {
589 return rv.get_cell(0, 0);
590 }
591
592 if let Some(v) = rv.as_1x1() {
593 return v;
594 }
595
596 let (rows, cols) = rv.dims();
597 let sr = rv.start_row();
598 let sc = rv.start_col();
599 let er = rv.end_row();
600 let ec = rv.end_col();
601
602 if cols == 1 {
607 if cur_r0 < sr || cur_r0 > er {
608 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
609 }
610 let rel_r = cur_r0 - sr;
611 return rv.get_cell(rel_r, 0);
612 }
613
614 if rows == 1 {
615 if cur_c0 < sc || cur_c0 > ec {
616 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
617 }
618 let rel_c = cur_c0 - sc;
619 return rv.get_cell(0, rel_c);
620 }
621
622 if cur_r0 < sr || cur_r0 > er || cur_c0 < sc || cur_c0 > ec {
623 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
624 }
625 let rel_r = cur_r0 - sr;
626 let rel_c = cur_c0 - sc;
627 rv.get_cell(rel_r, rel_c)
628 }
629 }
630 }
631
632 fn implicit_intersection_from_reference(&self, reference: &ReferenceType) -> LiteralValue {
633 let (cur_r1, cur_c1) = match self.current_cell {
634 Some(cell) => (
635 cell.coord.row().saturating_add(1),
636 cell.coord.col().saturating_add(1),
637 ),
638 None => (1u32, 1u32),
639 };
640
641 match reference {
642 ReferenceType::Cell {
643 sheet, row, col, ..
644 } => {
645 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
646 match self
647 .context
648 .resolve_cell_reference(Some(sheet_name), *row, *col)
649 {
650 Ok(v) => v,
651 Err(e) => LiteralValue::Error(e),
652 }
653 }
654 ReferenceType::Range {
655 sheet,
656 start_row,
657 start_col,
658 end_row,
659 end_col,
660 ..
661 } => {
662 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
663
664 let (sr, sc, er, ec) = match (start_row, start_col, end_row, end_col) {
665 (Some(sr), Some(sc), Some(er), Some(ec)) => (*sr, *sc, *er, *ec),
666 _ => {
667 let cv = match self.eval_reference_to_calc(reference) {
670 Ok(cv) => cv,
671 Err(e) => return LiteralValue::Error(e),
672 };
673 return self.eval_implicit_intersection_calc(cv);
674 }
675 };
676
677 let (mut sr, mut er) = (sr, er);
679 let (mut sc, mut ec) = (sc, ec);
680 if sr > er {
681 std::mem::swap(&mut sr, &mut er);
682 }
683 if sc > ec {
684 std::mem::swap(&mut sc, &mut ec);
685 }
686
687 let pick = if sc == ec {
688 if cur_r1 < sr || cur_r1 > er {
690 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
691 }
692 (cur_r1, sc)
693 } else if sr == er {
694 if cur_c1 < sc || cur_c1 > ec {
696 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
697 }
698 (sr, cur_c1)
699 } else {
700 if cur_r1 < sr || cur_r1 > er || cur_c1 < sc || cur_c1 > ec {
702 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
703 }
704 (cur_r1, cur_c1)
705 };
706
707 match self
708 .context
709 .resolve_cell_reference(Some(sheet_name), pick.0, pick.1)
710 {
711 Ok(v) => v,
712 Err(e) => LiteralValue::Error(e),
713 }
714 }
715 other => {
717 let cv = match self.eval_reference_to_calc(other) {
718 Ok(cv) => cv,
719 Err(e) => return LiteralValue::Error(e),
720 };
721 self.eval_implicit_intersection_calc(cv)
722 }
723 }
724 }
725
726 fn apply_number_unary<F>(&self, v: LiteralValue, f: F) -> Result<LiteralValue, ExcelError>
727 where
728 F: Fn(f64) -> f64,
729 {
730 match crate::coercion::to_number_lenient_with_locale(&v, &self.context.locale()) {
731 Ok(n) => match crate::coercion::sanitize_numeric(f(n)) {
732 Ok(n2) => Ok(LiteralValue::Number(n2)),
733 Err(e) => Ok(LiteralValue::Error(e)),
734 },
735 Err(e) => Ok(LiteralValue::Error(e)),
736 }
737 }
738
739 fn eval_binary(
741 &self,
742 op: &str,
743 left: &ASTNode,
744 right: &ASTNode,
745 ) -> Result<LiteralValue, ExcelError> {
746 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
748 let l = self.evaluate_ast(left)?.into_literal();
749 let r = self.evaluate_ast(right)?.into_literal();
750 return self.compare(op, l, r);
751 }
752
753 let l_val = self.evaluate_ast(left)?.into_literal();
754 let r_val = self.evaluate_ast(right)?.into_literal();
755
756 match op {
757 "+" => self.add_sub_date_aware('+', l_val, r_val),
758 "-" => self.add_sub_date_aware('-', l_val, r_val),
759 "*" => self.numeric_binary(l_val, r_val, |a, b| a * b),
760 "/" => self.divide(l_val, r_val),
761 "^" => self.power(l_val, r_val),
762 "&" => Ok(LiteralValue::Text(format!(
763 "{}{}",
764 crate::coercion::to_text_invariant(&l_val),
765 crate::coercion::to_text_invariant(&r_val)
766 ))),
767 ":" => {
768 let lref = self.evaluate_ast_as_reference(left)?;
770 let rref = self.evaluate_ast_as_reference(right)?;
771 match crate::reference::combine_references(&lref, &rref) {
772 Ok(_r) => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
773 "Reference produced by ':' cannot be used directly as a value",
774 )),
775 Err(e) => Ok(LiteralValue::Error(e)),
776 }
777 }
778 _ => {
779 Err(ExcelError::new(ExcelErrorKind::NImpl)
780 .with_message(format!("Binary op '{op}'")))
781 }
782 }
783 }
784
785 fn add_sub_date_aware(
786 &self,
787 op: char,
788 left: LiteralValue,
789 right: LiteralValue,
790 ) -> Result<LiteralValue, ExcelError> {
791 debug_assert!(op == '+' || op == '-');
792
793 self.broadcast_apply(left, right, |l, r| {
794 use LiteralValue::*;
795
796 let date_system = self.context.date_system();
797
798 let date_like_serial = |v: &LiteralValue| -> Option<f64> {
799 match v {
800 Date(d) => Some(crate::builtins::datetime::date_to_serial_for(
801 date_system,
802 d,
803 )),
804 DateTime(dt) => Some(crate::builtins::datetime::datetime_to_serial_for(
805 date_system,
806 dt,
807 )),
808 _ => None,
809 }
810 };
811
812 let to_num = |v: &LiteralValue| -> Result<f64, ExcelError> {
813 crate::coercion::to_number_lenient_with_locale(v, &self.context.locale())
814 };
815
816 let serial_to_literal = |serial: f64| -> LiteralValue {
817 match crate::coercion::sanitize_numeric(serial) {
818 Ok(serial) => {
819 match crate::builtins::datetime::serial_to_datetime_for(date_system, serial)
820 {
821 Ok(dt) => {
822 if dt.time() == chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
823 Date(dt.date())
824 } else {
825 DateTime(dt)
826 }
827 }
828 Err(e) => Error(e),
829 }
830 }
831 Err(e) => Error(e),
832 }
833 };
834
835 if let Some(ls) = date_like_serial(&l) {
837 match op {
838 '+' => {
839 let rn = to_num(&r)?;
840 return Ok(serial_to_literal(ls + rn));
841 }
842 '-' => {
843 if let Some(rs) = date_like_serial(&r) {
845 return Ok(Number(ls - rs));
846 }
847 let rn = to_num(&r)?;
848 return Ok(serial_to_literal(ls - rn));
849 }
850 _ => unreachable!(),
851 }
852 }
853
854 if op == '+'
856 && let Some(rs) = date_like_serial(&r)
857 {
858 let ln = to_num(&l)?;
859 return Ok(serial_to_literal(ln + rs));
860 }
861
862 self.numeric_binary(l, r, |a, b| if op == '+' { a + b } else { a - b })
864 })
865 }
866
867 fn eval_function_to_calc(
869 &self,
870 name: &str,
871 args: &[ASTNode],
872 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
873 if let Some(fun) = self.context.get_function("", name) {
874 let handles: Vec<ArgumentHandle> =
875 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
876 let fctx = DefaultFunctionContext::new_with_sheet(
878 self.context,
879 self.current_cell,
880 self.current_sheet,
881 );
882 fun.dispatch(&handles, &fctx)
883 } else {
884 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
886 ExcelError::new(ExcelErrorKind::Name)
887 .with_message(format!("Unknown function: {name}")),
888 )))
889 }
890 }
891
892 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
893 self.eval_function_to_calc(name, args)
894 .map(|cv| cv.into_literal())
895 }
896
897 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
898 DefaultFunctionContext::new_with_sheet(self.context, cell_ref.cloned(), self.current_sheet)
899 }
900
901 fn eval_array_literal_to_calc(
903 &self,
904 rows: &[Vec<ASTNode>],
905 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
906 let mut out = Vec::with_capacity(rows.len());
907 for row in rows {
908 let mut r = Vec::with_capacity(row.len());
909 for cell in row {
910 r.push(self.evaluate_ast(cell)?.into_literal());
911 }
912 out.push(r);
913 }
914 Ok(crate::traits::CalcValue::Range(
915 crate::engine::range_view::RangeView::from_owned_rows(out, self.context.date_system()),
916 ))
917 }
918
919 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
920 self.eval_array_literal_to_calc(rows)
921 .map(|cv| cv.into_literal())
922 }
923
924 fn numeric_binary<F>(
926 &self,
927 left: LiteralValue,
928 right: LiteralValue,
929 f: F,
930 ) -> Result<LiteralValue, ExcelError>
931 where
932 F: Fn(f64, f64) -> f64 + Copy,
933 {
934 self.broadcast_apply(left, right, |l, r| {
935 let a = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
936 let b = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
937 match (a, b) {
938 (Ok(a), Ok(b)) => match crate::coercion::sanitize_numeric(f(a, b)) {
939 Ok(n2) => Ok(LiteralValue::Number(n2)),
940 Err(e) => Ok(LiteralValue::Error(e)),
941 },
942 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
943 }
944 })
945 }
946
947 fn divide(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
948 self.broadcast_apply(left, right, |l, r| {
949 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
950 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
951 let (a, b) = match (ln, rn) {
952 (Ok(a), Ok(b)) => (a, b),
953 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
954 };
955 if b == 0.0 {
956 return Ok(LiteralValue::Error(ExcelError::from_error_string(
957 "#DIV/0!",
958 )));
959 }
960 match crate::coercion::sanitize_numeric(a / b) {
961 Ok(n) => Ok(LiteralValue::Number(n)),
962 Err(e) => Ok(LiteralValue::Error(e)),
963 }
964 })
965 }
966
967 fn power(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
968 self.broadcast_apply(left, right, |l, r| {
969 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
970 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
971 let (a, b) = match (ln, rn) {
972 (Ok(a), Ok(b)) => (a, b),
973 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
974 };
975 if a < 0.0 && b.fract() != 0.0 {
977 return Ok(LiteralValue::Error(ExcelError::new_num()));
978 }
979 match crate::coercion::sanitize_numeric(a.powf(b)) {
980 Ok(n) => Ok(LiteralValue::Number(n)),
981 Err(e) => Ok(LiteralValue::Error(e)),
982 }
983 })
984 }
985
986 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
987 where
988 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
989 {
990 let mut out = Vec::with_capacity(arr.len());
991 for row in arr {
992 let mut new_row = Vec::with_capacity(row.len());
993 for cell in row {
994 new_row.push(match f(cell) {
995 Ok(v) => v,
996 Err(e) => LiteralValue::Error(e),
997 });
998 }
999 out.push(new_row);
1000 }
1001 Ok(LiteralValue::Array(out))
1002 }
1003
1004 fn combine_arrays<F>(
1005 &self,
1006 l: Vec<Vec<LiteralValue>>,
1007 r: Vec<Vec<LiteralValue>>,
1008 f: F,
1009 ) -> Result<LiteralValue, ExcelError>
1010 where
1011 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1012 {
1013 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
1015 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
1016 let target = match broadcast_shape(&[l_shape, r_shape]) {
1017 Ok(s) => s,
1018 Err(e) => return Ok(LiteralValue::Error(e)),
1019 };
1020
1021 let mut out = Vec::with_capacity(target.0);
1022 for i in 0..target.0 {
1023 let mut row = Vec::with_capacity(target.1);
1024 for j in 0..target.1 {
1025 let (li, lj) = project_index((i, j), l_shape);
1026 let (ri, rj) = project_index((i, j), r_shape);
1027 let lv = l
1028 .get(li)
1029 .and_then(|r| r.get(lj))
1030 .cloned()
1031 .unwrap_or(LiteralValue::Empty);
1032 let rv = r
1033 .get(ri)
1034 .and_then(|r| r.get(rj))
1035 .cloned()
1036 .unwrap_or(LiteralValue::Empty);
1037 row.push(match f(lv, rv) {
1038 Ok(v) => v,
1039 Err(e) => LiteralValue::Error(e),
1040 });
1041 }
1042 out.push(row);
1043 }
1044 Ok(LiteralValue::Array(out))
1045 }
1046
1047 fn broadcast_apply<F>(
1048 &self,
1049 left: LiteralValue,
1050 right: LiteralValue,
1051 f: F,
1052 ) -> Result<LiteralValue, ExcelError>
1053 where
1054 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1055 {
1056 use LiteralValue::*;
1057 match (left, right) {
1058 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
1059 (Array(arr), v) => {
1060 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1061 let shape_r = (1usize, 1usize);
1062 let target = match broadcast_shape(&[shape_l, shape_r]) {
1063 Ok(s) => s,
1064 Err(e) => return Ok(LiteralValue::Error(e)),
1065 };
1066 let mut out = Vec::with_capacity(target.0);
1067 for i in 0..target.0 {
1068 let mut row = Vec::with_capacity(target.1);
1069 for j in 0..target.1 {
1070 let (li, lj) = project_index((i, j), shape_l);
1071 let lv = arr
1072 .get(li)
1073 .and_then(|r| r.get(lj))
1074 .cloned()
1075 .unwrap_or(LiteralValue::Empty);
1076 row.push(match f(lv, v.clone()) {
1077 Ok(vv) => vv,
1078 Err(e) => LiteralValue::Error(e),
1079 });
1080 }
1081 out.push(row);
1082 }
1083 Ok(LiteralValue::Array(out))
1084 }
1085 (v, Array(arr)) => {
1086 let shape_l = (1usize, 1usize);
1087 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1088 let target = match broadcast_shape(&[shape_l, shape_r]) {
1089 Ok(s) => s,
1090 Err(e) => return Ok(LiteralValue::Error(e)),
1091 };
1092 let mut out = Vec::with_capacity(target.0);
1093 for i in 0..target.0 {
1094 let mut row = Vec::with_capacity(target.1);
1095 for j in 0..target.1 {
1096 let (ri, rj) = project_index((i, j), shape_r);
1097 let rv = arr
1098 .get(ri)
1099 .and_then(|r| r.get(rj))
1100 .cloned()
1101 .unwrap_or(LiteralValue::Empty);
1102 row.push(match f(v.clone(), rv) {
1103 Ok(vv) => vv,
1104 Err(e) => LiteralValue::Error(e),
1105 });
1106 }
1107 out.push(row);
1108 }
1109 Ok(LiteralValue::Array(out))
1110 }
1111 (l, r) => f(l, r),
1112 }
1113 }
1114
1115 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
1117 coercion::to_number_lenient(v)
1118 }
1119
1120 fn coerce_text(&self, v: &LiteralValue) -> String {
1121 coercion::to_text_invariant(v)
1122 }
1123
1124 fn compare(
1126 &self,
1127 op: &str,
1128 left: LiteralValue,
1129 right: LiteralValue,
1130 ) -> Result<LiteralValue, ExcelError> {
1131 use LiteralValue::*;
1132 if matches!(left, Error(_)) {
1133 return Ok(left);
1134 }
1135 if matches!(right, Error(_)) {
1136 return Ok(right);
1137 }
1138
1139 match (left, right) {
1141 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
1142 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
1143 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
1144 (l, r) => {
1145 let res = match (l, r) {
1146 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
1147 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
1148 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
1149 (Boolean(a), Boolean(b)) => {
1150 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
1151 }
1152 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
1153 (a, b) => {
1154 let an = crate::coercion::to_number_lenient_with_locale(
1156 &a,
1157 &self.context.locale(),
1158 )
1159 .ok();
1160 let bn = crate::coercion::to_number_lenient_with_locale(
1161 &b,
1162 &self.context.locale(),
1163 )
1164 .ok();
1165 if let (Some(a), Some(b)) = (an, bn) {
1166 self.cmp_f64(a, b, op)
1167 } else {
1168 self.cmp_text(
1169 &crate::coercion::to_text_invariant(&a),
1170 &crate::coercion::to_text_invariant(&b),
1171 op,
1172 )
1173 }
1174 }
1175 };
1176 Ok(LiteralValue::Boolean(res))
1177 }
1178 }
1179 }
1180
1181 fn cmp_f64(&self, a: f64, b: f64, op: &str) -> bool {
1182 match op {
1183 "=" => a == b,
1184 "<>" => a != b,
1185 ">" => a > b,
1186 "<" => a < b,
1187 ">=" => a >= b,
1188 "<=" => a <= b,
1189 _ => unreachable!(),
1190 }
1191 }
1192 fn cmp_text(&self, a: &str, b: &str, op: &str) -> bool {
1193 let loc = self.context.locale();
1194 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
1195 self.cmp_f64(
1196 a.cmp(&b) as i32 as f64,
1197 0.0,
1198 match op {
1199 "=" => "=",
1200 "<>" => "<>",
1201 ">" => ">",
1202 "<" => "<",
1203 ">=" => ">=",
1204 "<=" => "<=",
1205 _ => unreachable!(),
1206 },
1207 )
1208 }
1209}