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};
9use rustc_hash::FxHashMap;
10use std::{borrow::Cow, sync::Arc};
11
12use crate::engine::arena::ast::SheetKey;
13use crate::engine::arena::{AstNodeData, AstNodeId, CompactRefType, DataStore};
14use crate::engine::sheet_registry::SheetRegistry;
15use crate::formula_plane::template_canonical::LiteralSlotId;
16
17#[derive(Clone)]
18pub enum LocalBinding {
19 Value(LiteralValue),
20 Callable(Arc<dyn crate::traits::CustomCallable>),
21}
22
23#[derive(Clone, Default)]
24pub struct LocalEnv {
25 head: Option<Arc<EnvFrame>>,
26}
27
28#[derive(Clone)]
29struct EnvFrame {
30 parent: Option<Arc<EnvFrame>>,
31 bindings: FxHashMap<String, LocalBinding>,
32}
33
34impl LocalEnv {
35 #[inline(always)]
36 pub fn is_empty(&self) -> bool {
37 self.head.is_none()
38 }
39
40 fn norm(name: &str) -> String {
41 name.to_ascii_uppercase()
42 }
43
44 pub fn lookup(&self, name: &str) -> Option<LocalBinding> {
45 self.head.as_ref()?;
46 let key = Self::norm(name);
47 let mut cur = self.head.as_ref().cloned();
48 while let Some(frame) = cur {
49 if let Some(v) = frame.bindings.get(&key) {
50 return Some(v.clone());
51 }
52 cur = frame.parent.clone();
53 }
54 None
55 }
56
57 pub fn with_binding(&self, name: &str, value: LocalBinding) -> Self {
58 let mut bindings = FxHashMap::default();
59 bindings.insert(Self::norm(name), value);
60 Self {
61 head: Some(Arc::new(EnvFrame {
62 parent: self.head.clone(),
63 bindings,
64 })),
65 }
66 }
67}
68
69#[derive(Clone, Copy)]
70pub(crate) struct InterpreterParameterBindings<'a> {
71 pub(crate) literal_slots_by_node: &'a FxHashMap<AstNodeId, LiteralSlotId>,
72 pub(crate) literal_values: &'a [LiteralValue],
73}
74
75pub struct Interpreter<'a> {
76 pub context: &'a dyn EvaluationContext,
77 current_sheet: &'a str,
78 current_cell: Option<crate::CellRef>,
79 local_env: LocalEnv,
80 reference_row_delta: i64,
81 reference_col_delta: i64,
82 disable_ast_planner: bool,
83 parameter_bindings: Option<InterpreterParameterBindings<'a>>,
84}
85
86impl<'a> Interpreter<'a> {
87 pub fn new(context: &'a dyn EvaluationContext, current_sheet: &'a str) -> Self {
88 Self {
89 context,
90 current_sheet,
91 current_cell: None,
92 local_env: LocalEnv::default(),
93 reference_row_delta: 0,
94 reference_col_delta: 0,
95 disable_ast_planner: false,
96 parameter_bindings: None,
97 }
98 }
99
100 pub fn new_with_cell(
101 context: &'a dyn EvaluationContext,
102 current_sheet: &'a str,
103 cell: crate::CellRef,
104 ) -> Self {
105 Self {
106 context,
107 current_sheet,
108 current_cell: Some(cell),
109 local_env: LocalEnv::default(),
110 reference_row_delta: 0,
111 reference_col_delta: 0,
112 disable_ast_planner: false,
113 parameter_bindings: None,
114 }
115 }
116
117 pub fn current_sheet(&self) -> &'a str {
118 self.current_sheet
119 }
120
121 pub fn local_env(&self) -> &LocalEnv {
122 &self.local_env
123 }
124
125 pub(crate) fn with_current_cell(&self, cell: crate::CellRef) -> Self {
126 Self {
127 context: self.context,
128 current_sheet: self.current_sheet,
129 current_cell: Some(cell),
130 local_env: self.local_env.clone(),
131 reference_row_delta: self.reference_row_delta,
132 reference_col_delta: self.reference_col_delta,
133 disable_ast_planner: self.disable_ast_planner,
134 parameter_bindings: self.parameter_bindings,
135 }
136 }
137
138 pub fn with_local_env(&self, env: LocalEnv) -> Self {
139 Self {
140 context: self.context,
141 current_sheet: self.current_sheet,
142 current_cell: self.current_cell,
143 local_env: env,
144 reference_row_delta: self.reference_row_delta,
145 reference_col_delta: self.reference_col_delta,
146 disable_ast_planner: self.disable_ast_planner,
147 parameter_bindings: self.parameter_bindings,
148 }
149 }
150
151 pub(crate) fn with_parameter_bindings(
152 &self,
153 bindings: InterpreterParameterBindings<'a>,
154 ) -> Self {
155 Self {
156 context: self.context,
157 current_sheet: self.current_sheet,
158 current_cell: self.current_cell,
159 local_env: self.local_env.clone(),
160 reference_row_delta: self.reference_row_delta,
161 reference_col_delta: self.reference_col_delta,
162 disable_ast_planner: self.disable_ast_planner,
163 parameter_bindings: Some(bindings),
164 }
165 }
166
167 fn effective_reference<'r>(
168 &self,
169 reference: &'r ReferenceType,
170 ) -> Result<Cow<'r, ReferenceType>, ExcelError> {
171 if self.reference_row_delta == 0 && self.reference_col_delta == 0 {
172 return Ok(Cow::Borrowed(reference));
173 }
174
175 Ok(Cow::Owned(relocate_reference_for_offset(
176 reference,
177 self.reference_row_delta,
178 self.reference_col_delta,
179 )?))
180 }
181
182 fn resolve_local_reference(
183 &self,
184 reference: &ReferenceType,
185 ) -> Option<crate::traits::CalcValue<'a>> {
186 if self.local_env.is_empty() {
187 return None;
188 }
189 let name = match reference {
190 ReferenceType::NamedRange(name) => name,
191 _ => return None,
192 };
193 match self.local_env.lookup(name)? {
194 LocalBinding::Value(v) => Some(crate::traits::CalcValue::Scalar(v)),
195 LocalBinding::Callable(c) => Some(crate::traits::CalcValue::Callable(c)),
196 }
197 }
198
199 fn resolve_local_callable(&self, name: &str) -> Option<Arc<dyn crate::traits::CustomCallable>> {
200 if self.local_env.is_empty() {
201 return None;
202 }
203 match self.local_env.lookup(name)? {
204 LocalBinding::Callable(c) => Some(c),
205 LocalBinding::Value(_) => None,
206 }
207 }
208
209 pub fn resolve_local_name(&self, name: &str) -> Option<LocalBinding> {
210 self.local_env.lookup(name)
211 }
212
213 pub fn resolve_range_view<'c>(
214 &'c self,
215 reference: &ReferenceType,
216 current_sheet: &str,
217 ) -> Result<crate::engine::range_view::RangeView<'c>, ExcelError> {
218 self.context.resolve_range_view(reference, current_sheet)
219 }
220
221 pub fn evaluate_ast_as_reference(&self, node: &ASTNode) -> Result<ReferenceType, ExcelError> {
226 match &node.node_type {
227 ASTNodeType::Reference { reference, .. } => {
228 self.reference_for_current_offset(reference)
229 }
230 ASTNodeType::Function { name, args } => {
231 if let Some(fun) = self.context.get_function("", name) {
232 let handles: Vec<ArgumentHandle> =
234 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
235 let fctx = DefaultFunctionContext::new_with_sheet(
236 self.context,
237 None,
238 self.current_sheet,
239 );
240 if let Some(res) = fun.eval_reference(&handles, &fctx) {
241 res
242 } else {
243 Err(ExcelError::new(ExcelErrorKind::Ref)
244 .with_message("Function does not return a reference"))
245 }
246 } else {
247 Err(ExcelError::new(ExcelErrorKind::Name)
248 .with_message(format!("Unknown function: {name}")))
249 }
250 }
251 ASTNodeType::BinaryOp { op, left, right } if op == ":" => {
252 let lref = self.evaluate_ast_as_reference(left)?;
253 let rref = self.evaluate_ast_as_reference(right)?;
254 crate::reference::combine_references(&lref, &rref)
255 }
256 ASTNodeType::Array(_)
257 | ASTNodeType::UnaryOp { .. }
258 | ASTNodeType::BinaryOp { .. }
259 | ASTNodeType::Call { .. }
260 | ASTNodeType::Literal(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
261 .with_message("Expression cannot be used as a reference")),
262 }
263 }
264
265 pub(crate) fn evaluate_arena_ast_as_reference(
266 &self,
267 node_id: AstNodeId,
268 data_store: &DataStore,
269 sheet_registry: &SheetRegistry,
270 ) -> Result<ReferenceType, ExcelError> {
271 let node = data_store.get_node(node_id).ok_or_else(|| {
272 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
273 })?;
274
275 match node {
276 AstNodeData::Reference { ref_type, .. } => {
277 let reference =
278 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
279 self.reference_for_current_offset(&reference)
280 }
281 AstNodeData::Function { name_id, .. } => {
282 let name = data_store.resolve_ast_string(*name_id);
283 let fun = self.context.get_function("", name).ok_or_else(|| {
284 ExcelError::new(ExcelErrorKind::Name)
285 .with_message(format!("Unknown function: {name}"))
286 })?;
287
288 let args = data_store.get_args(node_id).ok_or_else(|| {
289 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
290 })?;
291
292 let handles: Vec<ArgumentHandle> = args
293 .iter()
294 .copied()
295 .map(|arg_id| {
296 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
297 })
298 .collect();
299
300 let fctx =
301 DefaultFunctionContext::new_with_sheet(self.context, None, self.current_sheet);
302
303 fun.eval_reference(&handles, &fctx).ok_or_else(|| {
304 ExcelError::new(ExcelErrorKind::Ref)
305 .with_message("Function does not return a reference")
306 })?
307 }
308 AstNodeData::BinaryOp {
309 op_id,
310 left_id,
311 right_id,
312 } => {
313 let op = data_store.resolve_ast_string(*op_id);
314 if op != ":" {
315 return Err(ExcelError::new(ExcelErrorKind::Ref)
316 .with_message("Expression cannot be used as a reference"));
317 }
318 let lref =
319 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
320 let rref =
321 self.evaluate_arena_ast_as_reference(*right_id, data_store, sheet_registry)?;
322 crate::reference::combine_references(&lref, &rref)
323 }
324 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
325 .with_message("Expression cannot be used as a reference")),
326 }
327 }
328
329 pub fn evaluate_ast(&self, node: &ASTNode) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
331 self.evaluate_ast_uncached(node)
332 }
333
334 pub(crate) fn evaluate_ast_with_offset(
335 &self,
336 node: &ASTNode,
337 row_delta: i64,
338 col_delta: i64,
339 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
340 let offset = Self {
341 context: self.context,
342 current_sheet: self.current_sheet,
343 current_cell: self.current_cell,
344 local_env: self.local_env.clone(),
345 reference_row_delta: row_delta,
346 reference_col_delta: col_delta,
347 disable_ast_planner: true,
348 parameter_bindings: self.parameter_bindings,
349 };
350 offset.evaluate_ast_uncached(node)
351 }
352
353 pub(crate) fn reference_for_current_offset(
354 &self,
355 reference: &ReferenceType,
356 ) -> Result<ReferenceType, ExcelError> {
357 self.effective_reference(reference)
358 .map(|reference| reference.into_owned())
359 }
360
361 pub(crate) fn evaluate_arena_ast_with_offset(
362 &self,
363 node_id: AstNodeId,
364 row_delta: i64,
365 col_delta: i64,
366 data_store: &DataStore,
367 sheet_registry: &SheetRegistry,
368 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
369 let offset = Self {
370 context: self.context,
371 current_sheet: self.current_sheet,
372 current_cell: self.current_cell,
373 local_env: self.local_env.clone(),
374 reference_row_delta: row_delta,
375 reference_col_delta: col_delta,
376 disable_ast_planner: true,
377 parameter_bindings: self.parameter_bindings,
378 };
379 offset.evaluate_arena_ast(node_id, data_store, sheet_registry)
380 }
381
382 pub(crate) fn evaluate_arena_ast(
383 &self,
384 node_id: AstNodeId,
385 data_store: &DataStore,
386 sheet_registry: &SheetRegistry,
387 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
388 let node = data_store.get_node(node_id).ok_or_else(|| {
389 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
390 })?;
391
392 match node {
393 AstNodeData::Literal(vref) => {
394 if let Some(bindings) = self.parameter_bindings
395 && let Some(slot_id) = bindings.literal_slots_by_node.get(&node_id)
396 && let Some(value) = bindings.literal_values.get(slot_id.0 as usize)
397 {
398 return Ok(crate::traits::CalcValue::Scalar(value.clone()));
399 }
400 Ok(crate::traits::CalcValue::Scalar(
401 data_store.retrieve_value(*vref),
402 ))
403 }
404 AstNodeData::Reference { ref_type, .. } => {
405 if self.local_env.is_empty()
406 && let CompactRefType::Cell {
407 sheet,
408 row,
409 col,
410 row_abs,
411 col_abs,
412 } = ref_type
413 && *row > 0
414 && *col > 0
415 {
416 let sheet_name = match sheet {
417 Some(SheetKey::Id(id)) => Some(sheet_registry.name(*id)),
418 Some(SheetKey::Name(name_id)) => {
419 Some(data_store.resolve_ast_string(*name_id))
420 }
421 None => None,
422 };
423 let row = shift_axis_for_offset(*row, self.reference_row_delta, *row_abs)?;
424 let col = shift_axis_for_offset(*col, self.reference_col_delta, *col_abs)?;
425 let value = self.context.resolve_cell_reference_value(
426 sheet_name,
427 row,
428 col,
429 self.current_sheet,
430 )?;
431 Ok(crate::traits::CalcValue::Scalar(value))
432 } else {
433 let reference =
434 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
435 let reference = self.effective_reference(&reference)?;
436 if let Some(local) = self.resolve_local_reference(&reference) {
437 return Ok(local);
438 }
439 self.eval_reference_to_calc(&reference)
440 }
441 }
442 AstNodeData::UnaryOp { op_id, expr_id } => {
443 let expr = self.evaluate_arena_ast(*expr_id, data_store, sheet_registry)?;
444
445 let op = data_store.resolve_ast_string(*op_id);
446 if op == "@" {
447 if let Some(AstNodeData::Reference { ref_type, .. }) =
450 data_store.get_node(*expr_id)
451 {
452 let reference = data_store
453 .reconstruct_reference_type_for_eval(ref_type, sheet_registry);
454 let v = self.implicit_intersection_from_reference(&reference);
455 return Ok(crate::traits::CalcValue::Scalar(v));
456 }
457
458 let v = self.eval_implicit_intersection_calc(expr);
459 return Ok(crate::traits::CalcValue::Scalar(v));
460 }
461 let v = expr.into_literal();
463 match v {
464 LiteralValue::Array(arr) => self
465 .map_array(arr, |cell| self.eval_unary_scalar(op, cell))
466 .map(crate::traits::CalcValue::Scalar),
467 other => self
468 .eval_unary_scalar(op, other)
469 .map(crate::traits::CalcValue::Scalar),
470 }
471 }
472 AstNodeData::BinaryOp {
473 op_id,
474 left_id,
475 right_id,
476 } => {
477 let op = data_store.resolve_ast_string(*op_id);
478 if op == ":" {
479 let lref =
480 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
481 let rref = self.evaluate_arena_ast_as_reference(
482 *right_id,
483 data_store,
484 sheet_registry,
485 )?;
486 return match crate::reference::combine_references(&lref, &rref) {
487 Ok(_r) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
488 ExcelError::new(ExcelErrorKind::Ref).with_message(
489 "Reference produced by ':' cannot be used directly as a value",
490 ),
491 ))),
492 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
493 };
494 }
495
496 let left = self
497 .evaluate_arena_ast(*left_id, data_store, sheet_registry)?
498 .into_literal();
499 let right = self
500 .evaluate_arena_ast(*right_id, data_store, sheet_registry)?
501 .into_literal();
502
503 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
504 return self
505 .compare(op, left, right)
506 .map(crate::traits::CalcValue::Scalar);
507 }
508
509 match op {
510 "+" => self
511 .add_sub_date_aware('+', left, right)
512 .map(crate::traits::CalcValue::Scalar),
513 "-" => self
514 .add_sub_date_aware('-', left, right)
515 .map(crate::traits::CalcValue::Scalar),
516 "*" => self
517 .numeric_binary(left, right, |a, b| a * b)
518 .map(crate::traits::CalcValue::Scalar),
519 "/" => self
520 .divide(left, right)
521 .map(crate::traits::CalcValue::Scalar),
522 "^" => self
523 .power(left, right)
524 .map(crate::traits::CalcValue::Scalar),
525 "&" => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
526 format!(
527 "{}{}",
528 crate::coercion::to_text_invariant(&left),
529 crate::coercion::to_text_invariant(&right)
530 ),
531 ))),
532 _ => Err(ExcelError::new(ExcelErrorKind::NImpl)
533 .with_message(format!("Binary op '{op}'"))),
534 }
535 }
536 AstNodeData::Array { .. } => {
537 let (rows, cols, elements) =
538 data_store.get_array_elems(node_id).ok_or_else(|| {
539 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
540 })?;
541
542 let rows_usize = rows as usize;
543 let cols_usize = cols as usize;
544 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
545 for r in 0..rows_usize {
546 let mut row = Vec::with_capacity(cols_usize);
547 for c in 0..cols_usize {
548 let idx = r * cols_usize + c;
549 if let Some(&elem_id) = elements.get(idx) {
550 row.push(
551 self.evaluate_arena_ast(elem_id, data_store, sheet_registry)?
552 .into_literal(),
553 );
554 }
555 }
556 out.push(row);
557 }
558
559 Ok(crate::traits::CalcValue::Range(
560 crate::engine::range_view::RangeView::from_owned_rows(
561 out,
562 self.context.date_system(),
563 ),
564 ))
565 }
566 AstNodeData::Function { name_id, .. } => {
567 let name = data_store.resolve_ast_string(*name_id);
568 let args = data_store.get_args(node_id).ok_or_else(|| {
569 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
570 })?;
571
572 if let Some(fun) = self.context.get_function("", name) {
573 let handles: Vec<ArgumentHandle> = args
574 .iter()
575 .copied()
576 .map(|arg_id| {
577 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
578 })
579 .collect();
580
581 let fctx = DefaultFunctionContext::new_with_sheet(
582 self.context,
583 self.current_cell,
584 self.current_sheet,
585 );
586
587 return fun.dispatch(&handles, &fctx);
588 }
589
590 if let Some(callable) = self.resolve_local_callable(name) {
591 let mut eval_args = Vec::with_capacity(args.len());
592 for arg_id in args {
593 eval_args.push(
594 self.evaluate_arena_ast(*arg_id, data_store, sheet_registry)?
595 .into_literal(),
596 );
597 }
598 return callable.invoke(self, &eval_args);
599 }
600
601 Err(ExcelError::new(ExcelErrorKind::Name)
602 .with_message(format!("Unknown function: {name}")))
603 }
604 }
605 }
606
607 fn evaluate_ast_uncached(
608 &self,
609 node: &ASTNode,
610 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
611 if self.disable_ast_planner {
612 return self.eval_tree_uncached(node);
613 }
614
615 let current_sheet = self.current_sheet.to_string();
619 let range_probe = |reference: &ReferenceType| -> Option<(u32, u32)> {
620 use formualizer_parse::parser::ReferenceType as RT;
622 match reference {
623 RT::Range {
624 sheet,
625 start_row,
626 start_col,
627 end_row,
628 end_col,
629 ..
630 } => {
631 let sheet_name = sheet.as_deref().unwrap_or(¤t_sheet);
632 let mut sr = *start_row;
634 let mut sc = *start_col;
635 let mut er = *end_row;
636 let mut ec = *end_col;
637
638 if sr.is_none() && er.is_none() {
640 let scv = sc.unwrap_or(1);
642 let ecv = ec.unwrap_or(scv);
643 sr = Some(1);
644 if let Some((_, max_r)) =
645 self.context.used_rows_for_columns(sheet_name, scv, ecv)
646 {
647 er = Some(max_r);
648 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
649 er = Some(max_rows);
650 }
651 }
652
653 if sc.is_none() && ec.is_none() {
655 let srv = sr.unwrap_or(1);
657 let erv = er.unwrap_or(srv);
658 sc = Some(1);
659 if let Some((_, max_c)) =
660 self.context.used_cols_for_rows(sheet_name, srv, erv)
661 {
662 ec = Some(max_c);
663 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
664 ec = Some(max_cols);
665 }
666 }
667
668 if sr.is_some() && er.is_none() {
670 let scv = sc.unwrap_or(1);
671 let ecv = ec.unwrap_or(scv);
672 if let Some((_, max_r)) =
673 self.context.used_rows_for_columns(sheet_name, scv, ecv)
674 {
675 er = Some(max_r);
676 } else if let Some((max_rows, _)) = self.context.sheet_bounds(sheet_name) {
677 er = Some(max_rows);
678 }
679 }
680 if er.is_some() && sr.is_none() {
681 sr = Some(1);
683 }
684 if sc.is_some() && ec.is_none() {
685 let srv = sr.unwrap_or(1);
686 let erv = er.unwrap_or(srv);
687 if let Some((_, max_c)) =
688 self.context.used_cols_for_rows(sheet_name, srv, erv)
689 {
690 ec = Some(max_c);
691 } else if let Some((_, max_cols)) = self.context.sheet_bounds(sheet_name) {
692 ec = Some(max_cols);
693 }
694 }
695 if ec.is_some() && sc.is_none() {
696 sc = Some(1);
698 }
699
700 let sr = sr.unwrap_or(1);
701 let sc = sc.unwrap_or(1);
702 let er = er.unwrap_or(sr.saturating_sub(1));
703 let ec = ec.unwrap_or(sc.saturating_sub(1));
704 if er < sr || ec < sc {
705 return Some((0, 0));
706 }
707 Some((er.saturating_sub(sr) + 1, ec.saturating_sub(sc) + 1))
708 }
709 RT::Cell { .. } => Some((1, 1)),
710 _ => None,
711 }
712 };
713 let fn_lookup = |ns: &str, name: &str| self.context.get_function(ns, name);
714
715 let mut planner = crate::planner::Planner::new(crate::planner::PlanConfig::default())
716 .with_range_probe(&range_probe)
717 .with_function_lookup(&fn_lookup);
718 let plan = planner.plan(node);
719 self.eval_with_plan(node, &plan.root)
720 }
721
722 fn eval_tree_uncached(
723 &self,
724 node: &ASTNode,
725 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
726 match &node.node_type {
727 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
728 ASTNodeType::Reference { reference, .. } => self.eval_ast_reference_to_calc(reference),
729 ASTNodeType::UnaryOp { op, expr } => self
730 .eval_unary(op, expr)
731 .map(crate::traits::CalcValue::Scalar),
732 ASTNodeType::BinaryOp { op, left, right } => self
733 .eval_binary(op, left, right)
734 .map(crate::traits::CalcValue::Scalar),
735 ASTNodeType::Function { name, args } => self.eval_function_to_calc(name, args),
736 ASTNodeType::Call { .. } => Err(ExcelError::new(ExcelErrorKind::NImpl)
737 .with_message("Immediate-invocation calls are not yet supported")),
738 ASTNodeType::Array(rows) => self.eval_array_literal_to_calc(rows),
739 }
740 }
741
742 fn eval_with_plan(
743 &self,
744 node: &ASTNode,
745 plan_node: &crate::planner::PlanNode,
746 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
747 match &node.node_type {
748 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
749 ASTNodeType::Reference { reference, .. } => self.eval_ast_reference_to_calc(reference),
750 ASTNodeType::UnaryOp { op, expr } => {
751 self.eval_unary(op, expr)
754 .map(crate::traits::CalcValue::Scalar)
755 }
756 ASTNodeType::BinaryOp { op, left, right } => self
757 .eval_binary(op, left, right)
758 .map(crate::traits::CalcValue::Scalar),
759 ASTNodeType::Function { name, args } => {
760 let strategy = plan_node.strategy;
761 if let Some(fun) = self.context.get_function("", name) {
762 use crate::function::FnCaps;
763 use crate::planner::ExecStrategy;
764 let caps = fun.caps();
765
766 if caps.contains(FnCaps::SHORT_CIRCUIT) || caps.contains(FnCaps::VOLATILE) {
768 return self.eval_function_to_calc(name, args);
769 }
770
771 if matches!(strategy, ExecStrategy::ArgParallel)
775 && caps.contains(FnCaps::PARALLEL_ARGS)
776 {
777 for arg in args {
779 match &arg.node_type {
780 ASTNodeType::Reference { reference, .. } => {
781 if let Ok(reference) = self.effective_reference(reference) {
782 let _ = self
783 .context
784 .resolve_range_view(&reference, self.current_sheet);
785 }
786 }
787 _ => {
788 let _ = self.evaluate_ast(arg);
789 }
790 }
791 }
792 return self.eval_function_to_calc(name, args);
793 }
794
795 return self.eval_function_to_calc(name, args);
797 }
798 self.eval_function_to_calc(name, args)
799 }
800 ASTNodeType::Call { .. } => Err(ExcelError::new(ExcelErrorKind::NImpl)
801 .with_message("Immediate-invocation calls are not yet supported")),
802 ASTNodeType::Array(rows) => self.eval_array_literal_to_calc(rows),
803 }
804 }
805
806 fn eval_ast_reference_to_calc(
808 &self,
809 reference: &ReferenceType,
810 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
811 if !self.local_env.is_empty() {
812 let reference = self.effective_reference(reference)?;
813 if let Some(local) = self.resolve_local_reference(&reference) {
814 return Ok(local);
815 }
816 return self.eval_reference_to_calc(&reference);
817 }
818
819 if let ReferenceType::Cell {
820 sheet,
821 row,
822 col,
823 row_abs,
824 col_abs,
825 } = reference
826 {
827 let row = shift_axis_for_offset(*row, self.reference_row_delta, *row_abs)?;
828 let col = shift_axis_for_offset(*col, self.reference_col_delta, *col_abs)?;
829 return Ok(crate::traits::CalcValue::Scalar(
830 self.context.resolve_cell_reference_value(
831 sheet.as_deref(),
832 row,
833 col,
834 self.current_sheet,
835 )?,
836 ));
837 }
838
839 let reference = self.effective_reference(reference)?;
840 self.eval_reference_to_calc(&reference)
841 }
842
843 fn eval_reference_to_calc(
844 &self,
845 reference: &ReferenceType,
846 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
847 if let ReferenceType::Cell {
848 sheet, row, col, ..
849 } = reference
850 {
851 return Ok(crate::traits::CalcValue::Scalar(
852 self.context.resolve_cell_reference_value(
853 sheet.as_deref(),
854 *row,
855 *col,
856 self.current_sheet,
857 )?,
858 ));
859 }
860
861 let view = self
862 .context
863 .resolve_range_view(reference, self.current_sheet)?
864 .with_cancel_token(self.context.cancellation_token());
865 Ok(crate::traits::CalcValue::Range(view))
866 }
867
868 fn eval_reference(&self, reference: &ReferenceType) -> Result<LiteralValue, ExcelError> {
869 self.eval_reference_to_calc(reference)
870 .map(|cv| cv.into_literal())
871 }
872
873 fn eval_unary(&self, op: &str, expr: &ASTNode) -> Result<LiteralValue, ExcelError> {
875 if op == "@" {
876 if let ASTNodeType::Reference { reference, .. } = &expr.node_type {
877 let reference = self.effective_reference(reference)?;
878 return Ok(self.implicit_intersection_from_reference(&reference));
879 }
880
881 let cv = self.evaluate_ast(expr)?;
882 return Ok(self.eval_implicit_intersection_calc(cv));
883 }
884
885 let v = self.evaluate_ast(expr)?.into_literal();
886 match v {
887 LiteralValue::Array(arr) => {
888 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
889 }
890 other => self.eval_unary_scalar(op, other),
891 }
892 }
893
894 fn eval_unary_scalar(&self, op: &str, v: LiteralValue) -> Result<LiteralValue, ExcelError> {
895 match op {
896 "+" => Ok(v),
901 "-" => self.apply_number_unary(v, |n| -n),
902 "%" => self.apply_number_unary(v, |n| n / 100.0),
903 _ => {
904 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
905 }
906 }
907 }
908
909 fn eval_implicit_intersection_calc(&self, cv: crate::traits::CalcValue<'a>) -> LiteralValue {
910 let (cur_r0, cur_c0) = match self.current_cell {
911 Some(cell) => (cell.coord.row() as usize, cell.coord.col() as usize),
912 None => (0usize, 0usize),
913 };
914
915 match cv {
916 crate::traits::CalcValue::Scalar(v) => match v {
917 LiteralValue::Array(arr) => {
918 if arr.is_empty() || arr.first().map(|r| r.is_empty()).unwrap_or(true) {
919 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
920 }
921 arr[0][0].clone()
922 }
923 other => other,
924 },
925 crate::traits::CalcValue::Range(rv) => {
926 if rv.is_empty() {
927 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
928 }
929
930 if rv.sheet_name() == "__tmp" {
935 return rv.get_cell(0, 0);
936 }
937
938 if let Some(v) = rv.as_1x1() {
939 return v;
940 }
941
942 let (rows, cols) = rv.dims();
943 let sr = rv.start_row();
944 let sc = rv.start_col();
945 let er = rv.end_row();
946 let ec = rv.end_col();
947
948 if cols == 1 {
953 if cur_r0 < sr || cur_r0 > er {
954 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
955 }
956 let rel_r = cur_r0 - sr;
957 return rv.get_cell(rel_r, 0);
958 }
959
960 if rows == 1 {
961 if cur_c0 < sc || cur_c0 > ec {
962 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
963 }
964 let rel_c = cur_c0 - sc;
965 return rv.get_cell(0, rel_c);
966 }
967
968 if cur_r0 < sr || cur_r0 > er || cur_c0 < sc || cur_c0 > ec {
969 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
970 }
971 let rel_r = cur_r0 - sr;
972 let rel_c = cur_c0 - sc;
973 rv.get_cell(rel_r, rel_c)
974 }
975 crate::traits::CalcValue::Callable(_) => LiteralValue::Error(
976 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
977 ),
978 }
979 }
980
981 fn implicit_intersection_from_reference(&self, reference: &ReferenceType) -> LiteralValue {
982 let (cur_r1, cur_c1) = match self.current_cell {
983 Some(cell) => (
984 cell.coord.row().saturating_add(1),
985 cell.coord.col().saturating_add(1),
986 ),
987 None => (1u32, 1u32),
988 };
989
990 match reference {
991 ReferenceType::Cell {
992 sheet, row, col, ..
993 } => {
994 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
995 match self
996 .context
997 .resolve_cell_reference(Some(sheet_name), *row, *col)
998 {
999 Ok(v) => v,
1000 Err(e) => LiteralValue::Error(e),
1001 }
1002 }
1003 ReferenceType::Range {
1004 sheet,
1005 start_row,
1006 start_col,
1007 end_row,
1008 end_col,
1009 ..
1010 } => {
1011 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
1012
1013 let (sr, sc, er, ec) = match (start_row, start_col, end_row, end_col) {
1014 (Some(sr), Some(sc), Some(er), Some(ec)) => (*sr, *sc, *er, *ec),
1015 _ => {
1016 let cv = match self.eval_reference_to_calc(reference) {
1019 Ok(cv) => cv,
1020 Err(e) => return LiteralValue::Error(e),
1021 };
1022 return self.eval_implicit_intersection_calc(cv);
1023 }
1024 };
1025
1026 let (mut sr, mut er) = (sr, er);
1028 let (mut sc, mut ec) = (sc, ec);
1029 if sr > er {
1030 std::mem::swap(&mut sr, &mut er);
1031 }
1032 if sc > ec {
1033 std::mem::swap(&mut sc, &mut ec);
1034 }
1035
1036 let pick = if sc == ec {
1037 if cur_r1 < sr || cur_r1 > er {
1039 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1040 }
1041 (cur_r1, sc)
1042 } else if sr == er {
1043 if cur_c1 < sc || cur_c1 > ec {
1045 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1046 }
1047 (sr, cur_c1)
1048 } else {
1049 if cur_r1 < sr || cur_r1 > er || cur_c1 < sc || cur_c1 > ec {
1051 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1052 }
1053 (cur_r1, cur_c1)
1054 };
1055
1056 match self
1057 .context
1058 .resolve_cell_reference(Some(sheet_name), pick.0, pick.1)
1059 {
1060 Ok(v) => v,
1061 Err(e) => LiteralValue::Error(e),
1062 }
1063 }
1064 other => {
1066 let cv = match self.eval_reference_to_calc(other) {
1067 Ok(cv) => cv,
1068 Err(e) => return LiteralValue::Error(e),
1069 };
1070 self.eval_implicit_intersection_calc(cv)
1071 }
1072 }
1073 }
1074
1075 fn apply_number_unary<F>(&self, v: LiteralValue, f: F) -> Result<LiteralValue, ExcelError>
1076 where
1077 F: Fn(f64) -> f64,
1078 {
1079 match crate::coercion::to_number_lenient_with_locale(&v, &self.context.locale()) {
1080 Ok(n) => match crate::coercion::sanitize_numeric(f(n)) {
1081 Ok(n2) => Ok(LiteralValue::Number(n2)),
1082 Err(e) => Ok(LiteralValue::Error(e)),
1083 },
1084 Err(e) => Ok(LiteralValue::Error(e)),
1085 }
1086 }
1087
1088 fn eval_binary(
1090 &self,
1091 op: &str,
1092 left: &ASTNode,
1093 right: &ASTNode,
1094 ) -> Result<LiteralValue, ExcelError> {
1095 if matches!(op, "=" | "<>" | ">" | "<" | ">=" | "<=") {
1097 let l = self.evaluate_ast(left)?.into_literal();
1098 let r = self.evaluate_ast(right)?.into_literal();
1099 return self.compare(op, l, r);
1100 }
1101
1102 let l_val = self.evaluate_ast(left)?.into_literal();
1103 let r_val = self.evaluate_ast(right)?.into_literal();
1104
1105 match op {
1106 "+" => self.add_sub_date_aware('+', l_val, r_val),
1107 "-" => self.add_sub_date_aware('-', l_val, r_val),
1108 "*" => self.numeric_binary(l_val, r_val, |a, b| a * b),
1109 "/" => self.divide(l_val, r_val),
1110 "^" => self.power(l_val, r_val),
1111 "&" => Ok(LiteralValue::Text(format!(
1112 "{}{}",
1113 crate::coercion::to_text_invariant(&l_val),
1114 crate::coercion::to_text_invariant(&r_val)
1115 ))),
1116 ":" => {
1117 let lref = self.evaluate_ast_as_reference(left)?;
1119 let rref = self.evaluate_ast_as_reference(right)?;
1120 match crate::reference::combine_references(&lref, &rref) {
1121 Ok(_r) => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1122 "Reference produced by ':' cannot be used directly as a value",
1123 )),
1124 Err(e) => Ok(LiteralValue::Error(e)),
1125 }
1126 }
1127 _ => {
1128 Err(ExcelError::new(ExcelErrorKind::NImpl)
1129 .with_message(format!("Binary op '{op}'")))
1130 }
1131 }
1132 }
1133
1134 fn add_sub_date_aware(
1135 &self,
1136 op: char,
1137 left: LiteralValue,
1138 right: LiteralValue,
1139 ) -> Result<LiteralValue, ExcelError> {
1140 debug_assert!(op == '+' || op == '-');
1141
1142 self.broadcast_apply(left, right, |l, r| {
1143 use LiteralValue::*;
1144
1145 let date_system = self.context.date_system();
1146
1147 let date_like_serial = |v: &LiteralValue| -> Option<f64> {
1148 match v {
1149 Date(d) => Some(crate::builtins::datetime::date_to_serial_for(
1150 date_system,
1151 d,
1152 )),
1153 DateTime(dt) => Some(crate::builtins::datetime::datetime_to_serial_for(
1154 date_system,
1155 dt,
1156 )),
1157 _ => None,
1158 }
1159 };
1160
1161 let to_num = |v: &LiteralValue| -> Result<f64, ExcelError> {
1162 crate::coercion::to_number_lenient_with_locale(v, &self.context.locale())
1163 };
1164
1165 let serial_to_literal = |serial: f64| -> LiteralValue {
1166 match crate::coercion::sanitize_numeric(serial) {
1167 Ok(serial) => {
1168 match crate::builtins::datetime::serial_to_datetime_for(date_system, serial)
1169 {
1170 Ok(dt) => {
1171 if dt.time() == chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
1172 Date(dt.date())
1173 } else {
1174 DateTime(dt)
1175 }
1176 }
1177 Err(e) => Error(e),
1178 }
1179 }
1180 Err(e) => Error(e),
1181 }
1182 };
1183
1184 if let Some(ls) = date_like_serial(&l) {
1186 match op {
1187 '+' => {
1188 let rn = to_num(&r)?;
1189 return Ok(serial_to_literal(ls + rn));
1190 }
1191 '-' => {
1192 if let Some(rs) = date_like_serial(&r) {
1194 return Ok(Number(ls - rs));
1195 }
1196 let rn = to_num(&r)?;
1197 return Ok(serial_to_literal(ls - rn));
1198 }
1199 _ => unreachable!(),
1200 }
1201 }
1202
1203 if op == '+'
1205 && let Some(rs) = date_like_serial(&r)
1206 {
1207 let ln = to_num(&l)?;
1208 return Ok(serial_to_literal(ln + rs));
1209 }
1210
1211 self.numeric_binary(l, r, |a, b| if op == '+' { a + b } else { a - b })
1213 })
1214 }
1215
1216 fn eval_function_to_calc(
1218 &self,
1219 name: &str,
1220 args: &[ASTNode],
1221 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1222 if let Some(fun) = self.context.get_function("", name) {
1223 let handles: Vec<ArgumentHandle> =
1224 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
1225 let fctx = DefaultFunctionContext::new_with_sheet(
1227 self.context,
1228 self.current_cell,
1229 self.current_sheet,
1230 );
1231 return fun.dispatch(&handles, &fctx);
1232 }
1233
1234 if let Some(callable) = self.resolve_local_callable(name) {
1235 let mut eval_args = Vec::with_capacity(args.len());
1236 for arg in args {
1237 eval_args.push(self.evaluate_ast(arg)?.into_literal());
1238 }
1239 return callable.invoke(self, &eval_args);
1240 }
1241
1242 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1244 ExcelError::new(ExcelErrorKind::Name).with_message(format!("Unknown function: {name}")),
1245 )))
1246 }
1247
1248 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
1249 self.eval_function_to_calc(name, args)
1250 .map(|cv| cv.into_literal())
1251 }
1252
1253 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
1254 DefaultFunctionContext::new_with_sheet(self.context, cell_ref.cloned(), self.current_sheet)
1255 }
1256
1257 fn eval_array_literal_to_calc(
1259 &self,
1260 rows: &[Vec<ASTNode>],
1261 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1262 let mut out = Vec::with_capacity(rows.len());
1263 for row in rows {
1264 let mut r = Vec::with_capacity(row.len());
1265 for cell in row {
1266 r.push(self.evaluate_ast(cell)?.into_literal());
1267 }
1268 out.push(r);
1269 }
1270 Ok(crate::traits::CalcValue::Range(
1271 crate::engine::range_view::RangeView::from_owned_rows(out, self.context.date_system()),
1272 ))
1273 }
1274
1275 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
1276 self.eval_array_literal_to_calc(rows)
1277 .map(|cv| cv.into_literal())
1278 }
1279
1280 fn numeric_binary<F>(
1282 &self,
1283 left: LiteralValue,
1284 right: LiteralValue,
1285 f: F,
1286 ) -> Result<LiteralValue, ExcelError>
1287 where
1288 F: Fn(f64, f64) -> f64 + Copy,
1289 {
1290 self.broadcast_apply(left, right, |l, r| {
1291 let a = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1292 let b = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1293 match (a, b) {
1294 (Ok(a), Ok(b)) => match crate::coercion::sanitize_numeric(f(a, b)) {
1295 Ok(n2) => Ok(LiteralValue::Number(n2)),
1296 Err(e) => Ok(LiteralValue::Error(e)),
1297 },
1298 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
1299 }
1300 })
1301 }
1302
1303 fn divide(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
1304 self.broadcast_apply(left, right, |l, r| {
1305 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1306 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1307 let (a, b) = match (ln, rn) {
1308 (Ok(a), Ok(b)) => (a, b),
1309 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
1310 };
1311 if b == 0.0 {
1312 return Ok(LiteralValue::Error(ExcelError::from_error_string(
1313 "#DIV/0!",
1314 )));
1315 }
1316 match crate::coercion::sanitize_numeric(a / b) {
1317 Ok(n) => Ok(LiteralValue::Number(n)),
1318 Err(e) => Ok(LiteralValue::Error(e)),
1319 }
1320 })
1321 }
1322
1323 fn power(&self, left: LiteralValue, right: LiteralValue) -> Result<LiteralValue, ExcelError> {
1324 self.broadcast_apply(left, right, |l, r| {
1325 let ln = crate::coercion::to_number_lenient_with_locale(&l, &self.context.locale());
1326 let rn = crate::coercion::to_number_lenient_with_locale(&r, &self.context.locale());
1327 let (a, b) = match (ln, rn) {
1328 (Ok(a), Ok(b)) => (a, b),
1329 (Err(e), _) | (_, Err(e)) => return Ok(LiteralValue::Error(e)),
1330 };
1331 if a < 0.0 && b.fract() != 0.0 {
1333 return Ok(LiteralValue::Error(ExcelError::new_num()));
1334 }
1335 match crate::coercion::sanitize_numeric(a.powf(b)) {
1336 Ok(n) => Ok(LiteralValue::Number(n)),
1337 Err(e) => Ok(LiteralValue::Error(e)),
1338 }
1339 })
1340 }
1341
1342 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
1343 where
1344 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1345 {
1346 let mut out = Vec::with_capacity(arr.len());
1347 for row in arr {
1348 let mut new_row = Vec::with_capacity(row.len());
1349 for cell in row {
1350 new_row.push(match f(cell) {
1351 Ok(v) => v,
1352 Err(e) => LiteralValue::Error(e),
1353 });
1354 }
1355 out.push(new_row);
1356 }
1357 Ok(LiteralValue::Array(out))
1358 }
1359
1360 fn combine_arrays<F>(
1361 &self,
1362 l: Vec<Vec<LiteralValue>>,
1363 r: Vec<Vec<LiteralValue>>,
1364 f: F,
1365 ) -> Result<LiteralValue, ExcelError>
1366 where
1367 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1368 {
1369 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
1371 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
1372 let target = match broadcast_shape(&[l_shape, r_shape]) {
1373 Ok(s) => s,
1374 Err(e) => return Ok(LiteralValue::Error(e)),
1375 };
1376
1377 let mut out = Vec::with_capacity(target.0);
1378 for i in 0..target.0 {
1379 let mut row = Vec::with_capacity(target.1);
1380 for j in 0..target.1 {
1381 let (li, lj) = project_index((i, j), l_shape);
1382 let (ri, rj) = project_index((i, j), r_shape);
1383 let lv = l
1384 .get(li)
1385 .and_then(|r| r.get(lj))
1386 .cloned()
1387 .unwrap_or(LiteralValue::Empty);
1388 let rv = r
1389 .get(ri)
1390 .and_then(|r| r.get(rj))
1391 .cloned()
1392 .unwrap_or(LiteralValue::Empty);
1393 row.push(match f(lv, rv) {
1394 Ok(v) => v,
1395 Err(e) => LiteralValue::Error(e),
1396 });
1397 }
1398 out.push(row);
1399 }
1400 Ok(LiteralValue::Array(out))
1401 }
1402
1403 fn broadcast_apply<F>(
1404 &self,
1405 left: LiteralValue,
1406 right: LiteralValue,
1407 f: F,
1408 ) -> Result<LiteralValue, ExcelError>
1409 where
1410 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1411 {
1412 use LiteralValue::*;
1413 match (left, right) {
1414 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
1415 (Array(arr), v) => {
1416 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1417 let shape_r = (1usize, 1usize);
1418 let target = match broadcast_shape(&[shape_l, shape_r]) {
1419 Ok(s) => s,
1420 Err(e) => return Ok(LiteralValue::Error(e)),
1421 };
1422 let mut out = Vec::with_capacity(target.0);
1423 for i in 0..target.0 {
1424 let mut row = Vec::with_capacity(target.1);
1425 for j in 0..target.1 {
1426 let (li, lj) = project_index((i, j), shape_l);
1427 let lv = arr
1428 .get(li)
1429 .and_then(|r| r.get(lj))
1430 .cloned()
1431 .unwrap_or(LiteralValue::Empty);
1432 row.push(match f(lv, v.clone()) {
1433 Ok(vv) => vv,
1434 Err(e) => LiteralValue::Error(e),
1435 });
1436 }
1437 out.push(row);
1438 }
1439 Ok(LiteralValue::Array(out))
1440 }
1441 (v, Array(arr)) => {
1442 let shape_l = (1usize, 1usize);
1443 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
1444 let target = match broadcast_shape(&[shape_l, shape_r]) {
1445 Ok(s) => s,
1446 Err(e) => return Ok(LiteralValue::Error(e)),
1447 };
1448 let mut out = Vec::with_capacity(target.0);
1449 for i in 0..target.0 {
1450 let mut row = Vec::with_capacity(target.1);
1451 for j in 0..target.1 {
1452 let (ri, rj) = project_index((i, j), shape_r);
1453 let rv = arr
1454 .get(ri)
1455 .and_then(|r| r.get(rj))
1456 .cloned()
1457 .unwrap_or(LiteralValue::Empty);
1458 row.push(match f(v.clone(), rv) {
1459 Ok(vv) => vv,
1460 Err(e) => LiteralValue::Error(e),
1461 });
1462 }
1463 out.push(row);
1464 }
1465 Ok(LiteralValue::Array(out))
1466 }
1467 (l, r) => f(l, r),
1468 }
1469 }
1470
1471 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
1473 coercion::to_number_lenient(v)
1474 }
1475
1476 fn coerce_text(&self, v: &LiteralValue) -> String {
1477 coercion::to_text_invariant(v)
1478 }
1479
1480 fn compare(
1482 &self,
1483 op: &str,
1484 left: LiteralValue,
1485 right: LiteralValue,
1486 ) -> Result<LiteralValue, ExcelError> {
1487 use LiteralValue::*;
1488 if matches!(left, Error(_)) {
1489 return Ok(left);
1490 }
1491 if matches!(right, Error(_)) {
1492 return Ok(right);
1493 }
1494
1495 match (left, right) {
1497 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
1498 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
1499 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
1500 (l, r) => {
1501 let res = match (l, r) {
1502 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
1503 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
1504 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
1505 (Boolean(a), Boolean(b)) => {
1506 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
1507 }
1508 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
1509 (a, b) => {
1510 let an = crate::coercion::to_number_lenient_with_locale(
1512 &a,
1513 &self.context.locale(),
1514 )
1515 .ok();
1516 let bn = crate::coercion::to_number_lenient_with_locale(
1517 &b,
1518 &self.context.locale(),
1519 )
1520 .ok();
1521 if let (Some(a), Some(b)) = (an, bn) {
1522 self.cmp_f64(a, b, op)
1523 } else {
1524 self.cmp_text(
1525 &crate::coercion::to_text_invariant(&a),
1526 &crate::coercion::to_text_invariant(&b),
1527 op,
1528 )
1529 }
1530 }
1531 };
1532 Ok(LiteralValue::Boolean(res))
1533 }
1534 }
1535 }
1536
1537 fn cmp_f64(&self, a: f64, b: f64, op: &str) -> bool {
1538 match op {
1539 "=" => a == b,
1540 "<>" => a != b,
1541 ">" => a > b,
1542 "<" => a < b,
1543 ">=" => a >= b,
1544 "<=" => a <= b,
1545 _ => unreachable!(),
1546 }
1547 }
1548 fn cmp_text(&self, a: &str, b: &str, op: &str) -> bool {
1549 let loc = self.context.locale();
1550 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
1551 self.cmp_f64(
1552 a.cmp(&b) as i32 as f64,
1553 0.0,
1554 match op {
1555 "=" => "=",
1556 "<>" => "<>",
1557 ">" => ">",
1558 "<" => "<",
1559 ">=" => ">=",
1560 "<=" => "<=",
1561 _ => unreachable!(),
1562 },
1563 )
1564 }
1565}
1566
1567fn relocate_reference_for_offset(
1568 reference: &ReferenceType,
1569 row_delta: i64,
1570 col_delta: i64,
1571) -> Result<ReferenceType, ExcelError> {
1572 match reference {
1573 ReferenceType::Cell {
1574 sheet,
1575 row,
1576 col,
1577 row_abs,
1578 col_abs,
1579 } => Ok(ReferenceType::Cell {
1580 sheet: sheet.clone(),
1581 row: shift_axis_for_offset(*row, row_delta, *row_abs)?,
1582 col: shift_axis_for_offset(*col, col_delta, *col_abs)?,
1583 row_abs: *row_abs,
1584 col_abs: *col_abs,
1585 }),
1586 ReferenceType::Range {
1587 sheet,
1588 start_row,
1589 start_col,
1590 end_row,
1591 end_col,
1592 start_row_abs,
1593 start_col_abs,
1594 end_row_abs,
1595 end_col_abs,
1596 } => Ok(ReferenceType::Range {
1597 sheet: sheet.clone(),
1598 start_row: shift_optional_axis_for_offset(*start_row, row_delta, *start_row_abs)?,
1599 start_col: shift_optional_axis_for_offset(*start_col, col_delta, *start_col_abs)?,
1600 end_row: shift_optional_axis_for_offset(*end_row, row_delta, *end_row_abs)?,
1601 end_col: shift_optional_axis_for_offset(*end_col, col_delta, *end_col_abs)?,
1602 start_row_abs: *start_row_abs,
1603 start_col_abs: *start_col_abs,
1604 end_row_abs: *end_row_abs,
1605 end_col_abs: *end_col_abs,
1606 }),
1607 ReferenceType::NamedRange(name) => Ok(ReferenceType::NamedRange(name.clone())),
1610 ReferenceType::Table(_)
1611 | ReferenceType::Cell3D { .. }
1612 | ReferenceType::Range3D { .. }
1613 | ReferenceType::External(_) => Err(unsupported_reference_relocation_error()),
1614 }
1615}
1616
1617fn shift_optional_axis_for_offset(
1618 value: Option<u32>,
1619 delta: i64,
1620 is_absolute: bool,
1621) -> Result<Option<u32>, ExcelError> {
1622 value
1623 .map(|value| shift_axis_for_offset(value, delta, is_absolute))
1624 .transpose()
1625}
1626
1627fn shift_axis_for_offset(value: u32, delta: i64, is_absolute: bool) -> Result<u32, ExcelError> {
1628 if is_absolute {
1629 return Ok(value);
1630 }
1631 let shifted = i64::from(value) + delta;
1632 if shifted < 1 || shifted > i64::from(u32::MAX) {
1633 return Err(unsupported_reference_relocation_error());
1634 }
1635 Ok(shifted as u32)
1636}
1637
1638fn unsupported_reference_relocation_error() -> ExcelError {
1639 ExcelError::new(ExcelErrorKind::Ref)
1640 .with_message("Unsupported reference relocation for FormulaPlane span evaluation")
1641}