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