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, Debug, Eq, PartialEq)]
19pub struct ReferenceInfo {
20 pub first_sheet_index: Option<usize>,
22 pub sheet_count: Option<usize>,
24 pub first_cell: Option<CellRef>,
26}
27
28pub trait Range: Debug + Send + Sync {
31 fn get(&self, row: usize, col: usize) -> Result<LiteralValue, ExcelError>;
32 fn dimensions(&self) -> (usize, usize);
33
34 fn is_sparse(&self) -> bool {
35 false
36 }
37
38 fn is_infinite(&self) -> bool {
40 false
41 }
42
43 fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
44 Cow::Owned(
45 (0..self.dimensions().0)
46 .map(|r| {
47 (0..self.dimensions().1)
48 .map(|c| self.get(r, c).unwrap_or(LiteralValue::Empty))
49 .collect()
50 })
51 .collect(),
52 )
53 }
54
55 fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
56 let (rows, cols) = self.dimensions();
57 Box::new((0..rows).flat_map(move |r| (0..cols).map(move |c| self.get(r, c).unwrap())))
58 }
59 fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
60 let (rows, cols) = self.dimensions();
61 Box::new((0..rows).map(move |r| (0..cols).map(|c| self.get(r, c).unwrap()).collect()))
62 }
63
64 fn as_any(&self) -> &dyn Any;
66}
67
68impl Range for Box<dyn Range> {
70 fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
71 (**self).get(r, c)
72 }
73 fn dimensions(&self) -> (usize, usize) {
74 (**self).dimensions()
75 }
76 fn is_sparse(&self) -> bool {
77 (**self).is_sparse()
78 }
79 fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
80 (**self).materialise()
81 }
82 fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
83 (**self).iter_cells()
84 }
85 fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
86 (**self).iter_rows()
87 }
88 fn as_any(&self) -> &dyn Any {
89 (**self).as_any()
90 }
91}
92
93pub type CowValue<'a> = Cow<'a, LiteralValue>;
96
97pub trait CustomCallable: Send + Sync {
98 fn arity(&self) -> usize;
99
100 fn invoke<'ctx>(
101 &self,
102 interp: &Interpreter<'ctx>,
103 args: &[LiteralValue],
104 ) -> Result<CalcValue<'ctx>, ExcelError>;
105
106 fn invoke_with_references<'ctx>(
120 &self,
121 interp: &Interpreter<'ctx>,
122 args: &[LiteralValue],
123 references: &[Option<ReferenceType>],
124 ) -> Result<CalcValue<'ctx>, ExcelError> {
125 let _ = references;
126 self.invoke(interp, args)
127 }
128}
129
130#[derive(Clone)]
131pub enum CalcValue<'a> {
132 Scalar(LiteralValue),
133 AnnotatedScalar(LiteralValue, crate::format::FormatId),
135 Range(RangeView<'a>),
136 Callable(Arc<dyn CustomCallable>),
137}
138
139#[derive(Clone)]
145pub(crate) enum ResolvedArgument<'a> {
146 Range(RangeView<'a>),
147 ReferenceError(ExcelError),
148 Value(CalcValue<'a>),
149}
150
151impl std::fmt::Debug for ResolvedArgument<'_> {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 match self {
154 Self::Range(view) => f.debug_tuple("Range").field(view).finish(),
155 Self::ReferenceError(error) => f.debug_tuple("ReferenceError").field(error).finish(),
156 Self::Value(value) => f.debug_tuple("Value").field(value).finish(),
157 }
158 }
159}
160
161impl<'a> std::fmt::Debug for CalcValue<'a> {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 match self {
164 CalcValue::Scalar(v) => f.debug_tuple("Scalar").field(v).finish(),
165 CalcValue::AnnotatedScalar(v, format) => f
166 .debug_tuple("AnnotatedScalar")
167 .field(v)
168 .field(format)
169 .finish(),
170 CalcValue::Range(rv) => {
171 let (r, c) = rv.dims();
172 f.debug_tuple("Range").field(&(r, c)).finish()
173 }
174 CalcValue::Callable(_) => f.write_str("Callable(<opaque>)"),
175 }
176 }
177}
178
179#[cfg(test)]
182thread_local! {
183 pub(crate) static RANGE_MATERIALIZED_CELLS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
184}
185
186impl<'a> CalcValue<'a> {
187 pub fn into_literal(self) -> LiteralValue {
188 match self {
189 CalcValue::Scalar(s) | CalcValue::AnnotatedScalar(s, _) => s,
190 CalcValue::Range(rv) => {
191 let (rows, cols) = rv.dims();
192 #[cfg(test)]
193 RANGE_MATERIALIZED_CELLS.with(|count| count.set(count.get() + rows * cols));
194 if rows == 1 && cols == 1 {
195 rv.get_cell(0, 0)
196 } else if let Some(error) = rv.materialization_error() {
197 LiteralValue::Error(error)
198 } else {
199 let mut data = Vec::with_capacity(rows);
200 for row_idx in 0..rows {
201 let mut row = Vec::with_capacity(cols);
202 for col_idx in 0..cols {
203 row.push(rv.get_cell(row_idx, col_idx));
204 }
205 data.push(row);
206 }
207 LiteralValue::Array(data)
208 }
209 }
210 CalcValue::Callable(_) => LiteralValue::Error(
211 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
212 ),
213 }
214 }
215
216 pub fn as_scalar(&self) -> Option<&LiteralValue> {
217 match self {
218 CalcValue::Scalar(s) | CalcValue::AnnotatedScalar(s, _) => Some(s),
219 _ => None,
220 }
221 }
222
223 pub fn format_id(&self) -> Option<crate::format::FormatId> {
224 match self {
225 CalcValue::AnnotatedScalar(_, format) => Some(*format),
226 _ => None,
227 }
228 }
229
230 pub fn with_format(self, format: Option<crate::format::FormatId>) -> Self {
231 let format = format.filter(|id| *id != crate::format::FormatId::GENERAL);
232 match (self, format) {
233 (CalcValue::Scalar(value) | CalcValue::AnnotatedScalar(value, _), Some(id)) => {
234 CalcValue::AnnotatedScalar(value, id)
235 }
236 (CalcValue::AnnotatedScalar(value, _), None) => CalcValue::Scalar(value),
237 (other, _) => other,
238 }
239 }
240
241 pub fn into_scalar_parts(self) -> (LiteralValue, Option<crate::format::FormatId>) {
242 match self {
243 CalcValue::Scalar(value) => (value, None),
244 CalcValue::AnnotatedScalar(value, format) => (value, Some(format)),
245 other => (other.into_literal(), None),
246 }
247 }
248
249 pub fn as_range(&self) -> Option<&RangeView<'a>> {
250 match self {
251 CalcValue::Range(r) => Some(r),
252 _ => None,
253 }
254 }
255
256 pub fn as_callable(&self) -> Option<&Arc<dyn CustomCallable>> {
257 match self {
258 CalcValue::Callable(c) => Some(c),
259 _ => None,
260 }
261 }
262
263 pub fn into_owned(self) -> LiteralValue {
264 self.into_literal()
265 }
266}
267
268impl From<CalcValue<'_>> for LiteralValue {
269 fn from(val: CalcValue<'_>) -> Self {
270 val.into_literal()
271 }
272}
273
274impl<'a> PartialEq<LiteralValue> for CalcValue<'a> {
275 fn eq(&self, other: &LiteralValue) -> bool {
276 match self {
277 CalcValue::Scalar(s) | CalcValue::AnnotatedScalar(s, _) => s == other,
278 CalcValue::Range(rv) => match other {
279 LiteralValue::Array(arr) => {
280 let (rows, cols) = rv.dims();
281 if arr.len() != rows {
282 return false;
283 }
284 for (r, row) in arr.iter().enumerate() {
285 if row.len() != cols {
286 return false;
287 }
288 for (c, cell) in row.iter().enumerate() {
289 if &rv.get_cell(r, c) != cell {
290 return false;
291 }
292 }
293 }
294 true
295 }
296 _ => {
297 let (rows, cols) = rv.dims();
298 rows == 1 && cols == 1 && &rv.get_cell(0, 0) == other
299 }
300 },
301 CalcValue::Callable(_) => false,
302 }
303 }
304}
305
306impl<'a> PartialEq<CalcValue<'a>> for LiteralValue {
307 fn eq(&self, other: &CalcValue<'a>) -> bool {
308 other == self
309 }
310}
311
312pub enum EvaluatedArg<'a> {
313 LiteralValue(CowValue<'a>),
314 Range(Box<dyn Range>),
315}
316
317enum ArgumentExpr<'a> {
318 Ast(&'a ASTNode),
319 Arena {
320 id: crate::engine::arena::AstNodeId,
321 data_store: &'a crate::engine::arena::DataStore,
322 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
323 },
324}
325
326pub struct ArgumentHandle<'a, 'b> {
327 expr: ArgumentExpr<'a>,
328 interp: &'a Interpreter<'b>,
329 caches: std::cell::OnceCell<Box<ArgumentCaches<'b>>>,
330 cached_value: std::cell::OnceCell<Result<MemoizedValue<'b>, ExcelError>>,
336}
337
338enum MemoizedValue<'b> {
340 Scalar(LiteralValue, Option<crate::format::FormatId>),
341 Other(Box<CalcValue<'b>>),
342}
343
344impl<'b> MemoizedValue<'b> {
345 fn new(value: CalcValue<'b>) -> Self {
346 match value {
347 CalcValue::Scalar(value) => Self::Scalar(value, None),
348 CalcValue::AnnotatedScalar(value, format) => Self::Scalar(value, Some(format)),
349 other => Self::Other(Box::new(other)),
350 }
351 }
352
353 fn value(&self) -> CalcValue<'b> {
354 match self {
355 Self::Scalar(value, None) => CalcValue::Scalar(value.clone()),
356 Self::Scalar(value, Some(format)) => CalcValue::AnnotatedScalar(value.clone(), *format),
357 Self::Other(value) => (**value).clone(),
358 }
359 }
360}
361
362#[derive(Default)]
363struct ArgumentCaches<'b> {
364 cached_ast: std::cell::OnceCell<ASTNode>,
365 cached_ref: std::cell::OnceCell<ReferenceType>,
366 cached_reference_or_value:
367 std::cell::OnceCell<Result<crate::function::FunctionResolution<'b>, ExcelError>>,
368 cached_resolved: std::cell::OnceCell<Result<ResolvedArgument<'b>, ExcelError>>,
369}
370
371impl<'a, 'b> ArgumentHandle<'a, 'b> {
372 fn caches(&self) -> &ArgumentCaches<'b> {
373 self.caches
374 .get_or_init(|| Box::new(ArgumentCaches::default()))
375 }
376
377 pub(crate) fn new(node: &'a ASTNode, interp: &'a Interpreter<'b>) -> Self {
378 Self {
379 expr: ArgumentExpr::Ast(node),
380 interp,
381 caches: std::cell::OnceCell::new(),
382 cached_value: std::cell::OnceCell::new(),
383 }
384 }
385
386 pub(crate) fn new_arena(
387 id: crate::engine::arena::AstNodeId,
388 interp: &'a Interpreter<'b>,
389 data_store: &'a crate::engine::arena::DataStore,
390 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
391 ) -> Self {
392 Self {
393 expr: ArgumentExpr::Arena {
394 id,
395 data_store,
396 sheet_registry,
397 },
398 interp,
399 caches: std::cell::OnceCell::new(),
400 cached_value: std::cell::OnceCell::new(),
401 }
402 }
403
404 pub(crate) fn date_system(&self) -> crate::engine::DateSystem {
410 self.interp.context.date_system()
411 }
412
413 pub fn is_omitted(&self) -> bool {
417 match &self.expr {
418 ArgumentExpr::Ast(node) => matches!(node.node_type, ASTNodeType::Omitted),
419 ArgumentExpr::Arena { id, data_store, .. } => matches!(
420 data_store.get_node(*id),
421 Some(crate::engine::arena::AstNodeData::Omitted)
422 ),
423 }
424 }
425
426 pub(crate) fn bare_reference(&self) -> Option<ReferenceType> {
439 let is_reference = match &self.expr {
440 ArgumentExpr::Ast(node) => matches!(node.node_type, ASTNodeType::Reference { .. }),
441 ArgumentExpr::Arena { id, data_store, .. } => matches!(
442 data_store.get_node(*id),
443 Some(crate::engine::arena::AstNodeData::Reference { .. })
444 ),
445 };
446 if !is_reference {
447 return None;
448 }
449 self.as_reference_or_eval().ok()
450 }
451
452 pub(crate) fn spill_reference(&self) -> Result<ReferenceType, ExcelError> {
457 match &self.expr {
458 ArgumentExpr::Ast(node) => self.interp.ast_spill_reference(node),
459 ArgumentExpr::Arena {
460 id,
461 data_store,
462 sheet_registry,
463 } => self
464 .interp
465 .arena_spill_reference(*id, data_store, sheet_registry),
466 }
467 }
468
469 pub(crate) fn has_reference_semantics(&self) -> bool {
475 self.reference_attempt().is_some()
476 }
477
478 pub(crate) fn may_return_reference(&self) -> bool {
483 match &self.expr {
484 ArgumentExpr::Ast(node) => match &node.node_type {
485 ASTNodeType::Reference { reference, .. } => match reference {
486 ReferenceType::NamedRange(name)
488 if self.interp.resolve_local_name(name).is_some() =>
489 {
490 self.interp.resolve_local_bound_reference(name).is_some()
491 }
492 _ => true,
493 },
494 ASTNodeType::BinaryOp { op, .. } => *op == formualizer_parse::BinaryOperator::Range,
495 ASTNodeType::UnaryOp { op, .. } => *op == formualizer_parse::UnaryOperator::Spill,
496 ASTNodeType::Function { name, .. } => self
497 .interp
498 .ast_function_capabilities(name)
499 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)),
500 _ => false,
501 },
502 ArgumentExpr::Arena { id, data_store, .. } => match data_store.get_node(*id) {
503 Some(crate::engine::arena::AstNodeData::Reference { ref_type, .. }) => {
504 let ref_type = data_store.compact_ref(*ref_type);
505 match ref_type {
506 crate::engine::arena::CompactRefType::NamedRange(name_id)
508 if self
509 .interp
510 .resolve_local_name(data_store.resolve_ast_string(*name_id))
511 .is_some() =>
512 {
513 self.interp
514 .resolve_local_bound_reference(
515 data_store.resolve_ast_string(*name_id),
516 )
517 .is_some()
518 }
519 _ => true,
520 }
521 }
522 Some(crate::engine::arena::AstNodeData::BinaryOp { operator, .. }) => {
523 *operator == formualizer_parse::BinaryOperator::Range
524 }
525 Some(crate::engine::arena::AstNodeData::UnaryOp { operator, .. }) => {
526 *operator == formualizer_parse::UnaryOperator::Spill
527 }
528 Some(crate::engine::arena::AstNodeData::Function { name_id, .. }) => self
529 .interp
530 .arena_function_capabilities(*name_id, data_store)
531 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)),
532 _ => false,
533 },
534 }
535 }
536
537 pub fn value(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
538 self.cached_value
539 .get_or_init(|| self.compute_value().map(MemoizedValue::new))
540 .as_ref()
541 .map(MemoizedValue::value)
542 .map_err(Clone::clone)
543 }
544
545 pub(crate) fn value_for_text(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
551 if self.is_omitted() {
552 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
553 String::new(),
554 )))
555 } else {
556 self.value()
557 }
558 }
559
560 pub(crate) fn resolve_once_for_text(&self) -> Result<ResolvedArgument<'b>, ExcelError> {
561 if self.is_omitted() {
562 Ok(ResolvedArgument::Value(crate::traits::CalcValue::Scalar(
563 LiteralValue::Text(String::new()),
564 )))
565 } else {
566 self.resolve_once()
567 }
568 }
569
570 fn compute_value(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
571 match &self.expr {
572 ArgumentExpr::Ast(node) => match &node.node_type {
573 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
574 ASTNodeType::Omitted => {
577 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
578 }
579 _ => self.interp.evaluate_ast(node),
580 },
581 ArgumentExpr::Arena {
582 id,
583 data_store,
584 sheet_registry,
585 } => {
586 if matches!(
587 data_store.get_node(*id),
588 Some(crate::engine::arena::AstNodeData::Omitted)
589 ) {
590 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
591 } else {
592 self.interp
593 .evaluate_arena_ast(*id, data_store, sheet_registry)
594 }
595 }
596 }
597 }
598
599 pub fn value_with_env(
600 &self,
601 env: crate::interpreter::LocalEnv,
602 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
603 let scoped = self.interp.with_local_env(env);
604 match &self.expr {
605 ArgumentExpr::Ast(node) => match &node.node_type {
606 ASTNodeType::Literal(v) => Ok(crate::traits::CalcValue::Scalar(v.clone())),
607 ASTNodeType::Omitted => {
608 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
609 }
610 _ => scoped.evaluate_ast(node),
611 },
612 ArgumentExpr::Arena {
613 id,
614 data_store,
615 sheet_registry,
616 } => {
617 if matches!(
618 data_store.get_node(*id),
619 Some(crate::engine::arena::AstNodeData::Omitted)
620 ) {
621 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)))
622 } else {
623 scoped.evaluate_arena_ast(*id, data_store, sheet_registry)
624 }
625 }
626 }
627 }
628
629 pub fn current_env(&self) -> crate::interpreter::LocalEnv {
630 self.interp.local_env().clone()
631 }
632
633 pub(crate) fn bound_reference_in_env(
640 &self,
641 env: &crate::interpreter::LocalEnv,
642 ) -> Option<ReferenceType> {
643 match &self.expr {
644 ArgumentExpr::Ast(node) => match &node.node_type {
645 ASTNodeType::Reference { reference, .. } => {
646 if let ReferenceType::NamedRange(name) = reference
647 && let Some(binding) = env.lookup(name)
648 {
649 return match binding {
651 crate::interpreter::LocalBinding::ValueWithReference {
652 reference,
653 ..
654 } => Some(reference),
655 _ => None,
656 };
657 }
658 self.interp.reference_for_current_offset(reference).ok()
659 }
660 ASTNodeType::BinaryOp { op, .. }
661 if *op == formualizer_parse::BinaryOperator::Range =>
662 {
663 self.interp
664 .with_local_env(env.clone())
665 .evaluate_ast_as_reference(node)
666 .ok()
667 }
668 ASTNodeType::UnaryOp { op, expr }
669 if *op == formualizer_parse::UnaryOperator::Spill =>
670 {
671 self.interp
672 .with_local_env(env.clone())
673 .ast_spill_reference(expr)
674 .ok()
675 }
676 _ => None,
677 },
678 ArgumentExpr::Arena {
679 id,
680 data_store,
681 sheet_registry,
682 } => match data_store.get_node(*id) {
683 Some(crate::engine::arena::AstNodeData::Reference { ref_type, .. }) => {
684 let ref_type = data_store.compact_ref(*ref_type);
685 if let crate::engine::arena::CompactRefType::NamedRange(name_id) = ref_type
686 && let Some(binding) = env.lookup(data_store.resolve_ast_string(*name_id))
687 {
688 return match binding {
689 crate::interpreter::LocalBinding::ValueWithReference {
690 reference,
691 ..
692 } => Some(reference),
693 _ => None,
694 };
695 }
696 let reference =
697 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry);
698 self.interp.reference_for_current_offset(&reference).ok()
699 }
700 Some(crate::engine::arena::AstNodeData::BinaryOp { operator, .. })
701 if *operator == formualizer_parse::BinaryOperator::Range =>
702 {
703 self.interp
704 .with_local_env(env.clone())
705 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry)
706 .ok()
707 }
708 Some(crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id })
709 if *operator == formualizer_parse::UnaryOperator::Spill =>
710 {
711 self.interp
712 .with_local_env(env.clone())
713 .arena_spill_reference(*expr_id, data_store, sheet_registry)
714 .ok()
715 }
716 _ => None,
717 },
718 }
719 }
720
721 pub fn inline_array_literal(&self) -> Result<Option<Vec<Vec<LiteralValue>>>, ExcelError> {
722 match &self.expr {
723 ArgumentExpr::Ast(node) => match &node.node_type {
724 ASTNodeType::Literal(LiteralValue::Array(arr)) => Ok(Some(arr.clone())),
725 _ => Ok(None),
726 },
727 ArgumentExpr::Arena {
728 id,
729 data_store,
730 sheet_registry,
731 } => {
732 let node = data_store.get_node(*id).ok_or_else(|| {
733 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
734 })?;
735 match node {
736 crate::engine::arena::AstNodeData::Literal(vref) => {
737 match data_store.retrieve_value(*vref) {
738 LiteralValue::Array(arr) => Ok(Some(arr)),
739 _ => Ok(None),
740 }
741 }
742 _ => {
743 let _ = sheet_registry;
746 Ok(None)
747 }
748 }
749 }
750 }
751 }
752
753 fn local_named_reference(&self, name: &str) -> Option<Result<ReferenceType, ExcelError>> {
772 self.interp.resolve_local_name(name)?;
774 Some(
775 self.interp
776 .resolve_local_bound_reference(name)
777 .ok_or_else(|| {
778 ExcelError::new(ExcelErrorKind::Value)
779 .with_message("LET/LAMBDA local is not a reference")
780 }),
781 )
782 }
783
784 fn reference_for_eval(&self) -> Result<ReferenceType, ExcelError> {
785 match &self.expr {
786 ArgumentExpr::Ast(node) => match &node.node_type {
787 ASTNodeType::Reference { reference, .. } => {
788 if let ReferenceType::NamedRange(name) = reference
791 && let Some(local) = self.local_named_reference(name)
792 {
793 return local;
794 }
795 self.interp.reference_for_current_offset(reference)
796 }
797 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
798 self.interp.evaluate_ast_as_reference(node)
799 }
800 ASTNodeType::UnaryOp { op, expr }
801 if *op == formualizer_parse::UnaryOperator::Spill =>
802 {
803 self.interp.ast_spill_reference(expr)
804 }
805 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
806 .with_message("Expected a reference (by-ref argument)")),
807 },
808 ArgumentExpr::Arena {
809 id,
810 data_store,
811 sheet_registry,
812 } => {
813 let node = data_store.get_node(*id).ok_or_else(|| {
814 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
815 })?;
816 match node {
817 crate::engine::arena::AstNodeData::Reference { ref_type, .. } => {
818 let ref_type = data_store.compact_ref(*ref_type);
819 if let crate::engine::arena::CompactRefType::NamedRange(name_id) = ref_type
821 && let Some(local) =
822 self.local_named_reference(data_store.resolve_ast_string(*name_id))
823 {
824 return local;
825 }
826 let reference = data_store
827 .reconstruct_reference_type_for_eval(ref_type, sheet_registry);
828 self.interp.reference_for_current_offset(&reference)
829 }
830 crate::engine::arena::AstNodeData::Function { .. }
831 | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
832 .interp
833 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
834 crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id }
835 if *operator == formualizer_parse::UnaryOperator::Spill =>
836 {
837 self.interp
838 .arena_spill_reference(*expr_id, data_store, sheet_registry)
839 }
840 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
841 .with_message("Expected a reference (by-ref argument)")),
842 }
843 }
844 }
845 }
846
847 fn function_resolution_attempt(
848 &self,
849 ) -> Option<Result<crate::function::FunctionResolution<'b>, ExcelError>> {
850 match &self.expr {
851 ArgumentExpr::Ast(node) => {
852 let ASTNodeType::Function { name, args } = &node.node_type else {
853 return None;
854 };
855 if !self
856 .interp
857 .ast_function_capabilities(name)
858 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE))
859 {
860 return None;
861 }
862 let fun = match self.interp.context.get_function("", name) {
863 Some(fun) => fun,
864 None => {
865 return Some(Err(ExcelError::new(ExcelErrorKind::Name)
866 .with_message(format!("Unknown function: {name}"))));
867 }
868 };
869 let handles: Vec<_> = args
870 .iter()
871 .map(|arg| ArgumentHandle::new(arg, self.interp))
872 .collect();
873 let ctx = DefaultFunctionContext::new_with_sheet(
874 self.interp.context,
875 None,
876 self.interp.current_sheet(),
877 );
878 Some(fun.resolve_reference_or_value(&handles, &ctx, &|| self.value()))
879 }
880 ArgumentExpr::Arena {
881 id,
882 data_store,
883 sheet_registry,
884 } => {
885 let node = match data_store.get_node(*id) {
886 Some(node) => node,
887 None => {
888 return Some(Err(
889 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
890 ));
891 }
892 };
893 let crate::engine::arena::AstNodeData::Function { name_id, .. } = node else {
894 return None;
895 };
896 let name = data_store.resolve_ast_string(*name_id);
897 if !self
898 .interp
899 .ast_function_capabilities(name)
900 .is_some_and(|caps| caps.contains(crate::function::FnCaps::RETURNS_REFERENCE))
901 {
902 return None;
903 }
904 let fun = match self.interp.context.get_function("", name) {
905 Some(fun) => fun,
906 None => {
907 return Some(Err(ExcelError::new(ExcelErrorKind::Name)
908 .with_message(format!("Unknown function: {name}"))));
909 }
910 };
911 let args = match data_store.get_args(*id) {
912 Some(args) => args,
913 None => {
914 return Some(Err(ExcelError::new(ExcelErrorKind::Value)
915 .with_message("Missing function args")));
916 }
917 };
918 let handles: Vec<_> = args
919 .iter()
920 .copied()
921 .map(|arg_id| {
922 ArgumentHandle::new_arena(arg_id, self.interp, data_store, sheet_registry)
923 })
924 .collect();
925 let ctx = DefaultFunctionContext::new_with_sheet(
926 self.interp.context,
927 None,
928 self.interp.current_sheet(),
929 );
930 Some(fun.resolve_reference_or_value(&handles, &ctx, &|| self.value()))
931 }
932 }
933 }
934
935 fn reference_attempt(&self) -> Option<Result<ReferenceType, ExcelError>> {
936 match &self.expr {
937 ArgumentExpr::Ast(node) => match &node.node_type {
938 ASTNodeType::Reference { reference, .. } => {
939 if let ReferenceType::NamedRange(name) = reference
944 && self.interp.resolve_local_name(name).is_some()
945 {
946 return self.interp.resolve_local_bound_reference(name).map(Ok);
947 }
948 Some(self.interp.reference_for_current_offset(reference))
949 }
950 ASTNodeType::BinaryOp { op, .. }
951 if *op == formualizer_parse::BinaryOperator::Range =>
952 {
953 Some(self.interp.evaluate_ast_as_reference(node))
954 }
955 ASTNodeType::UnaryOp { op, expr }
956 if *op == formualizer_parse::UnaryOperator::Spill =>
957 {
958 Some(self.interp.ast_spill_reference(expr))
959 }
960 ASTNodeType::Function { name, .. }
961 if self
962 .interp
963 .ast_function_capabilities(name)
964 .is_some_and(|caps| {
965 caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)
966 }) =>
967 {
968 self.interp.try_evaluate_ast_as_reference(node)
969 }
970 _ => None,
971 },
972 ArgumentExpr::Arena {
973 id,
974 data_store,
975 sheet_registry,
976 } => {
977 let node = match data_store.get_node(*id) {
978 Some(node) => node,
979 None => {
980 return Some(Err(
981 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
982 ));
983 }
984 };
985 match node {
986 crate::engine::arena::AstNodeData::Reference { ref_type, .. } => {
987 let ref_type = data_store.compact_ref(*ref_type);
988 if let crate::engine::arena::CompactRefType::NamedRange(name_id) = ref_type
990 {
991 let name = data_store.resolve_ast_string(*name_id);
992 if self.interp.resolve_local_name(name).is_some() {
993 return self.interp.resolve_local_bound_reference(name).map(Ok);
994 }
995 }
996 let reference = data_store
997 .reconstruct_reference_type_for_eval(ref_type, sheet_registry);
998 Some(self.interp.reference_for_current_offset(&reference))
999 }
1000 crate::engine::arena::AstNodeData::BinaryOp { operator, .. }
1001 if *operator == formualizer_parse::BinaryOperator::Range =>
1002 {
1003 Some(self.interp.evaluate_arena_ast_as_reference(
1004 *id,
1005 data_store,
1006 sheet_registry,
1007 ))
1008 }
1009 crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id }
1010 if *operator == formualizer_parse::UnaryOperator::Spill =>
1011 {
1012 Some(self.interp.arena_spill_reference(
1013 *expr_id,
1014 data_store,
1015 sheet_registry,
1016 ))
1017 }
1018 crate::engine::arena::AstNodeData::Function { name_id, .. } => {
1019 if self
1020 .interp
1021 .arena_function_capabilities(*name_id, data_store)
1022 .is_some_and(|caps| {
1023 caps.contains(crate::function::FnCaps::RETURNS_REFERENCE)
1024 })
1025 {
1026 self.interp.try_evaluate_arena_ast_as_reference(
1027 *id,
1028 data_store,
1029 sheet_registry,
1030 )
1031 } else {
1032 None
1033 }
1034 }
1035 _ => None,
1036 }
1037 }
1038 }
1039 }
1040
1041 pub(crate) fn resolve_reference_or_value(
1042 &self,
1043 ) -> Result<crate::function::FunctionResolution<'b>, ExcelError> {
1044 let resolved = self
1045 .caches()
1046 .cached_reference_or_value
1047 .get_or_init(|| self.compute_reference_or_value())
1048 .clone();
1049 if let Ok(crate::function::FunctionResolution::Value(value)) = &resolved {
1050 let _ = self.cached_value.set(Ok(MemoizedValue::new(value.clone())));
1051 }
1052 resolved
1053 }
1054
1055 fn compute_reference_or_value(
1056 &self,
1057 ) -> Result<crate::function::FunctionResolution<'b>, ExcelError> {
1058 if let Some(result) = self.function_resolution_attempt() {
1059 return result;
1060 }
1061 if let Some(reference) = self.reference_attempt() {
1062 return Ok(match reference {
1063 Ok(reference) => crate::function::FunctionResolution::Reference(reference),
1064 Err(error) => crate::function::FunctionResolution::ReferenceError(error),
1065 });
1066 }
1067 self.value().map(crate::function::FunctionResolution::Value)
1068 }
1069
1070 pub(crate) fn resolve_once(&self) -> Result<ResolvedArgument<'b>, ExcelError> {
1076 self.caches()
1077 .cached_resolved
1078 .get_or_init(|| self.compute_resolved_argument())
1079 .clone()
1080 }
1081
1082 pub(crate) fn with_context_cancel_token(&self, view: RangeView<'b>) -> RangeView<'b> {
1084 let view = view.with_cell_budget(self.interp.context.materialized_array_cells());
1085 match self.interp.context.cancellation_token() {
1086 Some(token) => view.with_cancel_token(Some(token)),
1087 None => view,
1088 }
1089 }
1090
1091 fn compute_resolved_argument(&self) -> Result<ResolvedArgument<'b>, ExcelError> {
1092 if let ArgumentExpr::Arena { id, data_store, .. } = &self.expr
1093 && let Some(result) = self.interp.resolve_arena_argument_range(*id, data_store)
1094 {
1095 return match result {
1096 Ok(view) => Ok(ResolvedArgument::Range(
1097 self.with_context_cancel_token(view),
1098 )),
1099 Err(error) if error.kind == ExcelErrorKind::Cancelled => Err(error),
1100 Err(error) => Ok(ResolvedArgument::ReferenceError(error)),
1101 };
1102 }
1103 let value = match self.resolve_reference_or_value()? {
1104 crate::function::FunctionResolution::Reference(reference) => {
1105 return match self
1106 .interp
1107 .context
1108 .resolve_range_view(&reference, self.interp.current_sheet())
1109 {
1110 Ok(view) => Ok(ResolvedArgument::Range(
1111 self.with_context_cancel_token(view),
1112 )),
1113 Err(error) if error.kind == ExcelErrorKind::Cancelled => Err(error),
1114 Err(error) => Ok(ResolvedArgument::ReferenceError(error)),
1115 };
1116 }
1117 crate::function::FunctionResolution::ReferenceError(error)
1118 if error.kind == ExcelErrorKind::Cancelled =>
1119 {
1120 return Err(error);
1121 }
1122 crate::function::FunctionResolution::ReferenceError(error) => {
1123 return Ok(ResolvedArgument::ReferenceError(error));
1124 }
1125 crate::function::FunctionResolution::Value(value) => value,
1126 };
1127
1128 match value {
1129 CalcValue::Range(view) => Ok(ResolvedArgument::Range(
1130 self.with_context_cancel_token(view),
1131 )),
1132 CalcValue::Scalar(LiteralValue::Array(rows)) => {
1133 let view = RangeView::try_from_owned_rows(
1134 rows,
1135 self.interp.context.date_system(),
1136 self.interp.context.cancellation_token(),
1137 )?;
1138 Ok(ResolvedArgument::Range(view))
1139 }
1140 other => Ok(ResolvedArgument::Value(other)),
1141 }
1142 }
1143
1144 fn check_materialized_cells(&self, rows: usize, cols: usize) -> Result<(), ExcelError> {
1147 match crate::engine::resource_ledger::materialized_cells_exceed(
1148 rows,
1149 cols,
1150 self.interp.context.materialized_array_cells(),
1151 ) {
1152 Some(error) => Err(error),
1153 None => Ok(()),
1154 }
1155 }
1156
1157 pub fn range(&self) -> Result<Box<dyn Range>, ExcelError> {
1158 match &self.expr {
1159 ArgumentExpr::Ast(node) => match &node.node_type {
1160 ASTNodeType::Reference { reference, .. } => {
1161 let reference = self.interp.reference_for_current_offset(reference)?;
1163 let view = self
1164 .interp
1165 .context
1166 .resolve_range_view(&reference, self.interp.current_sheet())?;
1167 let (rows, cols) = view.dims();
1168 self.check_materialized_cells(rows, cols)?;
1169 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
1170 view.for_each_row(&mut |row| {
1171 let row_data: Vec<LiteralValue> = (0..cols)
1172 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
1173 .collect();
1174 out.push(row_data);
1175 Ok(())
1176 })?;
1177 Ok(Box::new(InMemoryRange::new(out)))
1178 }
1179 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
1180 let reference = self.reference_for_eval()?;
1181 let view = self
1182 .interp
1183 .context
1184 .resolve_range_view(&reference, self.interp.current_sheet())?;
1185 let (rows, cols) = view.dims();
1186 self.check_materialized_cells(rows, cols)?;
1187 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
1188 view.for_each_row(&mut |row| {
1189 let row_data: Vec<LiteralValue> = (0..cols)
1190 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
1191 .collect();
1192 out.push(row_data);
1193 Ok(())
1194 })?;
1195 Ok(Box::new(InMemoryRange::new(out)))
1196 }
1197 ASTNodeType::Array(rows) => {
1198 let mut materialized = Vec::new();
1199 for row in rows {
1200 let mut materialized_row = Vec::new();
1201 for cell in row {
1202 materialized_row.push(self.interp.evaluate_ast(cell)?.into_literal());
1203 }
1204 materialized.push(materialized_row);
1205 }
1206 Ok(Box::new(InMemoryRange::new(materialized)))
1207 }
1208 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1209 .with_message(format!("Expected a range, got {:?}", node.node_type))),
1210 },
1211 ArgumentExpr::Arena { id, data_store, .. } => {
1212 let node = data_store.get_node(*id).ok_or_else(|| {
1213 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1214 })?;
1215
1216 match node {
1217 crate::engine::arena::AstNodeData::Reference { .. }
1218 | crate::engine::arena::AstNodeData::Function { .. }
1219 | crate::engine::arena::AstNodeData::BinaryOp { .. } => {
1220 let reference = self.reference_for_eval()?;
1221 let view = self
1222 .interp
1223 .context
1224 .resolve_range_view(&reference, self.interp.current_sheet())?;
1225 let (rows, cols) = view.dims();
1226 self.check_materialized_cells(rows, cols)?;
1227 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
1228 view.for_each_row(&mut |row| {
1229 let row_data: Vec<LiteralValue> = (0..cols)
1230 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
1231 .collect();
1232 out.push(row_data);
1233 Ok(())
1234 })?;
1235 Ok(Box::new(InMemoryRange::new(out)))
1236 }
1237 crate::engine::arena::AstNodeData::Array { .. } => {
1238 let (rows, cols, elements) =
1239 data_store.get_array_elems(*id).ok_or_else(|| {
1240 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
1241 })?;
1242 let rows_usize = rows as usize;
1243 let cols_usize = cols as usize;
1244 let mut materialized: Vec<Vec<LiteralValue>> =
1245 Vec::with_capacity(rows_usize);
1246 for r in 0..rows_usize {
1247 let mut row = Vec::with_capacity(cols_usize);
1248 for c in 0..cols_usize {
1249 let idx = r * cols_usize + c;
1250 let elem_id = elements.get(idx).copied().ok_or_else(|| {
1251 ExcelError::new(ExcelErrorKind::Value)
1252 .with_message("Invalid array")
1253 })?;
1254 let v = self.interp.evaluate_arena_ast(
1255 elem_id,
1256 data_store,
1257 self.sheet_registry(),
1258 )?;
1259 row.push(v.into_literal());
1260 }
1261 materialized.push(row);
1262 }
1263 Ok(Box::new(InMemoryRange::new(materialized)))
1264 }
1265 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1266 .with_message("Argument cannot be interpreted as a range.")),
1267 }
1268 }
1269 }
1270 }
1271
1272 fn sheet_registry(&self) -> &crate::engine::sheet_registry::SheetRegistry {
1273 match &self.expr {
1274 ArgumentExpr::Ast(_) => {
1275 unreachable!("sheet_registry only used for arena ArgumentHandle")
1277 }
1278 ArgumentExpr::Arena { sheet_registry, .. } => sheet_registry,
1279 }
1280 }
1281
1282 pub fn range_view(&self) -> Result<RangeView<'b>, ExcelError> {
1290 match self.resolve_once()? {
1291 ResolvedArgument::Range(view) => Ok(view),
1292 ResolvedArgument::ReferenceError(error) => Err(error),
1295 ResolvedArgument::Value(CalcValue::Range(view)) => {
1300 Ok(self.with_context_cancel_token(view))
1301 }
1302 ResolvedArgument::Value(CalcValue::Scalar(LiteralValue::Array(rows))) => {
1303 RangeView::try_from_owned_rows(
1304 rows,
1305 self.interp.context.date_system(),
1306 self.interp.context.cancellation_token(),
1307 )
1308 }
1309 ResolvedArgument::Value(_) => Err(ExcelError::new(ExcelErrorKind::Ref)
1310 .with_message("Argument cannot be interpreted as a range.")),
1311 }
1312 }
1313
1314 pub fn range_view_or_scalar(&self) -> Result<RangeView<'b>, ExcelError> {
1334 match self.resolve_once()? {
1335 ResolvedArgument::Range(view) => Ok(view),
1336 ResolvedArgument::ReferenceError(error) => Err(error),
1337 ResolvedArgument::Value(CalcValue::Range(view)) => {
1338 Ok(self.with_context_cancel_token(view))
1339 }
1340 ResolvedArgument::Value(CalcValue::Scalar(LiteralValue::Array(rows))) => {
1341 RangeView::try_from_owned_rows(
1342 rows,
1343 self.interp.context.date_system(),
1344 self.interp.context.cancellation_token(),
1345 )
1346 }
1347 ResolvedArgument::Value(CalcValue::Scalar(LiteralValue::Error(error))) => Err(error),
1350 ResolvedArgument::Value(
1351 CalcValue::Scalar(scalar) | CalcValue::AnnotatedScalar(scalar, _),
1352 ) => RangeView::try_from_owned_rows(
1353 vec![vec![scalar]],
1354 self.interp.context.date_system(),
1355 self.interp.context.cancellation_token(),
1356 ),
1357 ResolvedArgument::Value(CalcValue::Callable(_)) => {
1359 Err(ExcelError::new(ExcelErrorKind::Ref)
1360 .with_message("Argument cannot be interpreted as a range."))
1361 }
1362 }
1363 }
1364
1365 pub fn value_or_range(&self) -> Result<EvaluatedArg<'_>, ExcelError> {
1366 self.range().map(EvaluatedArg::Range).or_else(|_| {
1367 self.value()
1368 .map(|cv| EvaluatedArg::LiteralValue(Cow::Owned(cv.into_literal())))
1369 })
1370 }
1371
1372 pub fn lazy_values_owned(
1377 &'a self,
1378 ) -> Result<Box<dyn Iterator<Item = LiteralValue> + 'a>, ExcelError> {
1379 fn resolved_values<'a, 'b>(
1383 handle: &'a ArgumentHandle<'a, 'b>,
1384 ) -> Result<Box<dyn Iterator<Item = LiteralValue> + 'a>, ExcelError> {
1385 match handle.resolve_once()? {
1386 ResolvedArgument::Range(view) => {
1387 let mut values = Vec::new();
1388 view.for_each_cell(&mut |value| {
1389 values.push(value.clone());
1390 Ok(())
1391 })?;
1392 Ok(Box::new(values.into_iter()))
1393 }
1394 ResolvedArgument::ReferenceError(error) => Err(error),
1395 ResolvedArgument::Value(value) => {
1396 Ok(Box::new(std::iter::once(value.into_literal())))
1397 }
1398 }
1399 }
1400
1401 match &self.expr {
1402 ArgumentExpr::Ast(node) => match &node.node_type {
1403 ASTNodeType::Reference { .. } => resolved_values(self),
1404 ASTNodeType::Array(rows) => {
1405 struct ArrayEvalIter<'a, 'b> {
1406 rows: &'a [Vec<ASTNode>],
1407 r: usize,
1408 c: usize,
1409 interp: &'a Interpreter<'b>,
1410 }
1411 impl<'a, 'b> Iterator for ArrayEvalIter<'a, 'b> {
1412 type Item = LiteralValue;
1413 fn next(&mut self) -> Option<Self::Item> {
1414 if self.rows.is_empty() {
1415 return None;
1416 }
1417 let rows = self.rows;
1418 let mut r = self.r;
1419 let mut c = self.c;
1420 if r >= rows.len() {
1421 return None;
1422 }
1423 let node = &rows[r][c];
1424 c += 1;
1426 if c >= rows[r].len() {
1427 r += 1;
1428 c = 0;
1429 }
1430 self.r = r;
1431 self.c = c;
1432 match self.interp.evaluate_ast(node) {
1433 Ok(cv) => Some(cv.into_literal()),
1434 Err(e) => Some(LiteralValue::Error(e)),
1435 }
1436 }
1437 }
1438 let it = ArrayEvalIter {
1439 rows,
1440 r: 0,
1441 c: 0,
1442 interp: self.interp,
1443 };
1444 Ok(Box::new(it))
1445 }
1446 _ => {
1447 let v = self.value()?.into_literal();
1449 Ok(Box::new(std::iter::once(v)))
1450 }
1451 },
1452 ArgumentExpr::Arena {
1453 id,
1454 data_store,
1455 sheet_registry,
1456 } => {
1457 let node = data_store.get_node(*id).ok_or_else(|| {
1458 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1459 })?;
1460
1461 match node {
1462 crate::engine::arena::AstNodeData::Reference { .. } => resolved_values(self),
1463 crate::engine::arena::AstNodeData::Array { .. } => {
1464 let (rows, cols, elements) =
1465 data_store.get_array_elems(*id).ok_or_else(|| {
1466 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
1467 })?;
1468
1469 struct ArenaArrayEvalIter<'a, 'b> {
1470 elements: &'a [crate::engine::arena::AstNodeId],
1471 idx: usize,
1472 interp: &'a Interpreter<'b>,
1473 data_store: &'a crate::engine::arena::DataStore,
1474 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
1475 }
1476
1477 impl<'a, 'b> Iterator for ArenaArrayEvalIter<'a, 'b> {
1478 type Item = LiteralValue;
1479
1480 fn next(&mut self) -> Option<Self::Item> {
1481 let id = self.elements.get(self.idx).copied()?;
1482 self.idx += 1;
1483 match self.interp.evaluate_arena_ast(
1484 id,
1485 self.data_store,
1486 self.sheet_registry,
1487 ) {
1488 Ok(cv) => Some(cv.into_literal()),
1489 Err(e) => Some(LiteralValue::Error(e)),
1490 }
1491 }
1492 }
1493
1494 let _ = (rows, cols);
1495 let it = ArenaArrayEvalIter {
1496 elements,
1497 idx: 0,
1498 interp: self.interp,
1499 data_store,
1500 sheet_registry,
1501 };
1502 Ok(Box::new(it))
1503 }
1504 _ => {
1505 let v = self
1506 .interp
1507 .evaluate_arena_ast(*id, data_store, sheet_registry)?;
1508 Ok(Box::new(std::iter::once(v.into_literal())))
1509 }
1510 }
1511 }
1512 }
1513 }
1514
1515 pub fn ast(&self) -> &ASTNode {
1516 match &self.expr {
1517 ArgumentExpr::Ast(node) => node,
1518 ArgumentExpr::Arena {
1519 id,
1520 data_store,
1521 sheet_registry,
1522 } => self.caches().cached_ast.get_or_init(|| {
1523 data_store
1524 .retrieve_ast(*id, sheet_registry)
1525 .unwrap_or_else(|| ASTNode {
1526 node_type: ASTNodeType::Literal(LiteralValue::Error(
1527 ExcelError::new(ExcelErrorKind::Value)
1528 .with_message("Missing formula AST"),
1529 )),
1530 source_token: None,
1531 contains_volatile: false,
1532 })
1533 }),
1534 }
1535 }
1536
1537 pub fn as_reference(&self) -> Result<&ReferenceType, ExcelError> {
1540 match &self.expr {
1541 ArgumentExpr::Ast(node) => match &node.node_type {
1542 ASTNodeType::Reference { reference, .. } => {
1543 if let ReferenceType::NamedRange(name) = reference
1545 && let Some(local) = self.local_named_reference(name)
1546 {
1547 let bound = local?;
1548 return Ok(self.caches().cached_ref.get_or_init(|| bound));
1549 }
1550 Ok(reference)
1551 }
1552 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1553 .with_message("Expected a reference (by-ref argument)")),
1554 },
1555 ArgumentExpr::Arena { .. } => {
1556 let reference = self.reference_for_eval()?;
1557 Ok(self.caches().cached_ref.get_or_init(|| reference))
1558 }
1559 }
1560 }
1561
1562 pub fn as_reference_or_eval(&self) -> Result<ReferenceType, ExcelError> {
1565 match &self.expr {
1566 ArgumentExpr::Ast(node) => match &node.node_type {
1567 ASTNodeType::Reference { reference, .. } => {
1568 if let ReferenceType::NamedRange(name) = reference
1570 && let Some(local) = self.local_named_reference(name)
1571 {
1572 return local;
1573 }
1574 self.interp.reference_for_current_offset(reference)
1575 }
1576 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
1577 self.interp.evaluate_ast_as_reference(node)
1578 }
1579 ASTNodeType::UnaryOp { op, expr }
1580 if *op == formualizer_parse::UnaryOperator::Spill =>
1581 {
1582 self.interp.ast_spill_reference(expr)
1583 }
1584 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1585 .with_message("Argument is not a reference")),
1586 },
1587 ArgumentExpr::Arena {
1588 id,
1589 data_store,
1590 sheet_registry,
1591 } => {
1592 let node = data_store.get_node(*id).ok_or_else(|| {
1593 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
1594 })?;
1595
1596 match node {
1597 crate::engine::arena::AstNodeData::Reference { .. } => {
1598 self.reference_for_eval()
1599 }
1600 crate::engine::arena::AstNodeData::Function { .. }
1601 | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
1602 .interp
1603 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
1604 crate::engine::arena::AstNodeData::UnaryOp { operator, expr_id }
1605 if *operator == formualizer_parse::UnaryOperator::Spill =>
1606 {
1607 self.interp
1608 .arena_spill_reference(*expr_id, data_store, sheet_registry)
1609 }
1610 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
1611 .with_message("Argument is not a reference")),
1612 }
1613 }
1614 }
1615 }
1616
1617 pub fn matches_kind(&self, k: formualizer_common::ArgKind) -> Result<bool, ExcelError> {
1619 Ok(match k {
1620 formualizer_common::ArgKind::Any => true,
1621 formualizer_common::ArgKind::Range => self.range().is_ok(),
1622 formualizer_common::ArgKind::Number => matches!(
1623 self.value()?.into_literal(),
1624 LiteralValue::Number(_) | LiteralValue::Int(_)
1625 ),
1626 formualizer_common::ArgKind::Text => {
1627 matches!(self.value()?.into_literal(), LiteralValue::Text(_))
1628 }
1629 formualizer_common::ArgKind::Logical => {
1630 matches!(self.value()?.into_literal(), LiteralValue::Boolean(_))
1631 }
1632 })
1633 }
1634}
1635
1636#[derive(Debug, Clone)]
1638pub struct InMemoryRange {
1639 data: Vec<Vec<LiteralValue>>,
1640}
1641impl InMemoryRange {
1642 pub fn new(d: Vec<Vec<LiteralValue>>) -> Self {
1643 Self { data: d }
1644 }
1645}
1646impl Range for InMemoryRange {
1647 fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
1648 Ok(self
1649 .data
1650 .get(r)
1651 .and_then(|row| row.get(c))
1652 .cloned()
1653 .unwrap_or(LiteralValue::Empty))
1654 }
1655 fn dimensions(&self) -> (usize, usize) {
1656 (self.data.len(), self.data.first().map_or(0, |r| r.len()))
1657 }
1658 fn as_any(&self) -> &dyn Any {
1659 self
1660 }
1661}
1662
1663pub trait Table: Debug + Send + Sync {
1666 fn get_cell(&self, row: usize, column: &str) -> Result<LiteralValue, ExcelError>;
1667 fn get_column(&self, column: &str) -> Result<Box<dyn Range>, ExcelError>;
1668 fn columns(&self) -> Vec<String> {
1670 vec![]
1671 }
1672 fn data_height(&self) -> usize {
1674 0
1675 }
1676 fn has_headers(&self) -> bool {
1678 false
1679 }
1680 fn has_totals(&self) -> bool {
1682 false
1683 }
1684 fn headers_row(&self) -> Option<Box<dyn Range>> {
1686 None
1687 }
1688 fn totals_row(&self) -> Option<Box<dyn Range>> {
1690 None
1691 }
1692 fn data_body(&self) -> Option<Box<dyn Range>> {
1694 None
1695 }
1696 fn clone_box(&self) -> Box<dyn Table>;
1697}
1698impl Table for Box<dyn Table> {
1699 fn get_cell(&self, r: usize, c: &str) -> Result<LiteralValue, ExcelError> {
1700 (**self).get_cell(r, c)
1701 }
1702 fn get_column(&self, c: &str) -> Result<Box<dyn Range>, ExcelError> {
1703 (**self).get_column(c)
1704 }
1705 fn columns(&self) -> Vec<String> {
1706 (**self).columns()
1707 }
1708 fn data_height(&self) -> usize {
1709 (**self).data_height()
1710 }
1711 fn has_headers(&self) -> bool {
1712 (**self).has_headers()
1713 }
1714 fn has_totals(&self) -> bool {
1715 (**self).has_totals()
1716 }
1717 fn headers_row(&self) -> Option<Box<dyn Range>> {
1718 (**self).headers_row()
1719 }
1720 fn totals_row(&self) -> Option<Box<dyn Range>> {
1721 (**self).totals_row()
1722 }
1723 fn data_body(&self) -> Option<Box<dyn Range>> {
1724 (**self).data_body()
1725 }
1726 fn clone_box(&self) -> Box<dyn Table> {
1727 (**self).clone_box()
1728 }
1729}
1730
1731pub trait ReferenceResolver: Send + Sync {
1734 fn resolve_cell_reference(
1735 &self,
1736 sheet: Option<&str>,
1737 row: u32,
1738 col: u32,
1739 ) -> Result<LiteralValue, ExcelError>;
1740}
1741pub trait RangeResolver: Send + Sync {
1742 fn resolve_range_reference(
1743 &self,
1744 sheet: Option<&str>,
1745 sr: Option<u32>,
1746 sc: Option<u32>,
1747 er: Option<u32>,
1748 ec: Option<u32>,
1749 ) -> Result<Box<dyn Range>, ExcelError>;
1750}
1751pub trait NamedRangeResolver: Send + Sync {
1752 fn resolve_named_range_reference(
1753 &self,
1754 name: &str,
1755 ) -> Result<Vec<Vec<LiteralValue>>, ExcelError>;
1756}
1757pub trait TableResolver: Send + Sync {
1758 fn resolve_table_reference(
1759 &self,
1760 tref: &formualizer_parse::parser::TableReference,
1761 ) -> Result<Box<dyn Table>, ExcelError>;
1762}
1763
1764pub trait SourceResolver: Send + Sync {
1765 fn source_scalar_version(&self, _name: &str) -> Option<u64> {
1766 None
1767 }
1768
1769 fn resolve_source_scalar(&self, name: &str) -> Result<LiteralValue, ExcelError> {
1770 Err(ExcelError::new(ExcelErrorKind::NImpl)
1771 .with_message(format!("Source scalar not supported: {name}")))
1772 }
1773
1774 fn source_table_version(&self, _name: &str) -> Option<u64> {
1775 None
1776 }
1777
1778 fn resolve_source_table(&self, name: &str) -> Result<Box<dyn Table>, ExcelError> {
1779 Err(ExcelError::new(ExcelErrorKind::NImpl)
1780 .with_message(format!("Source table not supported: {name}")))
1781 }
1782}
1783
1784pub trait Resolver: ReferenceResolver + RangeResolver + NamedRangeResolver + TableResolver {
1785 fn resolve_range_like(&self, r: &ReferenceType) -> Result<Box<dyn Range>, ExcelError> {
1786 match r {
1787 ReferenceType::Range {
1788 sheet,
1789 start_row,
1790 start_col,
1791 end_row,
1792 end_col,
1793 ..
1794 } => self.resolve_range_reference(
1795 sheet.as_deref(),
1796 *start_row,
1797 *start_col,
1798 *end_row,
1799 *end_col,
1800 ),
1801 ReferenceType::External(_) => Err(ExcelError::new(ExcelErrorKind::NImpl)
1802 .with_message("External references are not supported by Resolver".to_string())),
1803 ReferenceType::Table(tref) => {
1804 let t = self.resolve_table_reference(tref)?;
1805 match &tref.specifier {
1806 Some(TableSpecifier::Column(c)) => t.get_column(c),
1807 Some(TableSpecifier::ColumnRange(start, end)) => {
1808 let cols = t.columns();
1810 let start_key = start.to_lowercase();
1811 let end_key = end.to_lowercase();
1812 let start_idx = cols.iter().position(|n| n.to_lowercase() == start_key);
1813 let end_idx = cols.iter().position(|n| n.to_lowercase() == end_key);
1814 if let (Some(mut si), Some(mut ei)) = (start_idx, end_idx) {
1815 if si > ei {
1816 std::mem::swap(&mut si, &mut ei);
1817 }
1818 let h = t.data_height();
1820 let w = ei - si + 1;
1821 let mut rows = vec![vec![LiteralValue::Empty; w]; h];
1822 for (offset, ci) in (si..=ei).enumerate() {
1823 let cname = &cols[ci];
1824 let col_range = t.get_column(cname)?;
1825 let (rh, _) = col_range.dimensions();
1826 for (r, row) in rows.iter_mut().enumerate().take(h.min(rh)) {
1827 row[offset] = col_range.get(r, 0)?;
1828 }
1829 }
1830 Ok(Box::new(InMemoryRange::new(rows)))
1831 } else {
1832 Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1833 "Column range refers to unknown column(s)".to_string(),
1834 ))
1835 }
1836 }
1837 Some(TableSpecifier::SpecialItem(
1838 formualizer_parse::parser::SpecialItem::Headers,
1839 )) => {
1840 if let Some(h) = t.headers_row() {
1841 Ok(h)
1842 } else {
1843 Ok(Box::new(InMemoryRange::new(vec![])))
1844 }
1845 }
1846 Some(TableSpecifier::SpecialItem(
1847 formualizer_parse::parser::SpecialItem::Totals,
1848 )) => {
1849 if let Some(tr) = t.totals_row() {
1850 Ok(tr)
1851 } else {
1852 Ok(Box::new(InMemoryRange::new(vec![])))
1853 }
1854 }
1855 Some(TableSpecifier::SpecialItem(
1856 formualizer_parse::parser::SpecialItem::Data,
1857 )) => {
1858 if let Some(body) = t.data_body() {
1859 Ok(body)
1860 } else {
1861 Ok(Box::new(InMemoryRange::new(vec![])))
1862 }
1863 }
1864 Some(TableSpecifier::SpecialItem(
1865 formualizer_parse::parser::SpecialItem::All,
1866 )) => {
1867 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
1869 if let Some(h) = t.headers_row() {
1870 out.extend(h.iter_rows());
1871 }
1872 if let Some(body) = t.data_body() {
1873 out.extend(body.iter_rows());
1874 }
1875 if let Some(tr) = t.totals_row() {
1876 out.extend(tr.iter_rows());
1877 }
1878 Ok(Box::new(InMemoryRange::new(out)))
1879 }
1880 Some(TableSpecifier::SpecialItem(
1881 formualizer_parse::parser::SpecialItem::ThisRow,
1882 )) => Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1883 "@ (This Row) requires table-aware context; not yet supported".to_string(),
1884 )),
1885 Some(TableSpecifier::All) => {
1886 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
1888 if let Some(h) = t.headers_row() {
1889 out.extend(h.iter_rows());
1890 }
1891 if let Some(body) = t.data_body() {
1892 out.extend(body.iter_rows());
1893 }
1894 if let Some(tr) = t.totals_row() {
1895 out.extend(tr.iter_rows());
1896 }
1897 Ok(Box::new(InMemoryRange::new(out)))
1898 }
1899 Some(TableSpecifier::Data) => {
1900 if let Some(body) = t.data_body() {
1901 Ok(body)
1902 } else {
1903 Ok(Box::new(InMemoryRange::new(vec![])))
1904 }
1905 }
1906 Some(TableSpecifier::Combination(_)) => Err(ExcelError::new(
1908 ExcelErrorKind::NImpl,
1909 )
1910 .with_message("Complex structured references not yet supported".to_string())),
1911 Some(TableSpecifier::Row(_)) => Err(ExcelError::new(ExcelErrorKind::NImpl)
1912 .with_message("Row selectors (@/index) not yet supported".to_string())),
1913 Some(TableSpecifier::Headers) | Some(TableSpecifier::Totals) => {
1914 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1915 "Legacy Headers/Totals variants not used; use SpecialItem".to_string(),
1916 ))
1917 }
1918 None => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1919 "Table reference without specifier is unsupported".to_string(),
1920 )),
1921 }
1922 }
1923 ReferenceType::NamedRange(n) => {
1924 let v = self.resolve_named_range_reference(n)?;
1925 Ok(Box::new(InMemoryRange::new(v)))
1926 }
1927 ReferenceType::Cell {
1928 sheet, row, col, ..
1929 } => {
1930 let v = self.resolve_cell_reference(sheet.as_deref(), *row, *col)?;
1931 Ok(Box::new(InMemoryRange::new(vec![vec![v]])))
1932 }
1933 ReferenceType::Cell3D { .. } | ReferenceType::Range3D { .. } => {
1934 Err(ExcelError::new(ExcelErrorKind::NImpl)
1935 .with_message("3D references are not yet supported".to_string()))
1936 }
1937 }
1938 }
1939}
1940
1941pub trait FunctionProvider: Send + Sync {
1944 fn get_function(&self, ns: &str, name: &str) -> Option<Arc<dyn Function>>;
1945
1946 #[doc(hidden)]
1947 fn get_function_for_planning(&self, _ns: &str, _name: &str) -> Option<Arc<dyn Function>> {
1948 None
1949 }
1950
1951 #[doc(hidden)]
1954 fn planning_semantic_revision(&self) -> Option<u64> {
1955 None
1956 }
1957
1958 #[doc(hidden)]
1959 fn function_capabilities(&self, ns: &str, name: &str) -> Option<crate::function::FnCaps> {
1960 self.get_function(ns, name).map(|function| function.caps())
1961 }
1962
1963 fn function_semantic_identity(
1964 &self,
1965 ns: &str,
1966 name: &str,
1967 arity: usize,
1968 ) -> Option<crate::function_contract::FunctionSemanticIdentity> {
1969 crate::function_registry::resolve_semantic_identity(self, ns, name, arity)
1970 }
1971}
1972
1973pub trait EvaluationContext: Resolver + FunctionProvider + SourceResolver {
1974 fn thread_pool(&self) -> Option<&Arc<rayon::ThreadPool>> {
1977 None
1978 }
1979
1980 fn cancellation_token(&self) -> Option<crate::engine::CancelToken> {
1987 None
1988 }
1989
1990 fn chunk_hint(&self) -> Option<usize> {
1992 None
1993 }
1994
1995 fn materialized_array_cells(&self) -> u64 {
1999 crate::engine::resource_ledger::DEFAULT_MATERIALIZED_ARRAY_CELLS
2000 }
2001
2002 fn resolve_range_view<'c>(
2005 &'c self,
2006 _reference: &ReferenceType,
2007 _current_sheet: &str,
2008 ) -> Result<RangeView<'c>, ExcelError> {
2009 Err(ExcelError::new(ExcelErrorKind::NImpl))
2010 }
2011
2012 fn resolve_cell_reference_value(
2017 &self,
2018 sheet: Option<&str>,
2019 row: u32,
2020 col: u32,
2021 current_sheet: &str,
2022 ) -> Result<LiteralValue, ExcelError> {
2023 let reference = ReferenceType::Cell {
2024 sheet: sheet.map(str::to_string),
2025 row,
2026 col,
2027 row_abs: true,
2028 col_abs: true,
2029 };
2030 let view = self.resolve_range_view(&reference, current_sheet)?;
2031 Ok(view.as_1x1().unwrap_or(LiteralValue::Empty))
2032 }
2033
2034 fn resolve_cell_format(
2036 &self,
2037 _sheet: Option<&str>,
2038 _row: u32,
2039 _col: u32,
2040 _current_sheet: &str,
2041 ) -> Option<crate::format::FormatId> {
2042 None
2043 }
2044
2045 #[doc(hidden)]
2050 fn resolve_cell_reference_value_formatted(
2051 &self,
2052 sheet: Option<&str>,
2053 row: u32,
2054 col: u32,
2055 current_sheet: &str,
2056 ) -> Result<(LiteralValue, Option<crate::format::FormatId>), ExcelError> {
2057 let value = self.resolve_cell_reference_value(sheet, row, col, current_sheet)?;
2058 Ok((
2059 value,
2060 self.resolve_cell_format(sheet, row, col, current_sheet),
2061 ))
2062 }
2063
2064 fn format_class(
2066 &self,
2067 format: crate::format::FormatId,
2068 ) -> Option<formualizer_common::numfmt::FormatClass> {
2069 formualizer_common::numfmt::NumberFormat::builtin(format.0)
2070 .map(|format| format.class().clone())
2071 }
2072
2073 fn record_cell_derived_format(
2075 &self,
2076 _sheet: &str,
2077 _row: u32,
2078 _col: u32,
2079 _format: Option<crate::format::FormatId>,
2080 ) {
2081 }
2082
2083 fn locale(&self) -> crate::locale::Locale {
2085 crate::locale::Locale::invariant()
2086 }
2087
2088 fn workbook_sheet_count(&self) -> Option<usize> {
2090 None
2091 }
2092
2093 fn sheet_index_by_name(&self, _sheet: &str) -> Option<usize> {
2095 None
2096 }
2097
2098 fn current_sheet_index(&self, current_sheet: &str) -> Option<usize> {
2100 self.sheet_index_by_name(current_sheet)
2101 }
2102
2103 fn inspect_reference(
2105 &self,
2106 _reference: &ReferenceType,
2107 _current_sheet: &str,
2108 ) -> Result<Option<ReferenceInfo>, ExcelError> {
2109 Ok(None)
2110 }
2111
2112 fn formula_text_at_cell(&self, _cell: CellRef) -> Result<Option<String>, ExcelError> {
2114 Ok(None)
2115 }
2116
2117 fn resolve_spill_reference(
2130 &self,
2131 _anchor: &ReferenceType,
2132 _current_sheet: &str,
2133 ) -> Result<ReferenceType, ExcelError> {
2134 Err(ExcelError::new(ExcelErrorKind::Ref)
2135 .with_message("Spill references are not available in this context"))
2136 }
2137
2138 fn clock(&self) -> &dyn crate::timezone::ClockProvider {
2147 #[cfg(feature = "system-clock")]
2148 {
2149 static DEFAULT_CLOCK: std::sync::OnceLock<crate::timezone::SystemClock> =
2150 std::sync::OnceLock::new();
2151 DEFAULT_CLOCK.get_or_init(|| {
2152 crate::timezone::SystemClock::new(crate::timezone::TimeZoneSpec::default())
2153 })
2154 }
2155 #[cfg(not(feature = "system-clock"))]
2156 {
2157 static DEFAULT_CLOCK: std::sync::OnceLock<crate::timezone::FixedClock> =
2158 std::sync::OnceLock::new();
2159 DEFAULT_CLOCK.get_or_init(|| {
2160 crate::timezone::FixedClock::new(
2161 chrono::DateTime::UNIX_EPOCH,
2162 crate::timezone::TimeZoneSpec::Utc,
2163 )
2164 })
2165 }
2166 }
2167
2168 fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
2172 self.clock().timezone()
2173 }
2174
2175 fn volatile_level(&self) -> VolatileLevel {
2177 VolatileLevel::Always
2178 }
2179
2180 fn workbook_seed(&self) -> u64 {
2182 0xF0F0_D0D0_AAAA_5555
2183 }
2184
2185 fn recalc_epoch(&self) -> u64 {
2187 0
2188 }
2189
2190 fn used_rows_for_columns(
2195 &self,
2196 _sheet: &str,
2197 _start_col: u32,
2198 _end_col: u32,
2199 ) -> Option<(u32, u32)> {
2200 None
2201 }
2202
2203 fn used_cols_for_rows(
2206 &self,
2207 _sheet: &str,
2208 _start_row: u32,
2209 _end_row: u32,
2210 ) -> Option<(u32, u32)> {
2211 None
2212 }
2213
2214 fn sheet_bounds(&self, _sheet: &str) -> Option<(u32, u32)> {
2216 None
2217 }
2218
2219 fn data_snapshot_id(&self) -> u64 {
2221 0
2222 }
2223
2224 fn backend_caps(&self) -> BackendCaps {
2226 BackendCaps::default()
2227 }
2228
2229 fn date_system(&self) -> crate::engine::DateSystem {
2234 crate::engine::DateSystem::Excel1900
2235 }
2236
2237 fn build_lookup_index(
2240 &self,
2241 _view: &RangeView<'_>,
2242 _axis: LookupAxis,
2243 ) -> Option<std::sync::Arc<LookupIndex>> {
2244 None
2245 }
2246
2247 fn build_criteria_mask(
2250 &self,
2251 _view: &RangeView<'_>,
2252 _col_in_view: usize,
2253 _pred: &crate::args::CriteriaPredicate,
2254 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2255 None
2256 }
2257
2258 fn build_row_visibility_mask(
2261 &self,
2262 _view: &RangeView<'_>,
2263 _mode: VisibilityMaskMode,
2264 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2265 None
2266 }
2267
2268 fn nested_subtotal_cells(
2275 &self,
2276 _view: &RangeView<'_>,
2277 _include_aggregate: bool,
2278 ) -> Option<Vec<(usize, usize, usize)>> {
2279 None
2280 }
2281}
2282
2283#[derive(Copy, Clone, Debug, Default)]
2285pub struct BackendCaps {
2286 pub streaming: bool,
2288 pub used_region: bool,
2290 pub write: bool,
2292 pub tables: bool,
2294 pub async_stream: bool,
2296}
2297
2298#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2301pub enum VolatileLevel {
2302 Always,
2304 OnRecalc,
2306 OnOpen,
2308}
2309
2310pub trait FunctionContext<'ctx> {
2312 fn locale(&self) -> crate::locale::Locale;
2313 fn timezone(&self) -> &crate::timezone::TimeZoneSpec;
2314 fn clock(&self) -> &dyn crate::timezone::ClockProvider;
2315 fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>>;
2316 fn cancellation_token(&self) -> Option<crate::engine::CancelToken>;
2322 fn chunk_hint(&self) -> Option<usize>;
2323
2324 fn materialized_array_cells(&self) -> u64 {
2328 crate::engine::resource_ledger::DEFAULT_MATERIALIZED_ARRAY_CELLS
2329 }
2330
2331 fn current_sheet(&self) -> &str;
2333
2334 fn workbook_sheet_count(&self) -> Option<usize> {
2335 None
2336 }
2337
2338 fn sheet_index_by_name(&self, _sheet: &str) -> Option<usize> {
2339 None
2340 }
2341
2342 fn current_sheet_index(&self) -> Option<usize> {
2343 self.sheet_index_by_name(self.current_sheet())
2344 }
2345
2346 fn inspect_reference(
2347 &self,
2348 _reference: &ReferenceType,
2349 ) -> Result<Option<ReferenceInfo>, ExcelError> {
2350 Ok(None)
2351 }
2352
2353 fn formula_text_at_cell(&self, _cell: CellRef) -> Result<Option<String>, ExcelError> {
2354 Ok(None)
2355 }
2356
2357 fn volatile_level(&self) -> VolatileLevel;
2358 fn workbook_seed(&self) -> u64;
2359 fn recalc_epoch(&self) -> u64;
2360 fn current_cell(&self) -> Option<CellRef>;
2361
2362 fn resolve_range_view(
2364 &self,
2365 _reference: &ReferenceType,
2366 _current_sheet: &str,
2367 ) -> Result<RangeView<'ctx>, ExcelError>;
2368
2369 fn rng_for_current(&self, fn_salt: u64) -> rand::rngs::SmallRng {
2373 use crate::rng::{compose_seed, small_rng_from_lanes};
2374 let (sheet_id, row, col) = self
2375 .current_cell()
2376 .map(|c| (c.sheet_id as u32, c.coord.row(), c.coord.col()))
2377 .unwrap_or((0, 0, 0));
2378 let epoch = match self.volatile_level() {
2380 VolatileLevel::OnRecalc => self.recalc_epoch(),
2381 _ => 0,
2382 };
2383 let (l0, l1) = compose_seed(self.workbook_seed(), sheet_id, row, col, fn_salt, epoch);
2384 small_rng_from_lanes(l0, l1)
2385 }
2386
2387 fn date_system(&self) -> crate::engine::DateSystem {
2389 crate::engine::DateSystem::Excel1900
2390 }
2391
2392 fn get_lookup_index(
2395 &self,
2396 _view: &RangeView<'_>,
2397 _axis: LookupAxis,
2398 ) -> Option<std::sync::Arc<LookupIndex>> {
2399 None
2400 }
2401
2402 fn get_criteria_mask(
2405 &self,
2406 _view: &RangeView<'_>,
2407 _col_in_view: usize,
2408 _pred: &crate::args::CriteriaPredicate,
2409 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2410 None
2411 }
2412
2413 fn get_row_visibility_mask(
2415 &self,
2416 _view: &RangeView<'_>,
2417 _mode: VisibilityMaskMode,
2418 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2419 None
2420 }
2421
2422 fn get_nested_subtotal_cells(
2426 &self,
2427 _view: &RangeView<'_>,
2428 _include_aggregate: bool,
2429 ) -> Option<Vec<(usize, usize, usize)>> {
2430 None
2431 }
2432}
2433
2434pub struct DefaultFunctionContext<'a> {
2436 pub base: &'a dyn EvaluationContext,
2437 pub current: Option<CellRef>,
2438 pub current_sheet: &'a str,
2439}
2440
2441impl<'a> DefaultFunctionContext<'a> {
2442 pub fn new(
2443 base: &'a dyn EvaluationContext,
2444 current: Option<CellRef>,
2445 current_sheet: &'a str,
2446 ) -> Self {
2447 Self {
2448 base,
2449 current,
2450 current_sheet,
2451 }
2452 }
2453
2454 pub fn new_with_sheet(
2455 base: &'a dyn EvaluationContext,
2456 current: Option<CellRef>,
2457 current_sheet: &'a str,
2458 ) -> Self {
2459 Self::new(base, current, current_sheet)
2460 }
2461}
2462
2463impl<'a> FunctionContext<'a> for DefaultFunctionContext<'a> {
2464 fn locale(&self) -> crate::locale::Locale {
2465 self.base.locale()
2466 }
2467
2468 fn current_sheet(&self) -> &str {
2469 self.current_sheet
2470 }
2471
2472 fn workbook_sheet_count(&self) -> Option<usize> {
2473 self.base.workbook_sheet_count()
2474 }
2475
2476 fn sheet_index_by_name(&self, sheet: &str) -> Option<usize> {
2477 self.base.sheet_index_by_name(sheet)
2478 }
2479
2480 fn current_sheet_index(&self) -> Option<usize> {
2481 self.base.current_sheet_index(self.current_sheet)
2482 }
2483
2484 fn inspect_reference(
2485 &self,
2486 reference: &ReferenceType,
2487 ) -> Result<Option<ReferenceInfo>, ExcelError> {
2488 self.base.inspect_reference(reference, self.current_sheet)
2489 }
2490
2491 fn formula_text_at_cell(&self, cell: CellRef) -> Result<Option<String>, ExcelError> {
2492 self.base.formula_text_at_cell(cell)
2493 }
2494
2495 fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
2496 self.base.timezone()
2497 }
2498
2499 fn clock(&self) -> &dyn crate::timezone::ClockProvider {
2500 self.base.clock()
2501 }
2502 fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>> {
2503 self.base.thread_pool()
2504 }
2505 fn cancellation_token(&self) -> Option<crate::engine::CancelToken> {
2506 self.base.cancellation_token()
2507 }
2508 fn chunk_hint(&self) -> Option<usize> {
2509 self.base.chunk_hint()
2510 }
2511 fn materialized_array_cells(&self) -> u64 {
2512 self.base.materialized_array_cells()
2513 }
2514
2515 fn volatile_level(&self) -> VolatileLevel {
2516 self.base.volatile_level()
2517 }
2518 fn workbook_seed(&self) -> u64 {
2519 self.base.workbook_seed()
2520 }
2521 fn recalc_epoch(&self) -> u64 {
2522 self.base.recalc_epoch()
2523 }
2524 fn current_cell(&self) -> Option<CellRef> {
2525 self.current
2526 }
2527
2528 fn resolve_range_view(
2529 &self,
2530 reference: &ReferenceType,
2531 current_sheet: &str,
2532 ) -> Result<RangeView<'a>, ExcelError> {
2533 self.base
2534 .resolve_range_view(reference, current_sheet)
2535 .map(|view| view.with_cell_budget(self.base.materialized_array_cells()))
2536 }
2537
2538 fn date_system(&self) -> crate::engine::DateSystem {
2541 self.base.date_system()
2542 }
2543
2544 fn get_lookup_index(
2545 &self,
2546 view: &RangeView<'_>,
2547 axis: LookupAxis,
2548 ) -> Option<std::sync::Arc<LookupIndex>> {
2549 self.base.build_lookup_index(view, axis)
2550 }
2551
2552 fn get_criteria_mask(
2553 &self,
2554 view: &RangeView<'_>,
2555 col_in_view: usize,
2556 pred: &crate::args::CriteriaPredicate,
2557 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2558 self.base.build_criteria_mask(view, col_in_view, pred)
2559 }
2560
2561 fn get_row_visibility_mask(
2562 &self,
2563 view: &RangeView<'_>,
2564 mode: VisibilityMaskMode,
2565 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
2566 self.base.build_row_visibility_mask(view, mode)
2567 }
2568
2569 fn get_nested_subtotal_cells(
2570 &self,
2571 view: &RangeView<'_>,
2572 include_aggregate: bool,
2573 ) -> Option<Vec<(usize, usize, usize)>> {
2574 self.base.nested_subtotal_cells(view, include_aggregate)
2575 }
2576}
2577
2578#[cfg(test)]
2579mod argument_handle_size_tests {
2580 use super::*;
2581 #[expect(dead_code, reason = "layout-only regression fixture")]
2582 struct LegacyArgumentHandle<'a, 'b> {
2583 expr: ArgumentExpr<'a>,
2584 interp: &'a Interpreter<'b>,
2585 cached_ast: std::cell::OnceCell<ASTNode>,
2586 cached_ref: std::cell::OnceCell<ReferenceType>,
2587 cached_reference_or_value:
2588 std::cell::OnceCell<Result<crate::function::FunctionResolution<'b>, ExcelError>>,
2589 cached_resolved: std::cell::OnceCell<Result<ResolvedArgument<'b>, ExcelError>>,
2590 cached_value: std::cell::OnceCell<Result<crate::traits::CalcValue<'b>, ExcelError>>,
2599 }
2600
2601 #[test]
2602 fn argument_handle_fits_inline_call_storage() {
2603 let before = std::mem::size_of::<LegacyArgumentHandle<'_, '_>>();
2604 let after = std::mem::size_of::<ArgumentHandle<'_, '_>>();
2605 assert_eq!(after, 80, "ArgumentHandle layout changed");
2606 assert!(after < before / 4, "cold caches must stay out of line");
2607 }
2608}