Skip to main content

formualizer_eval/
traits.rs

1use crate::engine::range_view::RangeView;
2pub use crate::function::Function;
3use crate::interpreter::Interpreter;
4use crate::reference::CellRef;
5use formualizer_common::{
6    LiteralValue,
7    error::{ExcelError, ExcelErrorKind},
8};
9use std::any::Any;
10use std::borrow::Cow;
11use std::fmt::Debug;
12use std::sync::Arc;
13
14use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType, TableSpecifier};
15
16/* ───────────────────────────── Range ───────────────────────────── */
17
18pub trait Range: Debug + Send + Sync {
19    fn get(&self, row: usize, col: usize) -> Result<LiteralValue, ExcelError>;
20    fn dimensions(&self) -> (usize, usize);
21
22    fn is_sparse(&self) -> bool {
23        false
24    }
25
26    // Handle infinite ranges (A:A, 1:1)
27    fn is_infinite(&self) -> bool {
28        false
29    }
30
31    fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
32        Cow::Owned(
33            (0..self.dimensions().0)
34                .map(|r| {
35                    (0..self.dimensions().1)
36                        .map(|c| self.get(r, c).unwrap_or(LiteralValue::Empty))
37                        .collect()
38                })
39                .collect(),
40        )
41    }
42
43    fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
44        let (rows, cols) = self.dimensions();
45        Box::new((0..rows).flat_map(move |r| (0..cols).map(move |c| self.get(r, c).unwrap())))
46    }
47    fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
48        let (rows, cols) = self.dimensions();
49        Box::new((0..rows).map(move |r| (0..cols).map(|c| self.get(r, c).unwrap()).collect()))
50    }
51
52    /* down-cast hook for SIMD back-ends */
53    fn as_any(&self) -> &dyn Any;
54}
55
56/* blanket dyn passthrough */
57impl Range for Box<dyn Range> {
58    fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
59        (**self).get(r, c)
60    }
61    fn dimensions(&self) -> (usize, usize) {
62        (**self).dimensions()
63    }
64    fn is_sparse(&self) -> bool {
65        (**self).is_sparse()
66    }
67    fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
68        (**self).materialise()
69    }
70    fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
71        (**self).iter_cells()
72    }
73    fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
74        (**self).iter_rows()
75    }
76    fn as_any(&self) -> &dyn Any {
77        (**self).as_any()
78    }
79}
80
81/* ────────────────────── ArgumentHandle helpers ───────────────────── */
82
83pub type CowValue<'a> = Cow<'a, LiteralValue>;
84
85#[derive(Debug, Clone)]
86pub enum CalcValue<'a> {
87    Scalar(LiteralValue),
88    Range(RangeView<'a>),
89}
90
91impl<'a> CalcValue<'a> {
92    pub fn into_literal(self) -> LiteralValue {
93        match self {
94            CalcValue::Scalar(s) => s,
95            CalcValue::Range(rv) => {
96                let (rows, cols) = rv.dims();
97                if rows == 1 && cols == 1 {
98                    rv.get_cell(0, 0)
99                } else {
100                    let mut data = Vec::with_capacity(rows);
101                    // Use a simple materialization loop for now
102                    // In the future, this should be optimized.
103                    let _ = rv.for_each_row(&mut |row| {
104                        data.push(row.to_vec());
105                        Ok(())
106                    });
107                    LiteralValue::Array(data)
108                }
109            }
110        }
111    }
112
113    pub fn as_scalar(&self) -> Option<&LiteralValue> {
114        match self {
115            CalcValue::Scalar(s) => Some(s),
116            _ => None,
117        }
118    }
119
120    pub fn as_range(&self) -> Option<&RangeView<'a>> {
121        match self {
122            CalcValue::Range(r) => Some(r),
123            _ => None,
124        }
125    }
126
127    pub fn into_owned(self) -> LiteralValue {
128        self.into_literal()
129    }
130}
131
132impl From<CalcValue<'_>> for LiteralValue {
133    fn from(val: CalcValue<'_>) -> Self {
134        val.into_literal()
135    }
136}
137
138impl<'a> PartialEq<LiteralValue> for CalcValue<'a> {
139    fn eq(&self, other: &LiteralValue) -> bool {
140        match self {
141            CalcValue::Scalar(s) => s == other,
142            CalcValue::Range(rv) => match other {
143                LiteralValue::Array(arr) => {
144                    let (rows, cols) = rv.dims();
145                    if arr.len() != rows {
146                        return false;
147                    }
148                    for (r, row) in arr.iter().enumerate() {
149                        if row.len() != cols {
150                            return false;
151                        }
152                        for (c, cell) in row.iter().enumerate() {
153                            if &rv.get_cell(r, c) != cell {
154                                return false;
155                            }
156                        }
157                    }
158                    true
159                }
160                _ => {
161                    let (rows, cols) = rv.dims();
162                    rows == 1 && cols == 1 && &rv.get_cell(0, 0) == other
163                }
164            },
165        }
166    }
167}
168
169impl<'a> PartialEq<CalcValue<'a>> for LiteralValue {
170    fn eq(&self, other: &CalcValue<'a>) -> bool {
171        other == self
172    }
173}
174
175pub enum EvaluatedArg<'a> {
176    LiteralValue(CowValue<'a>),
177    Range(Box<dyn Range>),
178}
179
180enum ArgumentExpr<'a> {
181    Ast(&'a ASTNode),
182    Arena {
183        id: crate::engine::arena::AstNodeId,
184        data_store: &'a crate::engine::arena::DataStore,
185        sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
186    },
187}
188
189pub struct ArgumentHandle<'a, 'b> {
190    expr: ArgumentExpr<'a>,
191    interp: &'a Interpreter<'b>,
192    cached_ast: std::cell::OnceCell<ASTNode>,
193    cached_ref: std::cell::OnceCell<ReferenceType>,
194}
195
196impl<'a, 'b> ArgumentHandle<'a, 'b> {
197    pub(crate) fn new(node: &'a ASTNode, interp: &'a Interpreter<'b>) -> Self {
198        Self {
199            expr: ArgumentExpr::Ast(node),
200            interp,
201            cached_ast: std::cell::OnceCell::new(),
202            cached_ref: std::cell::OnceCell::new(),
203        }
204    }
205
206    pub(crate) fn new_arena(
207        id: crate::engine::arena::AstNodeId,
208        interp: &'a Interpreter<'b>,
209        data_store: &'a crate::engine::arena::DataStore,
210        sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
211    ) -> Self {
212        Self {
213            expr: ArgumentExpr::Arena {
214                id,
215                data_store,
216                sheet_registry,
217            },
218            interp,
219            cached_ast: std::cell::OnceCell::new(),
220            cached_ref: std::cell::OnceCell::new(),
221        }
222    }
223
224    pub fn value(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
225        match &self.expr {
226            ArgumentExpr::Ast(node) => {
227                if let ASTNodeType::Literal(ref v) = node.node_type {
228                    return Ok(crate::traits::CalcValue::Scalar(v.clone()));
229                }
230                self.interp.evaluate_ast(node)
231            }
232            ArgumentExpr::Arena {
233                id,
234                data_store,
235                sheet_registry,
236            } => self
237                .interp
238                .evaluate_arena_ast(*id, data_store, sheet_registry),
239        }
240    }
241
242    pub fn inline_array_literal(&self) -> Result<Option<Vec<Vec<LiteralValue>>>, ExcelError> {
243        match &self.expr {
244            ArgumentExpr::Ast(node) => match &node.node_type {
245                ASTNodeType::Literal(LiteralValue::Array(arr)) => Ok(Some(arr.clone())),
246                _ => Ok(None),
247            },
248            ArgumentExpr::Arena {
249                id,
250                data_store,
251                sheet_registry,
252            } => {
253                let node = data_store.get_node(*id).ok_or_else(|| {
254                    ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
255                })?;
256                match node {
257                    crate::engine::arena::AstNodeData::Literal(vref) => {
258                        match data_store.retrieve_value(*vref) {
259                            LiteralValue::Array(arr) => Ok(Some(arr)),
260                            _ => Ok(None),
261                        }
262                    }
263                    _ => {
264                        // preserve existing behavior: only a literal array (not a computed array)
265                        // is treated as "inline array literal".
266                        let _ = sheet_registry;
267                        Ok(None)
268                    }
269                }
270            }
271        }
272    }
273
274    fn reference_for_eval(&self) -> Result<ReferenceType, ExcelError> {
275        match &self.expr {
276            ArgumentExpr::Ast(node) => match &node.node_type {
277                ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
278                ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
279                    self.interp.evaluate_ast_as_reference(node)
280                }
281                _ => Err(ExcelError::new(ExcelErrorKind::Ref)
282                    .with_message("Expected a reference (by-ref argument)")),
283            },
284            ArgumentExpr::Arena {
285                id,
286                data_store,
287                sheet_registry,
288            } => {
289                let node = data_store.get_node(*id).ok_or_else(|| {
290                    ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
291                })?;
292                match node {
293                    crate::engine::arena::AstNodeData::Reference { ref_type, .. } => Ok(
294                        data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry)
295                    ),
296                    crate::engine::arena::AstNodeData::Function { .. }
297                    | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
298                        .interp
299                        .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
300                    _ => Err(ExcelError::new(ExcelErrorKind::Ref)
301                        .with_message("Expected a reference (by-ref argument)")),
302                }
303            }
304        }
305    }
306
307    pub fn range(&self) -> Result<Box<dyn Range>, ExcelError> {
308        match &self.expr {
309            ArgumentExpr::Ast(node) => match &node.node_type {
310                ASTNodeType::Reference { reference, .. } => {
311                    // Prefer RangeView since it has explicit current-sheet context.
312                    let view = self
313                        .interp
314                        .context
315                        .resolve_range_view(reference, self.interp.current_sheet())?;
316                    let (rows, cols) = view.dims();
317                    let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
318                    view.for_each_row(&mut |row| {
319                        let row_data: Vec<LiteralValue> = (0..cols)
320                            .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
321                            .collect();
322                        out.push(row_data);
323                        Ok(())
324                    })?;
325                    Ok(Box::new(InMemoryRange::new(out)))
326                }
327                ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
328                    let reference = self.reference_for_eval()?;
329                    let view = self
330                        .interp
331                        .context
332                        .resolve_range_view(&reference, self.interp.current_sheet())?;
333                    let (rows, cols) = view.dims();
334                    let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
335                    view.for_each_row(&mut |row| {
336                        let row_data: Vec<LiteralValue> = (0..cols)
337                            .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
338                            .collect();
339                        out.push(row_data);
340                        Ok(())
341                    })?;
342                    Ok(Box::new(InMemoryRange::new(out)))
343                }
344                ASTNodeType::Array(rows) => {
345                    let mut materialized = Vec::new();
346                    for row in rows {
347                        let mut materialized_row = Vec::new();
348                        for cell in row {
349                            materialized_row.push(self.interp.evaluate_ast(cell)?.into_literal());
350                        }
351                        materialized.push(materialized_row);
352                    }
353                    Ok(Box::new(InMemoryRange::new(materialized)))
354                }
355                _ => Err(ExcelError::new(ExcelErrorKind::Ref)
356                    .with_message(format!("Expected a range, got {:?}", node.node_type))),
357            },
358            ArgumentExpr::Arena { id, data_store, .. } => {
359                let node = data_store.get_node(*id).ok_or_else(|| {
360                    ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
361                })?;
362
363                match node {
364                    crate::engine::arena::AstNodeData::Reference { .. }
365                    | crate::engine::arena::AstNodeData::Function { .. }
366                    | crate::engine::arena::AstNodeData::BinaryOp { .. } => {
367                        let reference = self.reference_for_eval()?;
368                        let view = self
369                            .interp
370                            .context
371                            .resolve_range_view(&reference, self.interp.current_sheet())?;
372                        let (rows, cols) = view.dims();
373                        let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
374                        view.for_each_row(&mut |row| {
375                            let row_data: Vec<LiteralValue> = (0..cols)
376                                .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
377                                .collect();
378                            out.push(row_data);
379                            Ok(())
380                        })?;
381                        Ok(Box::new(InMemoryRange::new(out)))
382                    }
383                    crate::engine::arena::AstNodeData::Array { .. } => {
384                        let (rows, cols, elements) =
385                            data_store.get_array_elems(*id).ok_or_else(|| {
386                                ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
387                            })?;
388                        let rows_usize = rows as usize;
389                        let cols_usize = cols as usize;
390                        let mut materialized: Vec<Vec<LiteralValue>> =
391                            Vec::with_capacity(rows_usize);
392                        for r in 0..rows_usize {
393                            let mut row = Vec::with_capacity(cols_usize);
394                            for c in 0..cols_usize {
395                                let idx = r * cols_usize + c;
396                                let elem_id = elements.get(idx).copied().ok_or_else(|| {
397                                    ExcelError::new(ExcelErrorKind::Value)
398                                        .with_message("Invalid array")
399                                })?;
400                                let v = self.interp.evaluate_arena_ast(
401                                    elem_id,
402                                    data_store,
403                                    self.sheet_registry(),
404                                )?;
405                                row.push(v.into_literal());
406                            }
407                            materialized.push(row);
408                        }
409                        Ok(Box::new(InMemoryRange::new(materialized)))
410                    }
411                    _ => Err(ExcelError::new(ExcelErrorKind::Ref)
412                        .with_message("Argument cannot be interpreted as a range.")),
413                }
414            }
415        }
416    }
417
418    fn sheet_registry(&self) -> &crate::engine::sheet_registry::SheetRegistry {
419        match &self.expr {
420            ArgumentExpr::Ast(_) => {
421                // Not needed; used only in arena flows.
422                unreachable!("sheet_registry only used for arena ArgumentHandle")
423            }
424            ArgumentExpr::Arena { sheet_registry, .. } => sheet_registry,
425        }
426    }
427
428    /// Resolve as a RangeView (Phase 2 API). Only supports reference arguments.
429    pub fn range_view(&self) -> Result<RangeView<'b>, ExcelError> {
430        match &self.expr {
431            ArgumentExpr::Ast(node) => match &node.node_type {
432                ASTNodeType::Reference { reference, .. } => self
433                    .interp
434                    .context
435                    .resolve_range_view(reference, self.interp.current_sheet())
436                    .map(|v| v.with_cancel_token(self.interp.context.cancellation_token())),
437                // Treat array literals (LiteralValue::Array) as ranges for RangeView APIs
438                ASTNodeType::Literal(formualizer_common::LiteralValue::Array(arr)) => Ok(
439                    RangeView::from_owned_rows(arr.clone(), self.interp.context.date_system())
440                        .with_cancel_token(self.interp.context.cancellation_token()),
441                ),
442                ASTNodeType::Array(rows) => {
443                    let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows.len());
444                    for r in rows {
445                        let mut row_vals = Vec::with_capacity(r.len());
446                        for cell in r {
447                            row_vals.push(self.interp.evaluate_ast(cell)?.into_literal());
448                        }
449                        out.push(row_vals);
450                    }
451                    Ok(
452                        RangeView::from_owned_rows(out, self.interp.context.date_system())
453                            .with_cancel_token(self.interp.context.cancellation_token()),
454                    )
455                }
456                ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
457                    let reference = self.reference_for_eval()?;
458                    self.interp
459                        .context
460                        .resolve_range_view(&reference, self.interp.current_sheet())
461                        .map(|v| v.with_cancel_token(self.interp.context.cancellation_token()))
462                }
463                _ => Err(ExcelError::new(ExcelErrorKind::Ref)
464                    .with_message("Argument cannot be interpreted as a range.")),
465            },
466            ArgumentExpr::Arena {
467                id,
468                data_store,
469                sheet_registry,
470            } => {
471                let node = data_store.get_node(*id).ok_or_else(|| {
472                    ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
473                })?;
474
475                match node {
476                    crate::engine::arena::AstNodeData::Reference { .. }
477                    | crate::engine::arena::AstNodeData::Function { .. }
478                    | crate::engine::arena::AstNodeData::BinaryOp { .. } => {
479                        let reference = self.reference_for_eval()?;
480                        self.interp
481                            .context
482                            .resolve_range_view(&reference, self.interp.current_sheet())
483                            .map(|v| v.with_cancel_token(self.interp.context.cancellation_token()))
484                    }
485                    crate::engine::arena::AstNodeData::Literal(vref) => {
486                        match data_store.retrieve_value(*vref) {
487                            LiteralValue::Array(arr) => Ok(RangeView::from_owned_rows(
488                                arr,
489                                self.interp.context.date_system(),
490                            )
491                            .with_cancel_token(self.interp.context.cancellation_token())),
492                            _ => Err(ExcelError::new(ExcelErrorKind::Ref)
493                                .with_message("Argument cannot be interpreted as a range.")),
494                        }
495                    }
496                    crate::engine::arena::AstNodeData::Array { .. } => {
497                        let (rows, cols, elements) =
498                            data_store.get_array_elems(*id).ok_or_else(|| {
499                                ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
500                            })?;
501
502                        let rows_usize = rows as usize;
503                        let cols_usize = cols as usize;
504                        let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
505                        for r in 0..rows_usize {
506                            let mut row = Vec::with_capacity(cols_usize);
507                            for c in 0..cols_usize {
508                                let idx = r * cols_usize + c;
509                                let elem_id = elements.get(idx).copied().ok_or_else(|| {
510                                    ExcelError::new(ExcelErrorKind::Value)
511                                        .with_message("Invalid array")
512                                })?;
513                                let v = self.interp.evaluate_arena_ast(
514                                    elem_id,
515                                    data_store,
516                                    sheet_registry,
517                                )?;
518                                row.push(v.into_literal());
519                            }
520                            out.push(row);
521                        }
522                        Ok(
523                            RangeView::from_owned_rows(out, self.interp.context.date_system())
524                                .with_cancel_token(self.interp.context.cancellation_token()),
525                        )
526                    }
527                    _ => Err(ExcelError::new(ExcelErrorKind::Ref)
528                        .with_message("Argument cannot be interpreted as a range.")),
529                }
530            }
531        }
532    }
533
534    pub fn value_or_range(&self) -> Result<EvaluatedArg<'_>, ExcelError> {
535        self.range().map(EvaluatedArg::Range).or_else(|_| {
536            self.value()
537                .map(|cv| EvaluatedArg::LiteralValue(Cow::Owned(cv.into_literal())))
538        })
539    }
540
541    /// Lazily iterate values for this argument in row-major expansion order.
542    /// - Reference: stream via RangeView (row-major)
543    /// - Array literal: evaluate each element lazily per cell
544    /// - Scalar/other expressions: a single value
545    pub fn lazy_values_owned(
546        &'a self,
547    ) -> Result<Box<dyn Iterator<Item = LiteralValue> + 'a>, ExcelError> {
548        match &self.expr {
549            ArgumentExpr::Ast(node) => match &node.node_type {
550                ASTNodeType::Reference { .. } => {
551                    let view = self.range_view()?;
552                    let mut values: Vec<LiteralValue> = Vec::new();
553                    view.for_each_cell(&mut |v| {
554                        values.push(v.clone());
555                        Ok(())
556                    })?;
557                    Ok(Box::new(values.into_iter()))
558                }
559                ASTNodeType::Array(rows) => {
560                    struct ArrayEvalIter<'a, 'b> {
561                        rows: &'a [Vec<ASTNode>],
562                        r: usize,
563                        c: usize,
564                        interp: &'a Interpreter<'b>,
565                    }
566                    impl<'a, 'b> Iterator for ArrayEvalIter<'a, 'b> {
567                        type Item = LiteralValue;
568                        fn next(&mut self) -> Option<Self::Item> {
569                            if self.rows.is_empty() {
570                                return None;
571                            }
572                            let rows = self.rows;
573                            let mut r = self.r;
574                            let mut c = self.c;
575                            if r >= rows.len() {
576                                return None;
577                            }
578                            let node = &rows[r][c];
579                            // advance indices
580                            c += 1;
581                            if c >= rows[r].len() {
582                                r += 1;
583                                c = 0;
584                            }
585                            self.r = r;
586                            self.c = c;
587                            match self.interp.evaluate_ast(node) {
588                                Ok(cv) => Some(cv.into_literal()),
589                                Err(e) => Some(LiteralValue::Error(e)),
590                            }
591                        }
592                    }
593                    let it = ArrayEvalIter {
594                        rows,
595                        r: 0,
596                        c: 0,
597                        interp: self.interp,
598                    };
599                    Ok(Box::new(it))
600                }
601                _ => {
602                    // Single value expression
603                    let v = self.value()?.into_literal();
604                    Ok(Box::new(std::iter::once(v)))
605                }
606            },
607            ArgumentExpr::Arena {
608                id,
609                data_store,
610                sheet_registry,
611            } => {
612                let node = data_store.get_node(*id).ok_or_else(|| {
613                    ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
614                })?;
615
616                match node {
617                    crate::engine::arena::AstNodeData::Reference { .. } => {
618                        let view = self.range_view()?;
619                        let mut values: Vec<LiteralValue> = Vec::new();
620                        view.for_each_cell(&mut |v| {
621                            values.push(v.clone());
622                            Ok(())
623                        })?;
624                        Ok(Box::new(values.into_iter()))
625                    }
626                    crate::engine::arena::AstNodeData::Array { .. } => {
627                        let (rows, cols, elements) =
628                            data_store.get_array_elems(*id).ok_or_else(|| {
629                                ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
630                            })?;
631
632                        struct ArenaArrayEvalIter<'a, 'b> {
633                            elements: &'a [crate::engine::arena::AstNodeId],
634                            idx: usize,
635                            interp: &'a Interpreter<'b>,
636                            data_store: &'a crate::engine::arena::DataStore,
637                            sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
638                        }
639
640                        impl<'a, 'b> Iterator for ArenaArrayEvalIter<'a, 'b> {
641                            type Item = LiteralValue;
642
643                            fn next(&mut self) -> Option<Self::Item> {
644                                let id = self.elements.get(self.idx).copied()?;
645                                self.idx += 1;
646                                match self.interp.evaluate_arena_ast(
647                                    id,
648                                    self.data_store,
649                                    self.sheet_registry,
650                                ) {
651                                    Ok(cv) => Some(cv.into_literal()),
652                                    Err(e) => Some(LiteralValue::Error(e)),
653                                }
654                            }
655                        }
656
657                        let _ = (rows, cols);
658                        let it = ArenaArrayEvalIter {
659                            elements,
660                            idx: 0,
661                            interp: self.interp,
662                            data_store,
663                            sheet_registry,
664                        };
665                        Ok(Box::new(it))
666                    }
667                    _ => {
668                        let v = self
669                            .interp
670                            .evaluate_arena_ast(*id, data_store, sheet_registry)?;
671                        Ok(Box::new(std::iter::once(v.into_literal())))
672                    }
673                }
674            }
675        }
676    }
677
678    pub fn ast(&self) -> &ASTNode {
679        match &self.expr {
680            ArgumentExpr::Ast(node) => node,
681            ArgumentExpr::Arena {
682                id,
683                data_store,
684                sheet_registry,
685            } => self.cached_ast.get_or_init(|| {
686                data_store
687                    .retrieve_ast(*id, sheet_registry)
688                    .unwrap_or_else(|| ASTNode {
689                        node_type: ASTNodeType::Literal(LiteralValue::Error(
690                            ExcelError::new(ExcelErrorKind::Value)
691                                .with_message("Missing formula AST"),
692                        )),
693                        source_token: None,
694                        contains_volatile: false,
695                    })
696            }),
697        }
698    }
699
700    /// Returns the raw reference from the AST when this argument is a reference.
701    /// This does not evaluate the reference or materialize values.
702    pub fn as_reference(&self) -> Result<&ReferenceType, ExcelError> {
703        match &self.expr {
704            ArgumentExpr::Ast(node) => match &node.node_type {
705                ASTNodeType::Reference { reference, .. } => Ok(reference),
706                _ => Err(ExcelError::new(ExcelErrorKind::Ref)
707                    .with_message("Expected a reference (by-ref argument)")),
708            },
709            ArgumentExpr::Arena { .. } => {
710                let reference = self.reference_for_eval()?;
711                Ok(self.cached_ref.get_or_init(|| reference))
712            }
713        }
714    }
715
716    /// Returns a `ReferenceType` if this argument is a reference or a function that
717    /// can yield a reference via `eval_reference`. Materializes no values.
718    pub fn as_reference_or_eval(&self) -> Result<ReferenceType, ExcelError> {
719        match &self.expr {
720            ArgumentExpr::Ast(node) => match &node.node_type {
721                ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
722                ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
723                    self.interp.evaluate_ast_as_reference(node)
724                }
725                _ => Err(ExcelError::new(ExcelErrorKind::Ref)
726                    .with_message("Argument is not a reference")),
727            },
728            ArgumentExpr::Arena {
729                id,
730                data_store,
731                sheet_registry,
732            } => {
733                let node = data_store.get_node(*id).ok_or_else(|| {
734                    ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
735                })?;
736
737                match node {
738                    crate::engine::arena::AstNodeData::Reference { .. } => {
739                        self.reference_for_eval()
740                    }
741                    crate::engine::arena::AstNodeData::Function { .. }
742                    | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
743                        .interp
744                        .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
745                    _ => Err(ExcelError::new(ExcelErrorKind::Ref)
746                        .with_message("Argument is not a reference")),
747                }
748            }
749        }
750    }
751
752    /* tiny validator helper for macro */
753    pub fn matches_kind(&self, k: formualizer_common::ArgKind) -> Result<bool, ExcelError> {
754        Ok(match k {
755            formualizer_common::ArgKind::Any => true,
756            formualizer_common::ArgKind::Range => self.range().is_ok(),
757            formualizer_common::ArgKind::Number => matches!(
758                self.value()?.into_literal(),
759                LiteralValue::Number(_) | LiteralValue::Int(_)
760            ),
761            formualizer_common::ArgKind::Text => {
762                matches!(self.value()?.into_literal(), LiteralValue::Text(_))
763            }
764            formualizer_common::ArgKind::Logical => {
765                matches!(self.value()?.into_literal(), LiteralValue::Boolean(_))
766            }
767        })
768    }
769}
770
771/* simple Vec-backed range */
772#[derive(Debug, Clone)]
773pub struct InMemoryRange {
774    data: Vec<Vec<LiteralValue>>,
775}
776impl InMemoryRange {
777    pub fn new(d: Vec<Vec<LiteralValue>>) -> Self {
778        Self { data: d }
779    }
780}
781impl Range for InMemoryRange {
782    fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
783        Ok(self
784            .data
785            .get(r)
786            .and_then(|row| row.get(c))
787            .cloned()
788            .unwrap_or(LiteralValue::Empty))
789    }
790    fn dimensions(&self) -> (usize, usize) {
791        (self.data.len(), self.data.first().map_or(0, |r| r.len()))
792    }
793    fn as_any(&self) -> &dyn Any {
794        self
795    }
796}
797
798/* ───────────────────────── Table abstraction ───────────────────────── */
799
800pub trait Table: Debug + Send + Sync {
801    fn get_cell(&self, row: usize, column: &str) -> Result<LiteralValue, ExcelError>;
802    fn get_column(&self, column: &str) -> Result<Box<dyn Range>, ExcelError>;
803    /// Ordered list of column names
804    fn columns(&self) -> Vec<String> {
805        vec![]
806    }
807    /// Number of data rows (excluding headers/totals)
808    fn data_height(&self) -> usize {
809        0
810    }
811    /// Whether the table has a header row
812    fn has_headers(&self) -> bool {
813        false
814    }
815    /// Whether the table has a totals row
816    fn has_totals(&self) -> bool {
817        false
818    }
819    /// Headers row as a 1xW range
820    fn headers_row(&self) -> Option<Box<dyn Range>> {
821        None
822    }
823    /// Totals row as a 1xW range, if present
824    fn totals_row(&self) -> Option<Box<dyn Range>> {
825        None
826    }
827    /// Entire data body as HxW range
828    fn data_body(&self) -> Option<Box<dyn Range>> {
829        None
830    }
831    fn clone_box(&self) -> Box<dyn Table>;
832}
833impl Table for Box<dyn Table> {
834    fn get_cell(&self, r: usize, c: &str) -> Result<LiteralValue, ExcelError> {
835        (**self).get_cell(r, c)
836    }
837    fn get_column(&self, c: &str) -> Result<Box<dyn Range>, ExcelError> {
838        (**self).get_column(c)
839    }
840    fn columns(&self) -> Vec<String> {
841        (**self).columns()
842    }
843    fn data_height(&self) -> usize {
844        (**self).data_height()
845    }
846    fn has_headers(&self) -> bool {
847        (**self).has_headers()
848    }
849    fn has_totals(&self) -> bool {
850        (**self).has_totals()
851    }
852    fn headers_row(&self) -> Option<Box<dyn Range>> {
853        (**self).headers_row()
854    }
855    fn totals_row(&self) -> Option<Box<dyn Range>> {
856        (**self).totals_row()
857    }
858    fn data_body(&self) -> Option<Box<dyn Range>> {
859        (**self).data_body()
860    }
861    fn clone_box(&self) -> Box<dyn Table> {
862        (**self).clone_box()
863    }
864}
865
866/* ─────────────────────── Resolver super-trait ─────────────────────── */
867
868pub trait ReferenceResolver: Send + Sync {
869    fn resolve_cell_reference(
870        &self,
871        sheet: Option<&str>,
872        row: u32,
873        col: u32,
874    ) -> Result<LiteralValue, ExcelError>;
875}
876pub trait RangeResolver: Send + Sync {
877    fn resolve_range_reference(
878        &self,
879        sheet: Option<&str>,
880        sr: Option<u32>,
881        sc: Option<u32>,
882        er: Option<u32>,
883        ec: Option<u32>,
884    ) -> Result<Box<dyn Range>, ExcelError>;
885}
886pub trait NamedRangeResolver: Send + Sync {
887    fn resolve_named_range_reference(
888        &self,
889        name: &str,
890    ) -> Result<Vec<Vec<LiteralValue>>, ExcelError>;
891}
892pub trait TableResolver: Send + Sync {
893    fn resolve_table_reference(
894        &self,
895        tref: &formualizer_parse::parser::TableReference,
896    ) -> Result<Box<dyn Table>, ExcelError>;
897}
898
899pub trait SourceResolver: Send + Sync {
900    fn source_scalar_version(&self, _name: &str) -> Option<u64> {
901        None
902    }
903
904    fn resolve_source_scalar(&self, name: &str) -> Result<LiteralValue, ExcelError> {
905        Err(ExcelError::new(ExcelErrorKind::NImpl)
906            .with_message(format!("Source scalar not supported: {name}")))
907    }
908
909    fn source_table_version(&self, _name: &str) -> Option<u64> {
910        None
911    }
912
913    fn resolve_source_table(&self, name: &str) -> Result<Box<dyn Table>, ExcelError> {
914        Err(ExcelError::new(ExcelErrorKind::NImpl)
915            .with_message(format!("Source table not supported: {name}")))
916    }
917}
918
919pub trait Resolver: ReferenceResolver + RangeResolver + NamedRangeResolver + TableResolver {
920    fn resolve_range_like(&self, r: &ReferenceType) -> Result<Box<dyn Range>, ExcelError> {
921        match r {
922            ReferenceType::Range {
923                sheet,
924                start_row,
925                start_col,
926                end_row,
927                end_col,
928                ..
929            } => self.resolve_range_reference(
930                sheet.as_deref(),
931                *start_row,
932                *start_col,
933                *end_row,
934                *end_col,
935            ),
936            ReferenceType::External(_) => Err(ExcelError::new(ExcelErrorKind::NImpl)
937                .with_message("External references are not supported by Resolver".to_string())),
938            ReferenceType::Table(tref) => {
939                let t = self.resolve_table_reference(tref)?;
940                match &tref.specifier {
941                    Some(TableSpecifier::Column(c)) => t.get_column(c),
942                    Some(TableSpecifier::ColumnRange(start, end)) => {
943                        // Build a rectangular range from start..=end columns in table order
944                        let cols = t.columns();
945                        let start_idx = cols.iter().position(|n| n.eq_ignore_ascii_case(start));
946                        let end_idx = cols.iter().position(|n| n.eq_ignore_ascii_case(end));
947                        if let (Some(mut si), Some(mut ei)) = (start_idx, end_idx) {
948                            if si > ei {
949                                std::mem::swap(&mut si, &mut ei);
950                            }
951                            // Materialize by stacking columns into a 2D array
952                            let h = t.data_height();
953                            let w = ei - si + 1;
954                            let mut rows = vec![vec![LiteralValue::Empty; w]; h];
955                            for (offset, ci) in (si..=ei).enumerate() {
956                                let cname = &cols[ci];
957                                let col_range = t.get_column(cname)?;
958                                let (rh, _) = col_range.dimensions();
959                                for (r, row) in rows.iter_mut().enumerate().take(h.min(rh)) {
960                                    row[offset] = col_range.get(r, 0)?;
961                                }
962                            }
963                            Ok(Box::new(InMemoryRange::new(rows)))
964                        } else {
965                            Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
966                                "Column range refers to unknown column(s)".to_string(),
967                            ))
968                        }
969                    }
970                    Some(TableSpecifier::SpecialItem(
971                        formualizer_parse::parser::SpecialItem::Headers,
972                    )) => {
973                        if let Some(h) = t.headers_row() {
974                            Ok(h)
975                        } else {
976                            Ok(Box::new(InMemoryRange::new(vec![])))
977                        }
978                    }
979                    Some(TableSpecifier::SpecialItem(
980                        formualizer_parse::parser::SpecialItem::Totals,
981                    )) => {
982                        if let Some(tr) = t.totals_row() {
983                            Ok(tr)
984                        } else {
985                            Ok(Box::new(InMemoryRange::new(vec![])))
986                        }
987                    }
988                    Some(TableSpecifier::SpecialItem(
989                        formualizer_parse::parser::SpecialItem::Data,
990                    )) => {
991                        if let Some(body) = t.data_body() {
992                            Ok(body)
993                        } else {
994                            Ok(Box::new(InMemoryRange::new(vec![])))
995                        }
996                    }
997                    Some(TableSpecifier::SpecialItem(
998                        formualizer_parse::parser::SpecialItem::All,
999                    )) => {
1000                        // Equivalent to TableSpecifier::All handling
1001                        let mut out: Vec<Vec<LiteralValue>> = Vec::new();
1002                        if let Some(h) = t.headers_row() {
1003                            out.extend(h.iter_rows());
1004                        }
1005                        if let Some(body) = t.data_body() {
1006                            out.extend(body.iter_rows());
1007                        }
1008                        if let Some(tr) = t.totals_row() {
1009                            out.extend(tr.iter_rows());
1010                        }
1011                        Ok(Box::new(InMemoryRange::new(out)))
1012                    }
1013                    Some(TableSpecifier::SpecialItem(
1014                        formualizer_parse::parser::SpecialItem::ThisRow,
1015                    )) => Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1016                        "@ (This Row) requires table-aware context; not yet supported".to_string(),
1017                    )),
1018                    Some(TableSpecifier::All) => {
1019                        // Concatenate headers (if any), data, totals (if any)
1020                        let mut out: Vec<Vec<LiteralValue>> = Vec::new();
1021                        if let Some(h) = t.headers_row() {
1022                            out.extend(h.iter_rows());
1023                        }
1024                        if let Some(body) = t.data_body() {
1025                            out.extend(body.iter_rows());
1026                        }
1027                        if let Some(tr) = t.totals_row() {
1028                            out.extend(tr.iter_rows());
1029                        }
1030                        Ok(Box::new(InMemoryRange::new(out)))
1031                    }
1032                    Some(TableSpecifier::Data) => {
1033                        if let Some(body) = t.data_body() {
1034                            Ok(body)
1035                        } else {
1036                            Ok(Box::new(InMemoryRange::new(vec![])))
1037                        }
1038                    }
1039                    // Defer complex combinations and row selectors for tranche 1
1040                    Some(TableSpecifier::Combination(_)) => Err(ExcelError::new(
1041                        ExcelErrorKind::NImpl,
1042                    )
1043                    .with_message("Complex structured references not yet supported".to_string())),
1044                    Some(TableSpecifier::Row(_)) => Err(ExcelError::new(ExcelErrorKind::NImpl)
1045                        .with_message("Row selectors (@/index) not yet supported".to_string())),
1046                    Some(TableSpecifier::Headers) | Some(TableSpecifier::Totals) => {
1047                        Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1048                            "Legacy Headers/Totals variants not used; use SpecialItem".to_string(),
1049                        ))
1050                    }
1051                    None => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1052                        "Table reference without specifier is unsupported".to_string(),
1053                    )),
1054                }
1055            }
1056            ReferenceType::NamedRange(n) => {
1057                let v = self.resolve_named_range_reference(n)?;
1058                Ok(Box::new(InMemoryRange::new(v)))
1059            }
1060            ReferenceType::Cell {
1061                sheet, row, col, ..
1062            } => {
1063                let v = self.resolve_cell_reference(sheet.as_deref(), *row, *col)?;
1064                Ok(Box::new(InMemoryRange::new(vec![vec![v]])))
1065            }
1066        }
1067    }
1068}
1069
1070/* ───────────────────── EvaluationContext = Resolver+Fns ───────────── */
1071
1072pub trait FunctionProvider: Send + Sync {
1073    fn get_function(&self, ns: &str, name: &str) -> Option<Arc<dyn Function>>;
1074}
1075
1076pub trait EvaluationContext: Resolver + FunctionProvider + SourceResolver {
1077    /// Get access to the shared thread pool for parallel evaluation
1078    /// Returns None if parallel evaluation is disabled or unavailable
1079    fn thread_pool(&self) -> Option<&Arc<rayon::ThreadPool>> {
1080        None
1081    }
1082
1083    /// Optional cancellation token. When Some, long-running operations should periodically abort.
1084    fn cancellation_token(&self) -> Option<Arc<std::sync::atomic::AtomicBool>> {
1085        None
1086    }
1087
1088    /// Optional chunk size hint for streaming visitors.
1089    fn chunk_hint(&self) -> Option<usize> {
1090        None
1091    }
1092
1093    /// Resolve a reference into a `RangeView` with clear bounds.
1094    /// Implementations should resolve un/partially bounded references using used-region.
1095    fn resolve_range_view<'c>(
1096        &'c self,
1097        _reference: &ReferenceType,
1098        _current_sheet: &str,
1099    ) -> Result<RangeView<'c>, ExcelError> {
1100        Err(ExcelError::new(ExcelErrorKind::NImpl))
1101    }
1102
1103    /// Locale provider: invariant by default
1104    fn locale(&self) -> crate::locale::Locale {
1105        crate::locale::Locale::invariant()
1106    }
1107
1108    /// Timezone provider for date/time functions
1109    /// Default: Local (Excel-compatible behavior)
1110    /// Functions should use local timezone when this returns Local
1111    fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
1112        // Static default to avoid allocation
1113        static DEFAULT_TZ: std::sync::OnceLock<crate::timezone::TimeZoneSpec> =
1114            std::sync::OnceLock::new();
1115        DEFAULT_TZ.get_or_init(crate::timezone::TimeZoneSpec::default)
1116    }
1117
1118    /// Volatile granularity. Default Always for backwards compatibility.
1119    fn volatile_level(&self) -> VolatileLevel {
1120        VolatileLevel::Always
1121    }
1122
1123    /// A stable workbook seed for RNG composition.
1124    fn workbook_seed(&self) -> u64 {
1125        0xF0F0_D0D0_AAAA_5555
1126    }
1127
1128    /// Recalc epoch that increments on each full recalc when appropriate.
1129    fn recalc_epoch(&self) -> u64 {
1130        0
1131    }
1132
1133    /* ─────────────── Future-proof IO/backends hooks (default no-op) ─────────────── */
1134
1135    /// Optional: Return the min/max used rows for a set of columns on a sheet.
1136    /// When None, the backend does not provide used-region hints.
1137    fn used_rows_for_columns(
1138        &self,
1139        _sheet: &str,
1140        _start_col: u32,
1141        _end_col: u32,
1142    ) -> Option<(u32, u32)> {
1143        None
1144    }
1145
1146    /// Optional: Return the min/max used columns for a set of rows on a sheet.
1147    /// When None, the backend does not provide used-region hints.
1148    fn used_cols_for_rows(
1149        &self,
1150        _sheet: &str,
1151        _start_row: u32,
1152        _end_row: u32,
1153    ) -> Option<(u32, u32)> {
1154        None
1155    }
1156
1157    /// Optional: Physical sheet bounds (max rows, max cols) if known.
1158    fn sheet_bounds(&self, _sheet: &str) -> Option<(u32, u32)> {
1159        None
1160    }
1161
1162    /// Monotonic identifier for the current data snapshot; increments on mutation.
1163    fn data_snapshot_id(&self) -> u64 {
1164        0
1165    }
1166
1167    /// Backend capability advertisement for IO/adapters.
1168    fn backend_caps(&self) -> BackendCaps {
1169        BackendCaps::default()
1170    }
1171
1172    // Flats removed
1173
1174    /// Workbook date system selection (1900 vs 1904).
1175    /// Defaults to 1900 for compatibility.
1176    fn date_system(&self) -> crate::engine::DateSystem {
1177        crate::engine::DateSystem::Excel1900
1178    }
1179
1180    /// Optional: Build or fetch a cached boolean mask for a criterion over an Arrow-backed view.
1181    /// Implementations should return None if not supported.
1182    fn build_criteria_mask(
1183        &self,
1184        _view: &RangeView<'_>,
1185        _col_in_view: usize,
1186        _pred: &crate::args::CriteriaPredicate,
1187    ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1188        None
1189    }
1190}
1191
1192/// Minimal backend capability descriptor for planning and adapters.
1193#[derive(Copy, Clone, Debug, Default)]
1194pub struct BackendCaps {
1195    /// Provides lazy access (// TODO REMOVE?)
1196    pub streaming: bool,
1197    /// Can compute used-region for rows/columns
1198    pub used_region: bool,
1199    /// Supports write-back mutations via external sink
1200    pub write: bool,
1201    /// Provides table metadata/streaming beyond basic column access
1202    pub tables: bool,
1203    /// May provide asynchronous/lazy remote streams (reserved)
1204    pub async_stream: bool,
1205}
1206
1207/* ───────────────────── FunctionContext (narrow) ───────────────────── */
1208
1209#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1210pub enum VolatileLevel {
1211    /// Value can change at any edit; seed excludes recalc_epoch by default.
1212    Always,
1213    /// Value changes per recalculation; seed should include recalc_epoch.
1214    OnRecalc,
1215    /// Value changes per open; seed uses only workbook_seed.
1216    OnOpen,
1217}
1218
1219/// Minimal context exposed to functions (no engine/graph APIs)
1220pub trait FunctionContext<'ctx> {
1221    fn locale(&self) -> crate::locale::Locale;
1222    fn timezone(&self) -> &crate::timezone::TimeZoneSpec;
1223    fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>>;
1224    fn cancellation_token(&self) -> Option<Arc<std::sync::atomic::AtomicBool>>;
1225    fn chunk_hint(&self) -> Option<usize>;
1226
1227    /// Current formula sheet name.
1228    fn current_sheet(&self) -> &str;
1229
1230    fn volatile_level(&self) -> VolatileLevel;
1231    fn workbook_seed(&self) -> u64;
1232    fn recalc_epoch(&self) -> u64;
1233    fn current_cell(&self) -> Option<CellRef>;
1234
1235    /// Resolve a reference into a RangeView using the underlying engine context.
1236    fn resolve_range_view(
1237        &self,
1238        _reference: &ReferenceType,
1239        _current_sheet: &str,
1240    ) -> Result<RangeView<'ctx>, ExcelError>;
1241
1242    // Flats removed
1243
1244    /// Deterministic RNG seeded for the current evaluation site and function salt.
1245    fn rng_for_current(&self, fn_salt: u64) -> rand::rngs::SmallRng {
1246        use crate::rng::{compose_seed, small_rng_from_lanes};
1247        let (sheet_id, row, col) = self
1248            .current_cell()
1249            .map(|c| (c.sheet_id as u32, c.coord.row(), c.coord.col()))
1250            .unwrap_or((0, 0, 0));
1251        // Include epoch only for OnRecalc
1252        let epoch = match self.volatile_level() {
1253            VolatileLevel::OnRecalc => self.recalc_epoch(),
1254            _ => 0,
1255        };
1256        let (l0, l1) = compose_seed(self.workbook_seed(), sheet_id, row, col, fn_salt, epoch);
1257        small_rng_from_lanes(l0, l1)
1258    }
1259
1260    /// Workbook date system selection (1900 vs 1904).
1261    fn date_system(&self) -> crate::engine::DateSystem {
1262        crate::engine::DateSystem::Excel1900
1263    }
1264
1265    /// Optional: Build or fetch a cached boolean mask for a criterion over an Arrow-backed view.
1266    /// Returns None if not supported by the underlying context.
1267    fn get_criteria_mask(
1268        &self,
1269        _view: &RangeView<'_>,
1270        _col_in_view: usize,
1271        _pred: &crate::args::CriteriaPredicate,
1272    ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1273        None
1274    }
1275}
1276
1277/// Default adapter that wraps an EvaluationContext and provides the narrow FunctionContext.
1278pub struct DefaultFunctionContext<'a> {
1279    pub base: &'a dyn EvaluationContext,
1280    pub current: Option<CellRef>,
1281    pub current_sheet: &'a str,
1282}
1283
1284impl<'a> DefaultFunctionContext<'a> {
1285    pub fn new(
1286        base: &'a dyn EvaluationContext,
1287        current: Option<CellRef>,
1288        current_sheet: &'a str,
1289    ) -> Self {
1290        Self {
1291            base,
1292            current,
1293            current_sheet,
1294        }
1295    }
1296
1297    pub fn new_with_sheet(
1298        base: &'a dyn EvaluationContext,
1299        current: Option<CellRef>,
1300        current_sheet: &'a str,
1301    ) -> Self {
1302        Self::new(base, current, current_sheet)
1303    }
1304}
1305
1306impl<'a> FunctionContext<'a> for DefaultFunctionContext<'a> {
1307    fn locale(&self) -> crate::locale::Locale {
1308        self.base.locale()
1309    }
1310
1311    fn current_sheet(&self) -> &str {
1312        self.current_sheet
1313    }
1314    fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
1315        self.base.timezone()
1316    }
1317    fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>> {
1318        self.base.thread_pool()
1319    }
1320    fn cancellation_token(&self) -> Option<Arc<std::sync::atomic::AtomicBool>> {
1321        self.base.cancellation_token()
1322    }
1323    fn chunk_hint(&self) -> Option<usize> {
1324        self.base.chunk_hint()
1325    }
1326
1327    fn volatile_level(&self) -> VolatileLevel {
1328        self.base.volatile_level()
1329    }
1330    fn workbook_seed(&self) -> u64 {
1331        self.base.workbook_seed()
1332    }
1333    fn recalc_epoch(&self) -> u64 {
1334        self.base.recalc_epoch()
1335    }
1336    fn current_cell(&self) -> Option<CellRef> {
1337        self.current
1338    }
1339
1340    fn resolve_range_view(
1341        &self,
1342        reference: &ReferenceType,
1343        current_sheet: &str,
1344    ) -> Result<RangeView<'a>, ExcelError> {
1345        self.base.resolve_range_view(reference, current_sheet)
1346    }
1347
1348    // Flats removed
1349
1350    fn date_system(&self) -> crate::engine::DateSystem {
1351        self.base.date_system()
1352    }
1353
1354    fn get_criteria_mask(
1355        &self,
1356        view: &RangeView<'_>,
1357        col_in_view: usize,
1358        pred: &crate::args::CriteriaPredicate,
1359    ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1360        self.base.build_criteria_mask(view, col_in_view, pred)
1361    }
1362}