1use crate::engine::lookup_index_cache::{LookupAxis, LookupIndex};
2use crate::engine::range_view::RangeView;
3use crate::engine::row_visibility::VisibilityMaskMode;
4pub use crate::function::Function;
5use crate::interpreter::Interpreter;
6use crate::reference::CellRef;
7use formualizer_common::{
8 LiteralValue,
9 error::{ExcelError, ExcelErrorKind},
10};
11use std::any::Any;
12use std::borrow::Cow;
13use std::fmt::Debug;
14use std::sync::Arc;
15
16use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType, TableSpecifier};
17
18#[derive(Clone)]
21pub(crate) struct FunctionValueReferenceIntent {
22 pub function: Arc<dyn Function>,
23 pub binding_absent: bool,
24}
25
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub struct ReferenceInfo {
28 pub first_sheet_index: Option<usize>,
30 pub sheet_count: Option<usize>,
32 pub first_cell: Option<CellRef>,
34}
35
36pub trait Range: Debug + Send + Sync {
39 fn get(&self, row: usize, col: usize) -> Result<LiteralValue, ExcelError>;
40 fn dimensions(&self) -> (usize, usize);
41
42 fn is_sparse(&self) -> bool {
43 false
44 }
45
46 fn is_infinite(&self) -> bool {
48 false
49 }
50
51 fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
52 Cow::Owned(
53 (0..self.dimensions().0)
54 .map(|r| {
55 (0..self.dimensions().1)
56 .map(|c| self.get(r, c).unwrap_or(LiteralValue::Empty))
57 .collect()
58 })
59 .collect(),
60 )
61 }
62
63 fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
64 let (rows, cols) = self.dimensions();
65 Box::new((0..rows).flat_map(move |r| (0..cols).map(move |c| self.get(r, c).unwrap())))
66 }
67 fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
68 let (rows, cols) = self.dimensions();
69 Box::new((0..rows).map(move |r| (0..cols).map(|c| self.get(r, c).unwrap()).collect()))
70 }
71
72 fn as_any(&self) -> &dyn Any;
74}
75
76impl Range for Box<dyn Range> {
78 fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
79 (**self).get(r, c)
80 }
81 fn dimensions(&self) -> (usize, usize) {
82 (**self).dimensions()
83 }
84 fn is_sparse(&self) -> bool {
85 (**self).is_sparse()
86 }
87 fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
88 (**self).materialise()
89 }
90 fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
91 (**self).iter_cells()
92 }
93 fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
94 (**self).iter_rows()
95 }
96 fn as_any(&self) -> &dyn Any {
97 (**self).as_any()
98 }
99}
100
101pub type CowValue<'a> = Cow<'a, LiteralValue>;
104
105pub trait CustomCallable: Send + Sync {
106 fn arity(&self) -> usize;
107
108 fn invoke<'ctx>(
109 &self,
110 interp: &Interpreter<'ctx>,
111 args: &[LiteralValue],
112 ) -> Result<CalcValue<'ctx>, ExcelError>;
113
114 fn invoke_with_references<'ctx>(
128 &self,
129 interp: &Interpreter<'ctx>,
130 args: &[LiteralValue],
131 references: &[Option<ReferenceType>],
132 ) -> Result<CalcValue<'ctx>, ExcelError> {
133 let _ = references;
134 self.invoke(interp, args)
135 }
136}
137
138#[derive(Clone)]
139pub enum CalcValue<'a> {
140 Scalar(LiteralValue),
141 AnnotatedScalar(LiteralValue, crate::format::FormatId),
143 Range(RangeView<'a>),
144 Callable(Arc<dyn CustomCallable>),
145}
146
147#[derive(Clone)]
153pub(crate) enum ResolvedArgument<'a> {
154 Range(RangeView<'a>),
155 ReferenceError(ExcelError),
156 Value(CalcValue<'a>),
157}
158
159impl std::fmt::Debug for ResolvedArgument<'_> {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 match self {
162 Self::Range(view) => f.debug_tuple("Range").field(view).finish(),
163 Self::ReferenceError(error) => f.debug_tuple("ReferenceError").field(error).finish(),
164 Self::Value(value) => f.debug_tuple("Value").field(value).finish(),
165 }
166 }
167}
168
169impl<'a> std::fmt::Debug for CalcValue<'a> {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 match self {
172 CalcValue::Scalar(v) => f.debug_tuple("Scalar").field(v).finish(),
173 CalcValue::AnnotatedScalar(v, format) => f
174 .debug_tuple("AnnotatedScalar")
175 .field(v)
176 .field(format)
177 .finish(),
178 CalcValue::Range(rv) => {
179 let (r, c) = rv.dims();
180 f.debug_tuple("Range").field(&(r, c)).finish()
181 }
182 CalcValue::Callable(_) => f.write_str("Callable(<opaque>)"),
183 }
184 }
185}
186
187#[cfg(test)]
190thread_local! {
191 pub(crate) static RANGE_MATERIALIZED_CELLS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
192}
193
194impl<'a> CalcValue<'a> {
195 pub fn into_literal(self) -> LiteralValue {
196 match self {
197 CalcValue::Scalar(s) | CalcValue::AnnotatedScalar(s, _) => s,
198 CalcValue::Range(rv) => {
199 let (rows, cols) = rv.dims();
200 #[cfg(test)]
201 RANGE_MATERIALIZED_CELLS.with(|count| count.set(count.get() + rows * cols));
202 if rows == 1 && cols == 1 {
203 rv.get_cell(0, 0)
204 } else if let Some(error) = rv.materialization_error() {
205 LiteralValue::Error(error)
206 } else {
207 let mut data = Vec::with_capacity(rows);
208 for row_idx in 0..rows {
209 let mut row = Vec::with_capacity(cols);
210 for col_idx in 0..cols {
211 row.push(rv.get_cell(row_idx, col_idx));
212 }
213 data.push(row);
214 }
215 LiteralValue::Array(data)
216 }
217 }
218 CalcValue::Callable(_) => LiteralValue::Error(
219 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
220 ),
221 }
222 }
223
224 pub fn as_scalar(&self) -> Option<&LiteralValue> {
225 match self {
226 CalcValue::Scalar(s) | CalcValue::AnnotatedScalar(s, _) => Some(s),
227 _ => None,
228 }
229 }
230
231 pub fn format_id(&self) -> Option<crate::format::FormatId> {
232 match self {
233 CalcValue::AnnotatedScalar(_, format) => Some(*format),
234 _ => None,
235 }
236 }
237
238 pub fn with_format(self, format: Option<crate::format::FormatId>) -> Self {
239 let format = format.filter(|id| *id != crate::format::FormatId::GENERAL);
240 match (self, format) {
241 (CalcValue::Scalar(value) | CalcValue::AnnotatedScalar(value, _), Some(id)) => {
242 CalcValue::AnnotatedScalar(value, id)
243 }
244 (CalcValue::AnnotatedScalar(value, _), None) => CalcValue::Scalar(value),
245 (other, _) => other,
246 }
247 }
248
249 pub fn into_scalar_parts(self) -> (LiteralValue, Option<crate::format::FormatId>) {
250 match self {
251 CalcValue::Scalar(value) => (value, None),
252 CalcValue::AnnotatedScalar(value, format) => (value, Some(format)),
253 other => (other.into_literal(), None),
254 }
255 }
256
257 pub fn as_range(&self) -> Option<&RangeView<'a>> {
258 match self {
259 CalcValue::Range(r) => Some(r),
260 _ => None,
261 }
262 }
263
264 pub fn as_callable(&self) -> Option<&Arc<dyn CustomCallable>> {
265 match self {
266 CalcValue::Callable(c) => Some(c),
267 _ => None,
268 }
269 }
270
271 pub fn into_owned(self) -> LiteralValue {
272 self.into_literal()
273 }
274}
275
276impl From<CalcValue<'_>> for LiteralValue {
277 fn from(val: CalcValue<'_>) -> Self {
278 val.into_literal()
279 }
280}
281
282impl<'a> PartialEq<LiteralValue> for CalcValue<'a> {
283 fn eq(&self, other: &LiteralValue) -> bool {
284 match self {
285 CalcValue::Scalar(s) | CalcValue::AnnotatedScalar(s, _) => s == other,
286 CalcValue::Range(rv) => match other {
287 LiteralValue::Array(arr) => {
288 let (rows, cols) = rv.dims();
289 if arr.len() != rows {
290 return false;
291 }
292 for (r, row) in arr.iter().enumerate() {
293 if row.len() != cols {
294 return false;
295 }
296 for (c, cell) in row.iter().enumerate() {
297 if &rv.get_cell(r, c) != cell {
298 return false;
299 }
300 }
301 }
302 true
303 }
304 _ => {
305 let (rows, cols) = rv.dims();
306 rows == 1 && cols == 1 && &rv.get_cell(0, 0) == other
307 }
308 },
309 CalcValue::Callable(_) => false,
310 }
311 }
312}
313
314impl<'a> PartialEq<CalcValue<'a>> for LiteralValue {
315 fn eq(&self, other: &CalcValue<'a>) -> bool {
316 other == self
317 }
318}
319
320pub enum EvaluatedArg<'a> {
321 LiteralValue(CowValue<'a>),
322 Range(Box<dyn Range>),
323}
324
325enum ArgumentExpr<'a> {
326 Ast(&'a ASTNode),
327 Arena {
328 id: crate::engine::arena::AstNodeId,
329 data_store: &'a crate::engine::arena::DataStore,
330 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
331 },
332}
333
334pub struct ArgumentHandle<'a, 'b> {
335 expr: ArgumentExpr<'a>,
336 interp: &'a Interpreter<'b>,
337 caches: std::cell::OnceCell<Box<ArgumentCaches<'b>>>,
338 cached_value: std::cell::OnceCell<Result<MemoizedValue<'b>, ExcelError>>,
344}
345
346enum MemoizedValue<'b> {
348 Scalar(LiteralValue, Option<crate::format::FormatId>),
349 Other(Box<CalcValue<'b>>),
350}
351
352impl<'b> MemoizedValue<'b> {
353 fn new(value: CalcValue<'b>) -> Self {
354 match value {
355 CalcValue::Scalar(value) => Self::Scalar(value, None),
356 CalcValue::AnnotatedScalar(value, format) => Self::Scalar(value, Some(format)),
357 other => Self::Other(Box::new(other)),
358 }
359 }
360
361 fn value(&self) -> CalcValue<'b> {
362 match self {
363 Self::Scalar(value, None) => CalcValue::Scalar(value.clone()),
364 Self::Scalar(value, Some(format)) => CalcValue::AnnotatedScalar(value.clone(), *format),
365 Self::Other(value) => (**value).clone(),
366 }
367 }
368}
369
370#[derive(Default)]
371struct ArgumentCaches<'b> {
372 cached_ast: std::cell::OnceCell<ASTNode>,
373 cached_ref: std::cell::OnceCell<ReferenceType>,
374 cached_reference_or_value:
375 std::cell::OnceCell<Result<crate::function::FunctionResolution<'b>, ExcelError>>,
376 cached_resolved: std::cell::OnceCell<Result<ResolvedArgument<'b>, ExcelError>>,
377}
378
379impl<'a, 'b> ArgumentHandle<'a, 'b> {
380 fn caches(&self) -> &ArgumentCaches<'b> {
381 self.caches
382 .get_or_init(|| Box::new(ArgumentCaches::default()))
383 }
384
385 pub(crate) fn new(node: &'a ASTNode, interp: &'a Interpreter<'b>) -> Self {
386 Self {
387 expr: ArgumentExpr::Ast(node),
388 interp,
389 caches: std::cell::OnceCell::new(),
390 cached_value: std::cell::OnceCell::new(),
391 }
392 }
393
394 pub(crate) fn new_arena(
395 id: crate::engine::arena::AstNodeId,
396 interp: &'a Interpreter<'b>,
397 data_store: &'a crate::engine::arena::DataStore,
398 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
399 ) -> Self {
400 Self {
401 expr: ArgumentExpr::Arena {
402 id,
403 data_store,
404 sheet_registry,
405 },
406 interp,
407 caches: std::cell::OnceCell::new(),
408 cached_value: std::cell::OnceCell::new(),
409 }
410 }
411
412 pub(crate) fn date_system(&self) -> crate::engine::DateSystem {
418 self.interp.context.date_system()
419 }
420
421 pub fn is_omitted(&self) -> bool {
425 match &self.expr {
426 ArgumentExpr::Ast(node) => matches!(node.node_type, ASTNodeType::Omitted),
427 ArgumentExpr::Arena { id, data_store, .. } => matches!(
428 data_store.get_node(*id),
429 Some(crate::engine::arena::AstNodeData::Omitted)
430 ),
431 }
432 }
433
434 pub(crate) fn bare_reference(&self) -> Option<ReferenceType> {
447 let is_reference = match &self.expr {
448 ArgumentExpr::Ast(node) => matches!(node.node_type, ASTNodeType::Reference { .. }),
449 ArgumentExpr::Arena { id, data_store, .. } => matches!(
450 data_store.get_node(*id),
451 Some(crate::engine::arena::AstNodeData::Reference { .. })
452 ),
453 };
454 if !is_reference {
455 return None;
456 }
457 self.as_reference_or_eval().ok()
458 }
459
460 pub(crate) fn function_value_reference_intent(&self) -> Option<FunctionValueReferenceIntent> {
463 self.function_value_reference_intent_using(self.interp)
464 }
465
466 fn function_value_reference_intent_using(
467 &self,
468 interp: &crate::interpreter::Interpreter<'_>,
469 ) -> Option<FunctionValueReferenceIntent> {
470 let name = match &self.expr {
471 ArgumentExpr::Ast(node) => match &node.node_type {
472 ASTNodeType::Reference {
473 reference: ReferenceType::NamedRange(name),
474 ..
475 } => name.as_str(),
476 _ => return None,
477 },
478 ArgumentExpr::Arena { id, data_store, .. } => {
479 let crate::engine::arena::AstNodeData::Reference { ref_type, .. } =
480 data_store.get_node(*id)?
481 else {
482 return None;
483 };
484 let crate::engine::arena::CompactRefType::NamedRange(name) =
485 data_store.compact_ref(*ref_type)
486 else {
487 return None;
488 };
489 data_store.resolve_ast_string(*name)
490 }
491 };
492 if interp.has_local_name(name) {
493 return None;
494 }
495 let presence = interp
496 .context
497 .named_binding_presence(name, interp.current_sheet());
498 if presence == Some(true) {
499 return None;
500 }
501 let function = interp.ast_function_arc("", name)?;
502 Some(FunctionValueReferenceIntent {
503 function,
504 binding_absent: presence == Some(false),
505 })
506 }
507
508 pub(crate) fn function_value_reference_intent_in_env(
510 &self,
511 env: &crate::interpreter::LocalEnv,
512 ) -> Option<FunctionValueReferenceIntent> {
513 self.function_value_reference_intent_using(&self.interp.with_local_env(env.clone()))
514 }
515
516 pub(crate) fn spill_reference(&self) -> Result<ReferenceType, ExcelError> {
521 match &self.expr {
522 ArgumentExpr::Ast(node) => self.interp.ast_spill_reference(node),
523 ArgumentExpr::Arena {
524 id,
525 data_store,
526 sheet_registry,
527 } => self
528 .interp
529 .arena_spill_reference(*id, data_store, sheet_registry),
530 }
531 }
532
533 pub(crate) fn has_reference_semantics(&self) -> bool {
539 self.reference_attempt().is_some()
540 }
541
542 pub(crate) fn may_return_reference(&self) -> bool {
547 match &self.expr {
548 ArgumentExpr::Ast(node) => match &node.node_type {
549 ASTNodeType::Reference { reference, .. } => match reference {
550 ReferenceType::NamedRange(name)
552 if self.interp.resolve_local_name(name).is_some() =>
553 {
554 self.interp.resolve_local_bound_reference(name).is_some()
555 }
556 _ => true,
557 },
558 ASTNodeType::BinaryOp { op, .. } => *op == formualizer_parse::BinaryOperator::Range,
559 ASTNodeType::UnaryOp { op, .. } => *op == formualizer_parse::UnaryOperator::Spill,
560 ASTNodeType::Function { name, .. } => self
561 .interp
562 .ast_function_capabilities(name)
563 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)),
564 _ => false,
565 },
566 ArgumentExpr::Arena { id, data_store, .. } => match data_store.get_node(*id) {
567 Some(crate::engine::arena::AstNodeData::Reference { ref_type, .. }) => {
568 let ref_type = data_store.compact_ref(*ref_type);
569 match ref_type {
570 crate::engine::arena::CompactRefType::NamedRange(name_id)
572 if self
573 .interp
574 .resolve_local_name(data_store.resolve_ast_string(*name_id))
575 .is_some() =>
576 {
577 self.interp
578 .resolve_local_bound_reference(
579 data_store.resolve_ast_string(*name_id),
580 )
581 .is_some()
582 }
583 _ => true,
584 }
585 }
586 Some(crate::engine::arena::AstNodeData::BinaryOp { operator, .. }) => {
587 *operator == formualizer_parse::BinaryOperator::Range
588 }
589 Some(crate::engine::arena::AstNodeData::UnaryOp { operator, .. }) => {
590 *operator == formualizer_parse::UnaryOperator::Spill
591 }
592 Some(crate::engine::arena::AstNodeData::Function { name_id, .. }) => self
593 .interp
594 .arena_function_capabilities(*name_id, data_store)
595 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)),
596 _ => false,
597 },
598 }
599 }
600
601 pub fn value(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
602 let result = self
603 .cached_value
604 .get_or_init(|| self.compute_value().map(MemoizedValue::new))
605 .as_ref()
606 .map(MemoizedValue::value)
607 .map_err(Clone::clone);
608 if let Some(collector) = self.interp.diagnostics {
609 collector.input(&result);
610 }
611 result
612 }
613
614 pub(crate) fn value_for_error_inspection(&self) -> Result<LiteralValue, ExcelError> {
620 fn is_spreadsheet_error(error: &ExcelError) -> bool {
621 matches!(
622 error.kind,
623 ExcelErrorKind::Null
624 | ExcelErrorKind::Ref
625 | ExcelErrorKind::Name
626 | ExcelErrorKind::Value
627 | ExcelErrorKind::Div
628 | ExcelErrorKind::Na
629 | ExcelErrorKind::Num
630 | ExcelErrorKind::Error
631 | ExcelErrorKind::NImpl
632 | ExcelErrorKind::Spill
633 | ExcelErrorKind::Calc
634 | ExcelErrorKind::Circ
635 ) && matches!(
636 error.extra(),
637 formualizer_common::ExcelErrorExtra::None
638 | formualizer_common::ExcelErrorExtra::Spill { .. }
639 )
640 }
641
642 let value = match self.value() {
643 Ok(value) => value.into_literal(),
644 Err(error) if is_spreadsheet_error(&error) => LiteralValue::Error(error),
645 Err(error) => return Err(error),
646 };
647 if let LiteralValue::Error(error) = &value
648 && !is_spreadsheet_error(error)
649 {
650 return Err(error.clone());
651 }
652 Ok(value)
653 }
654
655 pub(crate) fn value_for_text(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
661 if self.is_omitted() {
662 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
663 String::new(),
664 )))
665 } else {
666 self.value()
667 }
668 }
669
670 pub(crate) fn resolve_once_for_text(&self) -> Result<ResolvedArgument<'b>, ExcelError> {
671 if self.is_omitted() {
672 Ok(ResolvedArgument::Value(crate::traits::CalcValue::Scalar(
673 LiteralValue::Text(String::new()),
674 )))
675 } else {
676 self.resolve_once()
677 }
678 }
679
680 fn compute_value(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
681 match &self.expr {
682 ArgumentExpr::Ast(node) => match &node.node_type {
683 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
684 ASTNodeType::Omitted => {
687 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
688 }
689 _ => self.interp.evaluate_ast_uncached(node),
690 },
691 ArgumentExpr::Arena {
692 id,
693 data_store,
694 sheet_registry,
695 } => {
696 if matches!(
697 data_store.get_node(*id),
698 Some(crate::engine::arena::AstNodeData::Omitted)
699 ) {
700 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
701 } else {
702 self.interp
703 .evaluate_arena_ast_inner(*id, data_store, sheet_registry)
704 }
705 }
706 }
707 }
708
709 pub fn value_with_env(
710 &self,
711 env: crate::interpreter::LocalEnv,
712 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
713 let scoped = self.interp.with_local_env(env);
714 let result = match &self.expr {
715 ArgumentExpr::Ast(node) => match &node.node_type {
716 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
717 ASTNodeType::Omitted => {
718 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
719 }
720 _ => scoped.evaluate_ast_uncached(node),
721 },
722 ArgumentExpr::Arena {
723 id,
724 data_store,
725 sheet_registry,
726 } => {
727 if matches!(
728 data_store.get_node(*id),
729 Some(crate::engine::arena::AstNodeData::Omitted)
730 ) {
731 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
732 } else {
733 scoped.evaluate_arena_ast_inner(*id, data_store, sheet_registry)
734 }
735 }
736 };
737 if let Some(collector) = self.interp.diagnostics {
738 collector.input(&result);
739 }
740 result
741 }
742
743 pub fn current_env(&self) -> crate::interpreter::LocalEnv {
744 self.interp.local_env().clone()
745 }
746
747 pub(crate) fn bound_reference_in_env(
754 &self,
755 env: &crate::interpreter::LocalEnv,
756 ) -> Option<ReferenceType> {
757 match &self.expr {
758 ArgumentExpr::Ast(node) => match &node.node_type {
759 ASTNodeType::Reference { reference, .. } => {
760 if let ReferenceType::NamedRange(name) = reference
761 && let Some(binding) = env.lookup(name)
762 {
763 return match binding {
765 crate::interpreter::LocalBinding::ValueWithReference {
766 reference,
767 ..
768 } => Some(reference),
769 _ => None,
770 };
771 }
772 self.interp.reference_for_current_offset(reference).ok()
773 }
774 ASTNodeType::BinaryOp { op, .. }
775 if *op == formualizer_parse::BinaryOperator::Range =>
776 {
777 self.interp
778 .with_local_env(env.clone())
779 .evaluate_ast_as_reference(node)
780 .ok()
781 }
782 ASTNodeType::UnaryOp { op, expr }
783 if *op == formualizer_parse::UnaryOperator::Spill =>
784 {
785 self.interp
786 .with_local_env(env.clone())
787 .ast_spill_reference(expr)
788 .ok()
789 }
790 _ => None,
791 },
792 ArgumentExpr::Arena {
793 id,
794 data_store,
795 sheet_registry,
796 } => match data_store.get_node(*id) {
797 Some(crate::engine::arena::AstNodeData::Reference { ref_type, .. }) => {
798 let ref_type = data_store.compact_ref(*ref_type);
799 if let crate::engine::arena::CompactRefType::NamedRange(name_id) = ref_type
800 && let Some(binding) = env.lookup(data_store.resolve_ast_string(*name_id))
801 {
802 return match binding {
803 crate::interpreter::LocalBinding::ValueWithReference {
804 reference,
805 ..
806 } => Some(reference),
807 _ => None,
808 };
809 }
810 let reference =
811 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
812 self.interp.reference_for_current_offset(&reference).ok()
813 }
814 Some(crate::engine::arena::AstNodeData::BinaryOp { operator, .. })
815 if *operator == formualizer_parse::BinaryOperator::Range =>
816 {
817 self.interp
818 .with_local_env(env.clone())
819 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry)
820 .ok()
821 }
822 Some(crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id })
823 if *operator == formualizer_parse::UnaryOperator::Spill =>
824 {
825 self.interp
826 .with_local_env(env.clone())
827 .arena_spill_reference(*expr_id, data_store, sheet_registry)
828 .ok()
829 }
830 _ => None,
831 },
832 }
833 }
834
835 pub fn inline_array_literal(&self) -> Result<Option<Vec<Vec<LiteralValue>>>, ExcelError> {
836 match &self.expr {
837 ArgumentExpr::Ast(node) => match &node.node_type {
838 ASTNodeType::Literal(LiteralValue::Array(arr)) => Ok(Some(arr.clone())),
839 _ => Ok(None),
840 },
841 ArgumentExpr::Arena {
842 id,
843 data_store,
844 sheet_registry,
845 } => {
846 let node = data_store.get_node(*id).ok_or_else(|| {
847 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
848 })?;
849 match node {
850 crate::engine::arena::AstNodeData::Literal(vref) => {
851 match data_store.retrieve_value(*vref) {
852 LiteralValue::Array(arr) => Ok(Some(arr)),
853 _ => Ok(None),
854 }
855 }
856 _ => {
857 let _ = sheet_registry;
860 Ok(None)
861 }
862 }
863 }
864 }
865 }
866
867 fn local_named_reference(&self, name: &str) -> Option<Result<ReferenceType, ExcelError>> {
886 self.interp.resolve_local_name(name)?;
888 Some(
889 self.interp
890 .resolve_local_bound_reference(name)
891 .ok_or_else(|| {
892 ExcelError::new(ExcelErrorKind::Value)
893 .with_message("LET/LAMBDA local is not a reference")
894 }),
895 )
896 }
897
898 fn reference_for_eval(&self) -> Result<ReferenceType, ExcelError> {
899 match &self.expr {
900 ArgumentExpr::Ast(node) => match &node.node_type {
901 ASTNodeType::Reference { reference, .. } => {
902 if let ReferenceType::NamedRange(name) = reference
905 && let Some(local) = self.local_named_reference(name)
906 {
907 return local;
908 }
909 self.interp.reference_for_current_offset(reference)
910 }
911 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
912 self.interp.evaluate_ast_as_reference(node)
913 }
914 ASTNodeType::UnaryOp { op, expr }
915 if *op == formualizer_parse::UnaryOperator::Spill =>
916 {
917 self.interp.ast_spill_reference(expr)
918 }
919 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
920 .with_message("Expected a reference (by-ref argument)")),
921 },
922 ArgumentExpr::Arena {
923 id,
924 data_store,
925 sheet_registry,
926 } => {
927 let node = data_store.get_node(*id).ok_or_else(|| {
928 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
929 })?;
930 match node {
931 crate::engine::arena::AstNodeData::Reference { ref_type, .. } => {
932 let ref_type = data_store.compact_ref(*ref_type);
933 if let crate::engine::arena::CompactRefType::NamedRange(name_id) = ref_type
935 && let Some(local) =
936 self.local_named_reference(data_store.resolve_ast_string(*name_id))
937 {
938 return local;
939 }
940 let reference = data_store
941 .reconstruct_reference_type_for_eval(ref_type, sheet_registry);
942 self.interp.reference_for_current_offset(&reference)
943 }
944 crate::engine::arena::AstNodeData::Function { .. }
945 | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
946 .interp
947 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
948 crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id }
949 if *operator == formualizer_parse::UnaryOperator::Spill =>
950 {
951 self.interp
952 .arena_spill_reference(*expr_id, data_store, sheet_registry)
953 }
954 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
955 .with_message("Expected a reference (by-ref argument)")),
956 }
957 }
958 }
959 }
960
961 fn function_resolution_attempt(
962 &self,
963 ) -> Option<Result<crate::function::FunctionResolution<'b>, ExcelError>> {
964 match &self.expr {
965 ArgumentExpr::Ast(node) => {
966 let ASTNodeType::Function { name, args } = &node.node_type else {
967 return None;
968 };
969 if !self
970 .interp
971 .ast_function_capabilities(name)
972 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE))
973 {
974 return None;
975 }
976 let fun = match self.interp.ast_function(name) {
977 Some(fun) => fun,
978 None => {
979 return Some(Err(ExcelError::new(ExcelErrorKind::Name)
980 .with_message(format!("Unknown function: {name}"))));
981 }
982 };
983 let handles: Vec<_> = args
984 .iter()
985 .map(|arg| ArgumentHandle::new(arg, self.interp))
986 .collect();
987 let ctx = DefaultFunctionContext::new_with_sheet(
988 self.interp.context,
989 None,
990 self.interp.current_sheet(),
991 );
992 let _origin = self.interp.reference_diagnostic_scope();
993 Some(self.interp.diagnostic_dispatch(
994 name,
995 &*fun,
996 fun.reports_known_limitations(),
997 || fun.resolve_reference_or_value(&handles, &ctx, &|| self.value()),
998 crate::operation_diagnostics::is_unimplemented_resolution,
999 ))
1000 }
1001 ArgumentExpr::Arena {
1002 id,
1003 data_store,
1004 sheet_registry,
1005 } => {
1006 let node = match data_store.get_node(*id) {
1007 Some(node) => node,
1008 None => {
1009 return Some(Err(
1010 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1011 ));
1012 }
1013 };
1014 let crate::engine::arena::AstNodeData::Function { name_id, .. } = node else {
1015 return None;
1016 };
1017 let name = data_store.resolve_ast_string(*name_id);
1018 if !self
1019 .interp
1020 .ast_function_capabilities(name)
1021 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE))
1022 {
1023 return None;
1024 }
1025 let fun = match self.interp.ast_function(name) {
1026 Some(fun) => fun,
1027 None => {
1028 return Some(Err(ExcelError::new(ExcelErrorKind::Name)
1029 .with_message(format!("Unknown function: {name}"))));
1030 }
1031 };
1032 let args = match data_store.get_args(*id) {
1033 Some(args) => args,
1034 None => {
1035 return Some(Err(ExcelError::new(ExcelErrorKind::Value)
1036 .with_message("Missing function args")));
1037 }
1038 };
1039 let handles: Vec<_> = args
1040 .iter()
1041 .copied()
1042 .map(|arg_id| {
1043 ArgumentHandle::new_arena(arg_id, self.interp, data_store, sheet_registry)
1044 })
1045 .collect();
1046 let ctx = DefaultFunctionContext::new_with_sheet(
1047 self.interp.context,
1048 None,
1049 self.interp.current_sheet(),
1050 );
1051 let _origin = self.interp.reference_diagnostic_scope();
1052 Some(self.interp.diagnostic_dispatch(
1053 name,
1054 &*fun,
1055 fun.reports_known_limitations(),
1056 || fun.resolve_reference_or_value(&handles, &ctx, &|| self.value()),
1057 crate::operation_diagnostics::is_unimplemented_resolution,
1058 ))
1059 }
1060 }
1061 }
1062
1063 fn reference_attempt(&self) -> Option<Result<ReferenceType, ExcelError>> {
1064 match &self.expr {
1065 ArgumentExpr::Ast(node) => match &node.node_type {
1066 ASTNodeType::Reference { reference, .. } => {
1067 if let ReferenceType::NamedRange(name) = reference
1072 && self.interp.resolve_local_name(name).is_some()
1073 {
1074 return self.interp.resolve_local_bound_reference(name).map(Ok);
1075 }
1076 Some(self.interp.reference_for_current_offset(reference))
1077 }
1078 ASTNodeType::BinaryOp { op, .. }
1079 if *op == formualizer_parse::BinaryOperator::Range =>
1080 {
1081 Some(self.interp.evaluate_ast_as_reference(node))
1082 }
1083 ASTNodeType::UnaryOp { op, expr }
1084 if *op == formualizer_parse::UnaryOperator::Spill =>
1085 {
1086 Some(self.interp.ast_spill_reference(expr))
1087 }
1088 ASTNodeType::Function { name, .. }
1089 if self
1090 .interp
1091 .ast_function_capabilities(name)
1092 .is_some_and(|caps| {
1093 caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)
1094 }) =>
1095 {
1096 self.interp.try_evaluate_ast_as_reference(node)
1097 }
1098 _ => None,
1099 },
1100 ArgumentExpr::Arena {
1101 id,
1102 data_store,
1103 sheet_registry,
1104 } => {
1105 let node = match data_store.get_node(*id) {
1106 Some(node) => node,
1107 None => {
1108 return Some(Err(
1109 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1110 ));
1111 }
1112 };
1113 match node {
1114 crate::engine::arena::AstNodeData::Reference { ref_type, .. } => {
1115 let ref_type = data_store.compact_ref(*ref_type);
1116 if let crate::engine::arena::CompactRefType::NamedRange(name_id) = ref_type
1118 {
1119 let name = data_store.resolve_ast_string(*name_id);
1120 if self.interp.resolve_local_name(name).is_some() {
1121 return self.interp.resolve_local_bound_reference(name).map(Ok);
1122 }
1123 }
1124 let reference = data_store
1125 .reconstruct_reference_type_for_eval(ref_type, sheet_registry);
1126 Some(self.interp.reference_for_current_offset(&reference))
1127 }
1128 crate::engine::arena::AstNodeData::BinaryOp { operator, .. }
1129 if *operator == formualizer_parse::BinaryOperator::Range =>
1130 {
1131 Some(self.interp.evaluate_arena_ast_as_reference(
1132 *id,
1133 data_store,
1134 sheet_registry,
1135 ))
1136 }
1137 crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id }
1138 if *operator == formualizer_parse::UnaryOperator::Spill =>
1139 {
1140 Some(self.interp.arena_spill_reference(
1141 *expr_id,
1142 data_store,
1143 sheet_registry,
1144 ))
1145 }
1146 crate::engine::arena::AstNodeData::Function { name_id, .. } => {
1147 if self
1148 .interp
1149 .arena_function_capabilities(*name_id, data_store)
1150 .is_some_and(|caps| {
1151 caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)
1152 })
1153 {
1154 self.interp.try_evaluate_arena_ast_as_reference(
1155 *id,
1156 data_store,
1157 sheet_registry,
1158 )
1159 } else {
1160 None
1161 }
1162 }
1163 _ => None,
1164 }
1165 }
1166 }
1167 }
1168
1169 pub(crate) fn resolve_reference_or_value(
1170 &self,
1171 ) -> Result<crate::function::FunctionResolution<'b>, ExcelError> {
1172 let resolved = self
1173 .caches()
1174 .cached_reference_or_value
1175 .get_or_init(|| self.compute_reference_or_value())
1176 .clone();
1177 if let Ok(crate::function::FunctionResolution::Value(value)) = &resolved {
1178 let _ = self.cached_value.set(Ok(MemoizedValue::new(value.clone())));
1179 }
1180 resolved
1181 }
1182
1183 fn compute_reference_or_value(
1184 &self,
1185 ) -> Result<crate::function::FunctionResolution<'b>, ExcelError> {
1186 if let Some(result) = self.function_resolution_attempt() {
1187 return result;
1188 }
1189 if let Some(reference) = self.reference_attempt() {
1190 return Ok(match reference {
1191 Ok(reference) => crate::function::FunctionResolution::Reference(reference),
1192 Err(error) => crate::function::FunctionResolution::ReferenceError(error),
1193 });
1194 }
1195 self.value().map(crate::function::FunctionResolution::Value)
1196 }
1197
1198 pub(crate) fn resolve_once(&self) -> Result<ResolvedArgument<'b>, ExcelError> {
1204 let result = self
1205 .caches()
1206 .cached_resolved
1207 .get_or_init(|| self.compute_resolved_argument())
1208 .clone();
1209 if let Some(collector) = self.interp.diagnostics {
1210 match &result {
1211 Ok(ResolvedArgument::Range(_)) => collector.ambiguous_input(),
1212 Ok(ResolvedArgument::Value(value)) => collector.input_value(value),
1213 Ok(ResolvedArgument::ReferenceError(e)) | Err(e) => collector.input_error(e),
1214 }
1215 }
1216 result
1217 }
1218
1219 pub(crate) fn with_context_cancel_token(&self, view: RangeView<'b>) -> RangeView<'b> {
1221 let view = view.with_cell_budget(self.interp.context.materialized_array_cells());
1222 match self.interp.context.cancellation_token() {
1223 Some(token) => view.with_cancel_token(Some(token)),
1224 None => view,
1225 }
1226 }
1227
1228 fn compute_resolved_argument(&self) -> Result<ResolvedArgument<'b>, ExcelError> {
1229 if let ArgumentExpr::Arena { id, data_store, .. } = &self.expr
1230 && let Some(result) = self.interp.resolve_arena_argument_range(*id, data_store)
1231 {
1232 return match result {
1233 Ok(view) => Ok(ResolvedArgument::Range(
1234 self.with_context_cancel_token(view),
1235 )),
1236 Err(error) if error.kind == ExcelErrorKind::Cancelled => Err(error),
1237 Err(error) => Ok(ResolvedArgument::ReferenceError(error)),
1238 };
1239 }
1240 let value = match self.resolve_reference_or_value()? {
1241 crate::function::FunctionResolution::Reference(reference) => {
1242 return match self
1243 .interp
1244 .context
1245 .resolve_range_view(&reference, self.interp.current_sheet())
1246 {
1247 Ok(view) => Ok(ResolvedArgument::Range(
1248 self.with_context_cancel_token(view),
1249 )),
1250 Err(error) if error.kind == ExcelErrorKind::Cancelled => Err(error),
1251 Err(error) => Ok(ResolvedArgument::ReferenceError(error)),
1252 };
1253 }
1254 crate::function::FunctionResolution::ReferenceError(error)
1255 if error.kind == ExcelErrorKind::Cancelled =>
1256 {
1257 return Err(error);
1258 }
1259 crate::function::FunctionResolution::ReferenceError(error) => {
1260 return Ok(ResolvedArgument::ReferenceError(error));
1261 }
1262 crate::function::FunctionResolution::Value(value) => value,
1263 };
1264
1265 match value {
1266 CalcValue::Range(view) => Ok(ResolvedArgument::Range(
1267 self.with_context_cancel_token(view),
1268 )),
1269 CalcValue::Scalar(LiteralValue::Array(rows)) => {
1270 let view = RangeView::try_from_owned_rows(
1271 rows,
1272 self.interp.context.date_system(),
1273 self.interp.context.cancellation_token(),
1274 )?;
1275 Ok(ResolvedArgument::Range(view))
1276 }
1277 other => Ok(ResolvedArgument::Value(other)),
1278 }
1279 }
1280
1281 fn check_materialized_cells(&self, rows: usize, cols: usize) -> Result<(), ExcelError> {
1284 match crate::engine::resource_ledger::materialized_cells_exceed(
1285 rows,
1286 cols,
1287 self.interp.context.materialized_array_cells(),
1288 ) {
1289 Some(error) => Err(error),
1290 None => Ok(()),
1291 }
1292 }
1293
1294 pub fn range(&self) -> Result<Box<dyn Range>, ExcelError> {
1295 if let Some(collector) = self.interp.diagnostics {
1296 collector.ambiguous_input();
1297 }
1298 match &self.expr {
1299 ArgumentExpr::Ast(node) => match &node.node_type {
1300 ASTNodeType::Reference { reference, .. } => {
1301 let reference = self.interp.reference_for_current_offset(reference)?;
1303 let view = self
1304 .interp
1305 .context
1306 .resolve_range_view(&reference, self.interp.current_sheet())?;
1307 let (rows, cols) = view.dims();
1308 self.check_materialized_cells(rows, cols)?;
1309 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
1310 view.for_each_row(&mut |row| {
1311 let row_data: Vec<LiteralValue> = (0..cols)
1312 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
1313 .collect();
1314 out.push(row_data);
1315 Ok(())
1316 })?;
1317 Ok(Box::new(InMemoryRange::new(out)))
1318 }
1319 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
1320 let reference = self.reference_for_eval()?;
1321 let view = self
1322 .interp
1323 .context
1324 .resolve_range_view(&reference, self.interp.current_sheet())?;
1325 let (rows, cols) = view.dims();
1326 self.check_materialized_cells(rows, cols)?;
1327 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
1328 view.for_each_row(&mut |row| {
1329 let row_data: Vec<LiteralValue> = (0..cols)
1330 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
1331 .collect();
1332 out.push(row_data);
1333 Ok(())
1334 })?;
1335 Ok(Box::new(InMemoryRange::new(out)))
1336 }
1337 ASTNodeType::Array(rows) => {
1338 let mut materialized = Vec::new();
1339 for row in rows {
1340 let mut materialized_row = Vec::new();
1341 for cell in row {
1342 materialized_row
1343 .push(self.interp.evaluate_ast_uncached(cell)?.into_literal());
1344 }
1345 materialized.push(materialized_row);
1346 }
1347 Ok(Box::new(InMemoryRange::new(materialized)))
1348 }
1349 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1350 .with_message(format!("Expected a range, got {:?}", node.node_type))),
1351 },
1352 ArgumentExpr::Arena { id, data_store, .. } => {
1353 let node = data_store.get_node(*id).ok_or_else(|| {
1354 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1355 })?;
1356
1357 match node {
1358 crate::engine::arena::AstNodeData::Reference { .. }
1359 | crate::engine::arena::AstNodeData::Function { .. }
1360 | crate::engine::arena::AstNodeData::BinaryOp { .. } => {
1361 let reference = self.reference_for_eval()?;
1362 let view = self
1363 .interp
1364 .context
1365 .resolve_range_view(&reference, self.interp.current_sheet())?;
1366 let (rows, cols) = view.dims();
1367 self.check_materialized_cells(rows, cols)?;
1368 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
1369 view.for_each_row(&mut |row| {
1370 let row_data: Vec<LiteralValue> = (0..cols)
1371 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
1372 .collect();
1373 out.push(row_data);
1374 Ok(())
1375 })?;
1376 Ok(Box::new(InMemoryRange::new(out)))
1377 }
1378 crate::engine::arena::AstNodeData::Array { .. } => {
1379 let (rows, cols, elements) =
1380 data_store.get_array_elems(*id).ok_or_else(|| {
1381 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
1382 })?;
1383 let rows_usize = rows as usize;
1384 let cols_usize = cols as usize;
1385 let mut materialized: Vec<Vec<LiteralValue>> =
1386 Vec::with_capacity(rows_usize);
1387 for r in 0..rows_usize {
1388 let mut row = Vec::with_capacity(cols_usize);
1389 for c in 0..cols_usize {
1390 let idx = r * cols_usize + c;
1391 let elem_id = elements.get(idx).copied().ok_or_else(|| {
1392 ExcelError::new(ExcelErrorKind::Value)
1393 .with_message("Invalid array")
1394 })?;
1395 let v = self.interp.evaluate_arena_ast_inner(
1396 elem_id,
1397 data_store,
1398 self.sheet_registry(),
1399 )?;
1400 row.push(v.into_literal());
1401 }
1402 materialized.push(row);
1403 }
1404 Ok(Box::new(InMemoryRange::new(materialized)))
1405 }
1406 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1407 .with_message("Argument cannot be interpreted as a range.")),
1408 }
1409 }
1410 }
1411 }
1412
1413 fn sheet_registry(&self) -> &crate::engine::sheet_registry::SheetRegistry {
1414 match &self.expr {
1415 ArgumentExpr::Ast(_) => {
1416 unreachable!("sheet_registry only used for arena ArgumentHandle")
1418 }
1419 ArgumentExpr::Arena { sheet_registry, .. } => sheet_registry,
1420 }
1421 }
1422
1423 pub fn range_view(&self) -> Result<RangeView<'b>, ExcelError> {
1431 match self.resolve_once()? {
1432 ResolvedArgument::Range(view) => Ok(view),
1433 ResolvedArgument::ReferenceError(error) => Err(error),
1436 ResolvedArgument::Value(CalcValue::Range(view)) => {
1441 Ok(self.with_context_cancel_token(view))
1442 }
1443 ResolvedArgument::Value(CalcValue::Scalar(LiteralValue::Array(rows))) => {
1444 RangeView::try_from_owned_rows(
1445 rows,
1446 self.interp.context.date_system(),
1447 self.interp.context.cancellation_token(),
1448 )
1449 }
1450 ResolvedArgument::Value(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
1451 .with_message("Argument cannot be interpreted as a range.")),
1452 }
1453 }
1454
1455 pub fn range_view_or_scalar(&self) -> Result<RangeView<'b>, ExcelError> {
1475 match self.resolve_once()? {
1476 ResolvedArgument::Range(view) => Ok(view),
1477 ResolvedArgument::ReferenceError(error) => Err(error),
1478 ResolvedArgument::Value(CalcValue::Range(view)) => {
1479 Ok(self.with_context_cancel_token(view))
1480 }
1481 ResolvedArgument::Value(CalcValue::Scalar(LiteralValue::Array(rows))) => {
1482 RangeView::try_from_owned_rows(
1483 rows,
1484 self.interp.context.date_system(),
1485 self.interp.context.cancellation_token(),
1486 )
1487 }
1488 ResolvedArgument::Value(CalcValue::Scalar(LiteralValue::Error(error))) => Err(error),
1491 ResolvedArgument::Value(
1492 CalcValue::Scalar(scalar) | CalcValue::AnnotatedScalar(scalar, _),
1493 ) => RangeView::try_from_owned_rows(
1494 vec![vec![scalar]],
1495 self.interp.context.date_system(),
1496 self.interp.context.cancellation_token(),
1497 ),
1498 ResolvedArgument::Value(CalcValue::Callable(_)) => {
1500 Err(ExcelError::new(ExcelErrorKind::Ref)
1501 .with_message("Argument cannot be interpreted as a range."))
1502 }
1503 }
1504 }
1505
1506 pub fn value_or_range(&self) -> Result<EvaluatedArg<'_>, ExcelError> {
1507 self.range().map(EvaluatedArg::Range).or_else(|_| {
1508 self.value()
1509 .map(|cv| EvaluatedArg::LiteralValue(Cow::Owned(cv.into_literal())))
1510 })
1511 }
1512
1513 pub fn lazy_values_owned(
1518 &'a self,
1519 ) -> Result<Box<dyn Iterator<Item = LiteralValue> + 'a>, ExcelError> {
1520 fn resolved_values<'a, 'b>(
1524 handle: &'a ArgumentHandle<'a, 'b>,
1525 ) -> Result<Box<dyn Iterator<Item = LiteralValue> + 'a>, ExcelError> {
1526 match handle.resolve_once()? {
1527 ResolvedArgument::Range(view) => {
1528 let mut values = Vec::new();
1529 view.for_each_cell(&mut |value| {
1530 values.push(value.clone());
1531 Ok(())
1532 })?;
1533 Ok(Box::new(values.into_iter()))
1534 }
1535 ResolvedArgument::ReferenceError(error) => Err(error),
1536 ResolvedArgument::Value(value) => {
1537 Ok(Box::new(std::iter::once(value.into_literal())))
1538 }
1539 }
1540 }
1541
1542 match &self.expr {
1543 ArgumentExpr::Ast(node) => match &node.node_type {
1544 ASTNodeType::Reference { .. } => resolved_values(self),
1545 ASTNodeType::Array(rows) => {
1546 struct ArrayEvalIter<'a, 'b> {
1547 rows: &'a [Vec<ASTNode>],
1548 r: usize,
1549 c: usize,
1550 interp: &'a Interpreter<'b>,
1551 }
1552 impl<'a, 'b> Iterator for ArrayEvalIter<'a, 'b> {
1553 type Item = LiteralValue;
1554 fn next(&mut self) -> Option<Self::Item> {
1555 if self.rows.is_empty() {
1556 return None;
1557 }
1558 let rows = self.rows;
1559 let mut r = self.r;
1560 let mut c = self.c;
1561 if r >= rows.len() {
1562 return None;
1563 }
1564 let node = &rows[r][c];
1565 c += 1;
1567 if c >= rows[r].len() {
1568 r += 1;
1569 c = 0;
1570 }
1571 self.r = r;
1572 self.c = c;
1573 match self.interp.evaluate_ast_uncached(node) {
1574 Ok(cv) => Some(cv.into_literal()),
1575 Err(e) => Some(LiteralValue::Error(e)),
1576 }
1577 }
1578 }
1579 let it = ArrayEvalIter {
1580 rows,
1581 r: 0,
1582 c: 0,
1583 interp: self.interp,
1584 };
1585 Ok(Box::new(it))
1586 }
1587 _ => {
1588 let v = self.value()?.into_literal();
1590 Ok(Box::new(std::iter::once(v)))
1591 }
1592 },
1593 ArgumentExpr::Arena {
1594 id,
1595 data_store,
1596 sheet_registry,
1597 } => {
1598 let node = data_store.get_node(*id).ok_or_else(|| {
1599 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1600 })?;
1601
1602 match node {
1603 crate::engine::arena::AstNodeData::Reference { .. } => resolved_values(self),
1604 crate::engine::arena::AstNodeData::Array { .. } => {
1605 let (rows, cols, elements) =
1606 data_store.get_array_elems(*id).ok_or_else(|| {
1607 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
1608 })?;
1609
1610 struct ArenaArrayEvalIter<'a, 'b> {
1611 elements: &'a [crate::engine::arena::AstNodeId],
1612 idx: usize,
1613 interp: &'a Interpreter<'b>,
1614 data_store: &'a crate::engine::arena::DataStore,
1615 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
1616 }
1617
1618 impl<'a, 'b> Iterator for ArenaArrayEvalIter<'a, 'b> {
1619 type Item = LiteralValue;
1620
1621 fn next(&mut self) -> Option<Self::Item> {
1622 let id = self.elements.get(self.idx).copied()?;
1623 self.idx += 1;
1624 match self.interp.evaluate_arena_ast_inner(
1625 id,
1626 self.data_store,
1627 self.sheet_registry,
1628 ) {
1629 Ok(cv) => Some(cv.into_literal()),
1630 Err(e) => Some(LiteralValue::Error(e)),
1631 }
1632 }
1633 }
1634
1635 let _ = (rows, cols);
1636 let it = ArenaArrayEvalIter {
1637 elements,
1638 idx: 0,
1639 interp: self.interp,
1640 data_store,
1641 sheet_registry,
1642 };
1643 Ok(Box::new(it))
1644 }
1645 _ => {
1646 let v = self.interp.evaluate_arena_ast_inner(
1647 *id,
1648 data_store,
1649 sheet_registry,
1650 )?;
1651 Ok(Box::new(std::iter::once(v.into_literal())))
1652 }
1653 }
1654 }
1655 }
1656 }
1657
1658 pub fn ast(&self) -> &ASTNode {
1659 match &self.expr {
1660 ArgumentExpr::Ast(node) => node,
1661 ArgumentExpr::Arena {
1662 id,
1663 data_store,
1664 sheet_registry,
1665 } => self.caches().cached_ast.get_or_init(|| {
1666 data_store
1667 .retrieve_ast(*id, sheet_registry)
1668 .unwrap_or_else(|| ASTNode {
1669 node_type: ASTNodeType::Literal(LiteralValue::Error(
1670 ExcelError::new(ExcelErrorKind::Value)
1671 .with_message("Missing formula AST"),
1672 )),
1673 source_token: None,
1674 contains_volatile: false,
1675 })
1676 }),
1677 }
1678 }
1679
1680 pub fn as_reference(&self) -> Result<&ReferenceType, ExcelError> {
1683 match &self.expr {
1684 ArgumentExpr::Ast(node) => match &node.node_type {
1685 ASTNodeType::Reference { reference, .. } => {
1686 if let ReferenceType::NamedRange(name) = reference
1688 && let Some(local) = self.local_named_reference(name)
1689 {
1690 let bound = local?;
1691 return Ok(self.caches().cached_ref.get_or_init(|| bound));
1692 }
1693 Ok(reference)
1694 }
1695 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1696 .with_message("Expected a reference (by-ref argument)")),
1697 },
1698 ArgumentExpr::Arena { .. } => {
1699 let reference = self.reference_for_eval()?;
1700 Ok(self.caches().cached_ref.get_or_init(|| reference))
1701 }
1702 }
1703 }
1704
1705 pub fn as_reference_or_eval(&self) -> Result<ReferenceType, ExcelError> {
1708 match &self.expr {
1709 ArgumentExpr::Ast(node) => match &node.node_type {
1710 ASTNodeType::Reference { reference, .. } => {
1711 if let ReferenceType::NamedRange(name) = reference
1713 && let Some(local) = self.local_named_reference(name)
1714 {
1715 return local;
1716 }
1717 self.interp.reference_for_current_offset(reference)
1718 }
1719 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
1720 self.interp.evaluate_ast_as_reference(node)
1721 }
1722 ASTNodeType::UnaryOp { op, expr }
1723 if *op == formualizer_parse::UnaryOperator::Spill =>
1724 {
1725 self.interp.ast_spill_reference(expr)
1726 }
1727 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1728 .with_message("Argument is not a reference")),
1729 },
1730 ArgumentExpr::Arena {
1731 id,
1732 data_store,
1733 sheet_registry,
1734 } => {
1735 let node = data_store.get_node(*id).ok_or_else(|| {
1736 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1737 })?;
1738
1739 match node {
1740 crate::engine::arena::AstNodeData::Reference { .. } => {
1741 self.reference_for_eval()
1742 }
1743 crate::engine::arena::AstNodeData::Function { .. }
1744 | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
1745 .interp
1746 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
1747 crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id }
1748 if *operator == formualizer_parse::UnaryOperator::Spill =>
1749 {
1750 self.interp
1751 .arena_spill_reference(*expr_id, data_store, sheet_registry)
1752 }
1753 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1754 .with_message("Argument is not a reference")),
1755 }
1756 }
1757 }
1758 }
1759
1760 pub fn matches_kind(&self, k: formualizer_common::ArgKind) -> Result<bool, ExcelError> {
1762 Ok(match k {
1763 formualizer_common::ArgKind::Any => true,
1764 formualizer_common::ArgKind::Range => self.range().is_ok(),
1765 formualizer_common::ArgKind::Number => matches!(
1766 self.value()?.into_literal(),
1767 LiteralValue::Number(_) | LiteralValue::Int(_)
1768 ),
1769 formualizer_common::ArgKind::Text => {
1770 matches!(self.value()?.into_literal(), LiteralValue::Text(_))
1771 }
1772 formualizer_common::ArgKind::Logical => {
1773 matches!(self.value()?.into_literal(), LiteralValue::Boolean(_))
1774 }
1775 })
1776 }
1777}
1778
1779#[derive(Debug, Clone)]
1781pub struct InMemoryRange {
1782 data: Vec<Vec<LiteralValue>>,
1783}
1784impl InMemoryRange {
1785 pub fn new(d: Vec<Vec<LiteralValue>>) -> Self {
1786 Self { data: d }
1787 }
1788}
1789impl Range for InMemoryRange {
1790 fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
1791 Ok(self
1792 .data
1793 .get(r)
1794 .and_then(|row| row.get(c))
1795 .cloned()
1796 .unwrap_or(LiteralValue::Empty))
1797 }
1798 fn dimensions(&self) -> (usize, usize) {
1799 (self.data.len(), self.data.first().map_or(0, |r| r.len()))
1800 }
1801 fn as_any(&self) -> &dyn Any {
1802 self
1803 }
1804}
1805
1806pub trait Table: Debug + Send + Sync {
1809 fn get_cell(&self, row: usize, column: &str) -> Result<LiteralValue, ExcelError>;
1810 fn get_column(&self, column: &str) -> Result<Box<dyn Range>, ExcelError>;
1811 fn columns(&self) -> Vec<String> {
1813 vec![]
1814 }
1815 fn data_height(&self) -> usize {
1817 0
1818 }
1819 fn has_headers(&self) -> bool {
1821 false
1822 }
1823 fn has_totals(&self) -> bool {
1825 false
1826 }
1827 fn headers_row(&self) -> Option<Box<dyn Range>> {
1829 None
1830 }
1831 fn totals_row(&self) -> Option<Box<dyn Range>> {
1833 None
1834 }
1835 fn data_body(&self) -> Option<Box<dyn Range>> {
1837 None
1838 }
1839 fn clone_box(&self) -> Box<dyn Table>;
1840}
1841impl Table for Box<dyn Table> {
1842 fn get_cell(&self, r: usize, c: &str) -> Result<LiteralValue, ExcelError> {
1843 (**self).get_cell(r, c)
1844 }
1845 fn get_column(&self, c: &str) -> Result<Box<dyn Range>, ExcelError> {
1846 (**self).get_column(c)
1847 }
1848 fn columns(&self) -> Vec<String> {
1849 (**self).columns()
1850 }
1851 fn data_height(&self) -> usize {
1852 (**self).data_height()
1853 }
1854 fn has_headers(&self) -> bool {
1855 (**self).has_headers()
1856 }
1857 fn has_totals(&self) -> bool {
1858 (**self).has_totals()
1859 }
1860 fn headers_row(&self) -> Option<Box<dyn Range>> {
1861 (**self).headers_row()
1862 }
1863 fn totals_row(&self) -> Option<Box<dyn Range>> {
1864 (**self).totals_row()
1865 }
1866 fn data_body(&self) -> Option<Box<dyn Range>> {
1867 (**self).data_body()
1868 }
1869 fn clone_box(&self) -> Box<dyn Table> {
1870 (**self).clone_box()
1871 }
1872}
1873
1874pub trait ReferenceResolver: Send + Sync {
1877 fn resolve_cell_reference(
1878 &self,
1879 sheet: Option<&str>,
1880 row: u32,
1881 col: u32,
1882 ) -> Result<LiteralValue, ExcelError>;
1883}
1884pub trait RangeResolver: Send + Sync {
1885 fn resolve_range_reference(
1886 &self,
1887 sheet: Option<&str>,
1888 sr: Option<u32>,
1889 sc: Option<u32>,
1890 er: Option<u32>,
1891 ec: Option<u32>,
1892 ) -> Result<Box<dyn Range>, ExcelError>;
1893}
1894pub trait NamedRangeResolver: Send + Sync {
1895 fn resolve_named_range_reference(
1896 &self,
1897 name: &str,
1898 ) -> Result<Vec<Vec<LiteralValue>>, ExcelError>;
1899}
1900pub trait TableResolver: Send + Sync {
1901 fn resolve_table_reference(
1902 &self,
1903 tref: &formualizer_parse::parser::TableReference,
1904 ) -> Result<Box<dyn Table>, ExcelError>;
1905}
1906
1907pub trait SourceResolver: Send + Sync {
1908 fn source_scalar_version(&self, _name: &str) -> Option<u64> {
1909 None
1910 }
1911
1912 fn resolve_source_scalar(&self, name: &str) -> Result<LiteralValue, ExcelError> {
1913 Err(ExcelError::new(ExcelErrorKind::NImpl)
1914 .with_message(format!("Source scalar not supported: {name}")))
1915 }
1916
1917 fn source_table_version(&self, _name: &str) -> Option<u64> {
1918 None
1919 }
1920
1921 fn resolve_source_table(&self, name: &str) -> Result<Box<dyn Table>, ExcelError> {
1922 Err(ExcelError::new(ExcelErrorKind::NImpl)
1923 .with_message(format!("Source table not supported: {name}")))
1924 }
1925}
1926
1927pub trait Resolver: ReferenceResolver + RangeResolver + NamedRangeResolver + TableResolver {
1928 fn resolve_range_like(&self, r: &ReferenceType) -> Result<Box<dyn Range>, ExcelError> {
1929 match r {
1930 ReferenceType::Range {
1931 sheet,
1932 start_row,
1933 start_col,
1934 end_row,
1935 end_col,
1936 ..
1937 } => self.resolve_range_reference(
1938 sheet.as_deref(),
1939 *start_row,
1940 *start_col,
1941 *end_row,
1942 *end_col,
1943 ),
1944 ReferenceType::External(_) => Err(ExcelError::new(ExcelErrorKind::NImpl)
1945 .with_message("External references are not supported by Resolver".to_string())),
1946 ReferenceType::Table(tref) => {
1947 let t = self.resolve_table_reference(tref)?;
1948 match &tref.specifier {
1949 Some(TableSpecifier::Column(c)) => t.get_column(c),
1950 Some(TableSpecifier::ColumnRange(start, end)) => {
1951 let cols = t.columns();
1953 let start_key = start.to_lowercase();
1954 let end_key = end.to_lowercase();
1955 let start_idx = cols.iter().position(|n| n.to_lowercase() == start_key);
1956 let end_idx = cols.iter().position(|n| n.to_lowercase() == end_key);
1957 if let (Some(mut si), Some(mut ei)) = (start_idx, end_idx) {
1958 if si > ei {
1959 std::mem::swap(&mut si, &mut ei);
1960 }
1961 let h = t.data_height();
1963 let w = ei - si + 1;
1964 let mut rows = vec![vec![LiteralValue::Empty; w]; h];
1965 for (offset, ci) in (si..=ei).enumerate() {
1966 let cname = &cols[ci];
1967 let col_range = t.get_column(cname)?;
1968 let (rh, _) = col_range.dimensions();
1969 for (r, row) in rows.iter_mut().enumerate().take(h.min(rh)) {
1970 row[offset] = col_range.get(r, 0)?;
1971 }
1972 }
1973 Ok(Box::new(InMemoryRange::new(rows)))
1974 } else {
1975 Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1976 "Column range refers to unknown column(s)".to_string(),
1977 ))
1978 }
1979 }
1980 Some(TableSpecifier::SpecialItem(
1981 formualizer_parse::parser::SpecialItem::Headers,
1982 )) => {
1983 if let Some(h) = t.headers_row() {
1984 Ok(h)
1985 } else {
1986 Ok(Box::new(InMemoryRange::new(vec![])))
1987 }
1988 }
1989 Some(TableSpecifier::SpecialItem(
1990 formualizer_parse::parser::SpecialItem::Totals,
1991 )) => {
1992 if let Some(tr) = t.totals_row() {
1993 Ok(tr)
1994 } else {
1995 Ok(Box::new(InMemoryRange::new(vec![])))
1996 }
1997 }
1998 Some(TableSpecifier::SpecialItem(
1999 formualizer_parse::parser::SpecialItem::Data,
2000 )) => {
2001 if let Some(body) = t.data_body() {
2002 Ok(body)
2003 } else {
2004 Ok(Box::new(InMemoryRange::new(vec![])))
2005 }
2006 }
2007 Some(TableSpecifier::SpecialItem(
2008 formualizer_parse::parser::SpecialItem::All,
2009 )) => {
2010 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
2012 if let Some(h) = t.headers_row() {
2013 out.extend(h.iter_rows());
2014 }
2015 if let Some(body) = t.data_body() {
2016 out.extend(body.iter_rows());
2017 }
2018 if let Some(tr) = t.totals_row() {
2019 out.extend(tr.iter_rows());
2020 }
2021 Ok(Box::new(InMemoryRange::new(out)))
2022 }
2023 Some(TableSpecifier::SpecialItem(
2024 formualizer_parse::parser::SpecialItem::ThisRow,
2025 )) => Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
2026 "@ (This Row) requires table-aware context; not yet supported".to_string(),
2027 )),
2028 Some(TableSpecifier::All) => {
2029 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
2031 if let Some(h) = t.headers_row() {
2032 out.extend(h.iter_rows());
2033 }
2034 if let Some(body) = t.data_body() {
2035 out.extend(body.iter_rows());
2036 }
2037 if let Some(tr) = t.totals_row() {
2038 out.extend(tr.iter_rows());
2039 }
2040 Ok(Box::new(InMemoryRange::new(out)))
2041 }
2042 Some(TableSpecifier::Data) => {
2043 if let Some(body) = t.data_body() {
2044 Ok(body)
2045 } else {
2046 Ok(Box::new(InMemoryRange::new(vec![])))
2047 }
2048 }
2049 Some(TableSpecifier::Combination(_)) => Err(ExcelError::new(
2051 ExcelErrorKind::NImpl,
2052 )
2053 .with_message("Complex structured references not yet supported".to_string())),
2054 Some(TableSpecifier::Row(_)) => Err(ExcelError::new(ExcelErrorKind::NImpl)
2055 .with_message("Row selectors (@/index) not yet supported".to_string())),
2056 Some(TableSpecifier::Headers) | Some(TableSpecifier::Totals) => {
2057 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
2058 "Legacy Headers/Totals variants not used; use SpecialItem".to_string(),
2059 ))
2060 }
2061 None => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
2062 "Table reference without specifier is unsupported".to_string(),
2063 )),
2064 }
2065 }
2066 ReferenceType::NamedRange(n) => {
2067 let v = self.resolve_named_range_reference(n)?;
2068 Ok(Box::new(InMemoryRange::new(v)))
2069 }
2070 ReferenceType::Cell {
2071 sheet, row, col, ..
2072 } => {
2073 let v = self.resolve_cell_reference(sheet.as_deref(), *row, *col)?;
2074 Ok(Box::new(InMemoryRange::new(vec![vec![v]])))
2075 }
2076 ReferenceType::Cell3D { .. } | ReferenceType::Range3D { .. } => {
2077 Err(ExcelError::new(ExcelErrorKind::NImpl)
2078 .with_message("3D references are not yet supported".to_string()))
2079 }
2080 }
2081 }
2082}
2083
2084pub trait FunctionProvider: Send + Sync {
2087 fn get_function(&self, ns: &str, name: &str) -> Option<Arc<dyn Function>>;
2088
2089 #[doc(hidden)]
2090 fn get_function_for_planning(&self, _ns: &str, _name: &str) -> Option<Arc<dyn Function>> {
2091 None
2092 }
2093
2094 #[doc(hidden)]
2097 fn planning_semantic_revision(&self) -> Option<u64> {
2098 None
2099 }
2100
2101 #[doc(hidden)]
2102 fn function_capabilities(&self, ns: &str, name: &str) -> Option<crate::function::FnCaps> {
2103 self.get_function(ns, name).map(|function| {
2104 crate::external_callbacks::metadata(
2105 function.as_ref(),
2106 crate::function::FnCaps::empty,
2107 || function.caps(),
2108 )
2109 })
2110 }
2111
2112 fn function_semantic_identity(
2113 &self,
2114 ns: &str,
2115 name: &str,
2116 arity: usize,
2117 ) -> Option<crate::function_contract::FunctionSemanticIdentity> {
2118 crate::function_registry::resolve_semantic_identity(self, ns, name, arity)
2119 }
2120}
2121
2122pub trait EvaluationContext: Resolver + FunctionProvider + SourceResolver {
2123 fn operation_diagnostic_collector(
2125 &self,
2126 ) -> Option<&crate::operation_diagnostics::OperationDiagnosticCollector> {
2127 None
2128 }
2129
2130 fn thread_pool(&self) -> Option<&Arc<rayon::ThreadPool>> {
2133 None
2134 }
2135
2136 fn cancellation_token(&self) -> Option<crate::engine::CancelToken> {
2143 None
2144 }
2145
2146 fn chunk_hint(&self) -> Option<usize> {
2148 None
2149 }
2150
2151 fn materialized_array_cells(&self) -> u64 {
2155 crate::engine::resource_ledger::DEFAULT_MATERIALIZED_ARRAY_CELLS
2156 }
2157
2158 fn resolve_range_view<'c>(
2161 &'c self,
2162 _reference: &ReferenceType,
2163 _current_sheet: &str,
2164 ) -> Result<RangeView<'c>, ExcelError> {
2165 Err(ExcelError::new(ExcelErrorKind::NImpl))
2166 }
2167
2168 fn resolve_cell_reference_value(
2173 &self,
2174 sheet: Option<&str>,
2175 row: u32,
2176 col: u32,
2177 current_sheet: &str,
2178 ) -> Result<LiteralValue, ExcelError> {
2179 let reference = ReferenceType::Cell {
2180 sheet: sheet.map(str::to_string),
2181 row,
2182 col,
2183 row_abs: true,
2184 col_abs: true,
2185 };
2186 let view = self.resolve_range_view(&reference, current_sheet)?;
2187 Ok(view.as_1x1().unwrap_or(LiteralValue::Empty))
2188 }
2189
2190 fn resolve_cell_format(
2192 &self,
2193 _sheet: Option<&str>,
2194 _row: u32,
2195 _col: u32,
2196 _current_sheet: &str,
2197 ) -> Option<crate::format::FormatId> {
2198 None
2199 }
2200
2201 #[doc(hidden)]
2206 fn resolve_cell_reference_value_formatted(
2207 &self,
2208 sheet: Option<&str>,
2209 row: u32,
2210 col: u32,
2211 current_sheet: &str,
2212 ) -> Result<(LiteralValue, Option<crate::format::FormatId>), ExcelError> {
2213 let value = self.resolve_cell_reference_value(sheet, row, col, current_sheet)?;
2214 Ok((
2215 value,
2216 self.resolve_cell_format(sheet, row, col, current_sheet),
2217 ))
2218 }
2219
2220 fn format_class(
2222 &self,
2223 format: crate::format::FormatId,
2224 ) -> Option<formualizer_common::numfmt::FormatClass> {
2225 formualizer_common::numfmt::NumberFormat::builtin(format.0)
2226 .map(|format| format.class().clone())
2227 }
2228
2229 fn record_cell_derived_format(
2231 &self,
2232 _sheet: &str,
2233 _row: u32,
2234 _col: u32,
2235 _format: Option<crate::format::FormatId>,
2236 ) {
2237 }
2238
2239 fn locale(&self) -> crate::locale::Locale {
2241 crate::locale::Locale::invariant()
2242 }
2243
2244 fn named_binding_presence(&self, _name: &str, _current_sheet: &str) -> Option<bool> {
2247 None
2248 }
2249
2250 fn workbook_sheet_count(&self) -> Option<usize> {
2252 None
2253 }
2254
2255 fn sheet_index_by_name(&self, _sheet: &str) -> Option<usize> {
2257 None
2258 }
2259
2260 fn current_sheet_index(&self, current_sheet: &str) -> Option<usize> {
2262 self.sheet_index_by_name(current_sheet)
2263 }
2264
2265 fn inspect_reference(
2267 &self,
2268 _reference: &ReferenceType,
2269 _current_sheet: &str,
2270 ) -> Result<Option<ReferenceInfo>, ExcelError> {
2271 Ok(None)
2272 }
2273
2274 fn has_formula_at_cell(&self, cell: CellRef) -> Result<bool, ExcelError> {
2277 self.formula_text_at_cell(cell).map(|text| text.is_some())
2278 }
2279
2280 fn formula_text_at_cell(&self, _cell: CellRef) -> Result<Option<String>, ExcelError> {
2282 Ok(None)
2283 }
2284
2285 fn resolve_spill_reference(
2298 &self,
2299 _anchor: &ReferenceType,
2300 _current_sheet: &str,
2301 ) -> Result<ReferenceType, ExcelError> {
2302 Err(ExcelError::new(ExcelErrorKind::Ref)
2303 .with_message("Spill references are not available in this context"))
2304 }
2305
2306 fn clock(&self) -> &dyn crate::timezone::ClockProvider {
2315 #[cfg(feature = "system-clock")]
2316 {
2317 static DEFAULT_CLOCK: std::sync::OnceLock<crate::timezone::SystemClock> =
2318 std::sync::OnceLock::new();
2319 DEFAULT_CLOCK.get_or_init(|| {
2320 crate::timezone::SystemClock::new(crate::timezone::TimeZoneSpec::default())
2321 })
2322 }
2323 #[cfg(not(feature = "system-clock"))]
2324 {
2325 static DEFAULT_CLOCK: std::sync::OnceLock<crate::timezone::FixedClock> =
2326 std::sync::OnceLock::new();
2327 DEFAULT_CLOCK.get_or_init(|| {
2328 crate::timezone::FixedClock::new(
2329 chrono::DateTime::UNIX_EPOCH,
2330 crate::timezone::TimeZoneSpec::Utc,
2331 )
2332 })
2333 }
2334 }
2335
2336 fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
2340 self.clock().timezone()
2341 }
2342
2343 fn volatile_level(&self) -> VolatileLevel {
2345 VolatileLevel::Always
2346 }
2347
2348 fn workbook_seed(&self) -> u64 {
2350 0xF0F0_D0D0_AAAA_5555
2351 }
2352
2353 fn recalc_epoch(&self) -> u64 {
2355 0
2356 }
2357
2358 fn used_rows_for_columns(
2363 &self,
2364 _sheet: &str,
2365 _start_col: u32,
2366 _end_col: u32,
2367 ) -> Option<(u32, u32)> {
2368 None
2369 }
2370
2371 fn used_cols_for_rows(
2374 &self,
2375 _sheet: &str,
2376 _start_row: u32,
2377 _end_row: u32,
2378 ) -> Option<(u32, u32)> {
2379 None
2380 }
2381
2382 fn sheet_bounds(&self, _sheet: &str) -> Option<(u32, u32)> {
2384 None
2385 }
2386
2387 fn data_snapshot_id(&self) -> u64 {
2389 0
2390 }
2391
2392 fn backend_caps(&self) -> BackendCaps {
2394 BackendCaps::default()
2395 }
2396
2397 fn date_system(&self) -> crate::engine::DateSystem {
2402 crate::engine::DateSystem::Excel1900
2403 }
2404
2405 fn build_lookup_index(
2408 &self,
2409 _view: &RangeView<'_>,
2410 _axis: LookupAxis,
2411 ) -> Option<std::sync::Arc<LookupIndex>> {
2412 None
2413 }
2414
2415 fn build_criteria_mask(
2418 &self,
2419 _view: &RangeView<'_>,
2420 _col_in_view: usize,
2421 _pred: &crate::args::CriteriaPredicate,
2422 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2423 None
2424 }
2425
2426 fn build_row_visibility_mask(
2429 &self,
2430 _view: &RangeView<'_>,
2431 _mode: VisibilityMaskMode,
2432 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2433 None
2434 }
2435
2436 fn nested_subtotal_cells(
2443 &self,
2444 _view: &RangeView<'_>,
2445 _include_aggregate: bool,
2446 ) -> Option<Vec<(usize, usize, usize)>> {
2447 None
2448 }
2449}
2450
2451#[derive(Copy, Clone, Debug, Default)]
2453pub struct BackendCaps {
2454 pub streaming: bool,
2456 pub used_region: bool,
2458 pub write: bool,
2460 pub tables: bool,
2462 pub async_stream: bool,
2464}
2465
2466#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2469pub enum VolatileLevel {
2470 Always,
2472 OnRecalc,
2474 OnOpen,
2476}
2477
2478pub trait FunctionContext<'ctx> {
2480 fn report_unavailable_operation(
2483 &self,
2484 _operation: crate::operation_diagnostics::UnavailableOperation,
2485 ) {
2486 }
2487
2488 fn locale(&self) -> crate::locale::Locale;
2489 fn timezone(&self) -> &crate::timezone::TimeZoneSpec;
2490 fn clock(&self) -> &dyn crate::timezone::ClockProvider;
2491 fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>>;
2492 fn cancellation_token(&self) -> Option<crate::engine::CancelToken>;
2498 fn chunk_hint(&self) -> Option<usize>;
2499
2500 fn materialized_array_cells(&self) -> u64 {
2504 crate::engine::resource_ledger::DEFAULT_MATERIALIZED_ARRAY_CELLS
2505 }
2506
2507 fn current_sheet(&self) -> &str;
2509
2510 fn workbook_sheet_count(&self) -> Option<usize> {
2511 None
2512 }
2513
2514 fn sheet_index_by_name(&self, _sheet: &str) -> Option<usize> {
2515 None
2516 }
2517
2518 fn current_sheet_index(&self) -> Option<usize> {
2519 self.sheet_index_by_name(self.current_sheet())
2520 }
2521
2522 fn inspect_reference(
2523 &self,
2524 _reference: &ReferenceType,
2525 ) -> Result<Option<ReferenceInfo>, ExcelError> {
2526 Ok(None)
2527 }
2528
2529 fn has_formula_at_cell(&self, cell: CellRef) -> Result<bool, ExcelError> {
2532 self.formula_text_at_cell(cell).map(|text| text.is_some())
2533 }
2534
2535 fn formula_text_at_cell(&self, _cell: CellRef) -> Result<Option<String>, ExcelError> {
2536 Ok(None)
2537 }
2538
2539 fn volatile_level(&self) -> VolatileLevel;
2540 fn workbook_seed(&self) -> u64;
2541 fn recalc_epoch(&self) -> u64;
2542 fn current_cell(&self) -> Option<CellRef>;
2543
2544 fn resolve_range_view(
2546 &self,
2547 _reference: &ReferenceType,
2548 _current_sheet: &str,
2549 ) -> Result<RangeView<'ctx>, ExcelError>;
2550
2551 fn rng_for_current(&self, fn_salt: u64) -> rand::rngs::SmallRng {
2555 use crate::rng::{compose_seed, small_rng_from_lanes};
2556 let (sheet_id, row, col) = self
2557 .current_cell()
2558 .map(|c| (c.sheet_id as u32, c.coord.row(), c.coord.col()))
2559 .unwrap_or((0, 0, 0));
2560 let epoch = match self.volatile_level() {
2562 VolatileLevel::OnRecalc => self.recalc_epoch(),
2563 _ => 0,
2564 };
2565 let (l0, l1) = compose_seed(self.workbook_seed(), sheet_id, row, col, fn_salt, epoch);
2566 small_rng_from_lanes(l0, l1)
2567 }
2568
2569 fn date_system(&self) -> crate::engine::DateSystem {
2571 crate::engine::DateSystem::Excel1900
2572 }
2573
2574 fn get_lookup_index(
2577 &self,
2578 _view: &RangeView<'_>,
2579 _axis: LookupAxis,
2580 ) -> Option<std::sync::Arc<LookupIndex>> {
2581 None
2582 }
2583
2584 fn get_criteria_mask(
2587 &self,
2588 _view: &RangeView<'_>,
2589 _col_in_view: usize,
2590 _pred: &crate::args::CriteriaPredicate,
2591 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2592 None
2593 }
2594
2595 fn get_row_visibility_mask(
2597 &self,
2598 _view: &RangeView<'_>,
2599 _mode: VisibilityMaskMode,
2600 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2601 None
2602 }
2603
2604 fn get_nested_subtotal_cells(
2608 &self,
2609 _view: &RangeView<'_>,
2610 _include_aggregate: bool,
2611 ) -> Option<Vec<(usize, usize, usize)>> {
2612 None
2613 }
2614}
2615
2616pub struct DefaultFunctionContext<'a> {
2618 pub base: &'a dyn EvaluationContext,
2619 pub current: Option<CellRef>,
2620 pub current_sheet: &'a str,
2621}
2622
2623impl<'a> DefaultFunctionContext<'a> {
2624 pub fn new(
2625 base: &'a dyn EvaluationContext,
2626 current: Option<CellRef>,
2627 current_sheet: &'a str,
2628 ) -> Self {
2629 Self {
2630 base,
2631 current,
2632 current_sheet,
2633 }
2634 }
2635
2636 pub fn new_with_sheet(
2637 base: &'a dyn EvaluationContext,
2638 current: Option<CellRef>,
2639 current_sheet: &'a str,
2640 ) -> Self {
2641 Self::new(base, current, current_sheet)
2642 }
2643}
2644
2645impl<'a> FunctionContext<'a> for DefaultFunctionContext<'a> {
2646 fn report_unavailable_operation(
2647 &self,
2648 operation: crate::operation_diagnostics::UnavailableOperation,
2649 ) {
2650 if let Some(collector) = self.base.operation_diagnostic_collector() {
2651 collector.record(self.current, operation);
2652 }
2653 }
2654
2655 fn locale(&self) -> crate::locale::Locale {
2656 self.base.locale()
2657 }
2658
2659 fn current_sheet(&self) -> &str {
2660 self.current_sheet
2661 }
2662
2663 fn workbook_sheet_count(&self) -> Option<usize> {
2664 self.base.workbook_sheet_count()
2665 }
2666
2667 fn sheet_index_by_name(&self, sheet: &str) -> Option<usize> {
2668 self.base.sheet_index_by_name(sheet)
2669 }
2670
2671 fn current_sheet_index(&self) -> Option<usize> {
2672 self.base.current_sheet_index(self.current_sheet)
2673 }
2674
2675 fn inspect_reference(
2676 &self,
2677 reference: &ReferenceType,
2678 ) -> Result<Option<ReferenceInfo>, ExcelError> {
2679 self.base.inspect_reference(reference, self.current_sheet)
2680 }
2681
2682 fn has_formula_at_cell(&self, cell: CellRef) -> Result<bool, ExcelError> {
2683 self.base.has_formula_at_cell(cell)
2684 }
2685
2686 fn formula_text_at_cell(&self, cell: CellRef) -> Result<Option<String>, ExcelError> {
2687 self.base.formula_text_at_cell(cell)
2688 }
2689
2690 fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
2691 self.base.timezone()
2692 }
2693
2694 fn clock(&self) -> &dyn crate::timezone::ClockProvider {
2695 self.base.clock()
2696 }
2697 fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>> {
2698 self.base.thread_pool()
2699 }
2700 fn cancellation_token(&self) -> Option<crate::engine::CancelToken> {
2701 self.base.cancellation_token()
2702 }
2703 fn chunk_hint(&self) -> Option<usize> {
2704 self.base.chunk_hint()
2705 }
2706 fn materialized_array_cells(&self) -> u64 {
2707 self.base.materialized_array_cells()
2708 }
2709
2710 fn volatile_level(&self) -> VolatileLevel {
2711 self.base.volatile_level()
2712 }
2713 fn workbook_seed(&self) -> u64 {
2714 self.base.workbook_seed()
2715 }
2716 fn recalc_epoch(&self) -> u64 {
2717 self.base.recalc_epoch()
2718 }
2719 fn current_cell(&self) -> Option<CellRef> {
2720 self.current
2721 }
2722
2723 fn resolve_range_view(
2724 &self,
2725 reference: &ReferenceType,
2726 current_sheet: &str,
2727 ) -> Result<RangeView<'a>, ExcelError> {
2728 self.base
2729 .resolve_range_view(reference, current_sheet)
2730 .map(|view| view.with_cell_budget(self.base.materialized_array_cells()))
2731 }
2732
2733 fn date_system(&self) -> crate::engine::DateSystem {
2736 self.base.date_system()
2737 }
2738
2739 fn get_lookup_index(
2740 &self,
2741 view: &RangeView<'_>,
2742 axis: LookupAxis,
2743 ) -> Option<std::sync::Arc<LookupIndex>> {
2744 self.base.build_lookup_index(view, axis)
2745 }
2746
2747 fn get_criteria_mask(
2748 &self,
2749 view: &RangeView<'_>,
2750 col_in_view: usize,
2751 pred: &crate::args::CriteriaPredicate,
2752 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2753 self.base.build_criteria_mask(view, col_in_view, pred)
2754 }
2755
2756 fn get_row_visibility_mask(
2757 &self,
2758 view: &RangeView<'_>,
2759 mode: VisibilityMaskMode,
2760 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2761 self.base.build_row_visibility_mask(view, mode)
2762 }
2763
2764 fn get_nested_subtotal_cells(
2765 &self,
2766 view: &RangeView<'_>,
2767 include_aggregate: bool,
2768 ) -> Option<Vec<(usize, usize, usize)>> {
2769 self.base.nested_subtotal_cells(view, include_aggregate)
2770 }
2771}
2772
2773#[cfg(test)]
2774mod argument_handle_size_tests {
2775 use super::*;
2776 #[expect(dead_code, reason = "layout-only regression fixture")]
2777 struct LegacyArgumentHandle<'a, 'b> {
2778 expr: ArgumentExpr<'a>,
2779 interp: &'a Interpreter<'b>,
2780 cached_ast: std::cell::OnceCell<ASTNode>,
2781 cached_ref: std::cell::OnceCell<ReferenceType>,
2782 cached_reference_or_value:
2783 std::cell::OnceCell<Result<crate::function::FunctionResolution<'b>, ExcelError>>,
2784 cached_resolved: std::cell::OnceCell<Result<ResolvedArgument<'b>, ExcelError>>,
2785 cached_value: std::cell::OnceCell<Result<crate::traits::CalcValue<'b>, ExcelError>>,
2794 }
2795
2796 #[test]
2797 fn argument_handle_fits_inline_call_storage() {
2798 let before = std::mem::size_of::<LegacyArgumentHandle<'_, '_>>();
2799 let after = std::mem::size_of::<ArgumentHandle<'_, '_>>();
2800 assert_eq!(after, 80, "ArgumentHandle layout changed");
2801 assert!(after < before / 4, "cold caches must stay out of line");
2802 }
2803}