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