Skip to main content

formualizer_eval/engine/arena/
data_store.rs

1/// Unified data storage for all value types using arenas
2/// Provides conversion between LiteralValue and ValueRef
3use super::array::ArrayArena;
4use super::ast::{AstArena, AstNodeId, CompactRefType, SheetKey};
5use super::error_arena::{ErrorArena, ErrorRef};
6use super::scalar::ScalarArena;
7use super::string_interner::{StringId, StringInterner};
8use super::value_ref::ValueRef;
9use crate::engine::sheet_registry::SheetRegistry;
10use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
11use formualizer_parse::parser::{
12    ASTNode, ASTNodeType, ExternalBookRef, ExternalReference, ReferenceType, TableReference,
13};
14
15/// Centralized data storage using arenas
16#[derive(Debug)]
17pub struct DataStore {
18    /// Scalar values (floats and large integers)
19    scalars: ScalarArena,
20
21    /// String values
22    strings: StringInterner,
23
24    /// Array values
25    arrays: ArrayArena,
26
27    /// AST nodes for formulas
28    asts: AstArena,
29
30    /// Error storage with message preservation
31    errors: ErrorArena,
32}
33
34impl DataStore {
35    pub fn new() -> Self {
36        Self {
37            scalars: ScalarArena::new(),
38            strings: StringInterner::new(),
39            arrays: ArrayArena::new(),
40            asts: AstArena::new(),
41            errors: ErrorArena::new(),
42        }
43    }
44
45    /// Batch store literal values; returns ValueRefs in same order.
46    pub fn store_values_batch<I>(&mut self, values: I) -> Vec<ValueRef>
47    where
48        I: IntoIterator<Item = LiteralValue>,
49    {
50        let iter = values.into_iter();
51        let (lower, _) = iter.size_hint();
52        let mut out = Vec::with_capacity(lower);
53        for v in iter {
54            out.push(self.store_value(v));
55        }
56        out
57    }
58
59    /// Batch store ASTs; returns AstNodeIds in same order.
60    pub fn store_asts_batch<'a, I>(
61        &mut self,
62        asts: I,
63        sheet_registry: &SheetRegistry,
64    ) -> Vec<AstNodeId>
65    where
66        I: IntoIterator<Item = &'a ASTNode>,
67    {
68        let iter = asts.into_iter();
69        let (lower, _) = iter.size_hint();
70        let mut out = Vec::with_capacity(lower);
71        for ast in iter {
72            out.push(self.store_ast(ast, sheet_registry));
73        }
74        out
75    }
76
77    pub fn with_capacity(estimated_cells: usize) -> Self {
78        Self {
79            scalars: ScalarArena::with_capacity(estimated_cells),
80            strings: StringInterner::with_capacity(estimated_cells / 10),
81            arrays: ArrayArena::with_capacity(estimated_cells / 100),
82            asts: AstArena::with_capacity(estimated_cells / 2),
83            errors: ErrorArena::with_capacity(estimated_cells / 20),
84        }
85    }
86
87    /// Store a LiteralValue and return a ValueRef
88    pub fn store_value(&mut self, value: LiteralValue) -> ValueRef {
89        match value {
90            LiteralValue::Empty => ValueRef::empty(),
91
92            LiteralValue::Number(n) => {
93                // Store as float in scalar arena
94                let idx = self.scalars.insert_float(n);
95                ValueRef::number(idx.as_u32())
96            }
97
98            LiteralValue::Text(s) => {
99                let id = self.strings.intern(&s);
100                ValueRef::string(id.as_u32())
101            }
102
103            LiteralValue::Boolean(b) => ValueRef::boolean(b),
104
105            LiteralValue::Error(err) => self.store_error(&err),
106
107            LiteralValue::Array(array) => {
108                // Convert nested array to ValueRefs
109                let rows = array.len() as u32;
110                let cols = array.first().map(|r| r.len()).unwrap_or(0) as u32;
111
112                let elements: Vec<ValueRef> = array
113                    .into_iter()
114                    .flatten()
115                    .map(|v| self.store_value(v))
116                    .collect();
117
118                let array_ref = self.arrays.insert(rows, cols, elements);
119                ValueRef::array(array_ref.as_u32())
120            }
121
122            LiteralValue::DateTime(dt) => {
123                // The arena uses Excel1900 only as a private, symmetric binary
124                // encoding for chrono values. This is not workbook serial
125                // state and the raw float is never exposed to formula logic.
126                let serial = formualizer_common::datetime_to_serial_for(
127                    formualizer_common::DateSystem::Excel1900,
128                    &dt,
129                );
130                let idx = self.scalars.insert_float(serial);
131                ValueRef::date_time(idx.as_u32())
132            }
133
134            LiteralValue::Date(d) => {
135                // Use the same private arena encoding as DateTime values.
136                let dt = d.and_hms_opt(0, 0, 0).unwrap();
137                let serial = formualizer_common::datetime_to_serial_for(
138                    formualizer_common::DateSystem::Excel1900,
139                    &dt,
140                );
141                let idx = self.scalars.insert_float(serial);
142                ValueRef::date_time(idx.as_u32())
143            }
144
145            LiteralValue::Time(t) => {
146                // Store time as fractional day.
147                let fraction = formualizer_common::time_to_fraction(&t);
148                let idx = self.scalars.insert_float(fraction);
149                ValueRef::date_time(idx.as_u32())
150            }
151
152            LiteralValue::Duration(dur) => {
153                // Store as integer seconds (chrono::Duration has num_seconds())
154                let secs = dur.num_seconds();
155                let idx = self.scalars.insert_integer(secs);
156                let raw_index = idx.as_u32() & 0x7FFF_FFFF;
157                ValueRef::duration(raw_index)
158            }
159
160            LiteralValue::Int(i) => {
161                // Try to use small int optimization
162                if let Some(vref) = ValueRef::small_int(i as i32) {
163                    vref
164                } else {
165                    // Store as large integer
166                    let idx = self.scalars.insert_integer(i);
167                    ValueRef::large_int(idx.as_u32())
168                }
169            }
170
171            LiteralValue::Pending => ValueRef::pending(),
172        }
173    }
174
175    /// Retrieve a LiteralValue from a ValueRef
176    pub fn retrieve_value(&self, value_ref: ValueRef) -> LiteralValue {
177        use super::value_ref::ValueType;
178
179        match value_ref.value_type() {
180            ValueType::Empty => LiteralValue::Empty,
181
182            ValueType::SmallInt => {
183                // Small integers are inlined
184                if let Some(i) = value_ref.as_small_int() {
185                    LiteralValue::Int(i as i64)
186                } else {
187                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
188                }
189            }
190
191            ValueType::LargeInt => {
192                if let Some(idx) = value_ref.arena_index() {
193                    let scalar_ref = super::scalar::ScalarRef::from_raw(idx | (1 << 31));
194                    if let Some(i) = self.scalars.get_integer(scalar_ref) {
195                        LiteralValue::Int(i)
196                    } else {
197                        LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
198                    }
199                } else {
200                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
201                }
202            }
203
204            ValueType::Number => {
205                if let Some(idx) = value_ref.arena_index() {
206                    let scalar_ref = super::scalar::ScalarRef::from_raw(idx);
207                    if let Some(f) = self.scalars.get_float(scalar_ref) {
208                        LiteralValue::Number(f)
209                    } else {
210                        LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
211                    }
212                } else {
213                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
214                }
215            }
216
217            ValueType::String => {
218                if let Some(idx) = value_ref.arena_index() {
219                    let string_id = StringId::from_raw(idx);
220                    let s = self.strings.resolve(string_id);
221                    LiteralValue::Text(s.to_string())
222                } else {
223                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
224                }
225            }
226
227            ValueType::Boolean => {
228                if let Some(b) = value_ref.as_boolean() {
229                    LiteralValue::Boolean(b)
230                } else {
231                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
232                }
233            }
234
235            ValueType::Error => {
236                if let Some(error_ref_raw) = value_ref.as_error_ref() {
237                    let error_ref = ErrorRef::from_raw(error_ref_raw);
238                    if let Some(error) = self.errors.get(error_ref) {
239                        LiteralValue::Error(error)
240                    } else {
241                        LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
242                    }
243                } else {
244                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
245                }
246            }
247
248            ValueType::Array => {
249                if let Some(idx) = value_ref.arena_index() {
250                    let array_ref = super::array::ArrayRef::from_raw(idx);
251                    if let Some(array_2d) = self.arrays.get_2d(array_ref) {
252                        // Convert back to LiteralValue array
253                        let result: Vec<Vec<LiteralValue>> = array_2d
254                            .into_iter()
255                            .map(|row| row.into_iter().map(|v| self.retrieve_value(v)).collect())
256                            .collect();
257                        LiteralValue::Array(result)
258                    } else {
259                        LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
260                    }
261                } else {
262                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
263                }
264            }
265
266            ValueType::DateTime => {
267                if let Some(idx) = value_ref.arena_index() {
268                    let scalar_ref = super::scalar::ScalarRef::from_raw(idx);
269                    if let Some(serial) = self.scalars.get_float(scalar_ref) {
270                        // This is a trusted private encoding, not an untrusted
271                        // workbook serial boundary. Use the compatibility
272                        // decoder so the arena retains its historical
273                        // pre-epoch behavior.
274                        LiteralValue::DateTime(formualizer_common::serial_to_datetime(serial))
275                    } else {
276                        LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
277                    }
278                } else {
279                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
280                }
281            }
282
283            ValueType::Duration => {
284                if let Some(idx) = value_ref.arena_index() {
285                    let scalar_ref = super::scalar::ScalarRef::from_raw(idx | (1 << 31));
286                    if let Some(secs) = self.scalars.get_integer(scalar_ref) {
287                        let dur = chrono::Duration::seconds(secs);
288                        LiteralValue::Duration(dur)
289                    } else {
290                        LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
291                    }
292                } else {
293                    LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
294                }
295            }
296
297            ValueType::Pending => LiteralValue::Pending,
298
299            ValueType::FormulaAst => {
300                // Formula ASTs shouldn't be returned as values
301                LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))
302            }
303        }
304    }
305
306    /// Store an AST node and return its ID
307    pub fn store_ast(&mut self, ast: &ASTNode, sheet_registry: &SheetRegistry) -> AstNodeId {
308        self.convert_ast_node(ast, sheet_registry)
309    }
310
311    /// Retrieve an AST node from its ID
312    pub fn retrieve_ast(&self, id: AstNodeId, sheet_registry: &SheetRegistry) -> Option<ASTNode> {
313        self.reconstruct_ast_node(id, sheet_registry)
314    }
315
316    pub fn resolve_ast_string(&self, id: StringId) -> &str {
317        self.asts.resolve_string(id)
318    }
319
320    pub(crate) fn sheet_names(&self) -> impl Iterator<Item = StringId> + '_ {
321        self.asts.sheet_names()
322    }
323
324    pub(crate) fn function_names(&self) -> impl Iterator<Item = StringId> + '_ {
325        self.asts.function_names()
326    }
327
328    pub(crate) fn resolution_revision(&self) -> u64 {
329        self.asts.resolution_revision()
330    }
331
332    pub(crate) fn ast_strings(&self) -> &StringInterner {
333        self.asts.strings()
334    }
335
336    pub fn reconstruct_reference_type_for_eval(
337        &self,
338        ref_type: &CompactRefType,
339        sheet_registry: &SheetRegistry,
340    ) -> ReferenceType {
341        self.reconstruct_reference_type(ref_type, sheet_registry)
342    }
343
344    /// See [`super::ast::AstArena::compact`].
345    pub(crate) fn compact_asts(
346        &mut self,
347        roots: impl IntoIterator<Item = AstNodeId>,
348    ) -> (Vec<u32>, super::string_interner::StringGarbage) {
349        self.asts.compact(roots)
350    }
351
352    pub(crate) fn ast_node_count(&self) -> usize {
353        self.asts.node_count()
354    }
355
356    pub(crate) fn expanded_node(&self, id: AstNodeId) -> Option<super::ast::AstNodeData> {
357        self.asts.get(id).map(|node| self.asts.expand_node(node))
358    }
359
360    /// See [`super::ast::AstArena::get_compact_ref`].
361    pub fn get_compact_ref(&self, id: super::ast::RefId) -> Option<&CompactRefType> {
362        self.asts.get_compact_ref(id)
363    }
364
365    pub(crate) fn compact_ref(&self, id: super::ast::RefId) -> &CompactRefType {
366        self.asts.compact_ref(id)
367    }
368
369    pub fn get_node(&self, id: AstNodeId) -> Option<&super::ast::AstNodeData<super::ast::RefId>> {
370        self.asts.get(id)
371    }
372
373    /// SUBTOTAL/AGGREGATE call bits of the formula rooted at `id` (see
374    /// `AstNodeEntry::subtotal_calls`).
375    pub(crate) fn ast_subtotal_calls(&self, id: AstNodeId) -> u8 {
376        self.asts.subtotal_calls(id) & (super::ast::SUBTOTAL_CALL | super::ast::AGGREGATE_CALL)
377    }
378
379    /// Conservative visibility guard metadata, including dotted reducer names.
380    /// Unlike `ast_subtotal_calls`, this is not a nested-evaluation policy.
381    pub(crate) fn ast_calls_visibility_reducer(&self, id: AstNodeId) -> bool {
382        self.asts.subtotal_calls(id) != 0
383    }
384
385    pub fn get_args(&self, id: AstNodeId) -> Option<&[AstNodeId]> {
386        self.asts.get_function_args(id)
387    }
388
389    pub fn get_array_elems(&self, id: AstNodeId) -> Option<(u16, u16, &[AstNodeId])> {
390        self.asts.get_array_elements_info(id)
391    }
392
393    pub fn ast_needs_structural_rewrite(&self, id: AstNodeId) -> bool {
394        let mut stack = vec![id];
395        while let Some(node_id) = stack.pop() {
396            let Some(node) = self.get_node(node_id) else {
397                continue;
398            };
399            match node {
400                super::ast::AstNodeData::Reference { ref_type, .. } => {
401                    let ref_type = self.compact_ref(*ref_type);
402                    if let CompactRefType::Table { name_id, .. } = ref_type
403                        && self.resolve_ast_string(*name_id).is_empty()
404                    {
405                        return true;
406                    }
407                }
408                super::ast::AstNodeData::UnaryOp { expr_id, .. } => stack.push(*expr_id),
409                super::ast::AstNodeData::BinaryOp {
410                    left_id, right_id, ..
411                } => {
412                    stack.push(*right_id);
413                    stack.push(*left_id);
414                }
415                super::ast::AstNodeData::Function { .. } => {
416                    if let Some(args) = self.get_args(node_id) {
417                        stack.extend(args.iter().rev().copied());
418                    }
419                }
420                super::ast::AstNodeData::Array { .. } => {
421                    if let Some((_, _, elems)) = self.get_array_elems(node_id) {
422                        stack.extend(elems.iter().rev().copied());
423                    }
424                }
425                super::ast::AstNodeData::Literal(_) | super::ast::AstNodeData::Omitted => {}
426            }
427        }
428        false
429    }
430
431    /// Convert ASTNode to arena representation
432    fn convert_ast_node(&mut self, node: &ASTNode, sheet_registry: &SheetRegistry) -> AstNodeId {
433        match &node.node_type {
434            ASTNodeType::Literal(lit) => {
435                let value_ref = self.store_value(lit.clone());
436                self.asts.insert_literal(value_ref)
437            }
438
439            ASTNodeType::Omitted => self.asts.insert_omitted(),
440
441            ASTNodeType::Reference {
442                original,
443                reference,
444            } => {
445                let ref_type = self.convert_reference_type(reference, sheet_registry);
446                self.asts.insert_reference(original, ref_type)
447            }
448
449            ASTNodeType::UnaryOp { op, expr } => {
450                let expr_id = self.convert_ast_node(expr, sheet_registry);
451                self.asts.insert_unary_op(*op, expr_id)
452            }
453
454            ASTNodeType::BinaryOp { op, left, right } => {
455                let left_id = self.convert_ast_node(left, sheet_registry);
456                let right_id = self.convert_ast_node(right, sheet_registry);
457                self.asts.insert_binary_op(*op, left_id, right_id)
458            }
459
460            ASTNodeType::Function { name, args } => {
461                let arg_ids: Vec<AstNodeId> = args
462                    .iter()
463                    .map(|arg| self.convert_ast_node(arg, sheet_registry))
464                    .collect();
465                self.asts.insert_function(name, arg_ids)
466            }
467
468            ASTNodeType::Array(rows) => {
469                let total_elements = rows.iter().map(|r| r.len()).sum();
470                let mut elements = Vec::with_capacity(total_elements);
471
472                let rows_count = rows.len() as u16;
473                let cols_count = rows.first().map(|r| r.len()).unwrap_or(0) as u16;
474
475                for row in rows {
476                    for elem in row {
477                        elements.push(self.convert_ast_node(elem, sheet_registry));
478                    }
479                }
480
481                self.asts.insert_array(rows_count, cols_count, elements)
482            }
483
484            // Postfix call (e.g. LAMBDA immediate-invocation), stored as a
485            // reserved-name function node whose first argument is the callee.
486            ASTNodeType::Call { callee, args } => {
487                let mut arg_ids = Vec::with_capacity(args.len() + 1);
488                arg_ids.push(self.convert_ast_node(callee, sheet_registry));
489                arg_ids.extend(
490                    args.iter()
491                        .map(|arg| self.convert_ast_node(arg, sheet_registry)),
492                );
493                self.asts
494                    .insert_function(super::ast::CALL_NODE_NAME, arg_ids)
495            }
496        }
497    }
498
499    /// Convert ReferenceType to CompactRefType
500    fn convert_reference_type(
501        &mut self,
502        ref_type: &ReferenceType,
503        sheet_registry: &SheetRegistry,
504    ) -> CompactRefType {
505        match ref_type {
506            ReferenceType::Cell {
507                sheet,
508                row,
509                col,
510                row_abs,
511                col_abs,
512            } => {
513                let sheet = match sheet.as_ref() {
514                    Some(s) => match sheet_registry.get_id(s) {
515                        Some(id) => Some(SheetKey::Id(id)),
516                        None => Some(SheetKey::Name(self.asts.strings_mut().intern(s))),
517                    },
518                    None => None,
519                };
520                CompactRefType::Cell {
521                    sheet,
522                    row: *row,
523                    col: *col,
524                    row_abs: *row_abs,
525                    col_abs: *col_abs,
526                }
527            }
528
529            ReferenceType::Range {
530                sheet,
531                start_row,
532                start_col,
533                end_row,
534                end_col,
535                start_row_abs,
536                start_col_abs,
537                end_row_abs,
538                end_col_abs,
539            } => {
540                let sheet = match sheet.as_ref() {
541                    Some(s) => match sheet_registry.get_id(s) {
542                        Some(id) => Some(SheetKey::Id(id)),
543                        None => Some(SheetKey::Name(self.asts.strings_mut().intern(s))),
544                    },
545                    None => None,
546                };
547                // For optional range bounds, use 0/u32::MAX as sentinels for unbounded
548                CompactRefType::Range {
549                    sheet,
550                    start_row: start_row.unwrap_or(0),
551                    start_col: start_col.unwrap_or(0),
552                    end_row: end_row.unwrap_or(u32::MAX),
553                    end_col: end_col.unwrap_or(u32::MAX),
554                    start_row_abs: *start_row_abs,
555                    start_col_abs: *start_col_abs,
556                    end_row_abs: *end_row_abs,
557                    end_col_abs: *end_col_abs,
558                }
559            }
560
561            ReferenceType::External(ext) => {
562                let raw_id = self.asts.strings_mut().intern(&ext.raw);
563                let book_id = self.asts.strings_mut().intern(ext.book.token());
564                let sheet_id = self.asts.strings_mut().intern(&ext.sheet);
565                CompactRefType::External {
566                    raw_id,
567                    book_id,
568                    sheet_id,
569                    kind: ext.kind,
570                }
571            }
572
573            ReferenceType::NamedRange(name) => {
574                let string_id = self.asts.strings_mut().intern(name);
575                CompactRefType::NamedRange(string_id)
576            }
577
578            ReferenceType::Table(table_ref) => {
579                let name_id = self.asts.strings_mut().intern(&table_ref.name);
580                let specifier_id = table_ref
581                    .specifier
582                    .as_ref()
583                    .map(|specifier| self.asts.intern_table_specifier(specifier));
584                CompactRefType::Table {
585                    name_id,
586                    specifier_id,
587                }
588            }
589
590            ReferenceType::Cell3D {
591                sheet_first,
592                sheet_last,
593                row,
594                col,
595                row_abs,
596                col_abs,
597            } => {
598                let sheet_first = self.asts.strings_mut().intern(sheet_first);
599                let sheet_last = self.asts.strings_mut().intern(sheet_last);
600                CompactRefType::Cell3D {
601                    sheet_first,
602                    sheet_last,
603                    row: *row,
604                    col: *col,
605                    row_abs: *row_abs,
606                    col_abs: *col_abs,
607                }
608            }
609
610            ReferenceType::Range3D {
611                sheet_first,
612                sheet_last,
613                start_row,
614                start_col,
615                end_row,
616                end_col,
617                start_row_abs,
618                start_col_abs,
619                end_row_abs,
620                end_col_abs,
621            } => {
622                let sheet_first = self.asts.strings_mut().intern(sheet_first);
623                let sheet_last = self.asts.strings_mut().intern(sheet_last);
624                CompactRefType::Range3D {
625                    sheet_first,
626                    sheet_last,
627                    start_row: start_row.unwrap_or(0),
628                    start_col: start_col.unwrap_or(0),
629                    end_row: end_row.unwrap_or(u32::MAX),
630                    end_col: end_col.unwrap_or(u32::MAX),
631                    start_row_abs: *start_row_abs,
632                    start_col_abs: *start_col_abs,
633                    end_row_abs: *end_row_abs,
634                    end_col_abs: *end_col_abs,
635                }
636            }
637        }
638    }
639
640    /// Reconstruct an ASTNode from arena representation
641    pub(crate) fn reconstruct_ast_node(
642        &self,
643        id: AstNodeId,
644        sheet_registry: &SheetRegistry,
645    ) -> Option<ASTNode> {
646        use super::ast::AstNodeData;
647
648        let node_data = self.asts.get(id)?;
649
650        let node_type = match node_data {
651            AstNodeData::Literal(value_ref) => {
652                let lit = self.retrieve_value(*value_ref);
653                ASTNodeType::Literal(lit)
654            }
655
656            AstNodeData::Omitted => ASTNodeType::Omitted,
657
658            AstNodeData::Reference {
659                original_id,
660                ref_type,
661            } => {
662                let original = self.asts.resolve_string(*original_id).to_string();
663                let reference =
664                    self.reconstruct_reference_type(self.compact_ref(*ref_type), sheet_registry);
665                ASTNodeType::Reference {
666                    original,
667                    reference,
668                }
669            }
670
671            AstNodeData::UnaryOp { operator, expr_id } => {
672                let op = *operator;
673                let expr = Box::new(self.reconstruct_ast_node(*expr_id, sheet_registry)?);
674                ASTNodeType::UnaryOp { op, expr }
675            }
676
677            AstNodeData::BinaryOp {
678                operator,
679                left_id,
680                right_id,
681            } => {
682                let op = *operator;
683                let left = Box::new(self.reconstruct_ast_node(*left_id, sheet_registry)?);
684                let right = Box::new(self.reconstruct_ast_node(*right_id, sheet_registry)?);
685                ASTNodeType::BinaryOp { op, left, right }
686            }
687
688            AstNodeData::Function { name_id, .. } => {
689                let name = self.asts.resolve_string(*name_id).to_string();
690                let arg_ids = self.asts.get_function_args(id)?;
691                let mut args: Vec<ASTNode> = arg_ids
692                    .iter()
693                    .filter_map(|&arg_id| self.reconstruct_ast_node(arg_id, sheet_registry))
694                    .collect();
695                if name == super::ast::CALL_NODE_NAME && !args.is_empty() {
696                    let callee = Box::new(args.remove(0));
697                    ASTNodeType::Call { callee, args }
698                } else {
699                    ASTNodeType::Function { name, args }
700                }
701            }
702
703            AstNodeData::Array { rows, cols, .. } => {
704                let elements = self.asts.get_array_elements(id)?;
705                let mut result = Vec::with_capacity(*rows as usize);
706
707                for r in 0..*rows {
708                    let mut row = Vec::with_capacity(*cols as usize);
709                    for c in 0..*cols {
710                        let idx = (r * *cols + c) as usize;
711                        if let Some(&elem_id) = elements.get(idx)
712                            && let Some(node) = self.reconstruct_ast_node(elem_id, sheet_registry)
713                        {
714                            row.push(node);
715                        }
716                    }
717                    result.push(row);
718                }
719
720                ASTNodeType::Array(result)
721            }
722        };
723
724        Some(ASTNode {
725            node_type,
726            source_token: None, // Token information is not preserved in arena
727            contains_volatile: false,
728        })
729    }
730
731    /// Reconstruct a ReferenceType from CompactRefType
732    fn reconstruct_reference_type(
733        &self,
734        ref_type: &CompactRefType,
735        sheet_registry: &SheetRegistry,
736    ) -> ReferenceType {
737        match ref_type {
738            CompactRefType::Cell {
739                sheet,
740                row,
741                col,
742                row_abs,
743                col_abs,
744            } => {
745                let sheet = match sheet {
746                    Some(SheetKey::Id(id)) => Some(sheet_registry.name(*id).to_string()),
747                    Some(SheetKey::Name(name_id)) => {
748                        Some(self.asts.resolve_string(*name_id).to_string())
749                    }
750                    None => None,
751                };
752                ReferenceType::Cell {
753                    sheet,
754                    row: *row,
755                    col: *col,
756                    row_abs: *row_abs,
757                    col_abs: *col_abs,
758                }
759            }
760
761            CompactRefType::Range {
762                sheet,
763                start_row,
764                start_col,
765                end_row,
766                end_col,
767                start_row_abs,
768                start_col_abs,
769                end_row_abs,
770                end_col_abs,
771            } => {
772                let sheet = match sheet {
773                    Some(SheetKey::Id(id)) => Some(sheet_registry.name(*id).to_string()),
774                    Some(SheetKey::Name(name_id)) => {
775                        Some(self.asts.resolve_string(*name_id).to_string())
776                    }
777                    None => None,
778                };
779                // Convert sentinel values back to None
780                ReferenceType::Range {
781                    sheet,
782                    start_row: if *start_row == 0 {
783                        None
784                    } else {
785                        Some(*start_row)
786                    },
787                    start_col: if *start_col == 0 {
788                        None
789                    } else {
790                        Some(*start_col)
791                    },
792                    end_row: if *end_row == u32::MAX {
793                        None
794                    } else {
795                        Some(*end_row)
796                    },
797                    end_col: if *end_col == u32::MAX {
798                        None
799                    } else {
800                        Some(*end_col)
801                    },
802                    start_row_abs: *start_row_abs,
803                    start_col_abs: *start_col_abs,
804                    end_row_abs: *end_row_abs,
805                    end_col_abs: *end_col_abs,
806                }
807            }
808
809            CompactRefType::External {
810                raw_id,
811                book_id,
812                sheet_id,
813                kind,
814            } => {
815                let raw = self.asts.resolve_string(*raw_id).to_string();
816                let book = self.asts.resolve_string(*book_id).to_string();
817                let sheet = self.asts.resolve_string(*sheet_id).to_string();
818                ReferenceType::External(ExternalReference {
819                    raw,
820                    book: ExternalBookRef::Token(book),
821                    sheet,
822                    kind: *kind,
823                })
824            }
825
826            CompactRefType::NamedRange(string_id) => {
827                let name = self.asts.resolve_string(*string_id).to_string();
828                ReferenceType::NamedRange(name)
829            }
830
831            CompactRefType::Table {
832                name_id,
833                specifier_id,
834            } => {
835                let name = self.asts.resolve_string(*name_id).to_string();
836                let specifier = specifier_id
837                    .and_then(|id| self.asts.resolve_table_specifier(id))
838                    .cloned();
839                ReferenceType::Table(TableReference { name, specifier })
840            }
841
842            CompactRefType::Cell3D {
843                sheet_first,
844                sheet_last,
845                row,
846                col,
847                row_abs,
848                col_abs,
849            } => ReferenceType::Cell3D {
850                sheet_first: self.asts.resolve_string(*sheet_first).to_string(),
851                sheet_last: self.asts.resolve_string(*sheet_last).to_string(),
852                row: *row,
853                col: *col,
854                row_abs: *row_abs,
855                col_abs: *col_abs,
856            },
857
858            CompactRefType::Range3D {
859                sheet_first,
860                sheet_last,
861                start_row,
862                start_col,
863                end_row,
864                end_col,
865                start_row_abs,
866                start_col_abs,
867                end_row_abs,
868                end_col_abs,
869            } => ReferenceType::Range3D {
870                sheet_first: self.asts.resolve_string(*sheet_first).to_string(),
871                sheet_last: self.asts.resolve_string(*sheet_last).to_string(),
872                start_row: if *start_row == 0 {
873                    None
874                } else {
875                    Some(*start_row)
876                },
877                start_col: if *start_col == 0 {
878                    None
879                } else {
880                    Some(*start_col)
881                },
882                end_row: if *end_row == u32::MAX {
883                    None
884                } else {
885                    Some(*end_row)
886                },
887                end_col: if *end_col == u32::MAX {
888                    None
889                } else {
890                    Some(*end_col)
891                },
892                start_row_abs: *start_row_abs,
893                start_col_abs: *start_col_abs,
894                end_row_abs: *end_row_abs,
895                end_col_abs: *end_col_abs,
896            },
897        }
898    }
899
900    /// Store an error with message preservation
901    fn store_error(&mut self, error: &ExcelError) -> ValueRef {
902        let error_ref = self.errors.insert(error);
903        ValueRef::error(error_ref.as_u32())
904    }
905
906    /// Get memory usage statistics
907    pub fn memory_usage(&self) -> DataStoreStats {
908        DataStoreStats {
909            scalar_bytes: self.scalars.memory_usage(),
910            string_bytes: self.strings.memory_usage(),
911            array_bytes: self.arrays.memory_usage(),
912            ast_bytes: self.asts.memory_usage(),
913            error_bytes: self.errors.memory_usage(),
914            total_scalars: self.scalars.len(),
915            total_strings: self.strings.len(),
916            total_arrays: self.arrays.len(),
917            total_ast_nodes: self.asts.stats().node_count,
918            total_errors: self.errors.len(),
919        }
920    }
921
922    /// Clear all data from the store
923    pub fn clear(&mut self) {
924        self.scalars.clear();
925        self.strings.clear();
926        self.arrays.clear();
927        self.asts.clear();
928        self.errors.clear();
929    }
930}
931
932impl Default for DataStore {
933    fn default() -> Self {
934        Self::new()
935    }
936}
937
938/// Statistics about data store memory usage
939#[derive(Debug, Clone)]
940pub struct DataStoreStats {
941    pub scalar_bytes: usize,
942    pub string_bytes: usize,
943    pub array_bytes: usize,
944    pub ast_bytes: usize,
945    pub error_bytes: usize,
946    pub total_scalars: usize,
947    pub total_strings: usize,
948    pub total_arrays: usize,
949    pub total_ast_nodes: usize,
950    pub total_errors: usize,
951}
952
953impl DataStoreStats {
954    pub fn total_bytes(&self) -> usize {
955        self.scalar_bytes + self.string_bytes + self.array_bytes + self.ast_bytes + self.error_bytes
956    }
957}
958
959// Helper trait implementations for ArrayRef and ScalarRef
960impl super::array::ArrayRef {
961    pub fn from_raw(raw: u32) -> Self {
962        super::array::ArrayRef(raw)
963    }
964}
965
966impl super::scalar::ScalarRef {
967    pub fn from_raw(raw: u32) -> Self {
968        Self { raw }
969    }
970
971    pub fn as_u32(self) -> u32 {
972        self.raw
973    }
974}
975
976#[cfg(test)]
977mod tests {
978    use super::*;
979
980    #[test]
981    fn visibility_reducer_prefixes_do_not_change_legacy_nested_skip_bits() {
982        let mut store = DataStore::new();
983        let registry = SheetRegistry::new();
984        for (formula, legacy) in [
985            ("=SUBTOTAL(9,1)", 1),
986            ("=_xlfn.SUBTOTAL(9,1)", 1),
987            ("=AGGREGATE(9,0,1)", 2),
988            ("=1+_xlws._xlfn.SUBTOTAL(109,1)", 0),
989            ("=_xll.AGGREGATE(9,0,1)", 0),
990            ("=FOO.SUBTOTAL(9,1)", 0),
991        ] {
992            let ast = formualizer_parse::parser::parse(formula).unwrap();
993            let id = store.store_ast(&ast, &registry);
994            assert!(store.ast_calls_visibility_reducer(id), "{formula}");
995            assert_eq!(store.ast_subtotal_calls(id), legacy, "{formula}");
996        }
997    }
998
999    #[test]
1000    fn test_data_store_empty_value() {
1001        let mut store = DataStore::new();
1002        let value_ref = store.store_value(LiteralValue::Empty);
1003        assert!(value_ref.is_empty());
1004
1005        let retrieved = store.retrieve_value(value_ref);
1006        assert_eq!(retrieved, LiteralValue::Empty);
1007    }
1008
1009    #[test]
1010    fn test_data_store_number() {
1011        let mut store = DataStore::new();
1012        let value_ref = store.store_value(LiteralValue::Number(42.5));
1013
1014        let retrieved = store.retrieve_value(value_ref);
1015        assert_eq!(retrieved, LiteralValue::Number(42.5));
1016    }
1017
1018    #[test]
1019    fn test_data_store_datetime_private_encoding_round_trips() {
1020        let mut store = DataStore::new();
1021        for datetime in [
1022            chrono::NaiveDate::from_ymd_opt(2024, 1, 15)
1023                .unwrap()
1024                .and_hms_opt(12, 30, 0)
1025                .unwrap(),
1026            chrono::NaiveDate::from_ymd_opt(1899, 12, 30)
1027                .unwrap()
1028                .and_hms_opt(0, 0, 0)
1029                .unwrap(),
1030        ] {
1031            let value_ref = store.store_value(LiteralValue::DateTime(datetime));
1032            assert_eq!(
1033                store.retrieve_value(value_ref),
1034                LiteralValue::DateTime(datetime)
1035            );
1036        }
1037    }
1038
1039    #[test]
1040    fn test_data_store_text() {
1041        let mut store = DataStore::new();
1042        let value_ref = store.store_value(LiteralValue::Text("Hello".to_string()));
1043
1044        let retrieved = store.retrieve_value(value_ref);
1045        assert_eq!(retrieved, LiteralValue::Text("Hello".to_string()));
1046    }
1047
1048    #[test]
1049    fn test_data_store_boolean() {
1050        let mut store = DataStore::new();
1051
1052        let true_ref = store.store_value(LiteralValue::Boolean(true));
1053        let false_ref = store.store_value(LiteralValue::Boolean(false));
1054
1055        assert_eq!(store.retrieve_value(true_ref), LiteralValue::Boolean(true));
1056        assert_eq!(
1057            store.retrieve_value(false_ref),
1058            LiteralValue::Boolean(false)
1059        );
1060    }
1061
1062    #[test]
1063    fn test_data_store_error() {
1064        let mut store = DataStore::new();
1065
1066        let error = ExcelError::new(ExcelErrorKind::Div);
1067        let value_ref = store.store_value(LiteralValue::Error(error.clone()));
1068
1069        let retrieved = store.retrieve_value(value_ref);
1070        match retrieved {
1071            LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
1072            _ => panic!("Expected error"),
1073        }
1074    }
1075
1076    #[test]
1077    fn test_data_store_array() {
1078        let mut store = DataStore::new();
1079
1080        let array = vec![
1081            vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)],
1082            vec![LiteralValue::Number(3.0), LiteralValue::Number(4.0)],
1083        ];
1084
1085        let value_ref = store.store_value(LiteralValue::Array(array.clone()));
1086        let retrieved = store.retrieve_value(value_ref);
1087
1088        assert_eq!(retrieved, LiteralValue::Array(array));
1089    }
1090
1091    #[test]
1092    fn test_data_store_ast_literal() {
1093        let mut store = DataStore::new();
1094        let mut sheet_registry = SheetRegistry::new();
1095        sheet_registry.id_for("Sheet1");
1096
1097        let ast = ASTNode {
1098            node_type: ASTNodeType::Literal(LiteralValue::Number(42.0)),
1099            source_token: None,
1100            contains_volatile: false,
1101        };
1102
1103        let ast_id = store.store_ast(&ast, &sheet_registry);
1104        let retrieved = store.retrieve_ast(ast_id, &sheet_registry).unwrap();
1105
1106        match retrieved.node_type {
1107            ASTNodeType::Literal(lit) => assert_eq!(lit, LiteralValue::Number(42.0)),
1108            _ => panic!("Expected literal"),
1109        }
1110    }
1111
1112    #[test]
1113    fn test_data_store_ast_binary_op() {
1114        let mut store = DataStore::new();
1115        let mut sheet_registry = SheetRegistry::new();
1116        sheet_registry.id_for("Sheet1");
1117
1118        let ast = ASTNode {
1119            node_type: ASTNodeType::BinaryOp {
1120                op: formualizer_parse::BinaryOperator::Add,
1121                left: Box::new(ASTNode {
1122                    node_type: ASTNodeType::Literal(LiteralValue::Number(1.0)),
1123                    source_token: None,
1124                    contains_volatile: false,
1125                }),
1126                right: Box::new(ASTNode {
1127                    node_type: ASTNodeType::Literal(LiteralValue::Number(2.0)),
1128                    source_token: None,
1129                    contains_volatile: false,
1130                }),
1131            },
1132            source_token: None,
1133            contains_volatile: false,
1134        };
1135
1136        let ast_id = store.store_ast(&ast, &sheet_registry);
1137        let retrieved = store.retrieve_ast(ast_id, &sheet_registry).unwrap();
1138
1139        match retrieved.node_type {
1140            ASTNodeType::BinaryOp { op, left, right } => {
1141                assert_eq!(op.as_str(), "+");
1142                match left.node_type {
1143                    ASTNodeType::Literal(lit) => assert_eq!(lit, LiteralValue::Number(1.0)),
1144                    _ => panic!("Expected literal"),
1145                }
1146                match right.node_type {
1147                    ASTNodeType::Literal(lit) => assert_eq!(lit, LiteralValue::Number(2.0)),
1148                    _ => panic!("Expected literal"),
1149                }
1150            }
1151            _ => panic!("Expected binary op"),
1152        }
1153    }
1154
1155    #[test]
1156    fn test_data_store_ast_function() {
1157        let mut store = DataStore::new();
1158        let mut sheet_registry = SheetRegistry::new();
1159        sheet_registry.id_for("Sheet1");
1160
1161        let ast = ASTNode {
1162            node_type: ASTNodeType::Function {
1163                name: "SUM".to_string(),
1164                args: vec![
1165                    ASTNode {
1166                        node_type: ASTNodeType::Literal(LiteralValue::Number(1.0)),
1167                        source_token: None,
1168                        contains_volatile: false,
1169                    },
1170                    ASTNode {
1171                        node_type: ASTNodeType::Literal(LiteralValue::Number(2.0)),
1172                        source_token: None,
1173                        contains_volatile: false,
1174                    },
1175                ],
1176            },
1177            source_token: None,
1178            contains_volatile: false,
1179        };
1180
1181        let ast_id = store.store_ast(&ast, &sheet_registry);
1182        let retrieved = store.retrieve_ast(ast_id, &sheet_registry).unwrap();
1183
1184        match retrieved.node_type {
1185            ASTNodeType::Function { name, args } => {
1186                assert_eq!(name, "SUM");
1187                assert_eq!(args.len(), 2);
1188            }
1189            _ => panic!("Expected function"),
1190        }
1191    }
1192
1193    #[test]
1194    fn test_data_store_memory_stats() {
1195        let mut store = DataStore::new();
1196
1197        // Add some data
1198        store.store_value(LiteralValue::Number(42.0));
1199        store.store_value(LiteralValue::Text("Hello".to_string()));
1200        store.store_value(LiteralValue::Array(vec![vec![LiteralValue::Number(1.0)]]));
1201
1202        let stats = store.memory_usage();
1203        assert!(stats.total_bytes() > 0);
1204        assert_eq!(stats.total_scalars, 2); // 42.0 and 1.0
1205        assert_eq!(stats.total_strings, 1); // "Hello"
1206        assert_eq!(stats.total_arrays, 1);
1207    }
1208
1209    #[test]
1210    fn test_data_store_clear() {
1211        let mut store = DataStore::new();
1212
1213        store.store_value(LiteralValue::Number(42.0));
1214        store.store_value(LiteralValue::Text("Hello".to_string()));
1215
1216        let stats = store.memory_usage();
1217        assert!(stats.total_scalars > 0);
1218        assert!(stats.total_strings > 0);
1219
1220        store.clear();
1221
1222        let stats = store.memory_usage();
1223        assert_eq!(stats.total_scalars, 0);
1224        assert_eq!(stats.total_strings, 0);
1225    }
1226}