1use crate::engine::range_view::RangeView;
2use crate::engine::row_visibility::VisibilityMaskMode;
3pub use crate::function::Function;
4use crate::interpreter::Interpreter;
5use crate::reference::CellRef;
6use formualizer_common::{
7 LiteralValue,
8 error::{ExcelError, ExcelErrorKind},
9};
10use std::any::Any;
11use std::borrow::Cow;
12use std::fmt::Debug;
13use std::sync::Arc;
14
15use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType, TableSpecifier};
16
17pub trait Range: Debug + Send + Sync {
20 fn get(&self, row: usize, col: usize) -> Result<LiteralValue, ExcelError>;
21 fn dimensions(&self) -> (usize, usize);
22
23 fn is_sparse(&self) -> bool {
24 false
25 }
26
27 fn is_infinite(&self) -> bool {
29 false
30 }
31
32 fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
33 Cow::Owned(
34 (0..self.dimensions().0)
35 .map(|r| {
36 (0..self.dimensions().1)
37 .map(|c| self.get(r, c).unwrap_or(LiteralValue::Empty))
38 .collect()
39 })
40 .collect(),
41 )
42 }
43
44 fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
45 let (rows, cols) = self.dimensions();
46 Box::new((0..rows).flat_map(move |r| (0..cols).map(move |c| self.get(r, c).unwrap())))
47 }
48 fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
49 let (rows, cols) = self.dimensions();
50 Box::new((0..rows).map(move |r| (0..cols).map(|c| self.get(r, c).unwrap()).collect()))
51 }
52
53 fn as_any(&self) -> &dyn Any;
55}
56
57impl Range for Box<dyn Range> {
59 fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
60 (**self).get(r, c)
61 }
62 fn dimensions(&self) -> (usize, usize) {
63 (**self).dimensions()
64 }
65 fn is_sparse(&self) -> bool {
66 (**self).is_sparse()
67 }
68 fn materialise(&self) -> Cow<'_, [Vec<LiteralValue>]> {
69 (**self).materialise()
70 }
71 fn iter_cells<'a>(&'a self) -> Box<dyn Iterator<Item = LiteralValue> + 'a> {
72 (**self).iter_cells()
73 }
74 fn iter_rows<'a>(&'a self) -> Box<dyn Iterator<Item = Vec<LiteralValue>> + 'a> {
75 (**self).iter_rows()
76 }
77 fn as_any(&self) -> &dyn Any {
78 (**self).as_any()
79 }
80}
81
82pub type CowValue<'a> = Cow<'a, LiteralValue>;
85
86pub trait CustomCallable: Send + Sync {
87 fn arity(&self) -> usize;
88
89 fn invoke<'ctx>(
90 &self,
91 interp: &Interpreter<'ctx>,
92 args: &[LiteralValue],
93 ) -> Result<CalcValue<'ctx>, ExcelError>;
94}
95
96#[derive(Clone)]
97pub enum CalcValue<'a> {
98 Scalar(LiteralValue),
99 Range(RangeView<'a>),
100 Callable(Arc<dyn CustomCallable>),
101}
102
103impl<'a> std::fmt::Debug for CalcValue<'a> {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 match self {
106 CalcValue::Scalar(v) => f.debug_tuple("Scalar").field(v).finish(),
107 CalcValue::Range(rv) => {
108 let (r, c) = rv.dims();
109 f.debug_tuple("Range").field(&(r, c)).finish()
110 }
111 CalcValue::Callable(_) => f.write_str("Callable(<opaque>)"),
112 }
113 }
114}
115
116impl<'a> CalcValue<'a> {
117 pub fn into_literal(self) -> LiteralValue {
118 match self {
119 CalcValue::Scalar(s) => s,
120 CalcValue::Range(rv) => {
121 let (rows, cols) = rv.dims();
122 if rows == 1 && cols == 1 {
123 rv.get_cell(0, 0)
124 } else {
125 let mut data = Vec::with_capacity(rows);
126 let _ = rv.for_each_row(&mut |row| {
129 data.push(row.to_vec());
130 Ok(())
131 });
132 LiteralValue::Array(data)
133 }
134 }
135 CalcValue::Callable(_) => LiteralValue::Error(
136 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
137 ),
138 }
139 }
140
141 pub fn as_scalar(&self) -> Option<&LiteralValue> {
142 match self {
143 CalcValue::Scalar(s) => Some(s),
144 _ => None,
145 }
146 }
147
148 pub fn as_range(&self) -> Option<&RangeView<'a>> {
149 match self {
150 CalcValue::Range(r) => Some(r),
151 _ => None,
152 }
153 }
154
155 pub fn as_callable(&self) -> Option<&Arc<dyn CustomCallable>> {
156 match self {
157 CalcValue::Callable(c) => Some(c),
158 _ => None,
159 }
160 }
161
162 pub fn into_owned(self) -> LiteralValue {
163 self.into_literal()
164 }
165}
166
167impl From<CalcValue<'_>> for LiteralValue {
168 fn from(val: CalcValue<'_>) -> Self {
169 val.into_literal()
170 }
171}
172
173impl<'a> PartialEq<LiteralValue> for CalcValue<'a> {
174 fn eq(&self, other: &LiteralValue) -> bool {
175 match self {
176 CalcValue::Scalar(s) => s == other,
177 CalcValue::Range(rv) => match other {
178 LiteralValue::Array(arr) => {
179 let (rows, cols) = rv.dims();
180 if arr.len() != rows {
181 return false;
182 }
183 for (r, row) in arr.iter().enumerate() {
184 if row.len() != cols {
185 return false;
186 }
187 for (c, cell) in row.iter().enumerate() {
188 if &rv.get_cell(r, c) != cell {
189 return false;
190 }
191 }
192 }
193 true
194 }
195 _ => {
196 let (rows, cols) = rv.dims();
197 rows == 1 && cols == 1 && &rv.get_cell(0, 0) == other
198 }
199 },
200 CalcValue::Callable(_) => false,
201 }
202 }
203}
204
205impl<'a> PartialEq<CalcValue<'a>> for LiteralValue {
206 fn eq(&self, other: &CalcValue<'a>) -> bool {
207 other == self
208 }
209}
210
211pub enum EvaluatedArg<'a> {
212 LiteralValue(CowValue<'a>),
213 Range(Box<dyn Range>),
214}
215
216enum ArgumentExpr<'a> {
217 Ast(&'a ASTNode),
218 Arena {
219 id: crate::engine::arena::AstNodeId,
220 data_store: &'a crate::engine::arena::DataStore,
221 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
222 },
223}
224
225pub struct ArgumentHandle<'a, 'b> {
226 expr: ArgumentExpr<'a>,
227 interp: &'a Interpreter<'b>,
228 cached_ast: std::cell::OnceCell<ASTNode>,
229 cached_ref: std::cell::OnceCell<ReferenceType>,
230}
231
232impl<'a, 'b> ArgumentHandle<'a, 'b> {
233 pub(crate) fn new(node: &'a ASTNode, interp: &'a Interpreter<'b>) -> Self {
234 Self {
235 expr: ArgumentExpr::Ast(node),
236 interp,
237 cached_ast: std::cell::OnceCell::new(),
238 cached_ref: std::cell::OnceCell::new(),
239 }
240 }
241
242 pub(crate) fn new_arena(
243 id: crate::engine::arena::AstNodeId,
244 interp: &'a Interpreter<'b>,
245 data_store: &'a crate::engine::arena::DataStore,
246 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
247 ) -> Self {
248 Self {
249 expr: ArgumentExpr::Arena {
250 id,
251 data_store,
252 sheet_registry,
253 },
254 interp,
255 cached_ast: std::cell::OnceCell::new(),
256 cached_ref: std::cell::OnceCell::new(),
257 }
258 }
259
260 pub fn value(&self) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
261 match &self.expr {
262 ArgumentExpr::Ast(node) => {
263 if let ASTNodeType::Literal(ref v) = node.node_type {
264 return Ok(crate::traits::CalcValue::Scalar(v.clone()));
265 }
266 self.interp.evaluate_ast(node)
267 }
268 ArgumentExpr::Arena {
269 id,
270 data_store,
271 sheet_registry,
272 } => self
273 .interp
274 .evaluate_arena_ast(*id, data_store, sheet_registry),
275 }
276 }
277
278 pub fn value_with_env(
279 &self,
280 env: crate::interpreter::LocalEnv,
281 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
282 let scoped = self.interp.with_local_env(env);
283 match &self.expr {
284 ArgumentExpr::Ast(node) => {
285 if let ASTNodeType::Literal(ref v) = node.node_type {
286 return Ok(crate::traits::CalcValue::Scalar(v.clone()));
287 }
288 scoped.evaluate_ast(node)
289 }
290 ArgumentExpr::Arena {
291 id,
292 data_store,
293 sheet_registry,
294 } => scoped.evaluate_arena_ast(*id, data_store, sheet_registry),
295 }
296 }
297
298 pub fn current_env(&self) -> crate::interpreter::LocalEnv {
299 self.interp.local_env().clone()
300 }
301
302 pub fn inline_array_literal(&self) -> Result<Option<Vec<Vec<LiteralValue>>>, ExcelError> {
303 match &self.expr {
304 ArgumentExpr::Ast(node) => match &node.node_type {
305 ASTNodeType::Literal(LiteralValue::Array(arr)) => Ok(Some(arr.clone())),
306 _ => Ok(None),
307 },
308 ArgumentExpr::Arena {
309 id,
310 data_store,
311 sheet_registry,
312 } => {
313 let node = data_store.get_node(*id).ok_or_else(|| {
314 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
315 })?;
316 match node {
317 crate::engine::arena::AstNodeData::Literal(vref) => {
318 match data_store.retrieve_value(*vref) {
319 LiteralValue::Array(arr) => Ok(Some(arr)),
320 _ => Ok(None),
321 }
322 }
323 _ => {
324 let _ = sheet_registry;
327 Ok(None)
328 }
329 }
330 }
331 }
332 }
333
334 fn reference_for_eval(&self) -> Result<ReferenceType, ExcelError> {
335 match &self.expr {
336 ArgumentExpr::Ast(node) => match &node.node_type {
337 ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
338 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
339 self.interp.evaluate_ast_as_reference(node)
340 }
341 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
342 .with_message("Expected a reference (by-ref argument)")),
343 },
344 ArgumentExpr::Arena {
345 id,
346 data_store,
347 sheet_registry,
348 } => {
349 let node = data_store.get_node(*id).ok_or_else(|| {
350 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
351 })?;
352 match node {
353 crate::engine::arena::AstNodeData::Reference { ref_type, .. } => Ok(
354 data_store.reconstruct_reference_type_for_eval(ref_type, sheet_registry)
355 ),
356 crate::engine::arena::AstNodeData::Function { .. }
357 | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
358 .interp
359 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
360 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
361 .with_message("Expected a reference (by-ref argument)")),
362 }
363 }
364 }
365 }
366
367 pub fn range(&self) -> Result<Box<dyn Range>, ExcelError> {
368 match &self.expr {
369 ArgumentExpr::Ast(node) => match &node.node_type {
370 ASTNodeType::Reference { reference, .. } => {
371 let view = self
373 .interp
374 .context
375 .resolve_range_view(reference, self.interp.current_sheet())?;
376 let (rows, cols) = view.dims();
377 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
378 view.for_each_row(&mut |row| {
379 let row_data: Vec<LiteralValue> = (0..cols)
380 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
381 .collect();
382 out.push(row_data);
383 Ok(())
384 })?;
385 Ok(Box::new(InMemoryRange::new(out)))
386 }
387 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
388 let reference = self.reference_for_eval()?;
389 let view = self
390 .interp
391 .context
392 .resolve_range_view(&reference, self.interp.current_sheet())?;
393 let (rows, cols) = view.dims();
394 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
395 view.for_each_row(&mut |row| {
396 let row_data: Vec<LiteralValue> = (0..cols)
397 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
398 .collect();
399 out.push(row_data);
400 Ok(())
401 })?;
402 Ok(Box::new(InMemoryRange::new(out)))
403 }
404 ASTNodeType::Array(rows) => {
405 let mut materialized = Vec::new();
406 for row in rows {
407 let mut materialized_row = Vec::new();
408 for cell in row {
409 materialized_row.push(self.interp.evaluate_ast(cell)?.into_literal());
410 }
411 materialized.push(materialized_row);
412 }
413 Ok(Box::new(InMemoryRange::new(materialized)))
414 }
415 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
416 .with_message(format!("Expected a range, got {:?}", node.node_type))),
417 },
418 ArgumentExpr::Arena { id, data_store, .. } => {
419 let node = data_store.get_node(*id).ok_or_else(|| {
420 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
421 })?;
422
423 match node {
424 crate::engine::arena::AstNodeData::Reference { .. }
425 | crate::engine::arena::AstNodeData::Function { .. }
426 | crate::engine::arena::AstNodeData::BinaryOp { .. } => {
427 let reference = self.reference_for_eval()?;
428 let view = self
429 .interp
430 .context
431 .resolve_range_view(&reference, self.interp.current_sheet())?;
432 let (rows, cols) = view.dims();
433 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
434 view.for_each_row(&mut |row| {
435 let row_data: Vec<LiteralValue> = (0..cols)
436 .map(|c| row.get(c).cloned().unwrap_or(LiteralValue::Empty))
437 .collect();
438 out.push(row_data);
439 Ok(())
440 })?;
441 Ok(Box::new(InMemoryRange::new(out)))
442 }
443 crate::engine::arena::AstNodeData::Array { .. } => {
444 let (rows, cols, elements) =
445 data_store.get_array_elems(*id).ok_or_else(|| {
446 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
447 })?;
448 let rows_usize = rows as usize;
449 let cols_usize = cols as usize;
450 let mut materialized: Vec<Vec<LiteralValue>> =
451 Vec::with_capacity(rows_usize);
452 for r in 0..rows_usize {
453 let mut row = Vec::with_capacity(cols_usize);
454 for c in 0..cols_usize {
455 let idx = r * cols_usize + c;
456 let elem_id = elements.get(idx).copied().ok_or_else(|| {
457 ExcelError::new(ExcelErrorKind::Value)
458 .with_message("Invalid array")
459 })?;
460 let v = self.interp.evaluate_arena_ast(
461 elem_id,
462 data_store,
463 self.sheet_registry(),
464 )?;
465 row.push(v.into_literal());
466 }
467 materialized.push(row);
468 }
469 Ok(Box::new(InMemoryRange::new(materialized)))
470 }
471 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
472 .with_message("Argument cannot be interpreted as a range.")),
473 }
474 }
475 }
476 }
477
478 fn sheet_registry(&self) -> &crate::engine::sheet_registry::SheetRegistry {
479 match &self.expr {
480 ArgumentExpr::Ast(_) => {
481 unreachable!("sheet_registry only used for arena ArgumentHandle")
483 }
484 ArgumentExpr::Arena { sheet_registry, .. } => sheet_registry,
485 }
486 }
487
488 pub fn range_view(&self) -> Result<RangeView<'b>, ExcelError> {
490 match &self.expr {
491 ArgumentExpr::Ast(node) => match &node.node_type {
492 ASTNodeType::Reference { reference, .. } => self
493 .interp
494 .context
495 .resolve_range_view(reference, self.interp.current_sheet())
496 .map(|v| v.with_cancel_token(self.interp.context.cancellation_token())),
497 ASTNodeType::Literal(formualizer_common::LiteralValue::Array(arr)) => Ok(
499 RangeView::from_owned_rows(arr.clone(), self.interp.context.date_system())
500 .with_cancel_token(self.interp.context.cancellation_token()),
501 ),
502 ASTNodeType::Array(rows) => {
503 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows.len());
504 for r in rows {
505 let mut row_vals = Vec::with_capacity(r.len());
506 for cell in r {
507 row_vals.push(self.interp.evaluate_ast(cell)?.into_literal());
508 }
509 out.push(row_vals);
510 }
511 Ok(
512 RangeView::from_owned_rows(out, self.interp.context.date_system())
513 .with_cancel_token(self.interp.context.cancellation_token()),
514 )
515 }
516 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
517 let reference = self.reference_for_eval()?;
518 self.interp
519 .context
520 .resolve_range_view(&reference, self.interp.current_sheet())
521 .map(|v| v.with_cancel_token(self.interp.context.cancellation_token()))
522 }
523 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
524 .with_message("Argument cannot be interpreted as a range.")),
525 },
526 ArgumentExpr::Arena {
527 id,
528 data_store,
529 sheet_registry,
530 } => {
531 let node = data_store.get_node(*id).ok_or_else(|| {
532 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
533 })?;
534
535 match node {
536 crate::engine::arena::AstNodeData::Reference { .. }
537 | crate::engine::arena::AstNodeData::Function { .. }
538 | crate::engine::arena::AstNodeData::BinaryOp { .. } => {
539 let reference = self.reference_for_eval()?;
540 self.interp
541 .context
542 .resolve_range_view(&reference, self.interp.current_sheet())
543 .map(|v| v.with_cancel_token(self.interp.context.cancellation_token()))
544 }
545 crate::engine::arena::AstNodeData::Literal(vref) => {
546 match data_store.retrieve_value(*vref) {
547 LiteralValue::Array(arr) => Ok(RangeView::from_owned_rows(
548 arr,
549 self.interp.context.date_system(),
550 )
551 .with_cancel_token(self.interp.context.cancellation_token())),
552 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
553 .with_message("Argument cannot be interpreted as a range.")),
554 }
555 }
556 crate::engine::arena::AstNodeData::Array { .. } => {
557 let (rows, cols, elements) =
558 data_store.get_array_elems(*id).ok_or_else(|| {
559 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
560 })?;
561
562 let rows_usize = rows as usize;
563 let cols_usize = cols as usize;
564 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows_usize);
565 for r in 0..rows_usize {
566 let mut row = Vec::with_capacity(cols_usize);
567 for c in 0..cols_usize {
568 let idx = r * cols_usize + c;
569 let elem_id = elements.get(idx).copied().ok_or_else(|| {
570 ExcelError::new(ExcelErrorKind::Value)
571 .with_message("Invalid array")
572 })?;
573 let v = self.interp.evaluate_arena_ast(
574 elem_id,
575 data_store,
576 sheet_registry,
577 )?;
578 row.push(v.into_literal());
579 }
580 out.push(row);
581 }
582 Ok(
583 RangeView::from_owned_rows(out, self.interp.context.date_system())
584 .with_cancel_token(self.interp.context.cancellation_token()),
585 )
586 }
587 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
588 .with_message("Argument cannot be interpreted as a range.")),
589 }
590 }
591 }
592 }
593
594 pub fn value_or_range(&self) -> Result<EvaluatedArg<'_>, ExcelError> {
595 self.range().map(EvaluatedArg::Range).or_else(|_| {
596 self.value()
597 .map(|cv| EvaluatedArg::LiteralValue(Cow::Owned(cv.into_literal())))
598 })
599 }
600
601 pub fn lazy_values_owned(
606 &'a self,
607 ) -> Result<Box<dyn Iterator<Item = LiteralValue> + 'a>, ExcelError> {
608 match &self.expr {
609 ArgumentExpr::Ast(node) => match &node.node_type {
610 ASTNodeType::Reference { .. } => {
611 let view = self.range_view()?;
612 let mut values: Vec<LiteralValue> = Vec::new();
613 view.for_each_cell(&mut |v| {
614 values.push(v.clone());
615 Ok(())
616 })?;
617 Ok(Box::new(values.into_iter()))
618 }
619 ASTNodeType::Array(rows) => {
620 struct ArrayEvalIter<'a, 'b> {
621 rows: &'a [Vec<ASTNode>],
622 r: usize,
623 c: usize,
624 interp: &'a Interpreter<'b>,
625 }
626 impl<'a, 'b> Iterator for ArrayEvalIter<'a, 'b> {
627 type Item = LiteralValue;
628 fn next(&mut self) -> Option<Self::Item> {
629 if self.rows.is_empty() {
630 return None;
631 }
632 let rows = self.rows;
633 let mut r = self.r;
634 let mut c = self.c;
635 if r >= rows.len() {
636 return None;
637 }
638 let node = &rows[r][c];
639 c += 1;
641 if c >= rows[r].len() {
642 r += 1;
643 c = 0;
644 }
645 self.r = r;
646 self.c = c;
647 match self.interp.evaluate_ast(node) {
648 Ok(cv) => Some(cv.into_literal()),
649 Err(e) => Some(LiteralValue::Error(e)),
650 }
651 }
652 }
653 let it = ArrayEvalIter {
654 rows,
655 r: 0,
656 c: 0,
657 interp: self.interp,
658 };
659 Ok(Box::new(it))
660 }
661 _ => {
662 let v = self.value()?.into_literal();
664 Ok(Box::new(std::iter::once(v)))
665 }
666 },
667 ArgumentExpr::Arena {
668 id,
669 data_store,
670 sheet_registry,
671 } => {
672 let node = data_store.get_node(*id).ok_or_else(|| {
673 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
674 })?;
675
676 match node {
677 crate::engine::arena::AstNodeData::Reference { .. } => {
678 let view = self.range_view()?;
679 let mut values: Vec<LiteralValue> = Vec::new();
680 view.for_each_cell(&mut |v| {
681 values.push(v.clone());
682 Ok(())
683 })?;
684 Ok(Box::new(values.into_iter()))
685 }
686 crate::engine::arena::AstNodeData::Array { .. } => {
687 let (rows, cols, elements) =
688 data_store.get_array_elems(*id).ok_or_else(|| {
689 ExcelError::new(ExcelErrorKind::Value).with_message("Invalid array")
690 })?;
691
692 struct ArenaArrayEvalIter<'a, 'b> {
693 elements: &'a [crate::engine::arena::AstNodeId],
694 idx: usize,
695 interp: &'a Interpreter<'b>,
696 data_store: &'a crate::engine::arena::DataStore,
697 sheet_registry: &'a crate::engine::sheet_registry::SheetRegistry,
698 }
699
700 impl<'a, 'b> Iterator for ArenaArrayEvalIter<'a, 'b> {
701 type Item = LiteralValue;
702
703 fn next(&mut self) -> Option<Self::Item> {
704 let id = self.elements.get(self.idx).copied()?;
705 self.idx += 1;
706 match self.interp.evaluate_arena_ast(
707 id,
708 self.data_store,
709 self.sheet_registry,
710 ) {
711 Ok(cv) => Some(cv.into_literal()),
712 Err(e) => Some(LiteralValue::Error(e)),
713 }
714 }
715 }
716
717 let _ = (rows, cols);
718 let it = ArenaArrayEvalIter {
719 elements,
720 idx: 0,
721 interp: self.interp,
722 data_store,
723 sheet_registry,
724 };
725 Ok(Box::new(it))
726 }
727 _ => {
728 let v = self
729 .interp
730 .evaluate_arena_ast(*id, data_store, sheet_registry)?;
731 Ok(Box::new(std::iter::once(v.into_literal())))
732 }
733 }
734 }
735 }
736 }
737
738 pub fn ast(&self) -> &ASTNode {
739 match &self.expr {
740 ArgumentExpr::Ast(node) => node,
741 ArgumentExpr::Arena {
742 id,
743 data_store,
744 sheet_registry,
745 } => self.cached_ast.get_or_init(|| {
746 data_store
747 .retrieve_ast(*id, sheet_registry)
748 .unwrap_or_else(|| ASTNode {
749 node_type: ASTNodeType::Literal(LiteralValue::Error(
750 ExcelError::new(ExcelErrorKind::Value)
751 .with_message("Missing formula AST"),
752 )),
753 source_token: None,
754 contains_volatile: false,
755 })
756 }),
757 }
758 }
759
760 pub fn as_reference(&self) -> Result<&ReferenceType, ExcelError> {
763 match &self.expr {
764 ArgumentExpr::Ast(node) => match &node.node_type {
765 ASTNodeType::Reference { reference, .. } => Ok(reference),
766 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
767 .with_message("Expected a reference (by-ref argument)")),
768 },
769 ArgumentExpr::Arena { .. } => {
770 let reference = self.reference_for_eval()?;
771 Ok(self.cached_ref.get_or_init(|| reference))
772 }
773 }
774 }
775
776 pub fn as_reference_or_eval(&self) -> Result<ReferenceType, ExcelError> {
779 match &self.expr {
780 ArgumentExpr::Ast(node) => match &node.node_type {
781 ASTNodeType::Reference { reference, .. } => Ok(reference.clone()),
782 ASTNodeType::Function { .. } | ASTNodeType::BinaryOp { .. } => {
783 self.interp.evaluate_ast_as_reference(node)
784 }
785 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
786 .with_message("Argument is not a reference")),
787 },
788 ArgumentExpr::Arena {
789 id,
790 data_store,
791 sheet_registry,
792 } => {
793 let node = data_store.get_node(*id).ok_or_else(|| {
794 ExcelError::new(ExcelErrorKind::Value).with_message("Missing AST node")
795 })?;
796
797 match node {
798 crate::engine::arena::AstNodeData::Reference { .. } => {
799 self.reference_for_eval()
800 }
801 crate::engine::arena::AstNodeData::Function { .. }
802 | crate::engine::arena::AstNodeData::BinaryOp { .. } => self
803 .interp
804 .evaluate_arena_ast_as_reference(*id, data_store, sheet_registry),
805 _ => Err(ExcelError::new(ExcelErrorKind::Ref)
806 .with_message("Argument is not a reference")),
807 }
808 }
809 }
810 }
811
812 pub fn matches_kind(&self, k: formualizer_common::ArgKind) -> Result<bool, ExcelError> {
814 Ok(match k {
815 formualizer_common::ArgKind::Any => true,
816 formualizer_common::ArgKind::Range => self.range().is_ok(),
817 formualizer_common::ArgKind::Number => matches!(
818 self.value()?.into_literal(),
819 LiteralValue::Number(_) | LiteralValue::Int(_)
820 ),
821 formualizer_common::ArgKind::Text => {
822 matches!(self.value()?.into_literal(), LiteralValue::Text(_))
823 }
824 formualizer_common::ArgKind::Logical => {
825 matches!(self.value()?.into_literal(), LiteralValue::Boolean(_))
826 }
827 })
828 }
829}
830
831#[derive(Debug, Clone)]
833pub struct InMemoryRange {
834 data: Vec<Vec<LiteralValue>>,
835}
836impl InMemoryRange {
837 pub fn new(d: Vec<Vec<LiteralValue>>) -> Self {
838 Self { data: d }
839 }
840}
841impl Range for InMemoryRange {
842 fn get(&self, r: usize, c: usize) -> Result<LiteralValue, ExcelError> {
843 Ok(self
844 .data
845 .get(r)
846 .and_then(|row| row.get(c))
847 .cloned()
848 .unwrap_or(LiteralValue::Empty))
849 }
850 fn dimensions(&self) -> (usize, usize) {
851 (self.data.len(), self.data.first().map_or(0, |r| r.len()))
852 }
853 fn as_any(&self) -> &dyn Any {
854 self
855 }
856}
857
858pub trait Table: Debug + Send + Sync {
861 fn get_cell(&self, row: usize, column: &str) -> Result<LiteralValue, ExcelError>;
862 fn get_column(&self, column: &str) -> Result<Box<dyn Range>, ExcelError>;
863 fn columns(&self) -> Vec<String> {
865 vec![]
866 }
867 fn data_height(&self) -> usize {
869 0
870 }
871 fn has_headers(&self) -> bool {
873 false
874 }
875 fn has_totals(&self) -> bool {
877 false
878 }
879 fn headers_row(&self) -> Option<Box<dyn Range>> {
881 None
882 }
883 fn totals_row(&self) -> Option<Box<dyn Range>> {
885 None
886 }
887 fn data_body(&self) -> Option<Box<dyn Range>> {
889 None
890 }
891 fn clone_box(&self) -> Box<dyn Table>;
892}
893impl Table for Box<dyn Table> {
894 fn get_cell(&self, r: usize, c: &str) -> Result<LiteralValue, ExcelError> {
895 (**self).get_cell(r, c)
896 }
897 fn get_column(&self, c: &str) -> Result<Box<dyn Range>, ExcelError> {
898 (**self).get_column(c)
899 }
900 fn columns(&self) -> Vec<String> {
901 (**self).columns()
902 }
903 fn data_height(&self) -> usize {
904 (**self).data_height()
905 }
906 fn has_headers(&self) -> bool {
907 (**self).has_headers()
908 }
909 fn has_totals(&self) -> bool {
910 (**self).has_totals()
911 }
912 fn headers_row(&self) -> Option<Box<dyn Range>> {
913 (**self).headers_row()
914 }
915 fn totals_row(&self) -> Option<Box<dyn Range>> {
916 (**self).totals_row()
917 }
918 fn data_body(&self) -> Option<Box<dyn Range>> {
919 (**self).data_body()
920 }
921 fn clone_box(&self) -> Box<dyn Table> {
922 (**self).clone_box()
923 }
924}
925
926pub trait ReferenceResolver: Send + Sync {
929 fn resolve_cell_reference(
930 &self,
931 sheet: Option<&str>,
932 row: u32,
933 col: u32,
934 ) -> Result<LiteralValue, ExcelError>;
935}
936pub trait RangeResolver: Send + Sync {
937 fn resolve_range_reference(
938 &self,
939 sheet: Option<&str>,
940 sr: Option<u32>,
941 sc: Option<u32>,
942 er: Option<u32>,
943 ec: Option<u32>,
944 ) -> Result<Box<dyn Range>, ExcelError>;
945}
946pub trait NamedRangeResolver: Send + Sync {
947 fn resolve_named_range_reference(
948 &self,
949 name: &str,
950 ) -> Result<Vec<Vec<LiteralValue>>, ExcelError>;
951}
952pub trait TableResolver: Send + Sync {
953 fn resolve_table_reference(
954 &self,
955 tref: &formualizer_parse::parser::TableReference,
956 ) -> Result<Box<dyn Table>, ExcelError>;
957}
958
959pub trait SourceResolver: Send + Sync {
960 fn source_scalar_version(&self, _name: &str) -> Option<u64> {
961 None
962 }
963
964 fn resolve_source_scalar(&self, name: &str) -> Result<LiteralValue, ExcelError> {
965 Err(ExcelError::new(ExcelErrorKind::NImpl)
966 .with_message(format!("Source scalar not supported: {name}")))
967 }
968
969 fn source_table_version(&self, _name: &str) -> Option<u64> {
970 None
971 }
972
973 fn resolve_source_table(&self, name: &str) -> Result<Box<dyn Table>, ExcelError> {
974 Err(ExcelError::new(ExcelErrorKind::NImpl)
975 .with_message(format!("Source table not supported: {name}")))
976 }
977}
978
979pub trait Resolver: ReferenceResolver + RangeResolver + NamedRangeResolver + TableResolver {
980 fn resolve_range_like(&self, r: &ReferenceType) -> Result<Box<dyn Range>, ExcelError> {
981 match r {
982 ReferenceType::Range {
983 sheet,
984 start_row,
985 start_col,
986 end_row,
987 end_col,
988 ..
989 } => self.resolve_range_reference(
990 sheet.as_deref(),
991 *start_row,
992 *start_col,
993 *end_row,
994 *end_col,
995 ),
996 ReferenceType::External(_) => Err(ExcelError::new(ExcelErrorKind::NImpl)
997 .with_message("External references are not supported by Resolver".to_string())),
998 ReferenceType::Table(tref) => {
999 let t = self.resolve_table_reference(tref)?;
1000 match &tref.specifier {
1001 Some(TableSpecifier::Column(c)) => t.get_column(c),
1002 Some(TableSpecifier::ColumnRange(start, end)) => {
1003 let cols = t.columns();
1005 let start_idx = cols.iter().position(|n| n.eq_ignore_ascii_case(start));
1006 let end_idx = cols.iter().position(|n| n.eq_ignore_ascii_case(end));
1007 if let (Some(mut si), Some(mut ei)) = (start_idx, end_idx) {
1008 if si > ei {
1009 std::mem::swap(&mut si, &mut ei);
1010 }
1011 let h = t.data_height();
1013 let w = ei - si + 1;
1014 let mut rows = vec![vec![LiteralValue::Empty; w]; h];
1015 for (offset, ci) in (si..=ei).enumerate() {
1016 let cname = &cols[ci];
1017 let col_range = t.get_column(cname)?;
1018 let (rh, _) = col_range.dimensions();
1019 for (r, row) in rows.iter_mut().enumerate().take(h.min(rh)) {
1020 row[offset] = col_range.get(r, 0)?;
1021 }
1022 }
1023 Ok(Box::new(InMemoryRange::new(rows)))
1024 } else {
1025 Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1026 "Column range refers to unknown column(s)".to_string(),
1027 ))
1028 }
1029 }
1030 Some(TableSpecifier::SpecialItem(
1031 formualizer_parse::parser::SpecialItem::Headers,
1032 )) => {
1033 if let Some(h) = t.headers_row() {
1034 Ok(h)
1035 } else {
1036 Ok(Box::new(InMemoryRange::new(vec![])))
1037 }
1038 }
1039 Some(TableSpecifier::SpecialItem(
1040 formualizer_parse::parser::SpecialItem::Totals,
1041 )) => {
1042 if let Some(tr) = t.totals_row() {
1043 Ok(tr)
1044 } else {
1045 Ok(Box::new(InMemoryRange::new(vec![])))
1046 }
1047 }
1048 Some(TableSpecifier::SpecialItem(
1049 formualizer_parse::parser::SpecialItem::Data,
1050 )) => {
1051 if let Some(body) = t.data_body() {
1052 Ok(body)
1053 } else {
1054 Ok(Box::new(InMemoryRange::new(vec![])))
1055 }
1056 }
1057 Some(TableSpecifier::SpecialItem(
1058 formualizer_parse::parser::SpecialItem::All,
1059 )) => {
1060 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
1062 if let Some(h) = t.headers_row() {
1063 out.extend(h.iter_rows());
1064 }
1065 if let Some(body) = t.data_body() {
1066 out.extend(body.iter_rows());
1067 }
1068 if let Some(tr) = t.totals_row() {
1069 out.extend(tr.iter_rows());
1070 }
1071 Ok(Box::new(InMemoryRange::new(out)))
1072 }
1073 Some(TableSpecifier::SpecialItem(
1074 formualizer_parse::parser::SpecialItem::ThisRow,
1075 )) => Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1076 "@ (This Row) requires table-aware context; not yet supported".to_string(),
1077 )),
1078 Some(TableSpecifier::All) => {
1079 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
1081 if let Some(h) = t.headers_row() {
1082 out.extend(h.iter_rows());
1083 }
1084 if let Some(body) = t.data_body() {
1085 out.extend(body.iter_rows());
1086 }
1087 if let Some(tr) = t.totals_row() {
1088 out.extend(tr.iter_rows());
1089 }
1090 Ok(Box::new(InMemoryRange::new(out)))
1091 }
1092 Some(TableSpecifier::Data) => {
1093 if let Some(body) = t.data_body() {
1094 Ok(body)
1095 } else {
1096 Ok(Box::new(InMemoryRange::new(vec![])))
1097 }
1098 }
1099 Some(TableSpecifier::Combination(_)) => Err(ExcelError::new(
1101 ExcelErrorKind::NImpl,
1102 )
1103 .with_message("Complex structured references not yet supported".to_string())),
1104 Some(TableSpecifier::Row(_)) => Err(ExcelError::new(ExcelErrorKind::NImpl)
1105 .with_message("Row selectors (@/index) not yet supported".to_string())),
1106 Some(TableSpecifier::Headers) | Some(TableSpecifier::Totals) => {
1107 Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1108 "Legacy Headers/Totals variants not used; use SpecialItem".to_string(),
1109 ))
1110 }
1111 None => Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1112 "Table reference without specifier is unsupported".to_string(),
1113 )),
1114 }
1115 }
1116 ReferenceType::NamedRange(n) => {
1117 let v = self.resolve_named_range_reference(n)?;
1118 Ok(Box::new(InMemoryRange::new(v)))
1119 }
1120 ReferenceType::Cell {
1121 sheet, row, col, ..
1122 } => {
1123 let v = self.resolve_cell_reference(sheet.as_deref(), *row, *col)?;
1124 Ok(Box::new(InMemoryRange::new(vec![vec![v]])))
1125 }
1126 }
1127 }
1128}
1129
1130pub trait FunctionProvider: Send + Sync {
1133 fn get_function(&self, ns: &str, name: &str) -> Option<Arc<dyn Function>>;
1134}
1135
1136pub trait EvaluationContext: Resolver + FunctionProvider + SourceResolver {
1137 fn thread_pool(&self) -> Option<&Arc<rayon::ThreadPool>> {
1140 None
1141 }
1142
1143 fn cancellation_token(&self) -> Option<Arc<std::sync::atomic::AtomicBool>> {
1145 None
1146 }
1147
1148 fn chunk_hint(&self) -> Option<usize> {
1150 None
1151 }
1152
1153 fn resolve_range_view<'c>(
1156 &'c self,
1157 _reference: &ReferenceType,
1158 _current_sheet: &str,
1159 ) -> Result<RangeView<'c>, ExcelError> {
1160 Err(ExcelError::new(ExcelErrorKind::NImpl))
1161 }
1162
1163 fn resolve_cell_reference_value(
1168 &self,
1169 sheet: Option<&str>,
1170 row: u32,
1171 col: u32,
1172 current_sheet: &str,
1173 ) -> Result<LiteralValue, ExcelError> {
1174 let reference = ReferenceType::Cell {
1175 sheet: sheet.map(str::to_string),
1176 row,
1177 col,
1178 row_abs: true,
1179 col_abs: true,
1180 };
1181 let view = self.resolve_range_view(&reference, current_sheet)?;
1182 Ok(view.as_1x1().unwrap_or(LiteralValue::Empty))
1183 }
1184
1185 fn locale(&self) -> crate::locale::Locale {
1187 crate::locale::Locale::invariant()
1188 }
1189
1190 fn clock(&self) -> &dyn crate::timezone::ClockProvider {
1194 static DEFAULT_CLOCK: std::sync::OnceLock<crate::timezone::SystemClock> =
1195 std::sync::OnceLock::new();
1196 DEFAULT_CLOCK.get_or_init(|| {
1197 crate::timezone::SystemClock::new(crate::timezone::TimeZoneSpec::default())
1198 })
1199 }
1200
1201 fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
1205 self.clock().timezone()
1206 }
1207
1208 fn volatile_level(&self) -> VolatileLevel {
1210 VolatileLevel::Always
1211 }
1212
1213 fn workbook_seed(&self) -> u64 {
1215 0xF0F0_D0D0_AAAA_5555
1216 }
1217
1218 fn recalc_epoch(&self) -> u64 {
1220 0
1221 }
1222
1223 fn used_rows_for_columns(
1228 &self,
1229 _sheet: &str,
1230 _start_col: u32,
1231 _end_col: u32,
1232 ) -> Option<(u32, u32)> {
1233 None
1234 }
1235
1236 fn used_cols_for_rows(
1239 &self,
1240 _sheet: &str,
1241 _start_row: u32,
1242 _end_row: u32,
1243 ) -> Option<(u32, u32)> {
1244 None
1245 }
1246
1247 fn sheet_bounds(&self, _sheet: &str) -> Option<(u32, u32)> {
1249 None
1250 }
1251
1252 fn data_snapshot_id(&self) -> u64 {
1254 0
1255 }
1256
1257 fn backend_caps(&self) -> BackendCaps {
1259 BackendCaps::default()
1260 }
1261
1262 fn date_system(&self) -> crate::engine::DateSystem {
1267 crate::engine::DateSystem::Excel1900
1268 }
1269
1270 fn build_criteria_mask(
1273 &self,
1274 _view: &RangeView<'_>,
1275 _col_in_view: usize,
1276 _pred: &crate::args::CriteriaPredicate,
1277 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1278 None
1279 }
1280
1281 fn build_row_visibility_mask(
1284 &self,
1285 _view: &RangeView<'_>,
1286 _mode: VisibilityMaskMode,
1287 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1288 None
1289 }
1290}
1291
1292#[derive(Copy, Clone, Debug, Default)]
1294pub struct BackendCaps {
1295 pub streaming: bool,
1297 pub used_region: bool,
1299 pub write: bool,
1301 pub tables: bool,
1303 pub async_stream: bool,
1305}
1306
1307#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1310pub enum VolatileLevel {
1311 Always,
1313 OnRecalc,
1315 OnOpen,
1317}
1318
1319pub trait FunctionContext<'ctx> {
1321 fn locale(&self) -> crate::locale::Locale;
1322 fn timezone(&self) -> &crate::timezone::TimeZoneSpec;
1323 fn clock(&self) -> &dyn crate::timezone::ClockProvider;
1324 fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>>;
1325 fn cancellation_token(&self) -> Option<Arc<std::sync::atomic::AtomicBool>>;
1326 fn chunk_hint(&self) -> Option<usize>;
1327
1328 fn current_sheet(&self) -> &str;
1330
1331 fn volatile_level(&self) -> VolatileLevel;
1332 fn workbook_seed(&self) -> u64;
1333 fn recalc_epoch(&self) -> u64;
1334 fn current_cell(&self) -> Option<CellRef>;
1335
1336 fn resolve_range_view(
1338 &self,
1339 _reference: &ReferenceType,
1340 _current_sheet: &str,
1341 ) -> Result<RangeView<'ctx>, ExcelError>;
1342
1343 fn rng_for_current(&self, fn_salt: u64) -> rand::rngs::SmallRng {
1347 use crate::rng::{compose_seed, small_rng_from_lanes};
1348 let (sheet_id, row, col) = self
1349 .current_cell()
1350 .map(|c| (c.sheet_id as u32, c.coord.row(), c.coord.col()))
1351 .unwrap_or((0, 0, 0));
1352 let epoch = match self.volatile_level() {
1354 VolatileLevel::OnRecalc => self.recalc_epoch(),
1355 _ => 0,
1356 };
1357 let (l0, l1) = compose_seed(self.workbook_seed(), sheet_id, row, col, fn_salt, epoch);
1358 small_rng_from_lanes(l0, l1)
1359 }
1360
1361 fn date_system(&self) -> crate::engine::DateSystem {
1363 crate::engine::DateSystem::Excel1900
1364 }
1365
1366 fn get_criteria_mask(
1369 &self,
1370 _view: &RangeView<'_>,
1371 _col_in_view: usize,
1372 _pred: &crate::args::CriteriaPredicate,
1373 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1374 None
1375 }
1376
1377 fn get_row_visibility_mask(
1379 &self,
1380 _view: &RangeView<'_>,
1381 _mode: VisibilityMaskMode,
1382 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1383 None
1384 }
1385}
1386
1387pub struct DefaultFunctionContext<'a> {
1389 pub base: &'a dyn EvaluationContext,
1390 pub current: Option<CellRef>,
1391 pub current_sheet: &'a str,
1392}
1393
1394impl<'a> DefaultFunctionContext<'a> {
1395 pub fn new(
1396 base: &'a dyn EvaluationContext,
1397 current: Option<CellRef>,
1398 current_sheet: &'a str,
1399 ) -> Self {
1400 Self {
1401 base,
1402 current,
1403 current_sheet,
1404 }
1405 }
1406
1407 pub fn new_with_sheet(
1408 base: &'a dyn EvaluationContext,
1409 current: Option<CellRef>,
1410 current_sheet: &'a str,
1411 ) -> Self {
1412 Self::new(base, current, current_sheet)
1413 }
1414}
1415
1416impl<'a> FunctionContext<'a> for DefaultFunctionContext<'a> {
1417 fn locale(&self) -> crate::locale::Locale {
1418 self.base.locale()
1419 }
1420
1421 fn current_sheet(&self) -> &str {
1422 self.current_sheet
1423 }
1424 fn timezone(&self) -> &crate::timezone::TimeZoneSpec {
1425 self.base.timezone()
1426 }
1427
1428 fn clock(&self) -> &dyn crate::timezone::ClockProvider {
1429 self.base.clock()
1430 }
1431 fn thread_pool(&self) -> Option<&std::sync::Arc<rayon::ThreadPool>> {
1432 self.base.thread_pool()
1433 }
1434 fn cancellation_token(&self) -> Option<Arc<std::sync::atomic::AtomicBool>> {
1435 self.base.cancellation_token()
1436 }
1437 fn chunk_hint(&self) -> Option<usize> {
1438 self.base.chunk_hint()
1439 }
1440
1441 fn volatile_level(&self) -> VolatileLevel {
1442 self.base.volatile_level()
1443 }
1444 fn workbook_seed(&self) -> u64 {
1445 self.base.workbook_seed()
1446 }
1447 fn recalc_epoch(&self) -> u64 {
1448 self.base.recalc_epoch()
1449 }
1450 fn current_cell(&self) -> Option<CellRef> {
1451 self.current
1452 }
1453
1454 fn resolve_range_view(
1455 &self,
1456 reference: &ReferenceType,
1457 current_sheet: &str,
1458 ) -> Result<RangeView<'a>, ExcelError> {
1459 self.base.resolve_range_view(reference, current_sheet)
1460 }
1461
1462 fn date_system(&self) -> crate::engine::DateSystem {
1465 self.base.date_system()
1466 }
1467
1468 fn get_criteria_mask(
1469 &self,
1470 view: &RangeView<'_>,
1471 col_in_view: usize,
1472 pred: &crate::args::CriteriaPredicate,
1473 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1474 self.base.build_criteria_mask(view, col_in_view, pred)
1475 }
1476
1477 fn get_row_visibility_mask(
1478 &self,
1479 view: &RangeView<'_>,
1480 mode: VisibilityMaskMode,
1481 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
1482 self.base.build_row_visibility_mask(view, mode)
1483 }
1484}