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 formualizer_parse::{BinaryOperator, UnaryOperator};
10use rustc_hash::FxHashMap;
11use std::{borrow::Cow, sync::Arc};
12
13use crate::engine::arena::ast::{CALL_NODE_NAME, SheetKey};
14use crate::engine::arena::{AstNodeData, AstNodeId, CompactRefType, DataStore};
15use crate::engine::sheet_registry::SheetRegistry;
16use crate::engine::template::canonical::LiteralSlotId;
17use crate::engine::used_extent::{
18 ExtentPolicy, OpenRangeBounds, resolve_used_extent_with_fallback,
19};
20
21fn spill_operand_error() -> ExcelError {
23 ExcelError::new(ExcelErrorKind::Ref)
24 .with_message("Spill reference operand must be a single cell or a name")
25}
26
27fn call_expression_error() -> ExcelError {
30 ExcelError::new(ExcelErrorKind::NImpl)
31 .with_message("Immediate-invocation calls are not yet supported")
32}
33
34pub(crate) fn probe_range_dimensions<C: EvaluationContext + ?Sized>(
35 context: &C,
36 current_sheet: &str,
37 reference: &ReferenceType,
38) -> Option<(u32, u32)> {
39 match reference {
40 ReferenceType::Range {
41 sheet,
42 start_row,
43 start_col,
44 end_row,
45 end_col,
46 ..
47 } => {
48 let sheet_name = sheet.as_deref().unwrap_or(current_sheet);
49 let extent = resolve_used_extent_with_fallback(
50 OpenRangeBounds {
51 start_row: *start_row,
52 start_column: *start_col,
53 end_row: *end_row,
54 end_column: *end_col,
55 },
56 ExtentPolicy::EvaluationCompat {
57 fallback_row: None,
58 fallback_column: None,
59 },
60 || context.sheet_bounds(sheet_name).map(|bounds| bounds.0),
61 || context.sheet_bounds(sheet_name).map(|bounds| bounds.1),
62 |first, last| context.used_rows_for_columns(sheet_name, first, last),
63 |first, last| context.used_cols_for_rows(sheet_name, first, last),
64 );
65 let Some(extent) = extent else {
66 return Some((0, 0));
67 };
68 Some((
69 extent.end_row - extent.start_row + 1,
70 extent.end_column - extent.start_column + 1,
71 ))
72 }
73 ReferenceType::Cell { .. } => Some((1, 1)),
74 _ => None,
75 }
76}
77
78#[derive(Clone)]
79pub enum LocalBinding {
80 Value(LiteralValue),
81 ValueWithReference {
87 value: LiteralValue,
88 reference: ReferenceType,
89 },
90 Callable(Arc<dyn crate::traits::CustomCallable>),
91}
92
93#[derive(Clone, Default)]
94pub struct LocalEnv {
95 head: Option<Arc<EnvFrame>>,
96}
97
98#[derive(Clone)]
99struct EnvFrame {
100 parent: Option<Arc<EnvFrame>>,
101 bindings: FxHashMap<String, LocalBinding>,
102}
103
104impl LocalEnv {
105 #[inline(always)]
106 pub fn is_empty(&self) -> bool {
107 self.head.is_none()
108 }
109
110 fn norm(name: &str) -> String {
111 name.to_ascii_uppercase()
112 }
113
114 pub fn lookup(&self, name: &str) -> Option<LocalBinding> {
115 self.head.as_ref()?;
116 let key = Self::norm(name);
117 let mut cur = self.head.as_ref().cloned();
118 while let Some(frame) = cur {
119 if let Some(v) = frame.bindings.get(&key) {
120 return Some(v.clone());
121 }
122 cur = frame.parent.clone();
123 }
124 None
125 }
126
127 pub fn with_binding(&self, name: &str, value: LocalBinding) -> Self {
128 let mut bindings = FxHashMap::default();
129 bindings.insert(Self::norm(name), value);
130 Self {
131 head: Some(Arc::new(EnvFrame {
132 parent: self.head.clone(),
133 bindings,
134 })),
135 }
136 }
137}
138
139#[derive(Clone, Copy)]
140pub(crate) struct InterpreterParameterBindings<'a> {
141 pub(crate) literal_slots_by_node: &'a FxHashMap<AstNodeId, LiteralSlotId>,
142 pub(crate) literal_values: &'a [LiteralValue],
143 pub(crate) invariant_values: Option<&'a InvariantValues>,
146}
147
148pub(crate) type InvariantValues =
150 FxHashMap<AstNodeId, (LiteralValue, Option<crate::format::FormatId>)>;
151
152pub(crate) trait ArenaCellResolver {
154 fn resolve_arena_range(
155 &self,
156 reference: &CompactRefType,
157 current: crate::SheetId,
158 row_delta: i64,
159 col_delta: i64,
160 ) -> Result<crate::engine::range_view::RangeView<'_>, ExcelError>;
161
162 fn resolve_arena_cell(
163 &self,
164 sheet: Option<SheetKey>,
165 current: crate::SheetId,
166 row: u32,
167 col: u32,
168 ) -> Result<(LiteralValue, Option<crate::format::FormatId>), ExcelError>;
169}
170
171type ResolvedCallable = (
172 Option<std::sync::Arc<dyn crate::function::Function>>,
173 Option<crate::function::FnCaps>,
174);
175
176#[derive(Default)]
178pub(crate) struct ResolvedFunctions {
179 functions: Vec<Option<std::sync::Arc<dyn crate::function::Function>>>,
180 slots: rustc_hash::FxHashMap<crate::engine::arena::StringId, usize>,
181 revision: Option<(u64, Option<u64>, u64)>,
182 ast_revision: Option<(u64, Option<u64>, u64, usize, u64)>,
183 ast_functions: rustc_hash::FxHashMap<Box<str>, ResolvedCallable>,
184 capabilities: Vec<Option<crate::function::FnCaps>>,
185}
186
187impl ResolvedFunctions {
188 pub(crate) fn refresh(
189 &mut self,
190 store: &DataStore,
191 provider: &dyn crate::traits::FunctionProvider,
192 ) {
193 let revision = (
194 crate::function_registry::semantic_epoch_lock_free(),
195 provider.planning_semantic_revision(),
196 store.resolution_revision(),
197 );
198 if self.revision != Some(revision) || revision.1.is_none() {
199 self.functions.clear();
200 self.capabilities.clear();
201 self.slots.clear();
202 self.ast_revision = None;
203 }
204 for id in store.function_names() {
207 if self.slots.contains_key(&id) {
208 continue;
209 }
210 let name = store.resolve_ast_string(id);
211 let function = if name == CALL_NODE_NAME {
212 None
213 } else {
214 provider.get_function("", name)
215 };
216 let caps = function.as_ref().map(|function| function.caps());
217 self.slots.insert(id, self.functions.len());
218 self.functions.push(function);
219 self.capabilities.push(caps);
220 }
221 self.revision = Some(revision);
223 }
224
225 pub(crate) fn is_current(
231 &self,
232 store: &DataStore,
233 provider: &dyn crate::traits::FunctionProvider,
234 ) -> bool {
235 let Some((epoch, Some(provider_revision), arena_revision)) = self.revision else {
236 return false;
237 };
238 epoch == crate::function_registry::semantic_epoch_lock_free()
239 && provider.planning_semantic_revision() == Some(provider_revision)
240 && store.resolution_revision() == arena_revision
241 && store
242 .function_names()
243 .all(|id| self.slots.contains_key(&id))
244 }
245
246 pub(crate) fn refresh_ast_functions<'n>(
247 &mut self,
248 store: &DataStore,
249 provider: &dyn crate::traits::FunctionProvider,
250 named: impl Iterator<Item = &'n ASTNode>,
251 symbol_revision: u64,
252 ) {
253 let Some((epoch, provider_revision, arena_revision)) = self.revision else {
254 return;
255 };
256 let revision = (
257 epoch,
258 provider_revision,
259 arena_revision,
260 self.functions.len(),
261 symbol_revision,
262 );
263 if self.ast_revision == Some(revision) {
264 return;
265 }
266 fn collect(node: &ASTNode, names: &mut std::collections::BTreeSet<String>) {
267 match &node.node_type {
268 ASTNodeType::Function { name, args } => {
269 names.insert(name.clone());
270 for arg in args {
271 collect(arg, names);
272 }
273 }
274 ASTNodeType::UnaryOp { expr, .. } => collect(expr, names),
275 ASTNodeType::BinaryOp { left, right, .. } => {
276 collect(left, names);
277 collect(right, names);
278 }
279 ASTNodeType::Array(rows) => {
280 for node in rows.iter().flatten() {
281 collect(node, names);
282 }
283 }
284 _ => {}
285 }
286 }
287 let mut names = std::collections::BTreeSet::new();
288 let definitions_changed = self
289 .ast_revision
290 .is_none_or(|previous| previous.4 != symbol_revision);
291 if definitions_changed {
292 for node in named {
293 collect(node, &mut names);
294 }
295 self.ast_functions.clear();
296 }
297 for (id, slot) in &self.slots {
298 let name = store.resolve_ast_string(*id);
299 if self.ast_functions.contains_key(name) {
300 continue;
301 }
302 let function = self.functions.get(*slot).cloned().flatten();
303 let caps = self.capabilities.get(*slot).copied().flatten();
304 self.ast_functions.insert(name.into(), (function, caps));
305 }
306 for name in names {
307 self.ast_functions
308 .entry(name.into_boxed_str())
309 .or_insert_with_key(|name| {
310 let function = provider.get_function("", name);
311 let caps = function.as_ref().map(|function| function.caps());
312 (function, caps)
313 });
314 }
315 self.ast_revision = Some(revision);
316 }
317
318 pub(crate) fn get_named(&self, name: &str) -> Option<&dyn crate::function::Function> {
321 self.ast_functions.get(name)?.0.as_deref()
322 }
323
324 pub(crate) fn get(
325 &self,
326 id: crate::engine::arena::StringId,
327 ) -> Option<&dyn crate::function::Function> {
328 self.functions
329 .get(*self.slots.get(&id)?)
330 .and_then(|f| f.as_deref())
331 }
332}
333
334enum ResolvedFunction<'a> {
335 Borrowed(&'a dyn crate::function::Function),
336 Owned(std::sync::Arc<dyn crate::function::Function>),
337}
338impl std::ops::Deref for ResolvedFunction<'_> {
339 type Target = dyn crate::function::Function;
340 fn deref(&self) -> &Self::Target {
341 match self {
342 Self::Borrowed(f) => *f,
343 Self::Owned(f) => f.as_ref(),
344 }
345 }
346}
347
348pub struct Interpreter<'a> {
349 pub context: &'a dyn EvaluationContext,
350 resolved_functions: Option<&'a ResolvedFunctions>,
351 arena_cells: Option<&'a dyn ArenaCellResolver>,
352 current_sheet: &'a str,
353 current_cell: Option<crate::CellRef>,
354 local_env: LocalEnv,
355 reference_row_delta: i64,
356 reference_col_delta: i64,
357 disable_ast_planner: bool,
358 parameter_bindings: Option<InterpreterParameterBindings<'a>>,
359}
360
361impl<'a> Interpreter<'a> {
362 pub fn new(context: &'a dyn EvaluationContext, current_sheet: &'a str) -> Self {
363 Self {
364 context,
365 resolved_functions: None,
366 arena_cells: None,
367 current_sheet,
368 current_cell: None,
369 local_env: LocalEnv::default(),
370 reference_row_delta: 0,
371 reference_col_delta: 0,
372 disable_ast_planner: false,
373 parameter_bindings: None,
374 }
375 }
376
377 pub fn new_with_cell(
378 context: &'a dyn EvaluationContext,
379 current_sheet: &'a str,
380 cell: crate::CellRef,
381 ) -> Self {
382 Self {
383 context,
384 resolved_functions: None,
385 arena_cells: None,
386 current_sheet,
387 current_cell: Some(cell),
388 local_env: LocalEnv::default(),
389 reference_row_delta: 0,
390 reference_col_delta: 0,
391 disable_ast_planner: false,
392 parameter_bindings: None,
393 }
394 }
395
396 pub(crate) fn with_arena_cells(mut self, resolver: &'a dyn ArenaCellResolver) -> Self {
397 self.arena_cells = Some(resolver);
398 self
399 }
400
401 pub(crate) fn with_resolved_functions(mut self, table: &'a ResolvedFunctions) -> Self {
402 self.resolved_functions = Some(table);
403 self
404 }
405
406 fn ast_function(&self, name: &str) -> Option<ResolvedFunction<'_>> {
407 if let Some(table) = self.resolved_functions
408 && let Some(function) = table.ast_functions.get(name)
409 {
410 return function.0.as_deref().map(ResolvedFunction::Borrowed);
411 }
412 self.context
413 .get_function("", name)
414 .map(ResolvedFunction::Owned)
415 }
416
417 pub(crate) fn ast_function_capabilities(&self, name: &str) -> Option<crate::function::FnCaps> {
418 if let Some(table) = self.resolved_functions
419 && let Some(caps) = table.ast_functions.get(name)
420 {
421 return caps.1;
422 }
423 self.context.function_capabilities("", name)
424 }
425
426 fn ast_function_arc(
427 &self,
428 ns: &str,
429 name: &str,
430 ) -> Option<std::sync::Arc<dyn crate::function::Function>> {
431 if ns.is_empty()
432 && let Some(table) = self.resolved_functions
433 && let Some(function) = table.ast_functions.get(name)
434 {
435 return function.0.clone();
436 }
437 self.context.get_function(ns, name)
438 }
439
440 fn arena_function(
441 &self,
442 id: crate::engine::arena::StringId,
443 store: &DataStore,
444 ) -> Option<ResolvedFunction<'_>> {
445 if let Some(table) = self.resolved_functions {
446 table.get(id).map(ResolvedFunction::Borrowed)
447 } else {
448 let name = store.resolve_ast_string(id);
449 if name == CALL_NODE_NAME {
450 return None;
451 }
452 self.context
453 .get_function("", name)
454 .map(ResolvedFunction::Owned)
455 }
456 }
457
458 pub(crate) fn arena_function_capabilities(
459 &self,
460 id: crate::engine::arena::StringId,
461 store: &DataStore,
462 ) -> Option<crate::function::FnCaps> {
463 if let Some(table) = self.resolved_functions {
464 table
465 .capabilities
466 .get(*table.slots.get(&id)?)
467 .copied()
468 .flatten()
469 } else {
470 self.context
471 .function_capabilities("", store.resolve_ast_string(id))
472 }
473 }
474
475 pub fn current_sheet(&self) -> &'a str {
476 self.current_sheet
477 }
478
479 pub fn local_env(&self) -> &LocalEnv {
480 &self.local_env
481 }
482
483 pub(crate) fn with_current_cell(&self, cell: crate::CellRef) -> Self {
484 Self {
485 context: self.context,
486 resolved_functions: self.resolved_functions,
487 arena_cells: self.arena_cells,
488 current_sheet: self.current_sheet,
489 current_cell: Some(cell),
490 local_env: self.local_env.clone(),
491 reference_row_delta: self.reference_row_delta,
492 reference_col_delta: self.reference_col_delta,
493 disable_ast_planner: self.disable_ast_planner,
494 parameter_bindings: self.parameter_bindings,
495 }
496 }
497
498 pub fn with_local_env(&self, env: LocalEnv) -> Self {
499 Self {
500 context: self.context,
501 resolved_functions: self.resolved_functions,
502 arena_cells: self.arena_cells,
503 current_sheet: self.current_sheet,
504 current_cell: self.current_cell,
505 local_env: env,
506 reference_row_delta: self.reference_row_delta,
507 reference_col_delta: self.reference_col_delta,
508 disable_ast_planner: self.disable_ast_planner,
509 parameter_bindings: self.parameter_bindings,
510 }
511 }
512
513 pub(crate) fn with_parameter_bindings(
514 &self,
515 bindings: InterpreterParameterBindings<'a>,
516 ) -> Self {
517 Self {
518 context: self.context,
519 resolved_functions: self.resolved_functions,
520 arena_cells: self.arena_cells,
521 current_sheet: self.current_sheet,
522 current_cell: self.current_cell,
523 local_env: self.local_env.clone(),
524 reference_row_delta: self.reference_row_delta,
525 reference_col_delta: self.reference_col_delta,
526 disable_ast_planner: self.disable_ast_planner,
527 parameter_bindings: Some(bindings),
528 }
529 }
530
531 fn effective_reference<'r>(
532 &self,
533 reference: &'r ReferenceType,
534 ) -> Result<Cow<'r, ReferenceType>, ExcelError> {
535 if self.reference_row_delta == 0 && self.reference_col_delta == 0 {
536 return Ok(Cow::Borrowed(reference));
537 }
538
539 Ok(Cow::Owned(relocate_reference_for_offset(
540 reference,
541 self.reference_row_delta,
542 self.reference_col_delta,
543 )?))
544 }
545
546 fn resolve_local_reference(
547 &self,
548 reference: &ReferenceType,
549 ) -> Option<crate::traits::CalcValue<'a>> {
550 if self.local_env.is_empty() {
551 return None;
552 }
553 let name = match reference {
554 ReferenceType::NamedRange(name) => name,
555 _ => return None,
556 };
557 match self.local_env.lookup(name)? {
558 LocalBinding::Value(v) => Some(crate::traits::CalcValue::Scalar(v)),
559 LocalBinding::ValueWithReference { value, .. } => {
560 Some(crate::traits::CalcValue::Scalar(value))
561 }
562 LocalBinding::Callable(c) => Some(crate::traits::CalcValue::Callable(c)),
563 }
564 }
565
566 fn resolve_local_callable(&self, name: &str) -> Option<Arc<dyn crate::traits::CustomCallable>> {
567 if self.local_env.is_empty() {
568 return None;
569 }
570 match self.local_env.lookup(name)? {
571 LocalBinding::Callable(c) => Some(c),
572 LocalBinding::Value(_) | LocalBinding::ValueWithReference { .. } => None,
573 }
574 }
575
576 pub fn resolve_local_name(&self, name: &str) -> Option<LocalBinding> {
577 self.local_env.lookup(name)
578 }
579
580 pub(crate) fn resolve_local_bound_reference(&self, name: &str) -> Option<ReferenceType> {
583 if self.local_env.is_empty() {
584 return None;
585 }
586 match self.local_env.lookup(name)? {
587 LocalBinding::ValueWithReference { reference, .. } => Some(reference),
588 LocalBinding::Value(_) | LocalBinding::Callable(_) => None,
589 }
590 }
591
592 pub fn resolve_range_view<'c>(
593 &'c self,
594 reference: &ReferenceType,
595 current_sheet: &str,
596 ) -> Result<crate::engine::range_view::RangeView<'c>, ExcelError> {
597 self.context
598 .resolve_range_view(reference, current_sheet)
599 .map(|view| view.with_cell_budget(self.context.materialized_array_cells()))
600 }
601
602 pub fn evaluate_ast_as_reference(&self, node: &ASTNode) -> Result<ReferenceType, ExcelError> {
607 match &node.node_type {
608 ASTNodeType::Reference { reference, .. } => {
609 self.reference_for_current_offset(reference)
610 }
611 ASTNodeType::Function { name, args } => {
612 if let Some(fun) = self.ast_function(name) {
613 let handles: smallvec::SmallVec<[ArgumentHandle; 4]> =
615 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
616 let fctx = DefaultFunctionContext::new_with_sheet(
617 self.context,
618 None,
619 self.current_sheet,
620 );
621 if let Some(res) = fun.eval_reference(&handles, &fctx) {
622 res
623 } else {
624 Err(ExcelError::new(ExcelErrorKind::Ref)
625 .with_message("Function does not return a reference"))
626 }
627 } else {
628 Err(ExcelError::new(ExcelErrorKind::Name)
629 .with_message(format!("Unknown function: {name}")))
630 }
631 }
632 ASTNodeType::BinaryOp { op, left, right } if *op == BinaryOperator::Range => {
633 let lref = self.evaluate_ast_as_reference(left)?;
634 let rref = self.evaluate_ast_as_reference(right)?;
635 crate::reference::combine_references(&lref, &rref)
636 }
637 ASTNodeType::UnaryOp { op, expr } if *op == UnaryOperator::Spill => {
638 self.ast_spill_reference(expr)
639 }
640 ASTNodeType::Array(_)
641 | ASTNodeType::UnaryOp { .. }
642 | ASTNodeType::BinaryOp { .. }
643 | ASTNodeType::Call { .. }
644 | ASTNodeType::Literal(_)
645 | ASTNodeType::Omitted => Err(ExcelError::new(ExcelErrorKind::Ref)
646 .with_message("Expression cannot be used as a reference")),
647 }
648 }
649
650 pub(crate) fn try_evaluate_ast_as_reference(
651 &self,
652 node: &ASTNode,
653 ) -> Option<Result<ReferenceType, ExcelError>> {
654 let ASTNodeType::Function { name, args } = &node.node_type else {
655 return Some(self.evaluate_ast_as_reference(node));
656 };
657 let fun = match self.ast_function(name) {
658 Some(fun) => fun,
659 None => {
660 return Some(Err(ExcelError::new(ExcelErrorKind::Name)
661 .with_message(format!("Unknown function: {name}"))));
662 }
663 };
664 let handles: smallvec::SmallVec<[ArgumentHandle; 4]> = args
665 .iter()
666 .map(|arg| ArgumentHandle::new(arg, self))
667 .collect();
668 let fctx = DefaultFunctionContext::new_with_sheet(self.context, None, self.current_sheet);
669 fun.eval_reference(&handles, &fctx)
670 }
671
672 pub(crate) fn evaluate_arena_ast_as_reference(
673 &self,
674 node_id: AstNodeId,
675 data_store: &DataStore,
676 sheet_registry: &SheetRegistry,
677 ) -> Result<ReferenceType, ExcelError> {
678 let node = data_store.get_node(node_id).ok_or_else(|| {
679 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
680 })?;
681
682 match node {
683 AstNodeData::Reference { ref_type, .. } => {
684 let ref_type = data_store.compact_ref(*ref_type);
685 let reference =
686 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
687 self.reference_for_current_offset(&reference)
688 }
689 AstNodeData::Function { name_id, .. } => {
690 let fun = self.arena_function(*name_id, data_store).ok_or_else(|| {
691 let name = data_store.resolve_ast_string(*name_id);
692 if name == CALL_NODE_NAME {
693 call_expression_error()
694 } else {
695 ExcelError::new(ExcelErrorKind::Name)
696 .with_message(format!("Unknown function: {name}"))
697 }
698 })?;
699
700 let args = data_store.get_args(node_id).ok_or_else(|| {
701 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
702 })?;
703
704 let handles: smallvec::SmallVec<[ArgumentHandle; 4]> = args
705 .iter()
706 .copied()
707 .map(|arg_id| {
708 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
709 })
710 .collect();
711
712 let fctx =
713 DefaultFunctionContext::new_with_sheet(self.context, None, self.current_sheet);
714
715 fun.eval_reference(&handles, &fctx).ok_or_else(|| {
716 ExcelError::new(ExcelErrorKind::Ref)
717 .with_message("Function does not return a reference")
718 })?
719 }
720 AstNodeData::BinaryOp {
721 operator,
722 left_id,
723 right_id,
724 } => {
725 let op = *operator;
726 if op != BinaryOperator::Range {
727 return Err(ExcelError::new(ExcelErrorKind::Ref)
728 .with_message("Expression cannot be used as a reference"));
729 }
730 let lref =
731 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
732 let rref =
733 self.evaluate_arena_ast_as_reference(*right_id, data_store, sheet_registry)?;
734 crate::reference::combine_references(&lref, &rref)
735 }
736 AstNodeData::UnaryOp { operator, expr_id } if *operator == UnaryOperator::Spill => {
737 self.arena_spill_reference(*expr_id, data_store, sheet_registry)
738 }
739 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
740 .with_message("Expression cannot be used as a reference")),
741 }
742 }
743
744 fn spill_anchor_operand(&self, reference: &ReferenceType) -> Result<ReferenceType, ExcelError> {
747 let anchor = match reference {
748 ReferenceType::NamedRange(name) if self.resolve_local_name(name).is_some() => self
749 .resolve_local_bound_reference(name)
750 .ok_or_else(spill_operand_error)?,
751 _ => self
752 .reference_for_current_offset(reference)
753 .map_err(|_| spill_operand_error())?,
754 };
755 match anchor {
756 ReferenceType::Cell { .. } | ReferenceType::NamedRange(_) => Ok(anchor),
757 _ => Err(spill_operand_error()),
758 }
759 }
760
761 pub(crate) fn resolve_spill_reference(
764 &self,
765 reference: &ReferenceType,
766 ) -> Result<ReferenceType, ExcelError> {
767 let anchor = self.spill_anchor_operand(reference)?;
768 self.context
769 .resolve_spill_reference(&anchor, self.current_sheet)
770 }
771
772 pub(crate) fn ast_spill_reference(
774 &self,
775 operand: &ASTNode,
776 ) -> Result<ReferenceType, ExcelError> {
777 match &operand.node_type {
778 ASTNodeType::Reference { reference, .. } => self.resolve_spill_reference(reference),
779 _ => Err(spill_operand_error()),
780 }
781 }
782
783 pub(crate) fn arena_spill_reference(
785 &self,
786 operand: AstNodeId,
787 data_store: &DataStore,
788 sheet_registry: &SheetRegistry,
789 ) -> Result<ReferenceType, ExcelError> {
790 match data_store.get_node(operand) {
791 Some(AstNodeData::Reference { ref_type, .. }) => {
792 let ref_type = data_store.compact_ref(*ref_type);
793 let reference =
794 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
795 self.resolve_spill_reference(&reference)
796 }
797 _ => Err(spill_operand_error()),
798 }
799 }
800
801 pub(crate) fn try_evaluate_arena_ast_as_reference(
802 &self,
803 node_id: AstNodeId,
804 data_store: &DataStore,
805 sheet_registry: &SheetRegistry,
806 ) -> Option<Result<ReferenceType, ExcelError>> {
807 let node = match data_store.get_node(node_id) {
808 Some(node) => node,
809 None => {
810 return Some(Err(
811 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
812 ));
813 }
814 };
815 let AstNodeData::Function { name_id, .. } = node else {
816 return Some(self.evaluate_arena_ast_as_reference(node_id, data_store, sheet_registry));
817 };
818 let fun = match self.arena_function(*name_id, data_store) {
819 Some(fun) => fun,
820 None => {
821 let name = data_store.resolve_ast_string(*name_id);
822 if name == CALL_NODE_NAME {
823 return Some(Err(call_expression_error()));
824 }
825 return Some(Err(ExcelError::new(ExcelErrorKind::Name)
826 .with_message(format!("Unknown function: {name}"))));
827 }
828 };
829 let args = match data_store.get_args(node_id) {
830 Some(args) => args,
831 None => {
832 return Some(Err(
833 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
834 ));
835 }
836 };
837 let handles: smallvec::SmallVec<[ArgumentHandle; 4]> = args
838 .iter()
839 .copied()
840 .map(|arg_id| ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry))
841 .collect();
842 let fctx = DefaultFunctionContext::new_with_sheet(self.context, None, self.current_sheet);
843 fun.eval_reference(&handles, &fctx)
844 }
845
846 pub fn evaluate_ast(&self, node: &ASTNode) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
848 self.evaluate_ast_uncached(node)
849 }
850
851 pub(crate) fn evaluate_ast_with_offset(
852 &self,
853 node: &ASTNode,
854 row_delta: i64,
855 col_delta: i64,
856 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
857 let offset = Self {
858 context: self.context,
859 resolved_functions: self.resolved_functions,
860 arena_cells: self.arena_cells,
861 current_sheet: self.current_sheet,
862 current_cell: self.current_cell,
863 local_env: self.local_env.clone(),
864 reference_row_delta: row_delta,
865 reference_col_delta: col_delta,
866 disable_ast_planner: true,
867 parameter_bindings: self.parameter_bindings,
868 };
869 offset.evaluate_ast_uncached(node)
870 }
871
872 pub(crate) fn reference_for_current_offset(
873 &self,
874 reference: &ReferenceType,
875 ) -> Result<ReferenceType, ExcelError> {
876 self.effective_reference(reference)
877 .map(|reference| reference.into_owned())
878 }
879
880 pub(crate) fn evaluate_formula_view(
883 &self,
884 view: crate::engine::graph::FormulaView,
885 data_store: &DataStore,
886 sheet_registry: &SheetRegistry,
887 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
888 if view.row_delta == 0 && view.col_delta == 0 {
889 self.evaluate_arena_ast(view.template, data_store, sheet_registry)
890 } else {
891 self.evaluate_arena_ast_with_offset(
892 view.template,
893 view.row_delta,
894 view.col_delta,
895 data_store,
896 sheet_registry,
897 )
898 }
899 }
900
901 pub(crate) fn evaluate_arena_ast_with_offset(
902 &self,
903 node_id: AstNodeId,
904 row_delta: i64,
905 col_delta: i64,
906 data_store: &DataStore,
907 sheet_registry: &SheetRegistry,
908 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
909 let offset = Self {
910 context: self.context,
911 resolved_functions: self.resolved_functions,
912 arena_cells: self.arena_cells,
913 current_sheet: self.current_sheet,
914 current_cell: self.current_cell,
915 local_env: self.local_env.clone(),
916 reference_row_delta: row_delta,
917 reference_col_delta: col_delta,
918 disable_ast_planner: true,
919 parameter_bindings: self.parameter_bindings,
920 };
921 offset.evaluate_arena_ast(node_id, data_store, sheet_registry)
922 }
923
924 #[inline]
925 fn annotated(
926 value: LiteralValue,
927 format: Option<crate::format::FormatId>,
928 ) -> crate::traits::CalcValue<'a> {
929 match format {
930 Some(format) => crate::traits::CalcValue::AnnotatedScalar(value, format),
931 None => crate::traits::CalcValue::Scalar(value),
932 }
933 }
934
935 fn annotate_cell_value(
936 &self,
937 sheet: Option<&str>,
938 row: u32,
939 col: u32,
940 value: LiteralValue,
941 ) -> crate::traits::CalcValue<'a> {
942 match self
943 .context
944 .resolve_cell_format(sheet, row, col, self.current_sheet)
945 {
946 Some(format) => crate::traits::CalcValue::AnnotatedScalar(value, format),
947 None => crate::traits::CalcValue::Scalar(value),
948 }
949 }
950
951 fn binary_format(
952 &self,
953 op: char,
954 left: Option<crate::format::FormatId>,
955 right: Option<crate::format::FormatId>,
956 ) -> Option<crate::format::FormatId> {
957 use formualizer_common::numfmt::FormatClass;
958 let class =
959 |id: Option<crate::format::FormatId>| id.and_then(|id| self.context.format_class(id));
960 let left = class(left);
961 let right = class(right);
962 let is_plain = |class: &Option<FormatClass>| {
963 matches!(
964 class,
965 None | Some(FormatClass::General | FormatClass::Number { .. })
966 )
967 };
968 match (op, left.as_ref(), right.as_ref()) {
973 ('+', Some(FormatClass::Date), Some(FormatClass::Time))
974 | ('+', Some(FormatClass::Time), Some(FormatClass::Date)) => {
975 Some(crate::format::FormatId::DATETIME)
976 }
977 ('+', Some(FormatClass::Date), Some(FormatClass::Percent { .. }))
978 | ('+', Some(FormatClass::Percent { .. }), Some(FormatClass::Date)) => {
979 Some(crate::format::FormatId::DATE)
980 }
981 ('+' | '-', Some(FormatClass::Date), r) if is_plain(&r.cloned()) => {
982 Some(crate::format::FormatId::DATE)
983 }
984 ('+', l, Some(FormatClass::Date)) if is_plain(&l.cloned()) => {
985 Some(crate::format::FormatId::DATE)
986 }
987 ('+' | '-', Some(FormatClass::Time), r) if is_plain(&r.cloned()) => {
988 Some(crate::format::FormatId::TIME)
989 }
990 ('+', l, Some(FormatClass::Time)) if is_plain(&l.cloned()) => {
991 Some(crate::format::FormatId::TIME)
992 }
993 ('+' | '-', Some(FormatClass::DateTime), r) if is_plain(&r.cloned()) => {
994 Some(crate::format::FormatId::DATETIME)
995 }
996 ('+', l, Some(FormatClass::DateTime)) if is_plain(&l.cloned()) => {
997 Some(crate::format::FormatId::DATETIME)
998 }
999 ('+' | '-', Some(FormatClass::Duration), r) if is_plain(&r.cloned()) => {
1000 Some(crate::format::FormatId::DURATION)
1001 }
1002 ('+', l, Some(FormatClass::Duration)) if is_plain(&l.cloned()) => {
1003 Some(crate::format::FormatId::DURATION)
1004 }
1005 _ => None,
1006 }
1007 }
1008
1009 fn annotate_numeric_result(
1010 &self,
1011 value: LiteralValue,
1012 format: Option<crate::format::FormatId>,
1013 ) -> crate::traits::CalcValue<'a> {
1014 match (value, format) {
1015 (value @ LiteralValue::Number(_), Some(format)) => {
1016 crate::traits::CalcValue::AnnotatedScalar(value, format)
1017 }
1018 (value, _) => crate::traits::CalcValue::Scalar(value),
1019 }
1020 }
1021
1022 pub(crate) fn apply_unary_op(
1025 &self,
1026 op: UnaryOperator,
1027 expr: crate::traits::CalcValue<'a>,
1028 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1029 let v = expr.into_literal();
1031 match v {
1032 LiteralValue::Array(arr) => self
1033 .map_array(arr, |cell| self.eval_unary_scalar(op, cell))
1034 .map(crate::traits::CalcValue::Scalar),
1035 other => self
1036 .eval_unary_scalar(op, other)
1037 .map(crate::traits::CalcValue::Scalar),
1038 }
1039 }
1040
1041 pub(crate) fn apply_binary_op(
1044 &self,
1045 op: BinaryOperator,
1046 left: LiteralValue,
1047 left_format: Option<crate::format::FormatId>,
1048 right: LiteralValue,
1049 right_format: Option<crate::format::FormatId>,
1050 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1051 if matches!(
1052 op,
1053 BinaryOperator::Equal
1054 | BinaryOperator::NotEqual
1055 | BinaryOperator::Greater
1056 | BinaryOperator::Less
1057 | BinaryOperator::GreaterEqual
1058 | BinaryOperator::LessEqual
1059 ) {
1060 return self
1061 .compare(op, left, right)
1062 .map(crate::traits::CalcValue::Scalar);
1063 }
1064
1065 match op {
1066 BinaryOperator::Add | BinaryOperator::Subtract => {
1067 let byte = if op == BinaryOperator::Add {
1068 b'+'
1069 } else {
1070 b'-'
1071 };
1072 let value = self.numeric_binary(left, right, byte)?;
1073 if left_format.is_none() && right_format.is_none() {
1074 Ok(crate::traits::CalcValue::Scalar(value))
1078 } else {
1079 Ok(self.annotate_numeric_result(
1080 value,
1081 self.binary_format(char::from(byte), left_format, right_format),
1082 ))
1083 }
1084 }
1085 BinaryOperator::Multiply => self
1086 .numeric_binary(left, right, b'*')
1087 .map(crate::traits::CalcValue::Scalar),
1088 BinaryOperator::Divide => self
1089 .numeric_binary(left, right, b'/')
1090 .map(crate::traits::CalcValue::Scalar),
1091 BinaryOperator::Power => self
1092 .numeric_binary(left, right, b'^')
1093 .map(crate::traits::CalcValue::Scalar),
1094 BinaryOperator::Concat => self
1095 .concat_values(left, right)
1096 .map(crate::traits::CalcValue::Scalar),
1097 _ => {
1098 Err(ExcelError::new(ExcelErrorKind::NImpl)
1099 .with_message(format!("Binary op '{op}'")))
1100 }
1101 }
1102 }
1103
1104 pub(crate) fn resolve_arena_argument_range(
1107 &self,
1108 node_id: AstNodeId,
1109 data_store: &DataStore,
1110 ) -> Option<Result<crate::engine::range_view::RangeView<'a>, ExcelError>> {
1111 let resolver = self.arena_cells?;
1112 let cell = self.current_cell?;
1113 if !self.local_env.is_empty() {
1114 return None;
1115 }
1116 let AstNodeData::Reference { ref_type, .. } = data_store.get_node(node_id)? else {
1117 return None;
1118 };
1119 let reference = data_store.compact_ref(*ref_type);
1120 if !matches!(reference, CompactRefType::Range { .. }) {
1121 return None;
1122 }
1123 Some(resolver.resolve_arena_range(
1124 reference,
1125 cell.sheet_id,
1126 self.reference_row_delta,
1127 self.reference_col_delta,
1128 ))
1129 }
1130
1131 pub(crate) fn evaluate_arena_ast(
1132 &self,
1133 node_id: AstNodeId,
1134 data_store: &DataStore,
1135 sheet_registry: &SheetRegistry,
1136 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1137 let node = data_store.get_node(node_id).ok_or_else(|| {
1138 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1139 })?;
1140
1141 match node {
1142 AstNodeData::Literal(vref) => {
1143 if let Some(bindings) = self.parameter_bindings
1144 && let Some(slot_id) = bindings.literal_slots_by_node.get(&node_id)
1145 && let Some(value) = bindings.literal_values.get(slot_id.0 as usize)
1146 {
1147 return Ok(crate::traits::CalcValue::Scalar(value.clone()));
1148 }
1149 Ok(crate::traits::CalcValue::Scalar(
1150 data_store.retrieve_value(*vref),
1151 ))
1152 }
1153 AstNodeData::Omitted => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0))),
1154 AstNodeData::Reference { ref_type, .. } => {
1155 let ref_type = data_store.compact_ref(*ref_type);
1156 if self.local_env.is_empty()
1157 && matches!(ref_type, CompactRefType::Range { .. })
1158 && let (Some(resolver), Some(cell)) = (self.arena_cells, self.current_cell)
1159 {
1160 let view = resolver
1161 .resolve_arena_range(
1162 ref_type,
1163 cell.sheet_id,
1164 self.reference_row_delta,
1165 self.reference_col_delta,
1166 )?
1167 .with_cancel_token(self.context.cancellation_token())
1168 .with_cell_budget(self.context.materialized_array_cells());
1169 return Ok(crate::traits::CalcValue::Range(view));
1170 }
1171 if self.local_env.is_empty()
1172 && let CompactRefType::Cell {
1173 sheet,
1174 row,
1175 col,
1176 row_abs,
1177 col_abs,
1178 } = ref_type
1179 && *row > 0
1180 && *col > 0
1181 {
1182 let row = shift_axis_for_offset(*row, self.reference_row_delta, *row_abs)?;
1183 let col = shift_axis_for_offset(*col, self.reference_col_delta, *col_abs)?;
1184 if let (Some(resolver), Some(cell)) = (self.arena_cells, self.current_cell) {
1185 let (value, format) =
1186 resolver.resolve_arena_cell(*sheet, cell.sheet_id, row, col)?;
1187 return Ok(Self::annotated(value, format));
1188 }
1189 let sheet_name = match sheet {
1190 Some(SheetKey::Id(id)) => Some(sheet_registry.name(*id)),
1191 Some(SheetKey::Name(name_id)) => {
1192 Some(data_store.resolve_ast_string(*name_id))
1193 }
1194 None => None,
1195 };
1196 let (value, format) = self.context.resolve_cell_reference_value_formatted(
1197 sheet_name,
1198 row,
1199 col,
1200 self.current_sheet,
1201 )?;
1202 Ok(Self::annotated(value, format))
1203 } else {
1204 let reference =
1205 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
1206 let reference = self.effective_reference(&reference)?;
1207 if let Some(local) = self.resolve_local_reference(&reference) {
1208 return Ok(local);
1209 }
1210 self.eval_reference_to_calc(&reference)
1211 }
1212 }
1213 AstNodeData::UnaryOp { operator, expr_id } => {
1214 let op = *operator;
1215 if op == UnaryOperator::Spill {
1216 let reference =
1217 self.arena_spill_reference(*expr_id, data_store, sheet_registry)?;
1218 return self.eval_reference_to_calc(&reference);
1219 }
1220 if op == UnaryOperator::ImplicitIntersection {
1221 if let Some(AstNodeData::Reference { ref_type, .. }) =
1227 data_store.get_node(*expr_id)
1228 {
1229 let reference = data_store.reconstruct_reference_type_for_eval(
1230 data_store.compact_ref(*ref_type),
1231 sheet_registry,
1232 );
1233 let v = match self.effective_reference(&reference) {
1234 Ok(reference) => self.implicit_intersection_from_reference(&reference),
1235 Err(e) => LiteralValue::Error(e),
1236 };
1237 return Ok(crate::traits::CalcValue::Scalar(v));
1238 }
1239
1240 let expr = self.evaluate_arena_ast(*expr_id, data_store, sheet_registry)?;
1241 let v = self.eval_implicit_intersection_calc(expr);
1242 return Ok(crate::traits::CalcValue::Scalar(v));
1243 }
1244 let expr = self.evaluate_arena_ast(*expr_id, data_store, sheet_registry)?;
1245 self.apply_unary_op(op, expr)
1246 }
1247 AstNodeData::BinaryOp {
1248 operator,
1249 left_id,
1250 right_id,
1251 } => {
1252 let op = *operator;
1253 if op == BinaryOperator::Range {
1254 let lref =
1255 self.evaluate_arena_ast_as_reference(*left_id, data_store, sheet_registry)?;
1256 let rref = self.evaluate_arena_ast_as_reference(
1257 *right_id,
1258 data_store,
1259 sheet_registry,
1260 )?;
1261 return match crate::reference::combine_references(&lref, &rref) {
1262 Ok(_r) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1263 ExcelError::new(ExcelErrorKind::Ref).with_message(
1264 "Reference produced by ':' cannot be used directly as a value",
1265 ),
1266 ))),
1267 Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1268 };
1269 }
1270
1271 let left_calc = self.evaluate_arena_ast(*left_id, data_store, sheet_registry)?;
1272 let left_format = left_calc.format_id();
1273 let left = left_calc.into_literal();
1274 let right_calc = self.evaluate_arena_ast(*right_id, data_store, sheet_registry)?;
1275 let right_format = right_calc.format_id();
1276 let right = right_calc.into_literal();
1277 self.apply_binary_op(op, left, left_format, right, right_format)
1278 }
1279 AstNodeData::Array { .. } => {
1280 let (rows, cols, elements) =
1281 data_store.get_array_elems(node_id).ok_or_else(|| {
1282 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
1283 })?;
1284
1285 let rows_usize = rows as usize;
1286 let cols_usize = cols as usize;
1287 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
1288 for r in 0..rows_usize {
1289 let mut row = Vec::with_capacity(cols_usize);
1290 for c in 0..cols_usize {
1291 let idx = r * cols_usize + c;
1292 if let Some(&elem_id) = elements.get(idx) {
1293 row.push(
1294 self.evaluate_arena_ast(elem_id, data_store, sheet_registry)?
1295 .into_literal(),
1296 );
1297 }
1298 }
1299 out.push(row);
1300 }
1301
1302 Ok(crate::traits::CalcValue::Range(
1303 crate::engine::range_view::RangeView::from_owned_rows(
1304 out,
1305 self.context.date_system(),
1306 ),
1307 ))
1308 }
1309 AstNodeData::Function { name_id, .. } => {
1310 if let Some(bindings) = self.parameter_bindings
1311 && let Some(values) = bindings.invariant_values
1312 && let Some((value, format)) = values.get(&node_id)
1313 {
1314 return Ok(Self::annotated(value.clone(), *format));
1315 }
1316 let function = self.arena_function(*name_id, data_store);
1317 if function.is_none() && data_store.resolve_ast_string(*name_id) == CALL_NODE_NAME {
1318 return Err(call_expression_error());
1319 }
1320 let args = data_store.get_args(node_id).ok_or_else(|| {
1321 ExcelError::new(ExcelErrorKind::Value).with_message("Missing function args")
1322 })?;
1323
1324 if let Some(fun) = function {
1325 let handles: smallvec::SmallVec<[ArgumentHandle; 4]> = args
1326 .iter()
1327 .copied()
1328 .map(|arg_id| {
1329 ArgumentHandle::new_arena(arg_id, self, data_store, sheet_registry)
1330 })
1331 .collect();
1332
1333 let fctx = DefaultFunctionContext::new_with_sheet(
1334 self.context,
1335 self.current_cell,
1336 self.current_sheet,
1337 );
1338
1339 return fun.dispatch(&handles, &fctx);
1340 }
1341
1342 let name = data_store.resolve_ast_string(*name_id);
1343 if let Some(callable) = self.resolve_local_callable(name) {
1344 let mut eval_args = Vec::with_capacity(args.len());
1345 let mut arg_references = Vec::with_capacity(args.len());
1346 for arg_id in args {
1347 arg_references.push(
1349 ArgumentHandle::new_arena(*arg_id, self, data_store, sheet_registry)
1350 .bound_reference_in_env(&self.local_env),
1351 );
1352 eval_args.push(
1353 self.evaluate_arena_ast(*arg_id, data_store, sheet_registry)?
1354 .into_literal(),
1355 );
1356 }
1357 return callable.invoke_with_references(self, &eval_args, &arg_references);
1358 }
1359
1360 Err(ExcelError::new(ExcelErrorKind::Name)
1361 .with_message(format!("Unknown function: {name}")))
1362 }
1363 }
1364 }
1365
1366 fn evaluate_ast_uncached(
1367 &self,
1368 node: &ASTNode,
1369 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1370 if self.disable_ast_planner {
1371 return self.eval_tree_uncached(node);
1372 }
1373
1374 let current_sheet = self.current_sheet.to_string();
1378 let range_probe = |reference: &ReferenceType| {
1379 probe_range_dimensions(self.context, ¤t_sheet, reference)
1380 };
1381 let fn_lookup = |ns: &str, name: &str| self.ast_function_arc(ns, name);
1382
1383 let mut planner = crate::planner::Planner::new(crate::planner::PlanConfig::default())
1384 .with_range_probe(&range_probe)
1385 .with_function_lookup(&fn_lookup);
1386 let plan = planner.plan(node);
1387 self.eval_with_plan(node, &plan.root)
1388 }
1389
1390 fn eval_tree_uncached(
1391 &self,
1392 node: &ASTNode,
1393 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1394 match &node.node_type {
1395 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
1396 ASTNodeType::Omitted => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0))),
1397 ASTNodeType::Reference { reference, .. } => self.eval_ast_reference_to_calc(reference),
1398 ASTNodeType::UnaryOp { op, expr } if *op == UnaryOperator::Spill => {
1399 self.eval_reference_to_calc(&self.ast_spill_reference(expr)?)
1400 }
1401 ASTNodeType::UnaryOp { op, expr } => self
1402 .eval_unary(*op, expr)
1403 .map(crate::traits::CalcValue::Scalar),
1404 ASTNodeType::BinaryOp { op, left, right } => self.eval_binary(*op, left, right),
1405 ASTNodeType::Function { name, args } => self.eval_function_to_calc(name, args),
1406 ASTNodeType::Call { .. } => Err(call_expression_error()),
1407 ASTNodeType::Array(rows) => self.eval_array_literal_to_calc(rows),
1408 }
1409 }
1410
1411 fn eval_with_plan(
1412 &self,
1413 node: &ASTNode,
1414 plan_node: &crate::planner::PlanNode,
1415 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1416 match &node.node_type {
1417 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
1418 ASTNodeType::Omitted => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0))),
1419 ASTNodeType::Reference { reference, .. } => self.eval_ast_reference_to_calc(reference),
1420 ASTNodeType::UnaryOp { op, expr } if *op == UnaryOperator::Spill => {
1421 self.eval_reference_to_calc(&self.ast_spill_reference(expr)?)
1422 }
1423 ASTNodeType::UnaryOp { op, expr } => {
1424 self.eval_unary(*op, expr)
1427 .map(crate::traits::CalcValue::Scalar)
1428 }
1429 ASTNodeType::BinaryOp { op, left, right } => self.eval_binary(*op, left, right),
1430 ASTNodeType::Function { name, args } => {
1431 let strategy = plan_node.strategy;
1432 if let Some(fun) = self.ast_function(name) {
1433 use crate::function::FnCaps;
1434 use crate::planner::ExecStrategy;
1435 let caps = fun.caps();
1436
1437 if caps.contains(FnCaps::SHORT_CIRCUIT) || caps.contains(FnCaps::VOLATILE) {
1439 return self.eval_function_to_calc(name, args);
1440 }
1441
1442 if matches!(strategy, ExecStrategy::ArgParallel)
1446 && caps.contains(FnCaps::PARALLEL_ARGS)
1447 {
1448 for arg in args {
1450 match &arg.node_type {
1451 ASTNodeType::Reference { reference, .. } => {
1452 if let Ok(reference) = self.effective_reference(reference) {
1453 let _ = self
1454 .context
1455 .resolve_range_view(&reference, self.current_sheet);
1456 }
1457 }
1458 _ => {
1459 let _ = self.evaluate_ast(arg);
1460 }
1461 }
1462 }
1463 return self.eval_function_to_calc(name, args);
1464 }
1465
1466 return self.eval_function_to_calc(name, args);
1468 }
1469 self.eval_function_to_calc(name, args)
1470 }
1471 ASTNodeType::Call { .. } => Err(call_expression_error()),
1472 ASTNodeType::Array(rows) => self.eval_array_literal_to_calc(rows),
1473 }
1474 }
1475
1476 fn eval_ast_reference_to_calc(
1478 &self,
1479 reference: &ReferenceType,
1480 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1481 if !self.local_env.is_empty() {
1482 let reference = self.effective_reference(reference)?;
1483 if let Some(local) = self.resolve_local_reference(&reference) {
1484 return Ok(local);
1485 }
1486 return self.eval_reference_to_calc(&reference);
1487 }
1488
1489 if let ReferenceType::Cell {
1490 sheet,
1491 row,
1492 col,
1493 row_abs,
1494 col_abs,
1495 } = reference
1496 {
1497 let row = shift_axis_for_offset(*row, self.reference_row_delta, *row_abs)?;
1498 let col = shift_axis_for_offset(*col, self.reference_col_delta, *col_abs)?;
1499 let (value, format) = self.context.resolve_cell_reference_value_formatted(
1500 sheet.as_deref(),
1501 row,
1502 col,
1503 self.current_sheet,
1504 )?;
1505 return Ok(Self::annotated(value, format));
1506 }
1507
1508 let reference = self.effective_reference(reference)?;
1509 self.eval_reference_to_calc(&reference)
1510 }
1511
1512 fn eval_reference_to_calc(
1513 &self,
1514 reference: &ReferenceType,
1515 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1516 if let ReferenceType::Cell {
1517 sheet, row, col, ..
1518 } = reference
1519 {
1520 let value = self.context.resolve_cell_reference_value(
1521 sheet.as_deref(),
1522 *row,
1523 *col,
1524 self.current_sheet,
1525 )?;
1526 return Ok(self.annotate_cell_value(sheet.as_deref(), *row, *col, value));
1527 }
1528
1529 let view = self
1530 .context
1531 .resolve_range_view(reference, self.current_sheet)?
1532 .with_cancel_token(self.context.cancellation_token())
1533 .with_cell_budget(self.context.materialized_array_cells());
1534 Ok(crate::traits::CalcValue::Range(view))
1535 }
1536
1537 fn eval_reference(&self, reference: &ReferenceType) -> Result<LiteralValue, ExcelError> {
1538 self.eval_reference_to_calc(reference)
1539 .map(|cv| cv.into_literal())
1540 }
1541
1542 fn eval_unary(&self, op: UnaryOperator, expr: &ASTNode) -> Result<LiteralValue, ExcelError> {
1544 if op == UnaryOperator::ImplicitIntersection {
1545 if let ASTNodeType::Reference { reference, .. } = &expr.node_type {
1546 let reference = self.effective_reference(reference)?;
1547 return Ok(self.implicit_intersection_from_reference(&reference));
1548 }
1549
1550 let cv = self.evaluate_ast(expr)?;
1551 return Ok(self.eval_implicit_intersection_calc(cv));
1552 }
1553
1554 let v = self.evaluate_ast(expr)?.into_literal();
1555 match v {
1556 LiteralValue::Array(arr) => {
1557 self.map_array(arr, |cell| self.eval_unary_scalar(op, cell))
1558 }
1559 other => self.eval_unary_scalar(op, other),
1560 }
1561 }
1562
1563 fn eval_unary_scalar(
1564 &self,
1565 op: UnaryOperator,
1566 v: LiteralValue,
1567 ) -> Result<LiteralValue, ExcelError> {
1568 match op {
1569 UnaryOperator::Plus => Ok(v),
1574 UnaryOperator::Minus => self.apply_number_unary(v, b'-'),
1575 UnaryOperator::Percent => self.apply_number_unary(v, b'%'),
1576 _ => {
1577 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(format!("Unary op '{op}'")))
1578 }
1579 }
1580 }
1581
1582 fn eval_implicit_intersection_calc(&self, cv: crate::traits::CalcValue<'a>) -> LiteralValue {
1583 let (cur_r0, cur_c0) = match self.current_cell {
1584 Some(cell) => (cell.coord.row() as usize, cell.coord.col() as usize),
1585 None => (0usize, 0usize),
1586 };
1587
1588 match cv {
1589 crate::traits::CalcValue::Scalar(v)
1590 | crate::traits::CalcValue::AnnotatedScalar(v, _) => match v {
1591 LiteralValue::Array(arr) => {
1592 if arr.is_empty() || arr.first().map(|r| r.is_empty()).unwrap_or(true) {
1593 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1594 }
1595 arr[0][0].clone()
1596 }
1597 other => other,
1598 },
1599 crate::traits::CalcValue::Range(rv) => {
1600 if rv.is_empty() {
1601 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1602 }
1603
1604 if rv.sheet_name() == "__tmp" {
1609 return rv.get_cell(0, 0);
1610 }
1611
1612 if let Some(v) = rv.as_1x1() {
1613 return v;
1614 }
1615
1616 let (rows, cols) = rv.dims();
1617 let sr = rv.start_row();
1618 let sc = rv.start_col();
1619 let er = rv.end_row();
1620 let ec = rv.end_col();
1621
1622 if cols == 1 {
1627 if cur_r0 < sr || cur_r0 > er {
1628 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1629 }
1630 let rel_r = cur_r0 - sr;
1631 return rv.get_cell(rel_r, 0);
1632 }
1633
1634 if rows == 1 {
1635 if cur_c0 < sc || cur_c0 > ec {
1636 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1637 }
1638 let rel_c = cur_c0 - sc;
1639 return rv.get_cell(0, rel_c);
1640 }
1641
1642 if cur_r0 < sr || cur_r0 > er || cur_c0 < sc || cur_c0 > ec {
1643 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1644 }
1645 let rel_r = cur_r0 - sr;
1646 let rel_c = cur_c0 - sc;
1647 rv.get_cell(rel_r, rel_c)
1648 }
1649 crate::traits::CalcValue::Callable(_) => LiteralValue::Error(
1650 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
1651 ),
1652 }
1653 }
1654
1655 fn implicit_intersection_from_reference(&self, reference: &ReferenceType) -> LiteralValue {
1656 let (cur_r1, cur_c1) = match self.current_cell {
1657 Some(cell) => (
1658 cell.coord.row().saturating_add(1),
1659 cell.coord.col().saturating_add(1),
1660 ),
1661 None => (1u32, 1u32),
1662 };
1663
1664 match reference {
1665 ReferenceType::Cell {
1666 sheet, row, col, ..
1667 } => {
1668 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
1669 match self
1670 .context
1671 .resolve_cell_reference(Some(sheet_name), *row, *col)
1672 {
1673 Ok(v) => v,
1674 Err(e) => LiteralValue::Error(e),
1675 }
1676 }
1677 ReferenceType::Range {
1678 sheet,
1679 start_row,
1680 start_col,
1681 end_row,
1682 end_col,
1683 ..
1684 } => {
1685 let sheet_name = sheet.as_deref().unwrap_or(self.current_sheet);
1686
1687 const SHEET_ROWS: u32 = 1_048_576;
1692 const SHEET_COLS: u32 = 16_384;
1693 let (sr, sc, er, ec) = (
1694 start_row.unwrap_or(1),
1695 start_col.unwrap_or(1),
1696 end_row.unwrap_or(SHEET_ROWS),
1697 end_col.unwrap_or(SHEET_COLS),
1698 );
1699
1700 let (mut sr, mut er) = (sr, er);
1702 let (mut sc, mut ec) = (sc, ec);
1703 if sr > er {
1704 std::mem::swap(&mut sr, &mut er);
1705 }
1706 if sc > ec {
1707 std::mem::swap(&mut sc, &mut ec);
1708 }
1709
1710 let pick = if sc == ec {
1711 if cur_r1 < sr || cur_r1 > er {
1713 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1714 }
1715 (cur_r1, sc)
1716 } else if sr == er {
1717 if cur_c1 < sc || cur_c1 > ec {
1719 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1720 }
1721 (sr, cur_c1)
1722 } else {
1723 if cur_r1 < sr || cur_r1 > er || cur_c1 < sc || cur_c1 > ec {
1725 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1726 }
1727 (cur_r1, cur_c1)
1728 };
1729
1730 match self
1731 .context
1732 .resolve_cell_reference(Some(sheet_name), pick.0, pick.1)
1733 {
1734 Ok(v) => v,
1735 Err(e) => LiteralValue::Error(e),
1736 }
1737 }
1738 ReferenceType::External(ext) => {
1741 let formualizer_parse::parser::ExternalRefKind::Range {
1742 start_row: Some(sr),
1743 start_col: Some(sc),
1744 end_row: Some(er),
1745 end_col: Some(ec),
1746 ..
1747 } = ext.kind
1748 else {
1749 return match self.eval_reference_to_calc(reference) {
1750 Ok(cv) => self.eval_implicit_intersection_calc(cv),
1751 Err(e) => LiteralValue::Error(e),
1752 };
1753 };
1754 let (sr, er) = (sr.min(er), sr.max(er));
1755 let (sc, ec) = (sc.min(ec), sc.max(ec));
1756 let (row, col) = if sc == ec {
1757 if cur_r1 < sr || cur_r1 > er {
1758 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1759 }
1760 (cur_r1, sc)
1761 } else if sr == er {
1762 if cur_c1 < sc || cur_c1 > ec {
1763 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1764 }
1765 (sr, cur_c1)
1766 } else {
1767 if cur_r1 < sr || cur_r1 > er || cur_c1 < sc || cur_c1 > ec {
1768 return LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value));
1769 }
1770 (cur_r1, cur_c1)
1771 };
1772 match self.eval_reference_to_calc(reference) {
1773 Ok(crate::traits::CalcValue::Range(rv)) => {
1774 rv.get_cell((row - sr) as usize, (col - sc) as usize)
1775 }
1776 Ok(cv) => self.eval_implicit_intersection_calc(cv),
1777 Err(e) => LiteralValue::Error(e),
1778 }
1779 }
1780 other => {
1782 let cv = match self.eval_reference_to_calc(other) {
1783 Ok(cv) => cv,
1784 Err(e) => return LiteralValue::Error(e),
1785 };
1786 self.eval_implicit_intersection_calc(cv)
1787 }
1788 }
1789 }
1790
1791 fn apply_number_unary(&self, v: LiteralValue, op: u8) -> Result<LiteralValue, ExcelError> {
1792 match crate::coercion::to_arithmetic_number_with_locale(
1793 &v,
1794 &self.context.locale(),
1795 self.context.date_system(),
1796 ) {
1797 Ok(n) => match unary_f64(op, n) {
1798 Ok(n2) => Ok(LiteralValue::Number(n2)),
1799 Err(e) => Ok(LiteralValue::Error(e)),
1800 },
1801 Err(e) => Ok(LiteralValue::Error(e)),
1802 }
1803 }
1804
1805 fn eval_binary(
1807 &self,
1808 op: BinaryOperator,
1809 left_node: &ASTNode,
1810 right_node: &ASTNode,
1811 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1812 let left_calc = self.evaluate_ast(left_node)?;
1813 let left_format = left_calc.format_id();
1814 let left = left_calc.into_literal();
1815 let right_calc = self.evaluate_ast(right_node)?;
1816 let right_format = right_calc.format_id();
1817 let right = right_calc.into_literal();
1818 if op == BinaryOperator::Range {
1819 let left_ref = self.evaluate_ast_as_reference(left_node)?;
1820 let right_ref = self.evaluate_ast_as_reference(right_node)?;
1821 return match crate::reference::combine_references(&left_ref, &right_ref) {
1822 Ok(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
1823 .with_message("Reference produced by ':' cannot be used directly as a value")),
1824 Err(error) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(error))),
1825 };
1826 }
1827 self.apply_binary_op(op, left, left_format, right, right_format)
1828 }
1829
1830 fn eval_function_to_calc(
1832 &self,
1833 name: &str,
1834 args: &[ASTNode],
1835 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1836 if let Some(fun) = self.ast_function(name) {
1837 let handles: smallvec::SmallVec<[ArgumentHandle; 4]> =
1838 args.iter().map(|n| ArgumentHandle::new(n, self)).collect();
1839 let fctx = DefaultFunctionContext::new_with_sheet(
1841 self.context,
1842 self.current_cell,
1843 self.current_sheet,
1844 );
1845 return fun.dispatch(&handles, &fctx);
1846 }
1847
1848 if let Some(callable) = self.resolve_local_callable(name) {
1849 let mut eval_args = Vec::with_capacity(args.len());
1850 let mut arg_references = Vec::with_capacity(args.len());
1851 for arg in args {
1852 arg_references
1856 .push(ArgumentHandle::new(arg, self).bound_reference_in_env(&self.local_env));
1857 eval_args.push(self.evaluate_ast(arg)?.into_literal());
1858 }
1859 return callable.invoke_with_references(self, &eval_args, &arg_references);
1860 }
1861
1862 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1864 ExcelError::new(ExcelErrorKind::Name).with_message(format!("Unknown function: {name}")),
1865 )))
1866 }
1867
1868 fn eval_function(&self, name: &str, args: &[ASTNode]) -> Result<LiteralValue, ExcelError> {
1869 self.eval_function_to_calc(name, args)
1870 .map(|cv| cv.into_literal())
1871 }
1872
1873 pub fn function_context(&self, cell_ref: Option<&CellRef>) -> DefaultFunctionContext<'_> {
1874 DefaultFunctionContext::new_with_sheet(self.context, cell_ref.cloned(), self.current_sheet)
1875 }
1876
1877 fn eval_array_literal_to_calc(
1879 &self,
1880 rows: &[Vec<ASTNode>],
1881 ) -> Result<crate::traits::CalcValue<'a>, ExcelError> {
1882 let mut out = Vec::with_capacity(rows.len());
1883 for row in rows {
1884 let mut r = Vec::with_capacity(row.len());
1885 for cell in row {
1886 r.push(self.evaluate_ast(cell)?.into_literal());
1887 }
1888 out.push(r);
1889 }
1890 Ok(crate::traits::CalcValue::Range(
1891 crate::engine::range_view::RangeView::from_owned_rows(out, self.context.date_system()),
1892 ))
1893 }
1894
1895 fn eval_array_literal(&self, rows: &[Vec<ASTNode>]) -> Result<LiteralValue, ExcelError> {
1896 self.eval_array_literal_to_calc(rows)
1897 .map(|cv| cv.into_literal())
1898 }
1899
1900 fn numeric_binary(
1904 &self,
1905 left: LiteralValue,
1906 right: LiteralValue,
1907 op: u8,
1908 ) -> Result<LiteralValue, ExcelError> {
1909 self.broadcast_apply(left, right, |l, r| {
1910 let a = crate::coercion::to_arithmetic_number_with_locale(
1911 &l,
1912 &self.context.locale(),
1913 self.context.date_system(),
1914 );
1915 let b = crate::coercion::to_arithmetic_number_with_locale(
1916 &r,
1917 &self.context.locale(),
1918 self.context.date_system(),
1919 );
1920 match (a, b) {
1921 (Ok(a), Ok(b)) => match arith_f64(op, a, b) {
1922 Ok(n) => Ok(LiteralValue::Number(n)),
1923 Err(e) => Ok(LiteralValue::Error(e)),
1924 },
1925 (Err(e), _) | (_, Err(e)) => Ok(LiteralValue::Error(e)),
1926 }
1927 })
1928 }
1929
1930 fn concat_values(
1934 &self,
1935 left: LiteralValue,
1936 right: LiteralValue,
1937 ) -> Result<LiteralValue, ExcelError> {
1938 fn concat_scalar(
1939 left: LiteralValue,
1940 right: LiteralValue,
1941 ) -> Result<LiteralValue, ExcelError> {
1942 Ok(match (left, right) {
1943 (LiteralValue::Error(error), _) | (_, LiteralValue::Error(error)) => {
1944 LiteralValue::Error(error)
1945 }
1946 (left, right) => LiteralValue::Text(format!(
1947 "{}{}",
1948 crate::coercion::to_text_invariant(&left),
1949 crate::coercion::to_text_invariant(&right)
1950 )),
1951 })
1952 }
1953 if matches!(left, LiteralValue::Array(_)) || matches!(right, LiteralValue::Array(_)) {
1955 self.broadcast_apply(left, right, concat_scalar)
1956 } else {
1957 concat_scalar(left, right)
1958 }
1959 }
1960
1961 fn map_array<F>(&self, arr: Vec<Vec<LiteralValue>>, f: F) -> Result<LiteralValue, ExcelError>
1962 where
1963 F: Fn(LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1964 {
1965 let mut out = Vec::with_capacity(arr.len());
1966 for row in arr {
1967 let mut new_row = Vec::with_capacity(row.len());
1968 for cell in row {
1969 new_row.push(match f(cell) {
1970 Ok(v) => v,
1971 Err(e) => LiteralValue::Error(e),
1972 });
1973 }
1974 out.push(new_row);
1975 }
1976 Ok(LiteralValue::Array(out))
1977 }
1978
1979 fn combine_arrays<F>(
1980 &self,
1981 l: Vec<Vec<LiteralValue>>,
1982 r: Vec<Vec<LiteralValue>>,
1983 f: F,
1984 ) -> Result<LiteralValue, ExcelError>
1985 where
1986 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
1987 {
1988 let l_shape = (l.len(), l.first().map(|r| r.len()).unwrap_or(0));
1990 let r_shape = (r.len(), r.first().map(|r| r.len()).unwrap_or(0));
1991 let target = match broadcast_shape(&[l_shape, r_shape]) {
1992 Ok(s) => s,
1993 Err(e) => return Ok(LiteralValue::Error(e)),
1994 };
1995 if let Some(e) = crate::engine::resource_ledger::materialized_cells_exceed(
1998 target.0,
1999 target.1,
2000 self.context.materialized_array_cells(),
2001 ) {
2002 return Ok(LiteralValue::Error(e));
2003 }
2004
2005 let mut out = Vec::with_capacity(target.0);
2006 for i in 0..target.0 {
2007 let mut row = Vec::with_capacity(target.1);
2008 for j in 0..target.1 {
2009 let (li, lj) = project_index((i, j), l_shape);
2010 let (ri, rj) = project_index((i, j), r_shape);
2011 let lv = l
2012 .get(li)
2013 .and_then(|r| r.get(lj))
2014 .cloned()
2015 .unwrap_or(LiteralValue::Empty);
2016 let rv = r
2017 .get(ri)
2018 .and_then(|r| r.get(rj))
2019 .cloned()
2020 .unwrap_or(LiteralValue::Empty);
2021 row.push(match f(lv, rv) {
2022 Ok(v) => v,
2023 Err(e) => LiteralValue::Error(e),
2024 });
2025 }
2026 out.push(row);
2027 }
2028 Ok(LiteralValue::Array(out))
2029 }
2030
2031 fn broadcast_apply<F>(
2032 &self,
2033 left: LiteralValue,
2034 right: LiteralValue,
2035 f: F,
2036 ) -> Result<LiteralValue, ExcelError>
2037 where
2038 F: Fn(LiteralValue, LiteralValue) -> Result<LiteralValue, ExcelError> + Copy,
2039 {
2040 use LiteralValue::*;
2041 match (left, right) {
2042 (Array(l), Array(r)) => self.combine_arrays(l, r, f),
2043 (Array(arr), v) => {
2044 let shape_l = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
2045 let shape_r = (1usize, 1usize);
2046 let target = match broadcast_shape(&[shape_l, shape_r]) {
2047 Ok(s) => s,
2048 Err(e) => return Ok(LiteralValue::Error(e)),
2049 };
2050 let mut out = Vec::with_capacity(target.0);
2051 for i in 0..target.0 {
2052 let mut row = Vec::with_capacity(target.1);
2053 for j in 0..target.1 {
2054 let (li, lj) = project_index((i, j), shape_l);
2055 let lv = arr
2056 .get(li)
2057 .and_then(|r| r.get(lj))
2058 .cloned()
2059 .unwrap_or(LiteralValue::Empty);
2060 row.push(match f(lv, v.clone()) {
2061 Ok(vv) => vv,
2062 Err(e) => LiteralValue::Error(e),
2063 });
2064 }
2065 out.push(row);
2066 }
2067 Ok(LiteralValue::Array(out))
2068 }
2069 (v, Array(arr)) => {
2070 let shape_l = (1usize, 1usize);
2071 let shape_r = (arr.len(), arr.first().map(|r| r.len()).unwrap_or(0));
2072 let target = match broadcast_shape(&[shape_l, shape_r]) {
2073 Ok(s) => s,
2074 Err(e) => return Ok(LiteralValue::Error(e)),
2075 };
2076 let mut out = Vec::with_capacity(target.0);
2077 for i in 0..target.0 {
2078 let mut row = Vec::with_capacity(target.1);
2079 for j in 0..target.1 {
2080 let (ri, rj) = project_index((i, j), shape_r);
2081 let rv = arr
2082 .get(ri)
2083 .and_then(|r| r.get(rj))
2084 .cloned()
2085 .unwrap_or(LiteralValue::Empty);
2086 row.push(match f(v.clone(), rv) {
2087 Ok(vv) => vv,
2088 Err(e) => LiteralValue::Error(e),
2089 });
2090 }
2091 out.push(row);
2092 }
2093 Ok(LiteralValue::Array(out))
2094 }
2095 (l, r) => f(l, r),
2096 }
2097 }
2098
2099 fn coerce_number(&self, v: &LiteralValue) -> Result<f64, ExcelError> {
2101 coercion::to_number_lenient(v)
2102 }
2103
2104 fn coerce_text(&self, v: &LiteralValue) -> String {
2105 coercion::to_text_invariant(v)
2106 }
2107
2108 fn compare(
2110 &self,
2111 op: BinaryOperator,
2112 left: LiteralValue,
2113 right: LiteralValue,
2114 ) -> Result<LiteralValue, ExcelError> {
2115 use LiteralValue::*;
2116 if matches!(left, Error(_)) {
2117 return Ok(left);
2118 }
2119 if matches!(right, Error(_)) {
2120 return Ok(right);
2121 }
2122
2123 match (left, right) {
2125 (Array(l), Array(r)) => self.combine_arrays(l, r, |a, b| self.compare(op, a, b)),
2126 (Array(arr), v) => self.broadcast_apply(Array(arr), v, |a, b| self.compare(op, a, b)),
2127 (v, Array(arr)) => self.broadcast_apply(v, Array(arr), |a, b| self.compare(op, a, b)),
2128 (l, r) => {
2129 let res = match (l, r) {
2130 (Number(a), Number(b)) => self.cmp_f64(a, b, op),
2133 (Int(a), Int(b)) => self.cmp_f64(a as f64, b as f64, op),
2134 (Int(a), Number(b)) => self.cmp_f64(a as f64, b, op),
2135 (Number(a), Int(b)) => self.cmp_f64(a, b as f64, op),
2136 (Boolean(a), Boolean(b)) => {
2137 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
2138 }
2139 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
2140 (a, b) => self.cmp_ranked(a, b, op),
2141 };
2142 Ok(LiteralValue::Boolean(res))
2143 }
2144 }
2145 }
2146
2147 fn cmp_ranked(&self, l: LiteralValue, r: LiteralValue, op: BinaryOperator) -> bool {
2163 use LiteralValue::*;
2164
2165 let (l, r) = match (l, r) {
2169 (Empty, Empty) => (Number(0.0), Number(0.0)),
2170 (Empty, r) => (empty_as_zero_of(&r), r),
2171 (l, Empty) => {
2172 let e = empty_as_zero_of(&l);
2173 (l, e)
2174 }
2175 (l, r) => (l, r),
2176 };
2177
2178 let (lr, rr) = (excel_type_rank(&l), excel_type_rank(&r));
2179 if lr != rr {
2180 return self.cmp_f64(lr as f64, rr as f64, op);
2181 }
2182
2183 match (l, r) {
2184 (Text(a), Text(b)) => self.cmp_text(&a, &b, op),
2185 (Boolean(a), Boolean(b)) => {
2186 self.cmp_f64(if a { 1.0 } else { 0.0 }, if b { 1.0 } else { 0.0 }, op)
2187 }
2188 (a, b) => {
2191 let an = crate::coercion::to_number_strict(&a).ok();
2192 let bn = crate::coercion::to_number_strict(&b).ok();
2193 if let (Some(a), Some(b)) = (an, bn) {
2194 self.cmp_f64(a, b, op)
2195 } else {
2196 self.cmp_text(
2200 &crate::coercion::to_text_invariant(&a),
2201 &crate::coercion::to_text_invariant(&b),
2202 op,
2203 )
2204 }
2205 }
2206 }
2207 }
2208
2209 fn cmp_f64(&self, a: f64, b: f64, op: BinaryOperator) -> bool {
2210 cmp_f64(a, b, op)
2211 }
2212 fn cmp_text(&self, a: &str, b: &str, op: BinaryOperator) -> bool {
2213 let loc = self.context.locale();
2214 let (a, b) = (loc.fold_case_invariant(a), loc.fold_case_invariant(b));
2215 self.cmp_f64(a.cmp(&b) as i32 as f64, 0.0, op)
2216 }
2217}
2218
2219#[inline]
2222pub(crate) fn cmp_f64(a: f64, b: f64, op: BinaryOperator) -> bool {
2223 match op {
2224 BinaryOperator::Equal => a == b,
2225 BinaryOperator::NotEqual => a != b,
2226 BinaryOperator::Greater => a > b,
2227 BinaryOperator::Less => a < b,
2228 BinaryOperator::GreaterEqual => a >= b,
2229 BinaryOperator::LessEqual => a <= b,
2230 _ => unreachable!(),
2231 }
2232}
2233
2234#[inline]
2239pub(crate) fn arith_f64(op: u8, a: f64, b: f64) -> Result<f64, ExcelError> {
2240 let v = match op {
2241 b'+' => a + b,
2242 b'-' => a - b,
2243 b'*' => a * b,
2244 b'/' => {
2245 if b == 0.0 {
2246 return Err(ExcelError::from_error_string("#DIV/0!"));
2247 }
2248 a / b
2249 }
2250 b'^' => {
2251 if a < 0.0 && b.fract() != 0.0 {
2253 return Err(ExcelError::new_num());
2254 }
2255 a.powf(b)
2256 }
2257 _ => unreachable!("arithmetic operator"),
2258 };
2259 crate::coercion::sanitize_numeric(v)
2260}
2261
2262#[inline]
2265pub(crate) fn unary_f64(op: u8, n: f64) -> Result<f64, ExcelError> {
2266 crate::coercion::sanitize_numeric(match op {
2267 b'-' => -n,
2268 b'%' => n / 100.0,
2269 _ => unreachable!("unary operator"),
2270 })
2271}
2272
2273fn excel_type_rank(v: &LiteralValue) -> u8 {
2281 match v {
2282 LiteralValue::Text(_) => 1,
2283 LiteralValue::Boolean(_) => 2,
2284 _ => 0,
2285 }
2286}
2287
2288fn empty_as_zero_of(other: &LiteralValue) -> LiteralValue {
2292 match other {
2293 LiteralValue::Text(_) => LiteralValue::Text(String::new()),
2294 LiteralValue::Boolean(_) => LiteralValue::Boolean(false),
2295 _ => LiteralValue::Number(0.0),
2296 }
2297}
2298
2299fn relocate_reference_for_offset(
2300 reference: &ReferenceType,
2301 row_delta: i64,
2302 col_delta: i64,
2303) -> Result<ReferenceType, ExcelError> {
2304 match reference {
2305 ReferenceType::Cell {
2306 sheet,
2307 row,
2308 col,
2309 row_abs,
2310 col_abs,
2311 } => Ok(ReferenceType::Cell {
2312 sheet: sheet.clone(),
2313 row: shift_axis_for_offset(*row, row_delta, *row_abs)?,
2314 col: shift_axis_for_offset(*col, col_delta, *col_abs)?,
2315 row_abs: *row_abs,
2316 col_abs: *col_abs,
2317 }),
2318 ReferenceType::Range {
2319 sheet,
2320 start_row,
2321 start_col,
2322 end_row,
2323 end_col,
2324 start_row_abs,
2325 start_col_abs,
2326 end_row_abs,
2327 end_col_abs,
2328 } => Ok(ReferenceType::Range {
2329 sheet: sheet.clone(),
2330 start_row: shift_optional_axis_for_offset(*start_row, row_delta, *start_row_abs)?,
2331 start_col: shift_optional_axis_for_offset(*start_col, col_delta, *start_col_abs)?,
2332 end_row: shift_optional_axis_for_offset(*end_row, row_delta, *end_row_abs)?,
2333 end_col: shift_optional_axis_for_offset(*end_col, col_delta, *end_col_abs)?,
2334 start_row_abs: *start_row_abs,
2335 start_col_abs: *start_col_abs,
2336 end_row_abs: *end_row_abs,
2337 end_col_abs: *end_col_abs,
2338 }),
2339 ReferenceType::NamedRange(name) => Ok(ReferenceType::NamedRange(name.clone())),
2342 ReferenceType::Table(_)
2343 | ReferenceType::Cell3D { .. }
2344 | ReferenceType::Range3D { .. }
2345 | ReferenceType::External(_) => Err(unsupported_reference_relocation_error()),
2346 }
2347}
2348
2349fn shift_optional_axis_for_offset(
2350 value: Option<u32>,
2351 delta: i64,
2352 is_absolute: bool,
2353) -> Result<Option<u32>, ExcelError> {
2354 value
2355 .map(|value| shift_axis_for_offset(value, delta, is_absolute))
2356 .transpose()
2357}
2358
2359pub(crate) fn shift_axis_for_offset(
2360 value: u32,
2361 delta: i64,
2362 is_absolute: bool,
2363) -> Result<u32, ExcelError> {
2364 if is_absolute {
2365 return Ok(value);
2366 }
2367 let shifted = i64::from(value) + delta;
2368 if shifted < 1 || shifted > i64::from(u32::MAX) {
2369 return Err(unsupported_reference_relocation_error());
2370 }
2371 Ok(shifted as u32)
2372}
2373
2374fn unsupported_reference_relocation_error() -> ExcelError {
2375 ExcelError::new(ExcelErrorKind::Ref)
2376 .with_message("Unsupported reference relocation for FormulaPlane span evaluation")
2377}
2378
2379#[cfg(test)]
2380mod format_algebra_tests {
2381 use super::*;
2382
2383 #[test]
2384 fn compact_argument_ranges_match_owned_resolution() -> Result<(), Box<dyn std::error::Error>> {
2385 use crate::traits::ResolvedArgument;
2386 let mut config = EvalConfig::default();
2387 config.evaluation_budgets.scratch.materialized_array_cells = Some(4);
2388 let mut engine = Engine::new(TestWorkbook::new(), config);
2389 let sheet_id = engine.add_sheet("Other")?;
2390 engine.set_cell_value("Other", 2, 2, LiteralValue::Number(17.0))?;
2391 engine.set_cell_value("Other", 4, 3, LiteralValue::Text("end".into()))?;
2392 let template = formualizer_parse::parser::parse("=A1:C3")?;
2393 let cell = crate::CellRef::new(
2394 sheet_id,
2395 crate::reference::Coord::from_excel(1, 1, true, true),
2396 );
2397 for qualified in [false, true] {
2398 for open_rows in [false, true] {
2399 for open_cols in [false, true] {
2400 for absolute in [false, true] {
2401 for delta in [-2, 0, 2] {
2402 let reference = ReferenceType::Range {
2403 sheet: qualified.then(|| "Other".to_string()),
2404 start_row: (!open_rows).then_some(1),
2405 start_col: (!open_cols).then_some(1),
2406 end_row: (!open_rows).then_some(3),
2407 end_col: (!open_cols).then_some(3),
2408 start_row_abs: absolute,
2409 start_col_abs: absolute,
2410 end_row_abs: absolute,
2411 end_col_abs: absolute,
2412 };
2413 let mut ast = template.clone();
2414 ast.node_type = ASTNodeType::Reference {
2415 original: reference.to_string(),
2416 reference,
2417 };
2418 let mut store = DataStore::new();
2419 let registry = engine.graph.sheet_reg();
2420 let id = store.store_ast(&ast, registry);
2421 let mut slow =
2422 Interpreter::new(&engine, "Other").with_current_cell(cell);
2423 slow.reference_row_delta = delta;
2424 slow.reference_col_delta = delta;
2425 let fast = slow.with_current_cell(cell).with_arena_cells(&engine);
2426 assert!(fast.resolve_arena_argument_range(id, &store).is_some());
2427 assert!(slow.resolve_arena_argument_range(id, &store).is_none());
2428 let a = ArgumentHandle::new_arena(id, &fast, &store, registry)
2429 .resolve_once();
2430 let b = ArgumentHandle::new_arena(id, &slow, &store, registry)
2431 .resolve_once();
2432 match (a, b) {
2433 (
2434 Ok(ResolvedArgument::Range(a)),
2435 Ok(ResolvedArgument::Range(b)),
2436 ) => {
2437 assert_eq!(a.dims(), b.dims());
2438 assert_eq!(
2439 (a.start_row(), a.start_col()),
2440 (b.start_row(), b.start_col())
2441 );
2442 assert_eq!(
2443 a.materialization_error().map(|e| e.kind),
2444 b.materialization_error().map(|e| e.kind)
2445 );
2446 let (rows, cols) = a.dims();
2447 for row in 0..rows.min(8) {
2448 for col in 0..cols.min(8) {
2449 assert_eq!(a.get_cell(row, col), b.get_cell(row, col));
2450 }
2451 }
2452 }
2453 (
2454 Ok(ResolvedArgument::ReferenceError(a)),
2455 Ok(ResolvedArgument::ReferenceError(b)),
2456 ) => assert_eq!(a, b),
2457 (Err(a), Err(b)) => assert_eq!(a, b),
2458 (a, b) => assert_eq!(format!("{a:?}"), format!("{b:?}")),
2459 }
2460 }
2461 }
2462 }
2463 }
2464 }
2465 Ok(())
2466 }
2467
2468 #[test]
2469 fn compact_ranges_match_owned_resolution_after_relocation() -> Result<(), ExcelError> {
2470 let mut engine = Engine::new(TestWorkbook::new(), EvalConfig::default());
2471 let sheet_id = engine.add_sheet("Other")?;
2472 engine.set_cell_value("Other", 2, 2, LiteralValue::Number(17.0))?;
2473 engine.set_cell_value("Other", 4, 3, LiteralValue::Text("end".into()))?;
2474 for qualified in [false, true] {
2475 for open_rows in [false, true] {
2476 for open_cols in [false, true] {
2477 for absolute in [false, true] {
2478 for delta in [-2, 0, 2] {
2479 let compact = CompactRefType::Range {
2480 sheet: qualified.then_some(SheetKey::Id(sheet_id)),
2481 start_row: if open_rows { 0 } else { 1 },
2482 start_col: if open_cols { 0 } else { 1 },
2483 end_row: if open_rows { u32::MAX } else { 3 },
2484 end_col: if open_cols { u32::MAX } else { 3 },
2485 start_row_abs: absolute,
2486 start_col_abs: absolute,
2487 end_row_abs: absolute,
2488 end_col_abs: absolute,
2489 };
2490 let owned = ReferenceType::Range {
2491 sheet: qualified.then(|| "Other".to_string()),
2492 start_row: (!open_rows).then_some(1),
2493 start_col: (!open_cols).then_some(1),
2494 end_row: (!open_rows).then_some(3),
2495 end_col: (!open_cols).then_some(3),
2496 start_row_abs: absolute,
2497 start_col_abs: absolute,
2498 end_row_abs: absolute,
2499 end_col_abs: absolute,
2500 };
2501 let expected = relocate_reference_for_offset(&owned, delta, delta)
2502 .and_then(|r| {
2503 crate::traits::EvaluationContext::resolve_range_view(
2504 &engine, &r, "Other",
2505 )
2506 });
2507 let actual =
2508 engine.resolve_arena_range(&compact, sheet_id, delta, delta);
2509 match (actual, expected) {
2510 (Ok(a), Ok(b)) => {
2511 assert_eq!(a.dims(), b.dims());
2512 assert_eq!(
2513 (a.start_row(), a.start_col()),
2514 (b.start_row(), b.start_col())
2515 );
2516 let (rows, cols) = a.dims();
2517 for row in 0..rows.min(8) {
2518 for col in 0..cols.min(8) {
2519 assert_eq!(a.get_cell(row, col), b.get_cell(row, col));
2520 }
2521 }
2522 }
2523 (Err(a), Err(b)) => assert_eq!(a.kind, b.kind),
2524 (a, b) => assert_eq!(format!("{a:?}"), format!("{b:?}")),
2525 }
2526 }
2527 }
2528 }
2529 }
2530 }
2531 engine.rename_sheet(sheet_id, "Renamed")?;
2532 assert!(
2533 engine
2534 .arrow_sheet_by_id(sheet_id)
2535 .is_some_and(|s| s.name.as_ref() == "Renamed")
2536 );
2537 assert!(engine.arrow_sheet_by_id_mut(sheet_id).is_some());
2538 let other = engine.add_sheet("Third")?;
2539 engine.sheet_store_mut().sheets.reverse();
2540 assert!(
2541 engine
2542 .arrow_sheet_by_id(sheet_id)
2543 .is_some_and(|s| s.name.as_ref() == "Renamed")
2544 );
2545 engine.remove_sheet(other)?;
2546 assert!(engine.arrow_sheet_by_id(sheet_id).is_some());
2547 Ok(())
2548 }
2549
2550 use crate::engine::{EvalConfig, eval::Engine};
2551 use crate::format::FormatId;
2552 use crate::test_workbook::TestWorkbook;
2553
2554 #[test]
2555 fn binary_fastpath_preserves_values_and_format_annotations() {
2556 let engine = Engine::new(TestWorkbook::new(), EvalConfig::default());
2557 let interpreter = Interpreter::new(&engine, "Sheet1");
2558 let values = [
2559 LiteralValue::Number(2.5),
2560 LiteralValue::Int(2),
2561 LiteralValue::Text("3".into()),
2562 LiteralValue::Text("not numeric".into()),
2563 LiteralValue::Boolean(true),
2564 LiteralValue::Empty,
2565 LiteralValue::Error(ExcelError::new(ExcelErrorKind::Na)),
2566 LiteralValue::Array(vec![vec![LiteralValue::Number(1.0)]]),
2567 ];
2568 let formats = [
2569 None,
2570 Some(FormatId::DATE),
2571 Some(FormatId::TIME),
2572 Some(FormatId(49)),
2573 ];
2574 for (op, byte) in [
2575 (BinaryOperator::Add, b'+'),
2576 (BinaryOperator::Subtract, b'-'),
2577 ] {
2578 for left in &values {
2579 for right in &values {
2580 for left_format in formats {
2581 for right_format in formats {
2582 let expected = interpreter
2583 .numeric_binary(left.clone(), right.clone(), byte)
2584 .map(|value| {
2585 interpreter.annotate_numeric_result(
2586 value,
2587 interpreter.binary_format(
2588 char::from(byte),
2589 left_format,
2590 right_format,
2591 ),
2592 )
2593 });
2594 let actual = interpreter.apply_binary_op(
2595 op,
2596 left.clone(),
2597 left_format,
2598 right.clone(),
2599 right_format,
2600 );
2601 match (actual, expected) {
2602 (Ok(actual), Ok(expected)) => {
2603 assert_eq!(actual.format_id(), expected.format_id());
2604 assert_eq!(actual.into_literal(), expected.into_literal());
2605 }
2606 (Err(actual), Err(expected)) => assert_eq!(actual, expected),
2607 _ => panic!("operator result kind changed"),
2608 }
2609 }
2610 }
2611 }
2612 }
2613 }
2614 }
2615
2616 #[test]
2617 fn temporal_binary_format_algebra_pins_positive_and_negative_cases() {
2618 let engine = Engine::new(TestWorkbook::new(), EvalConfig::default());
2619 let interpreter = Interpreter::new(&engine, "Sheet1");
2620
2621 assert_eq!(
2622 interpreter.binary_format('+', Some(FormatId::DATE), Some(FormatId::TIME)),
2623 Some(FormatId::DATETIME)
2624 );
2625 assert_eq!(
2626 interpreter.binary_format('+', Some(FormatId::DATE), Some(FormatId(9))),
2627 Some(FormatId::DATE),
2628 "Date + Percent follows the measured temporal-wins rule"
2629 );
2630 assert_eq!(
2631 interpreter.binary_format('-', Some(FormatId::DATE), Some(FormatId::DATE)),
2632 None,
2633 "Date - Date is an unformatted duration in days"
2634 );
2635 assert_eq!(
2636 interpreter.binary_format('+', Some(FormatId::DATE), Some(FormatId(49))),
2637 None,
2638 "Date + Text must not acquire a temporal annotation"
2639 );
2640 for (left, right) in [
2641 (FormatId::DATE, FormatId::DATE),
2642 (FormatId::DURATION, FormatId::DATE),
2643 (FormatId::DATE, FormatId(5)),
2644 (FormatId::DATETIME, FormatId::TIME),
2645 ] {
2646 assert_eq!(
2647 interpreter.binary_format('+', Some(left), Some(right)),
2648 None
2649 );
2650 }
2651 }
2652}
2653
2654#[cfg(test)]
2655mod resolved_function_tests {
2656 use super::*;
2657 use crate::function::Function;
2658 use crate::traits::FunctionProvider;
2659 use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
2660 use std::sync::{Arc, RwLock};
2661
2662 struct TestFunction;
2663 impl Function for TestFunction {
2664 fn name(&self) -> &'static str {
2665 "FN_TABLE_GLOBAL_REFRESH_TEST"
2666 }
2667 fn eval<'a, 'b, 'c>(
2668 &self,
2669 _: &'c [ArgumentHandle<'a, 'b>],
2670 _: &dyn crate::traits::FunctionContext<'b>,
2671 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2672 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(1.0)))
2673 }
2674 }
2675 #[derive(Default)]
2676 struct Provider {
2677 generation: AtomicU64,
2678 calls: AtomicUsize,
2679 function: RwLock<Option<Arc<dyn Function>>>,
2680 }
2681 impl FunctionProvider for Provider {
2682 fn planning_semantic_revision(&self) -> Option<u64> {
2683 Some(self.generation.load(Ordering::Acquire))
2684 }
2685 fn function_capabilities(&self, _: &str, _: &str) -> Option<crate::function::FnCaps> {
2686 self.function.read().unwrap().as_ref().map(|f| f.caps())
2687 }
2688 fn get_function(&self, _: &str, _: &str) -> Option<Arc<dyn Function>> {
2689 self.calls.fetch_add(1, Ordering::Relaxed);
2690 self.function.read().unwrap().clone()
2691 }
2692 }
2693 fn arena(name: &str) -> (DataStore, crate::engine::arena::StringId) {
2694 let mut store = DataStore::new();
2695 let registry = SheetRegistry::new();
2696 let ast = ASTNode::new(
2697 ASTNodeType::Function {
2698 name: name.into(),
2699 args: vec![],
2700 },
2701 None,
2702 );
2703 let node = store.store_ast(&ast, ®istry);
2704 let Some(AstNodeData::Function { name_id, .. }) = store.get_node(node) else {
2705 panic!("function node")
2706 };
2707 let id = *name_id;
2708 (store, id)
2709 }
2710 #[test]
2711 fn function_table_tracks_distinct_names_not_interner_size() {
2712 let mut store = DataStore::new();
2713 let registry = SheetRegistry::new();
2714 for row in 1..=200_000 {
2715 store.store_ast(
2716 &formualizer_parse::parser::parse(format!("=A{row}+1")).unwrap(),
2717 ®istry,
2718 );
2719 }
2720 store.store_ast(
2721 &formualizer_parse::parser::parse("=ABS(1)").unwrap(),
2722 ®istry,
2723 );
2724 let mut table = ResolvedFunctions::default();
2725 table.refresh(&store, &Provider::default());
2726 assert_eq!(table.functions.len(), 1);
2727 assert_eq!(table.capabilities.len(), 1);
2728 assert_eq!(table.slots.len(), 1);
2729 }
2730
2731 #[test]
2732 fn formula_edits_preserve_existing_function_slots() {
2733 let provider = Provider::default();
2734 let (mut store, id) = arena("EXISTING");
2735 let mut table = ResolvedFunctions::default();
2736 table.refresh(&store, &provider);
2737 let slot = table.slots[&id];
2738 let revision = table.revision;
2739 store.store_ast(
2740 &formualizer_parse::parser::parse("=EXISTING()+1").unwrap(),
2741 &SheetRegistry::new(),
2742 );
2743 table.refresh(&store, &provider);
2744 if table.revision == revision {
2747 assert_eq!(provider.calls.load(Ordering::Relaxed), 1);
2748 }
2749 let previous_calls = provider.calls.load(Ordering::Relaxed);
2750 let previous_revision = table.revision;
2751 store.store_ast(
2752 &formualizer_parse::parser::parse("=NEW_NAME()").unwrap(),
2753 &SheetRegistry::new(),
2754 );
2755 table.refresh(&store, &provider);
2756 if table.revision == previous_revision {
2757 assert_eq!(provider.calls.load(Ordering::Relaxed), previous_calls + 1);
2758 }
2759 if table.revision == revision {
2760 assert_eq!(table.slots[&id], slot);
2761 }
2762 assert_eq!(table.functions.len(), 2);
2763 }
2764
2765 #[test]
2766 fn unversioned_provider_is_resolved_fresh_each_request() {
2767 struct Unversioned(Provider);
2768 impl FunctionProvider for Unversioned {
2769 fn get_function(&self, ns: &str, name: &str) -> Option<Arc<dyn Function>> {
2770 self.0.get_function(ns, name)
2771 }
2772 }
2773 let provider = Unversioned(Provider::default());
2774 let (store, id) = arena("CHANGING");
2775 let mut table = ResolvedFunctions::default();
2776 table.refresh(&store, &provider);
2777 assert!(table.get(id).is_none());
2778 *provider.0.function.write().unwrap() = Some(Arc::new(TestFunction));
2779 table.refresh(&store, &provider);
2780 assert!(table.get(id).is_some());
2781 let previous = table.functions[0].clone().unwrap();
2782 *provider.0.function.write().unwrap() = Some(Arc::new(TestFunction));
2783 table.refresh(&store, &provider);
2784 assert!(!Arc::ptr_eq(
2785 &previous,
2786 table.functions[0].as_ref().unwrap()
2787 ));
2788 }
2789
2790 #[test]
2791 fn hits_and_misses_are_read_only_and_local_generations_refresh() {
2792 let provider = Provider::default();
2793 let (store, id) = arena("MISSING_THEN_REGISTERED");
2794 let mut table = ResolvedFunctions::default();
2795 table.refresh(&store, &provider);
2796 for _ in 0..10 {
2797 assert!(table.get(id).is_none());
2798 }
2799 assert_eq!(provider.calls.load(Ordering::Relaxed), 1);
2800 *provider.function.write().unwrap() = Some(Arc::new(TestFunction));
2801 provider.generation.fetch_add(1, Ordering::Release);
2802 assert!(table.get(id).is_none());
2804 table.refresh(&store, &provider);
2805 assert!(table.get(id).is_some());
2806 let calls = provider.calls.load(Ordering::Relaxed);
2807 for _ in 0..10 {
2808 assert!(table.get(id).is_some());
2809 }
2810 assert_eq!(provider.calls.load(Ordering::Relaxed), calls);
2811 *provider.function.write().unwrap() = None;
2812 provider.generation.fetch_add(1, Ordering::Release);
2813 assert!(table.get(id).is_some());
2814 table.refresh(&store, &provider);
2815 assert!(table.get(id).is_none());
2816 }
2817 #[test]
2818 fn global_epoch_and_excel_prefix_aliases_refresh() {
2819 struct Global;
2820 impl FunctionProvider for Global {
2821 fn get_function(&self, ns: &str, name: &str) -> Option<Arc<dyn Function>> {
2822 crate::function_registry::get(ns, name)
2823 }
2824 }
2825 let (store, id) = arena("_xlfn.FN_TABLE_GLOBAL_REFRESH_TEST");
2826 let mut table = ResolvedFunctions::default();
2827 table.refresh(&store, &Global);
2828 assert!(table.get(id).is_none());
2829 crate::function_registry::register_function(Arc::new(TestFunction));
2830 table.refresh(&store, &Global);
2831 assert_eq!(
2832 table.get(id).unwrap().name(),
2833 "FN_TABLE_GLOBAL_REFRESH_TEST"
2834 );
2835 let previous = table
2836 .functions
2837 .get(*table.slots.get(&id).unwrap())
2838 .unwrap()
2839 .as_ref()
2840 .unwrap()
2841 .clone();
2842 crate::function_registry::register_function(Arc::new(TestFunction));
2843 table.refresh(&store, &Global);
2844 let replacement = table
2845 .functions
2846 .get(*table.slots.get(&id).unwrap())
2847 .unwrap()
2848 .as_ref()
2849 .unwrap();
2850 assert!(!Arc::ptr_eq(&previous, replacement));
2851 }
2852}