1mod edit;
2mod functions;
3
4pub use edit::get_word_boundaries_from_str;
5
6use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, HashSet, VecDeque};
8use web_time::Instant;
9
10use super::cell::{
11 CellRef, CellType, Dependency, EngineError, EvalError, TextCellRef, generate_unique_id,
12};
13use super::column::DataColumn;
14use super::result_data::ResultData;
15#[derive(Default)]
17pub struct Context<'a> {
18 pub sheets: HashMap<String, &'a Sheet>,
20 pub pivot_tables: &'a [crate::core::pivot::PivotTable],
26 pub sheet_order: Vec<String>,
32}
33
34impl<'a> Context<'a> {
35 pub fn new() -> Self {
37 Self {
38 sheets: HashMap::new(),
39 pivot_tables: &[],
40 sheet_order: Vec::new(),
41 }
42 }
43
44 pub fn add_table(&mut self, name: String, sheet: &'a Sheet) {
46 self.sheets.insert(name, sheet);
47 }
48}
49
50enum LetScope<'a> {
58 Empty,
59 Bound {
60 name: &'a str,
61 value: &'a ResultData,
62 parent: &'a LetScope<'a>,
63 },
64}
65
66impl<'a> LetScope<'a> {
67 fn get(&self, name: &str) -> Option<&ResultData> {
68 match self {
69 LetScope::Empty => None,
70 LetScope::Bound {
71 name: n,
72 value,
73 parent,
74 } => {
75 if n.eq_ignore_ascii_case(name) {
76 Some(value)
77 } else {
78 parent.get(name)
79 }
80 }
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq)]
87pub enum Direction {
88 None,
90 Up,
92 Down,
94 Left,
96 Right,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct Sheet {
136 #[serde(default = "generate_unique_id")]
139 pub id: u64,
140 pub name: String,
142 pub(crate) columns: Vec<DataColumn>,
149 #[serde(default)]
151 pub(crate) row_heights: Vec<Option<f64>>,
152 #[serde(default)]
154 pub tables: Vec<crate::core::table::ExcelTable>,
155 #[serde(skip, default)]
158 pub dependencies: HashMap<Dependency, HashSet<CellRef>>,
159 #[serde(skip, default)]
162 pub dependencies_rev: HashMap<CellRef, HashSet<Dependency>>,
163 #[serde(skip)]
166 pub uncommitted_actions: Vec<crate::core::SheetAction>,
167 #[serde(default)]
169 pub locale: crate::core::locale::Locale,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct SheetInit {
176 #[serde(default)]
178 pub id: Option<u64>,
179 pub name: Option<String>,
181 pub rows: usize,
183 pub cols: usize,
185}
186
187impl Default for SheetInit {
188 fn default() -> Self {
189 Self {
190 id: None,
191 name: None,
192 rows: 10,
193 cols: 5,
194 }
195 }
196}
197
198#[derive(Clone, Copy, PartialEq, Eq)]
200enum BlankPolicy {
201 Zero,
203 Skip,
205 Reject,
207}
208
209impl Sheet {
210 pub fn new(args: SheetInit) -> Sheet {
213 let SheetInit {
214 id,
215 name,
216 rows,
217 cols,
218 } = args;
219 let sheet_id = id.unwrap_or_else(generate_unique_id);
220 let sheet_name = name.unwrap_or_else(|| "table_1".to_string());
221
222 let mut columns = Vec::with_capacity(cols);
223 for _ in 0..cols {
224 columns.push(DataColumn::new(rows));
225 }
226
227 let mut uncommitted_actions = Vec::new();
228 for c in 0..cols {
229 for r in 0..rows {
230 uncommitted_actions.push(crate::core::SheetAction::SetCellSrc {
231 sheet_name: sheet_name.clone(),
232 col: c,
233 row: r,
234 src: String::new(),
235 });
236 }
237 }
238
239 Self {
240 id: sheet_id,
241 name: sheet_name,
242 columns,
243 row_heights: vec![None; rows],
244 tables: Vec::new(),
245 dependencies: HashMap::new(),
246 dependencies_rev: HashMap::new(),
247 uncommitted_actions,
248 locale: crate::core::locale::Locale::default(),
249 }
250 }
251
252 pub fn setup_after_deserialization(&mut self) {
259 for col in &mut self.columns {
260 col.rebuild_after_load();
261 }
262 let row_count = self.row_count();
263 self.row_heights.resize(row_count, None);
264 self.mark_all_dirty();
265 }
266
267 pub(crate) fn get_all_sheets_for_compilation(&self, context: Option<&Context>) -> Vec<Sheet> {
271 let mut list = vec![self.clone()];
272 let mut seen = std::collections::HashSet::new();
273 seen.insert(self.id);
274 if let Some(ctx) = context {
275 for sheet in ctx.sheets.values() {
276 if !seen.contains(&sheet.id) {
277 seen.insert(sheet.id);
278 list.push((*sheet).clone());
279 }
280 }
281 }
282 list
283 }
284
285 pub fn mark_all_dirty(&mut self) {
290 for col in &mut self.columns {
291 col.dirty_indices.clear();
292 col.dirty_indices.extend(0..col.src.len());
293 }
294 }
295
296 pub fn commit(&mut self, context: Option<&Context>) -> Result<HashSet<CellRef>, EngineError> {
298 let mut queue: VecDeque<CellRef> = VecDeque::new();
299 let mut queue_set: HashSet<CellRef> = HashSet::new();
300 let mut updated_cells: HashSet<CellRef> = HashSet::new();
301
302 for (col_idx, col_data) in self.columns.iter_mut().enumerate() {
303 for row_idx in &col_data.dirty_indices {
304 let cell = CellRef::new(*row_idx, col_idx);
305 queue.push_back(cell);
306 queue_set.insert(cell);
307 updated_cells.insert(cell);
308 }
309 col_data.dirty_indices.clear();
310 }
311
312 let initial_queue_len = queue.len();
313 if initial_queue_len == 0 {
314 return Ok(updated_cells);
315 }
316
317 let start_commit = Instant::now();
318 log::info!(
319 "Sheet '{}' commit starting for {} dirty cells",
320 self.name,
321 initial_queue_len
322 );
323 let max_ops = 10000.max(initial_queue_len * 3);
324 let mut ops = 0;
325
326 let mut sheets_for_compilation = self.get_all_sheets_for_compilation(context);
327 let mut last_log_time = Instant::now();
328
329 while let Some(cell_ref) = queue.pop_front() {
330 queue_set.remove(&cell_ref);
331 ops += 1;
332 if ops > max_ops {
333 println!("Circular dependency or too many updates detected");
334 break;
335 }
336
337 if ops % 50000 == 0 {
338 log::info!(
339 "Sheet '{}' commit progress: {}/{} cells processed ({:.2?})",
340 self.name,
341 ops,
342 initial_queue_len,
343 last_log_time.elapsed()
344 );
345 last_log_time = Instant::now();
346 }
347
348 let cell_type_hint = self
349 .columns
350 .get(cell_ref.col)
351 .and_then(|c| c.cell_types.get(cell_ref.row).copied())
352 .unwrap_or(CellType::Auto);
353
354 let mut detected_num_format: Option<String> = None;
355 let (result, new_deps, compiled_to_cache, final_cell_type) = {
356 let src = self.get_src_str_ref(&cell_ref).unwrap_or("");
357 if cell_type_hint == CellType::String {
358 let val = if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
359 src[1..src.len() - 1].to_string()
360 } else {
361 src.to_string()
362 };
363 (ResultData::String(val), vec![], None, CellType::String)
364 } else if !src.starts_with('=') && cell_type_hint != CellType::Formula {
365 let (res, c_type) = if let Some(stripped) = src.strip_prefix('\'') {
366 (ResultData::String(stripped.to_string()), CellType::String)
367 } else if src.is_empty() {
368 (ResultData::None, CellType::Empty)
369 } else if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
370 (
371 ResultData::String(src[1..src.len() - 1].to_string()),
372 CellType::String,
373 )
374 } else if let Ok(i) = src.trim().parse::<i64>() {
375 (ResultData::Integer(i), CellType::Number)
376 } else if let Ok(f) = src.trim().parse::<f64>()
377 && f.is_finite()
378 {
379 (ResultData::Float(f), CellType::Number)
380 } else if crate::core::engine::result_data::is_excel_error_code(src) {
381 (ResultData::Error(src.to_uppercase()), CellType::Error)
382 } else if src.eq_ignore_ascii_case("true") {
383 (ResultData::Boolean(true), CellType::Boolean)
384 } else if src.eq_ignore_ascii_case("false") {
385 (ResultData::Boolean(false), CellType::Boolean)
386 } else if let Some((date, format)) = crate::core::date::parse_date_with_locale(
387 src.trim_matches(' '),
388 &self.locale,
389 ) {
390 detected_num_format = Some(format.to_format_code());
391 (
392 ResultData::Float(crate::core::date::date_to_excel_serial(date)),
393 CellType::Number,
394 )
395 } else if let Some(f) = crate::core::date_fn::parse_time_fraction(src) {
396 (ResultData::Float(f), CellType::Number)
397 } else {
398 (ResultData::String(src.to_string()), CellType::String)
399 };
400 (res, vec![], None, c_type)
401 } else {
402 let compiled =
403 crate::core::parser::compile_formula(src, &sheets_for_compilation);
404 let eval_src =
405 crate::core::parser::serialize_formula(&compiled, &sheets_for_compilation);
406 let (res, deps) = match self.eval_with_row(
407 &eval_src,
408 context,
409 Some(cell_ref.row),
410 Some(cell_ref.col),
411 ) {
412 Ok(r) => r,
413 Err(e) => (ResultData::Error(e.to_string()), vec![]),
414 };
415 let final_res = if let ResultData::None = res {
416 ResultData::Float(0.0)
417 } else {
418 res
419 };
420 (final_res, deps, Some(compiled), CellType::Formula)
421 }
422 };
423
424 if let Some(src_str) = self.get_src_str_ref(&cell_ref)
425 && let Some(stripped) = src_str.strip_prefix('\'')
426 {
427 let stripped_str = stripped.to_string();
428 if let Some(col) = self.columns.get_mut(cell_ref.col)
429 && cell_ref.row < col.src.len()
430 {
431 col.src[cell_ref.row] = stripped_str;
432 }
433 }
434
435 if let Some(col) = self.columns.get_mut(cell_ref.col)
436 && cell_ref.row < col.compiled_src.len()
437 {
438 col.compiled_src[cell_ref.row] = compiled_to_cache.unwrap_or_default();
439 }
440
441 if let Some(old_deps) = self.dependencies_rev.remove(&cell_ref) {
442 for provider in old_deps {
443 if let Some(dependents) = self.dependencies.get_mut(&provider) {
444 dependents.remove(&cell_ref);
445 }
446 }
447 }
448
449 if !new_deps.is_empty() {
450 let mut new_deps_set = HashSet::new();
451 for provider in new_deps {
452 new_deps_set.insert(provider.clone());
453 self.dependencies
454 .entry(provider)
455 .or_default()
456 .insert(cell_ref);
457 }
458 self.dependencies_rev.insert(cell_ref, new_deps_set);
459 }
460
461 let inherited = if detected_num_format.is_some()
462 || !matches!(result, ResultData::Float(_) | ResultData::Integer(_))
463 {
464 None
465 } else {
466 self.get_src_str_ref(&cell_ref)
467 .and_then(|src| src.strip_prefix('='))
468 .and_then(|body| crate::core::parser::parse_excel_formula(body).ok())
469 .and_then(|ast| self.inherited_date_format(&ast))
470 };
471 if let Some(code) = detected_num_format.or(inherited) {
472 let existing = self
473 .get_cell_style(cell_ref.row, cell_ref.col)
474 .and_then(|s| s.num_format.clone());
475 if existing.is_none() {
476 self.update_cell_style(cell_ref.row, cell_ref.col, |style| {
477 style.num_format = Some(code);
478 });
479 }
480 }
481
482 if let Some(col) = self.columns.get_mut(cell_ref.col)
483 && cell_ref.row < col.data.len()
484 {
485 col.cell_types[cell_ref.row] = final_cell_type;
486 col.data.set(cell_ref.row, result.clone());
487 updated_cells.insert(cell_ref);
488 }
489 if let Some(comp_sheet) = sheets_for_compilation
490 .iter_mut()
491 .find(|s| s.name == self.name)
492 && let Some(col) = comp_sheet.columns.get_mut(cell_ref.col)
493 && cell_ref.row < col.data.len()
494 {
495 col.cell_types[cell_ref.row] = final_cell_type;
496 col.data.set(cell_ref.row, result);
497 }
498
499 let local_dep_key = Dependency::Local(cell_ref);
500 if let Some(dependents) = self.dependencies.get(&local_dep_key) {
501 for dependent in dependents {
502 if !queue_set.contains(dependent) {
503 queue.push_back(*dependent);
504 queue_set.insert(*dependent);
505 }
506 }
507 }
508
509 let local_col_dep_key = Dependency::LocalColumn(cell_ref.col);
510 if let Some(dependents) = self.dependencies.get(&local_col_dep_key) {
511 for dependent in dependents {
512 if !queue_set.contains(dependent) {
513 queue.push_back(*dependent);
514 queue_set.insert(*dependent);
515 }
516 }
517 }
518 }
519 if initial_queue_len > 0 {
520 log::info!(
521 "Sheet '{}' commit finished. Processed {} cell updates. Total time: {:.2?}",
522 self.name,
523 ops,
524 start_commit.elapsed()
525 );
526 }
527 Ok(updated_cells)
528 }
529
530 pub fn eval_with_row(
543 &self,
544 input: &str,
545 context: Option<&Context>,
546 row: Option<usize>,
547 col: Option<usize>,
548 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
549 if input.is_empty() {
550 return Ok((ResultData::None, vec![]));
551 }
552 if let Some(formula) = input.strip_prefix('=') {
553 self.eval_excel(formula, context, row, col)
554 } else {
555 if let Ok(i) = input.parse::<i64>() {
556 Ok((ResultData::Integer(i), vec![]))
557 } else if let Ok(f) = input.parse::<f64>() {
558 Ok((ResultData::Float(f), vec![]))
559 } else if let Ok(b) = input.parse::<bool>() {
560 Ok((ResultData::Boolean(b), vec![]))
561 } else {
562 Ok((ResultData::String(input.to_string()), vec![]))
563 }
564 }
565 }
566
567 pub fn eval(
580 &self,
581 input: &str,
582 context: Option<&Context>,
583 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
584 self.eval_with_row(input, context, None, None)
585 }
586
587 fn eval_excel(
588 &self,
589 code: &str,
590 context: Option<&Context>,
591 row: Option<usize>,
592 col: Option<usize>,
593 ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
594 let ast = crate::core::parser::parse_excel_formula(code)
595 .map_err(|e| EngineError::EvalError(EvalError::UnknownFunction(e)))?;
596
597 let mut deps = Vec::new();
598 let result = match self.evaluate_ast(&ast, context, row, col, &mut deps, &LetScope::Empty) {
599 Ok(r) => r,
600 Err(EngineError::EvalError(EvalError::UnknownFunction(err_str)))
601 if err_str.starts_with('#') =>
602 {
603 ResultData::Error(err_str)
604 }
605 Err(e) => return Err(e),
606 };
607 Ok((result, deps))
608 }
609
610 fn evaluate_ast(
611 &self,
612 ast: &crate::core::parser::Expr,
613 context: Option<&Context>,
614 row: Option<usize>,
615 col: Option<usize>,
616 deps: &mut Vec<Dependency>,
617 scope: &LetScope<'_>,
618 ) -> Result<ResultData, EngineError> {
619 use crate::core::SheetSection;
620 use crate::core::parser::Expr;
621 use crate::core::parser::Op;
622
623 match ast {
624 Expr::Number(n) => Ok(ResultData::Float(*n)),
625 Expr::String(s) => Ok(ResultData::String(s.clone())),
626 Expr::Boolean(b) => Ok(ResultData::Boolean(*b)),
627 Expr::Error(code) => Ok(ResultData::Error(code.to_string())),
628 Expr::Identifier(name) => match scope.get(name) {
629 Some(val) => Ok(val.clone()),
630 None => Ok(ResultData::Error("#NAME?".to_string())),
631 },
632 Expr::StructuredRef {
633 sheet,
634 column,
635 is_this_row,
636 section,
637 } => {
638 let ref_name = match sheet {
639 Some(name) => name.clone(),
640 None => self.name.clone(),
641 };
642
643 let mut found: Option<(&Sheet, &crate::core::table::ExcelTable)> =
644 self.find_table(&ref_name).map(|t| (self, t));
645 if found.is_none()
646 && let Some(ctx) = context
647 {
648 for s in ctx.sheets.values() {
649 if let Some(t) = s.find_table(&ref_name) {
650 found = Some((s, t));
651 break;
652 }
653 }
654 }
655
656 if let Some((table_sheet, excel_table)) = found {
657 let is_self = table_sheet.name == self.name;
658 let sheet_name = table_sheet.name.clone();
659
660 let col_indices: Vec<(usize, usize)> = if let Some(col_name) = column {
661 let local = excel_table.local_column_index(col_name).ok_or_else(|| {
662 EngineError::EvalError(EvalError::UnknownFunction(format!(
663 "Column not found: {}",
664 col_name
665 )))
666 })?;
667 vec![(local, excel_table.start_col + local)]
668 } else {
669 (0..excel_table.columns.len())
670 .map(|local| (local, excel_table.start_col + local))
671 .collect()
672 };
673 let is_whole_table = column.is_none();
674
675 match section {
676 SheetSection::Headers => {
677 let names: Vec<ResultData> = col_indices
678 .iter()
679 .map(|&(local, _)| {
680 ResultData::String(
681 excel_table.columns.get(local).cloned().unwrap_or_default(),
682 )
683 })
684 .collect();
685 if is_whole_table {
686 Ok(ResultData::List(names))
687 } else {
688 Ok(names.into_iter().next().unwrap_or(ResultData::None))
689 }
690 }
691 SheetSection::Totals => {
692 if let Some(totals_row) = excel_table.totals_row() {
693 let mut results = Vec::new();
694 for &(_, col_idx) in &col_indices {
695 let cell_ref = CellRef::new(totals_row, col_idx);
696 if is_self {
697 deps.push(Dependency::Local(cell_ref));
698 } else {
699 deps.push(Dependency::Remote {
700 sheet: sheet_name.clone(),
701 cell: cell_ref,
702 });
703 }
704 results.push(table_sheet.get_result_data(&cell_ref));
705 }
706 if is_whole_table {
707 Ok(ResultData::List(results))
708 } else {
709 Ok(results.into_iter().next().unwrap_or(ResultData::None))
710 }
711 } else {
712 Ok(ResultData::None)
713 }
714 }
715 SheetSection::Data | SheetSection::All => {
716 if *is_this_row {
717 let r = row.ok_or_else(|| {
718 EngineError::EvalError(EvalError::UnknownFunction(
719 "This row reference cannot be evaluated without row context"
720 .to_string(),
721 ))
722 })?;
723 let mut results = Vec::new();
724 for &(_, col_idx) in &col_indices {
725 let cell_ref = CellRef::new(r, col_idx);
726 if is_self {
727 deps.push(Dependency::Local(cell_ref));
728 } else {
729 deps.push(Dependency::Remote {
730 sheet: sheet_name.clone(),
731 cell: cell_ref,
732 });
733 }
734 results.push(table_sheet.get_result_data(&cell_ref));
735 }
736 if is_whole_table {
737 Ok(ResultData::List(results))
738 } else {
739 Ok(results.into_iter().next().unwrap_or(ResultData::None))
740 }
741 } else {
742 let mut results = Vec::new();
743 for &(_, col_idx) in &col_indices {
744 for r in
745 excel_table.data_start_row()..=excel_table.data_end_row()
746 {
747 let cell_ref = CellRef::new(r, col_idx);
748 if is_self {
749 deps.push(Dependency::Local(cell_ref));
750 } else {
751 deps.push(Dependency::Remote {
752 sheet: sheet_name.clone(),
753 cell: cell_ref,
754 });
755 }
756 results.push(table_sheet.get_result_data(&cell_ref));
757 }
758 }
759 Ok(ResultData::List(results))
760 }
761 }
762 }
763 } else {
764 let sheet_name = ref_name;
765 let is_self = sheet_name == self.name;
766
767 let target_sheet = if is_self {
768 self
769 } else if let Some(ctx) = context {
770 if let Some(sheet) = ctx.sheets.get(&sheet_name) {
771 sheet
772 } else {
773 return Err(EngineError::EvalError(EvalError::UnknownFunction(
774 format!("Sheet not found: {}", sheet_name),
775 )));
776 }
777 } else {
778 return Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
779 "No context to resolve sheet reference: {}",
780 sheet_name
781 ))));
782 };
783
784 let col_indices: Vec<usize> = if let Some(col_name) = column {
785 let pos = target_sheet
786 .columns
787 .iter()
788 .position(|c| c.name == *col_name)
789 .ok_or_else(|| {
790 EngineError::EvalError(EvalError::UnknownFunction(format!(
791 "Column not found: {}",
792 col_name
793 )))
794 })?;
795 vec![pos]
796 } else {
797 (0..target_sheet.columns.len()).collect()
798 };
799 let is_whole_table = column.is_none();
800
801 match section {
802 SheetSection::Headers => {
803 let names: Vec<ResultData> = col_indices
804 .iter()
805 .map(|&idx| {
806 ResultData::String(
807 target_sheet
808 .columns
809 .get(idx)
810 .map(|c| c.name.clone())
811 .unwrap_or_default(),
812 )
813 })
814 .collect();
815 if is_whole_table {
816 Ok(ResultData::List(names))
817 } else {
818 Ok(names.into_iter().next().unwrap_or(ResultData::None))
819 }
820 }
821 SheetSection::Totals => Ok(ResultData::None),
822 SheetSection::Data | SheetSection::All => {
823 if *is_this_row {
824 let r = row.ok_or_else(|| {
825 EngineError::EvalError(EvalError::UnknownFunction(
826 "This row reference cannot be evaluated without row context"
827 .to_string(),
828 ))
829 })?;
830 let mut results = Vec::new();
831 for &col_idx in &col_indices {
832 let cell_ref = CellRef::new(r, col_idx);
833 if is_self {
834 deps.push(Dependency::Local(cell_ref));
835 } else {
836 deps.push(Dependency::Remote {
837 sheet: sheet_name.clone(),
838 cell: cell_ref,
839 });
840 }
841 results.push(target_sheet.get_result_data(&cell_ref));
842 }
843 if is_whole_table {
844 Ok(ResultData::List(results))
845 } else {
846 Ok(results.into_iter().next().unwrap_or(ResultData::None))
847 }
848 } else {
849 let mut results = Vec::new();
850 for &col_idx in &col_indices {
851 if is_self {
852 deps.push(Dependency::LocalColumn(col_idx));
853 } else {
854 deps.push(Dependency::RemoteColumn {
855 sheet: sheet_name.clone(),
856 col: col_idx,
857 });
858 }
859 for r in 0..target_sheet.row_count() {
860 let cell_ref = CellRef::new(r, col_idx);
861 results.push(target_sheet.get_result_data(&cell_ref));
862 }
863 }
864 Ok(ResultData::List(results))
865 }
866 }
867 }
868 }
869 }
870 Expr::CellRef {
871 sheet,
872 row: r_val,
873 col,
874 ..
875 } => {
876 let cell_ref = CellRef::new(*r_val, *col);
877 let is_self = match sheet {
878 Some(name) => name == &self.name,
879 None => true,
880 };
881
882 if is_self {
883 deps.push(Dependency::Local(cell_ref));
884 Ok(self.get_result_data(&cell_ref))
885 } else {
886 let name = sheet.as_ref().unwrap().clone();
887 deps.push(Dependency::Remote {
888 sheet: name.clone(),
889 cell: cell_ref,
890 });
891
892 if let Some(ctx) = context {
893 if let Some(t) = ctx.sheets.get(&name) {
894 Ok(t.get_result_data(&cell_ref))
895 } else {
896 Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
897 "Sheet not found: {}",
898 name
899 ))))
900 }
901 } else {
902 Err(EngineError::EvalError(EvalError::UnknownFunction(
903 "No context to resolve sheet reference".to_string(),
904 )))
905 }
906 }
907 }
908 Expr::RangeRef {
909 sheet,
910 start_row,
911 start_col,
912 end_row,
913 end_col,
914 ..
915 } => {
916 let is_self = match sheet {
917 Some(name) => name == &self.name,
918 None => true,
919 };
920
921 let target_sheet = if is_self {
922 Some(self)
923 } else {
924 context.and_then(|ctx| ctx.sheets.get(sheet.as_ref().unwrap()).copied())
925 };
926
927 let actual_end_row = if *end_row == usize::MAX {
928 target_sheet
929 .map(|t| t.row_count().saturating_sub(1))
930 .unwrap_or(0)
931 } else {
932 *end_row
933 };
934 let actual_end_col = if *end_col == usize::MAX {
935 target_sheet
936 .map(|t| t.col_count().saturating_sub(1))
937 .unwrap_or(0)
938 } else {
939 *end_col
940 };
941
942 let is_col_range = *end_row == usize::MAX;
943
944 let mut seen_col_deps: HashSet<usize> = HashSet::new();
945
946 let mut results = Vec::new();
947 for r in *start_row..=actual_end_row {
948 for c in *start_col..=actual_end_col {
949 let cell_ref = CellRef::new(r, c);
950 if is_self {
951 if is_col_range {
952 if seen_col_deps.insert(c) {
953 let col_dep = Dependency::LocalColumn(c);
954 if !deps.contains(&col_dep) {
955 deps.push(col_dep);
956 }
957 }
958 } else {
959 deps.push(Dependency::Local(cell_ref));
960 }
961 if row == Some(r) && col == Some(c) {
962 results.push(ResultData::None);
963 } else {
964 results.push(self.get_result_data(&cell_ref));
965 }
966 } else {
967 let name = sheet.as_ref().unwrap().clone();
968 if is_col_range {
969 if seen_col_deps.insert(c) {
970 let col_dep = Dependency::RemoteColumn {
971 sheet: name.clone(),
972 col: c,
973 };
974 if !deps.contains(&col_dep) {
975 deps.push(col_dep);
976 }
977 }
978 } else {
979 deps.push(Dependency::Remote {
980 sheet: name.clone(),
981 cell: cell_ref,
982 });
983 }
984 if let Some(ctx) = context {
985 if let Some(t) = ctx.sheets.get(&name) {
986 results.push(t.get_result_data(&cell_ref));
987 } else {
988 return Err(EngineError::EvalError(
989 EvalError::UnknownFunction(format!(
990 "Sheet not found: {}",
991 name
992 )),
993 ));
994 }
995 } else {
996 return Err(EngineError::EvalError(EvalError::UnknownFunction(
997 "No context to resolve sheet reference".to_string(),
998 )));
999 }
1000 }
1001 }
1002 }
1003 Ok(ResultData::List(results))
1004 }
1005 Expr::List(list) => {
1006 let mut results = Vec::new();
1007 for item in list {
1008 results.push(self.evaluate_ast(item, context, row, col, deps, scope)?);
1009 }
1010 Ok(ResultData::List(results))
1011 }
1012 Expr::Slice { expr, start, end } => {
1013 let target_val = self.evaluate_ast(expr, context, row, col, deps, scope)?;
1014 if let ResultData::Error(_) = &target_val {
1015 return Ok(target_val);
1016 }
1017 if let ResultData::List(list) = target_val {
1018 let len = list.len() as isize;
1019 let start_idx = if let Some(start_expr) = start {
1020 let s_val =
1021 self.evaluate_ast(start_expr, context, row, col, deps, scope)?;
1022 if let ResultData::Error(_) = &s_val {
1023 return Ok(s_val);
1024 }
1025 let s = self.to_f64(&s_val).unwrap_or(0.0) as isize;
1026 if s < 0 {
1027 (len + s).max(0) as usize
1028 } else {
1029 s.min(len) as usize
1030 }
1031 } else {
1032 0
1033 };
1034
1035 let end_idx = if let Some(end_expr) = end {
1036 let e_val = self.evaluate_ast(end_expr, context, row, col, deps, scope)?;
1037 if let ResultData::Error(_) = &e_val {
1038 return Ok(e_val);
1039 }
1040 let e = self.to_f64(&e_val).unwrap_or(len as f64) as isize;
1041 if e < 0 {
1042 (len + e).max(0) as usize
1043 } else {
1044 e.min(len) as usize
1045 }
1046 } else {
1047 len as usize
1048 };
1049
1050 let sliced = if start_idx < end_idx && start_idx < list.len() {
1051 list[start_idx..end_idx.min(list.len())].to_vec()
1052 } else {
1053 Vec::new()
1054 };
1055 Ok(ResultData::List(sliced))
1056 } else {
1057 Ok(ResultData::None)
1058 }
1059 }
1060 Expr::UnaryOp { op, expr } => {
1061 let val = self.evaluate_ast(expr, context, row, col, deps, scope)?;
1062 match op {
1063 Op::Sub => match val {
1064 ResultData::Float(f) => Ok(ResultData::Float(-f)),
1065 ResultData::Integer(i) => Ok(ResultData::Integer(-i)),
1066 _ => Err(EngineError::EvalError(EvalError::UnknownFunction(
1067 "Unary minus expects number".to_string(),
1068 ))),
1069 },
1070 _ => Ok(val),
1071 }
1072 }
1073 Expr::BinaryOp { op, left, right } => {
1074 let l_val = self.evaluate_ast(left, context, row, col, deps, scope)?;
1075
1076 match op {
1077 Op::Eq | Op::Ne | Op::Lt | Op::Gt | Op::Le | Op::Ge => {
1078 if let ResultData::Error(_) = &l_val {
1079 return Ok(l_val);
1080 }
1081 let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1082 if let ResultData::Error(_) = &r_val {
1083 return Ok(r_val);
1084 }
1085 let ord = Self::compare_excel_values(&l_val, &r_val);
1086 let b = match op {
1087 Op::Eq => ord.is_eq(),
1088 Op::Ne => !ord.is_eq(),
1089 Op::Lt => ord.is_lt(),
1090 Op::Gt => ord.is_gt(),
1091 Op::Le => ord.is_le(),
1092 Op::Ge => ord.is_ge(),
1093 _ => unreachable!(),
1094 };
1095 Ok(ResultData::Boolean(b))
1096 }
1097 _ => {
1098 if let ResultData::Error(_) = &l_val {
1099 return Ok(l_val);
1100 }
1101 let lf = match self.to_f64(&l_val) {
1102 Some(f) => f,
1103 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1104 };
1105 let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1106 if let ResultData::Error(_) = &r_val {
1107 return Ok(r_val);
1108 }
1109 let rf = match self.to_f64(&r_val) {
1110 Some(f) => f,
1111 None => return Ok(ResultData::Error("#VALUE!".to_string())),
1112 };
1113 match op {
1114 Op::Add => Ok(ResultData::Float(lf + rf)),
1115 Op::Sub => Ok(ResultData::Float(lf - rf)),
1116 Op::Mul => Ok(ResultData::Float(lf * rf)),
1117 Op::Div => {
1118 if rf == 0.0 {
1119 return Ok(ResultData::Error("#DIV/0!".to_string()));
1120 }
1121 Ok(ResultData::Float(lf / rf))
1122 }
1123 Op::Exp => {
1124 if lf == 0.0 && rf == 0.0 {
1125 return Ok(ResultData::Error("#NUM!".to_string()));
1126 }
1127 if lf == 0.0 && rf < 0.0 {
1128 return Ok(ResultData::Error("#DIV/0!".to_string()));
1129 }
1130 if lf < 0.0 {
1131 if rf.fract() != 0.0 || rf.abs() > 1e6 {
1132 return Ok(ResultData::Error("#NUM!".to_string()));
1133 }
1134 let res = lf.powi(rf as i32);
1135 if res.is_nan() || res.is_infinite() {
1136 return Ok(ResultData::Error("#NUM!".to_string()));
1137 }
1138 return Ok(ResultData::Float(res));
1139 }
1140 let res = lf.powf(rf);
1141 if res.is_nan() || res.is_infinite() {
1142 return Ok(ResultData::Error("#NUM!".to_string()));
1143 }
1144 Ok(ResultData::Float(res))
1145 }
1146 _ => unreachable!(),
1147 }
1148 }
1149 }
1150 }
1151 Expr::FunctionCall { name, args } => {
1152 self.evaluate_function(name, args, context, row, col, deps, scope)
1153 }
1154 }
1155 }
1156
1157 fn excel_type_rank(val: &ResultData) -> u8 {
1158 match val {
1159 ResultData::None => 0,
1160 ResultData::Integer(_) | ResultData::Float(_) => 1,
1161 ResultData::String(_) => 2,
1162 ResultData::Boolean(_) => 3,
1163 _ => 4,
1164 }
1165 }
1166
1167 fn compare_excel_values(l: &ResultData, r: &ResultData) -> std::cmp::Ordering {
1168 match (l, r) {
1169 (ResultData::None, ResultData::None) => return std::cmp::Ordering::Equal,
1170 (ResultData::None, ResultData::Integer(b)) => {
1171 return 0.0
1172 .partial_cmp(&(*b as f64))
1173 .unwrap_or(std::cmp::Ordering::Equal);
1174 }
1175 (ResultData::None, ResultData::Float(b)) => {
1176 return 0.0.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal);
1177 }
1178 (ResultData::Integer(a), ResultData::None) => {
1179 return (*a as f64)
1180 .partial_cmp(&0.0)
1181 .unwrap_or(std::cmp::Ordering::Equal);
1182 }
1183 (ResultData::Float(a), ResultData::None) => {
1184 return a.partial_cmp(&0.0).unwrap_or(std::cmp::Ordering::Equal);
1185 }
1186 (ResultData::None, ResultData::String(b)) => {
1187 return "".cmp(b.to_lowercase().as_str());
1188 }
1189 (ResultData::String(a), ResultData::None) => {
1190 return a.to_lowercase().as_str().cmp("");
1191 }
1192 (ResultData::None, ResultData::Boolean(b)) => {
1193 return false.cmp(b);
1194 }
1195 (ResultData::Boolean(a), ResultData::None) => {
1196 return a.cmp(&false);
1197 }
1198 _ => {}
1199 }
1200
1201 let rank_l = Self::excel_type_rank(l);
1202 let rank_r = Self::excel_type_rank(r);
1203 if rank_l != rank_r {
1204 return rank_l.cmp(&rank_r);
1205 }
1206 match (l, r) {
1207 (ResultData::Integer(a), ResultData::Integer(b)) => a.cmp(b),
1208 (ResultData::Float(a), ResultData::Float(b)) => {
1209 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1210 }
1211 (ResultData::Integer(a), ResultData::Float(b)) => (*a as f64)
1212 .partial_cmp(b)
1213 .unwrap_or(std::cmp::Ordering::Equal),
1214 (ResultData::Float(a), ResultData::Integer(b)) => a
1215 .partial_cmp(&(*b as f64))
1216 .unwrap_or(std::cmp::Ordering::Equal),
1217 (ResultData::Boolean(a), ResultData::Boolean(b)) => a.cmp(b),
1218 (ResultData::String(a), ResultData::String(b)) => Self::compare_excel_strings(a, b),
1219 _ => std::cmp::Ordering::Equal,
1220 }
1221 }
1222
1223 fn sort_compare_blanks_last(
1231 l: &ResultData,
1232 r: &ResultData,
1233 sort_order: f64,
1234 ) -> std::cmp::Ordering {
1235 match (matches!(l, ResultData::None), matches!(r, ResultData::None)) {
1236 (true, true) => std::cmp::Ordering::Equal,
1237 (true, false) => std::cmp::Ordering::Greater,
1238 (false, true) => std::cmp::Ordering::Less,
1239 (false, false) => {
1240 let ord = Self::compare_excel_values(l, r);
1241 if sort_order < 0.0 { ord.reverse() } else { ord }
1242 }
1243 }
1244 }
1245
1246 fn is_excel_number_str(s: &str) -> bool {
1247 let s = s.trim();
1248 if s.is_empty() {
1249 return false;
1250 }
1251 let bytes = s.as_bytes();
1252 let first = bytes[0];
1253 if first == b'e' || first == b'E' {
1254 return false;
1255 }
1256 if (first == b'+' || first == b'-') && bytes.len() > 1 {
1257 let second = bytes[1];
1258 if second == b'e' || second == b'E' {
1259 return false;
1260 }
1261 }
1262 true
1263 }
1264
1265 fn compare_excel_strings(a: &str, b: &str) -> std::cmp::Ordering {
1266 let char_weight = |ch: char| -> u32 {
1267 match ch {
1268 ' ' => 0,
1269 '_' => 1,
1270 '-' => 2,
1271 ',' => 3,
1272 ';' => 4,
1273 ':' => 5,
1274 '!' => 6,
1275 '?' => 7,
1276 '.' => 8,
1277 '\'' => 9,
1278 '"' => 10,
1279 '(' => 11,
1280 ')' => 12,
1281 '[' => 13,
1282 ']' => 14,
1283 '{' => 15,
1284 '}' => 16,
1285 '@' => 17,
1286 '*' => 18,
1287 '/' => 19,
1288 '\\' => 20,
1289 '&' => 21,
1290 '#' => 22,
1291 '%' => 23,
1292 '`' => 24,
1293 '^' => 25,
1294 '+' => 26,
1295 '<' => 27,
1296 '=' => 28,
1297 '>' => 29,
1298 '|' => 30,
1299 '~' => 31,
1300 '$' => 32,
1301 '0'..='9' => 33 + (ch as u32 - '0' as u32),
1302 'A'..='Z' => 43 + (ch as u32 - 'A' as u32),
1303 'a'..='z' => 43 + (ch as u32 - 'a' as u32),
1304 _ => ch
1305 .to_lowercase()
1306 .next()
1307 .map(|c| c as u32 + 200)
1308 .unwrap_or(ch as u32 + 200),
1309 }
1310 };
1311
1312 for (ca, cb) in a.chars().zip(b.chars()) {
1313 let wa = char_weight(ca);
1314 let wb = char_weight(cb);
1315 if wa != wb {
1316 return wa.cmp(&wb);
1317 }
1318 }
1319 a.len().cmp(&b.len())
1320 }
1321
1322 pub(crate) fn clean_float(val: f64) -> f64 {
1329 if val == 0.0 || !val.is_finite() {
1330 return val;
1331 }
1332 let abs_val = val.abs();
1333 let exp = abs_val.log10().floor() as i32;
1334 let factor = 10.0f64.powi(15 - 1 - exp);
1335 if factor.is_finite() && factor != 0.0 {
1336 let rounded = (val * factor).round() / factor;
1337 if (val - rounded).abs() <= 1e-14 * abs_val {
1338 return rounded;
1339 }
1340 }
1341 val
1342 }
1343
1344 pub(crate) fn to_f64(&self, val: &ResultData) -> Option<f64> {
1354 match val {
1355 ResultData::None => Some(0.0),
1356 ResultData::Float(f) => Some(*f),
1357 ResultData::Integer(i) => Some(*i as f64),
1358 ResultData::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
1359 ResultData::String(s) => {
1360 let s_trim = s.trim();
1361 if Self::is_excel_number_str(s_trim) {
1362 if let Ok(f) = s_trim.parse::<f64>() {
1363 return Some(f);
1364 }
1365 if let Some((date, _)) =
1366 crate::core::date::parse_date_with_locale(s_trim, &self.locale)
1367 {
1368 return Some(crate::core::date::date_to_excel_serial(date));
1369 }
1370 None
1371 } else if let Some((date, _)) =
1372 crate::core::date::parse_date_with_locale(s_trim, &self.locale)
1373 {
1374 Some(crate::core::date::date_to_excel_serial(date))
1375 } else {
1376 None
1377 }
1378 }
1379 _ => None,
1380 }
1381 }
1382
1383 fn to_f64_arg(&self, arg_opt: Option<&ResultData>, fn_name: &str) -> Result<f64, EngineError> {
1384 let val = arg_opt.ok_or_else(|| {
1385 EngineError::EvalError(EvalError::UnknownFunction(format!(
1386 "{} requires argument",
1387 fn_name
1388 )))
1389 })?;
1390 if let ResultData::Error(e) = val {
1391 return Err(EngineError::EvalError(EvalError::UnknownFunction(
1392 e.clone(),
1393 )));
1394 }
1395 self.to_f64(val).ok_or_else(|| {
1396 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1397 })
1398 }
1399
1400 fn find_error_in_args(args: &[ResultData]) -> Option<ResultData> {
1401 for arg in args {
1402 match arg {
1403 ResultData::Error(_) => return Some(arg.clone()),
1404 ResultData::List(list) => {
1405 if let Some(err) = Self::find_error_in_args(list) {
1406 return Some(err);
1407 }
1408 }
1409 _ => {}
1410 }
1411 }
1412 None
1413 }
1414
1415 fn check_arg_errors(&self, args: &[ResultData], is_direct: &[bool]) -> Option<ResultData> {
1416 for (i, arg) in args.iter().enumerate() {
1417 match arg {
1418 ResultData::Error(_) => return Some(arg.clone()),
1419 ResultData::List(list) => {
1420 if let Some(err) = self.check_arg_errors(list, &[]) {
1421 return Some(err);
1422 }
1423 }
1424 ResultData::String(_)
1425 if is_direct.get(i).copied().unwrap_or(false) && self.to_f64(arg).is_none() =>
1426 {
1427 return Some(ResultData::Error("#VALUE!".to_string()));
1428 }
1429 _ => {}
1430 }
1431 }
1432 None
1433 }
1434
1435 fn sum_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
1436 match arg {
1437 ResultData::Float(f) => *f,
1438 ResultData::Integer(i) => *i as f64,
1439 ResultData::Boolean(b) => {
1440 if is_direct {
1441 if *b { 1.0 } else { 0.0 }
1442 } else {
1443 0.0
1444 }
1445 }
1446 ResultData::String(_) => {
1447 if is_direct {
1448 self.to_f64(arg).unwrap_or(0.0)
1449 } else {
1450 0.0
1451 }
1452 }
1453 ResultData::List(list) => {
1454 let mut sum = 0.0;
1455 for item in list {
1456 sum += self.sum_helper(item, false);
1457 }
1458 sum
1459 }
1460 _ => 0.0,
1461 }
1462 }
1463
1464 fn flatten_finance_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1470 match arg {
1471 ResultData::Float(f) => vec![*f],
1472 ResultData::Integer(i) => vec![*i as f64],
1473 ResultData::Boolean(b) => {
1474 if is_direct {
1475 vec![if *b { 1.0 } else { 0.0 }]
1476 } else {
1477 vec![]
1478 }
1479 }
1480 ResultData::String(_) => {
1481 if is_direct {
1482 self.to_f64(arg).into_iter().collect()
1483 } else {
1484 vec![]
1485 }
1486 }
1487 ResultData::List(list) => list
1488 .iter()
1489 .flat_map(|v| self.flatten_finance_numbers(v, false))
1490 .collect(),
1491 _ => vec![],
1492 }
1493 }
1494
1495 fn flatten_stat_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1496 match arg {
1497 ResultData::Float(f) => vec![*f],
1498 ResultData::Integer(i) => vec![*i as f64],
1499 ResultData::Boolean(b) => {
1500 if is_direct {
1501 vec![if *b { 1.0 } else { 0.0 }]
1502 } else {
1503 vec![]
1504 }
1505 }
1506 ResultData::String(_) => {
1507 if is_direct {
1508 self.to_f64(arg).into_iter().collect()
1509 } else {
1510 vec![]
1511 }
1512 }
1513 ResultData::List(list) => list
1514 .iter()
1515 .flat_map(|v| self.flatten_stat_numbers(v, false))
1516 .collect(),
1517 _ => vec![],
1518 }
1519 }
1520
1521 fn flatten_positional(
1528 &self,
1529 arg: &ResultData,
1530 out: &mut Vec<Option<f64>>,
1531 first_err: &mut Option<String>,
1532 ) {
1533 match arg {
1534 ResultData::List(items) => {
1535 for item in items {
1536 self.flatten_positional(item, out, first_err);
1537 }
1538 }
1539 ResultData::Float(f) => out.push(Some(*f)),
1540 ResultData::Integer(i) => out.push(Some(*i as f64)),
1541 ResultData::Error(e) => {
1542 if first_err.is_none() {
1543 *first_err = Some(e.clone());
1544 }
1545 out.push(None);
1546 }
1547 _ => out.push(None),
1548 }
1549 }
1550
1551 fn positional_numbers(
1552 &self,
1553 arg: Option<&ResultData>,
1554 first_err: &mut Option<String>,
1555 ) -> Vec<Option<f64>> {
1556 let mut out = Vec::new();
1557 if let Some(a) = arg {
1558 self.flatten_positional(a, &mut out, first_err);
1559 }
1560 out
1561 }
1562
1563 fn pair_and_filter(
1586 xs_raw: Vec<Option<f64>>,
1587 ys_raw: Vec<Option<f64>>,
1588 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1589 if xs_raw.len() != ys_raw.len() {
1590 return Err("#N/A".to_string());
1591 }
1592 let mut xs = Vec::with_capacity(xs_raw.len());
1593 let mut ys = Vec::with_capacity(ys_raw.len());
1594 for (x, y) in xs_raw.into_iter().zip(ys_raw) {
1595 if let (Some(x), Some(y)) = (x, y) {
1596 xs.push(x);
1597 ys.push(y);
1598 }
1599 }
1600 Ok((xs, ys))
1601 }
1602
1603 fn paired_args(
1605 &self,
1606 x_arg: Option<&ResultData>,
1607 y_arg: Option<&ResultData>,
1608 ) -> Result<(Vec<f64>, Vec<f64>), String> {
1609 for arg in [x_arg, y_arg].into_iter().flatten() {
1610 let scalar = match arg {
1611 ResultData::List(items) if items.len() == 1 => &items[0],
1612 other => other,
1613 };
1614 if let ResultData::Error(e) = scalar {
1615 return Err(e.clone());
1616 }
1617 if Self::is_empty_scalar_operand(arg) {
1618 return Err("#VALUE!".to_string());
1619 }
1620 }
1621 let mut first_err = None;
1622 let xs_raw = self.positional_numbers(x_arg, &mut first_err);
1623 let ys_raw = self.positional_numbers(y_arg, &mut first_err);
1624 if xs_raw.len() != ys_raw.len() {
1625 return Err("#N/A".to_string());
1626 }
1627 if let Some(e) = first_err {
1628 return Err(e);
1629 }
1630 Self::pair_and_filter(xs_raw, ys_raw)
1631 }
1632
1633 fn flatten_strict_inner(
1664 &self,
1665 arg: &ResultData,
1666 blanks: BlankPolicy,
1667 coerce_text: bool,
1668 out: &mut Vec<f64>,
1669 ) -> Result<(), String> {
1670 match arg {
1671 ResultData::List(items) => {
1672 for item in items {
1673 self.flatten_strict_inner(item, blanks, coerce_text, out)?;
1674 }
1675 Ok(())
1676 }
1677 ResultData::Error(e) => Err(e.clone()),
1678 ResultData::Float(f) => {
1679 out.push(*f);
1680 Ok(())
1681 }
1682 ResultData::Integer(i) => {
1683 out.push(*i as f64);
1684 Ok(())
1685 }
1686 ResultData::None => match blanks {
1687 BlankPolicy::Zero => {
1688 out.push(0.0);
1689 Ok(())
1690 }
1691 BlankPolicy::Skip => Ok(()),
1692 BlankPolicy::Reject => Err("#VALUE!".to_string()),
1693 },
1694 ResultData::String(_) if coerce_text => match self.to_f64(arg) {
1695 Some(f) => {
1696 out.push(f);
1697 Ok(())
1698 }
1699 None => Err("#VALUE!".to_string()),
1700 },
1701 _ => Err("#VALUE!".to_string()),
1702 }
1703 }
1704
1705 fn flatten_strict_numbers(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1706 let mut out = Vec::new();
1707 self.flatten_strict_inner(arg, BlankPolicy::Zero, true, &mut out)?;
1708 Ok(out)
1709 }
1710
1711 fn flatten_skipping_blanks(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1714 let mut out = Vec::new();
1715 if let Some(a) = arg {
1716 self.flatten_strict_inner(a, BlankPolicy::Skip, true, &mut out)?;
1717 }
1718 Ok(out)
1719 }
1720
1721 fn flatten_skipping_blanks_no_text_coercion(
1728 &self,
1729 arg: Option<&ResultData>,
1730 ) -> Result<Vec<f64>, String> {
1731 let mut out = Vec::new();
1732 if let Some(a) = arg {
1733 self.flatten_strict_inner(a, BlankPolicy::Skip, false, &mut out)?;
1734 }
1735 Ok(out)
1736 }
1737
1738 fn flatten_numbers_only(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1741 let mut out = Vec::new();
1742 self.flatten_strict_inner(arg, BlankPolicy::Reject, false, &mut out)?;
1743 Ok(out)
1744 }
1745
1746 fn aggregate_range_number(val: &ResultData) -> Option<f64> {
1755 match val {
1756 ResultData::Float(f) => Some(*f),
1757 ResultData::Integer(i) => Some(*i as f64),
1758 _ => None,
1759 }
1760 }
1761
1762 fn flatten_numbers_only_arg(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1763 match arg {
1764 Some(a) => self.flatten_numbers_only(a),
1765 None => Ok(vec![]),
1766 }
1767 }
1768
1769 fn flatten_args_stat_numbers(
1783 &self,
1784 args: &[ResultData],
1785 is_direct: &[bool],
1786 ) -> Result<Vec<f64>, String> {
1787 let mut out = Vec::new();
1788 for (i, arg) in args.iter().enumerate() {
1789 let direct = is_direct.get(i).copied().unwrap_or(false);
1790 if direct && matches!(arg, ResultData::String(_)) && self.to_f64(arg).is_none() {
1791 return Err("#VALUE!".to_string());
1792 }
1793 out.extend(self.flatten_stat_numbers(arg, direct));
1794 }
1795 Ok(out)
1796 }
1797
1798 fn flatten_stat_numbers_a(
1815 &self,
1816 arg: &ResultData,
1817 is_direct: bool,
1818 ) -> Result<Vec<f64>, String> {
1819 Ok(match arg {
1820 ResultData::Float(f) => vec![*f],
1821 ResultData::Integer(i) => vec![*i as f64],
1822 ResultData::Boolean(b) => vec![if *b { 1.0 } else { 0.0 }],
1823 ResultData::String(_) => {
1824 if is_direct {
1825 match self.to_f64(arg) {
1826 Some(f) => vec![f],
1827 None => return Err("#VALUE!".to_string()),
1828 }
1829 } else {
1830 vec![0.0]
1831 }
1832 }
1833 ResultData::Error(e) => return Err(e.clone()),
1834 ResultData::List(list) => {
1835 let mut out = Vec::new();
1836 for v in list {
1837 out.extend(self.flatten_stat_numbers_a(v, false)?);
1838 }
1839 out
1840 }
1841 ResultData::None => vec![],
1842 _ => vec![0.0],
1843 })
1844 }
1845
1846 fn flatten_args_stat_numbers_a(
1849 &self,
1850 args: &[ResultData],
1851 is_direct: &[bool],
1852 ) -> Result<Vec<f64>, String> {
1853 let mut out = Vec::new();
1854 for (i, arg) in args.iter().enumerate() {
1855 out.extend(
1856 self.flatten_stat_numbers_a(arg, is_direct.get(i).copied().unwrap_or(false))?,
1857 );
1858 }
1859 Ok(out)
1860 }
1861
1862 fn extract_matrix(&self, arg: &ResultData) -> Vec<Vec<f64>> {
1863 match arg {
1864 ResultData::List(list) => {
1865 let mut rows = Vec::new();
1866 for item in list {
1867 match item {
1868 ResultData::List(sub_list) => {
1869 let row: Vec<f64> =
1870 sub_list.iter().flat_map(|v| self.to_f64(v)).collect();
1871 if !row.is_empty() {
1872 rows.push(row);
1873 }
1874 }
1875 _ => {
1876 if let Some(f) = self.to_f64(item) {
1877 rows.push(vec![f]);
1878 }
1879 }
1880 }
1881 }
1882 rows
1883 }
1884 _ => vec![],
1885 }
1886 }
1887
1888 fn matrix_from_arg(
1899 &self,
1900 expr: &crate::core::parser::Expr,
1901 value: &ResultData,
1902 ) -> Vec<Vec<f64>> {
1903 if let ResultData::List(items) = value
1904 && items.iter().any(|i| matches!(i, ResultData::List(_)))
1905 {
1906 return self.extract_matrix(value);
1907 }
1908 fn plain(v: &ResultData) -> Option<f64> {
1909 match v {
1910 ResultData::Float(f) => Some(*f),
1911 ResultData::Integer(i) => Some(*i as f64),
1912 _ => None,
1913 }
1914 }
1915 let items: Vec<&ResultData> = match value {
1916 ResultData::List(items) => items.iter().collect(),
1917 other => vec![other],
1918 };
1919 if items.iter().any(|v| plain(v).is_none()) {
1920 return Vec::new();
1921 }
1922 let flat: Vec<f64> = items.iter().filter_map(|v| plain(v)).collect();
1923 let cols = match Self::range_bounds(expr) {
1924 Some((_, _, start_col, _, end_col)) => end_col.saturating_sub(start_col) + 1,
1925 None => flat.len().max(1),
1926 };
1927 if cols == 0 || !flat.len().is_multiple_of(cols) {
1928 return self.extract_matrix(value);
1929 }
1930 flat.chunks(cols).map(|c| c.to_vec()).collect()
1931 }
1932
1933 fn paired_sum_has_no_numbers(&self, arg: Option<&ResultData>) -> bool {
1951 let mut ignored = None;
1952 let slots = self.positional_numbers(arg, &mut ignored);
1953 slots.iter().all(|v| v.is_none())
1954 }
1955
1956 fn is_empty_scalar_operand(arg: &ResultData) -> bool {
1970 let scalar = match arg {
1971 ResultData::List(items) if items.len() == 1 => &items[0],
1972 other => other,
1973 };
1974 matches!(scalar, ResultData::None)
1975 }
1976
1977 fn first_arg_is_boolean(args: &[ResultData]) -> bool {
1987 matches!(args.first(), Some(ResultData::Boolean(_)))
1988 }
1989
1990 fn opt_f64_arg(&self, args: &[ResultData], i: usize, default: f64) -> Result<f64, EngineError> {
1991 match args.get(i) {
1992 None => Ok(default),
1993 Some(ResultData::None) => Ok(0.0),
1994 Some(ResultData::Error(e)) => Err(EngineError::EvalError(EvalError::UnknownFunction(
1995 e.clone(),
1996 ))),
1997 Some(v) => self.to_f64(v).ok_or_else(|| {
1998 EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1999 }),
2000 }
2001 }
2002
2003 fn opt_f64(&self, args: &[ResultData], i: usize, default: f64) -> f64 {
2004 args.get(i).and_then(|v| self.to_f64(v)).unwrap_or(default)
2005 }
2006
2007 fn average_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, usize) {
2008 match arg {
2009 ResultData::Float(f) => (*f, 1),
2010 ResultData::Integer(i) => (*i as f64, 1),
2011 ResultData::Boolean(b) => {
2012 if is_direct {
2013 (if *b { 1.0 } else { 0.0 }, 1)
2014 } else {
2015 (0.0, 0)
2016 }
2017 }
2018 ResultData::String(_) => {
2019 if is_direct {
2020 if let Some(f) = self.to_f64(arg) {
2021 (f, 1)
2022 } else {
2023 (0.0, 0)
2024 }
2025 } else {
2026 (0.0, 0)
2027 }
2028 }
2029 ResultData::List(list) => {
2030 let mut sum = 0.0;
2031 let mut count = 0;
2032 for item in list {
2033 let (s, c) = self.average_helper(item, false);
2034 sum += s;
2035 count += c;
2036 }
2037 (sum, count)
2038 }
2039 _ => (0.0, 0),
2040 }
2041 }
2042
2043 fn count_helper(&self, arg: &ResultData) -> usize {
2044 match arg {
2045 ResultData::Float(_) | ResultData::Integer(_) => 1,
2046 ResultData::List(list) => {
2047 let mut count = 0;
2048 for item in list {
2049 count += self.count_helper(item);
2050 }
2051 count
2052 }
2053 _ => 0,
2054 }
2055 }
2056
2057 fn min_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2058 match arg {
2059 ResultData::Float(f) => *f,
2060 ResultData::Integer(i) => *i as f64,
2061 ResultData::Boolean(b) => {
2062 if is_direct {
2063 if *b { 1.0 } else { 0.0 }
2064 } else {
2065 f64::INFINITY
2066 }
2067 }
2068 ResultData::String(_) => {
2069 if is_direct {
2070 self.to_f64(arg).unwrap_or(f64::INFINITY)
2071 } else {
2072 f64::INFINITY
2073 }
2074 }
2075 ResultData::List(list) => {
2076 let mut min_val = f64::INFINITY;
2077 for item in list {
2078 min_val = min_val.min(self.min_helper(item, false));
2079 }
2080 min_val
2081 }
2082 _ => f64::INFINITY,
2083 }
2084 }
2085
2086 fn max_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
2087 match arg {
2088 ResultData::Float(f) => *f,
2089 ResultData::Integer(i) => *i as f64,
2090 ResultData::Boolean(b) => {
2091 if is_direct {
2092 if *b { 1.0 } else { 0.0 }
2093 } else {
2094 f64::NEG_INFINITY
2095 }
2096 }
2097 ResultData::String(_) => {
2098 if is_direct {
2099 self.to_f64(arg).unwrap_or(f64::NEG_INFINITY)
2100 } else {
2101 f64::NEG_INFINITY
2102 }
2103 }
2104 ResultData::List(list) => {
2105 let mut max_val = f64::NEG_INFINITY;
2106 for item in list {
2107 max_val = max_val.max(self.max_helper(item, false));
2108 }
2109 max_val
2110 }
2111 _ => f64::NEG_INFINITY,
2112 }
2113 }
2114
2115 fn concat_helper(&self, arg: &ResultData, out: &mut String) {
2116 match arg {
2117 ResultData::List(list) => {
2118 for item in list {
2119 self.concat_helper(item, out);
2120 }
2121 }
2122 other => {
2123 out.push_str(&other.to_string());
2124 }
2125 }
2126 }
2127
2128 fn counta_helper(&self, arg: &ResultData) -> usize {
2129 match arg {
2130 ResultData::None => 0,
2131 ResultData::List(list) => {
2132 let mut count = 0;
2133 for item in list {
2134 count += self.counta_helper(item);
2135 }
2136 count
2137 }
2138 _ => 1,
2139 }
2140 }
2141
2142 fn product_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, bool) {
2143 match arg {
2144 ResultData::Float(f) => (*f, true),
2145 ResultData::Integer(i) => (*i as f64, true),
2146 ResultData::Boolean(b) => {
2147 if is_direct {
2148 (if *b { 1.0 } else { 0.0 }, true)
2149 } else {
2150 (1.0, false)
2151 }
2152 }
2153 ResultData::String(_) => {
2154 if is_direct {
2155 if let Some(f) = self.to_f64(arg) {
2156 (f, true)
2157 } else {
2158 (1.0, false)
2159 }
2160 } else {
2161 (1.0, false)
2162 }
2163 }
2164 ResultData::List(list) => {
2165 let mut prod = 1.0;
2166 let mut has_nums = false;
2167 for item in list {
2168 let (p, h) = self.product_helper(item, false);
2169 if h {
2170 prod *= p;
2171 has_nums = true;
2172 }
2173 }
2174 (prod, has_nums)
2175 }
2176 _ => (1.0, false),
2177 }
2178 }
2179
2180 fn to_bool_opt(&self, val: &ResultData) -> Option<bool> {
2181 match val {
2182 ResultData::Boolean(b) => Some(*b),
2183 ResultData::Integer(i) => Some(*i != 0),
2184 ResultData::Float(f) => Some(*f != 0.0),
2185 ResultData::String(s) => {
2186 let s_trim = s.trim();
2187 if s_trim.eq_ignore_ascii_case("true") {
2188 Some(true)
2189 } else if s_trim.eq_ignore_ascii_case("false") {
2190 Some(false)
2191 } else if let Ok(f) = s_trim.parse::<f64>() {
2192 Some(f != 0.0)
2193 } else {
2194 None
2195 }
2196 }
2197 ResultData::None => Some(false),
2198 _ => None,
2199 }
2200 }
2201
2202 fn to_bool(&self, val: &ResultData) -> bool {
2203 self.to_bool_opt(val).unwrap_or(false)
2204 }
2205
2206 fn range_numeric(val: &ResultData) -> Option<f64> {
2216 match val {
2217 ResultData::Integer(i) => Some(*i as f64),
2218 ResultData::Float(f) => Some(*f),
2219 _ => None,
2220 }
2221 }
2222
2223 fn exact_lookup_matches(lookup: &ResultData, candidate: &ResultData) -> bool {
2233 if matches!(candidate, ResultData::None) {
2234 return false;
2235 }
2236 let lookup_key = match lookup {
2237 ResultData::None => "0".to_string(),
2238 other => other.to_string(),
2239 };
2240 candidate.to_string() == lookup_key
2241 }
2242
2243 fn wildcard_criteria_matches(pattern: &str, text: &str) -> bool {
2244 fn rec(pat: &[char], txt: &[char]) -> bool {
2245 if pat.is_empty() {
2246 return txt.is_empty();
2247 }
2248 match pat[0] {
2249 '*' => rec(&pat[1..], txt) || (!txt.is_empty() && rec(pat, &txt[1..])),
2250 '?' => !txt.is_empty() && rec(&pat[1..], &txt[1..]),
2251 '~' if pat.len() > 1 && matches!(pat[1], '*' | '?' | '~') => {
2252 !txt.is_empty() && pat[1] == txt[0] && rec(&pat[2..], &txt[1..])
2253 }
2254 ch => !txt.is_empty() && ch == txt[0] && rec(&pat[1..], &txt[1..]),
2255 }
2256 }
2257
2258 let pat = pattern.to_lowercase().chars().collect::<Vec<_>>();
2259 let txt = text.to_lowercase().chars().collect::<Vec<_>>();
2260 rec(&pat, &txt)
2261 }
2262
2263 fn criteria_text_eq(val: &ResultData, pattern: &str) -> bool {
2264 let text = val.to_string();
2265 if pattern.contains('*') || pattern.contains('?') {
2266 matches!(val, ResultData::String(_)) && Self::wildcard_criteria_matches(pattern, &text)
2267 } else {
2268 text.to_lowercase() == pattern.to_lowercase()
2269 }
2270 }
2271
2272 fn match_criteria(&self, val: &ResultData, criteria: &ResultData) -> bool {
2273 let crit_str = criteria.to_string();
2274 if let Some(rest) = crit_str.strip_prefix(">=") {
2275 let val_f = match Self::range_numeric(val) {
2276 Some(f) => f,
2277 None => return false,
2278 };
2279 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2280 val_f >= crit_f
2281 } else if let Some(rest) = crit_str.strip_prefix('>') {
2282 let val_f = match Self::range_numeric(val) {
2283 Some(f) => f,
2284 None => return false,
2285 };
2286 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2287 val_f > crit_f
2288 } else if let Some(rest) = crit_str.strip_prefix("<>") {
2289 let remainder = rest.trim();
2290 !Self::criteria_text_eq(val, remainder)
2291 } else if let Some(rest) = crit_str.strip_prefix("<=") {
2292 let val_f = match Self::range_numeric(val) {
2293 Some(f) => f,
2294 None => return false,
2295 };
2296 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2297 val_f <= crit_f
2298 } else if let Some(rest) = crit_str.strip_prefix('<') {
2299 let val_f = match Self::range_numeric(val) {
2300 Some(f) => f,
2301 None => return false,
2302 };
2303 let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2304 val_f < crit_f
2305 } else if let Some(rest) = crit_str.strip_prefix('=') {
2306 let remainder = rest.trim();
2307 Self::criteria_text_eq(val, remainder)
2308 } else {
2309 Self::criteria_text_eq(val, &crit_str)
2310 }
2311 }
2312
2313 fn range_bounds(
2319 expr: &crate::core::parser::Expr,
2320 ) -> Option<(Option<String>, usize, usize, usize, usize)> {
2321 use crate::core::parser::Expr;
2322 match expr {
2323 Expr::RangeRef {
2324 sheet,
2325 start_row,
2326 start_col,
2327 end_row,
2328 end_col,
2329 ..
2330 } => Some((sheet.clone(), *start_row, *start_col, *end_row, *end_col)),
2331 Expr::CellRef {
2332 sheet, row, col, ..
2333 } => Some((sheet.clone(), *row, *col, *row, *col)),
2334 _ => None,
2335 }
2336 }
2337
2338 fn materialize_range(
2345 &self,
2346 sheet_opt: &Option<String>,
2347 start_row: usize,
2348 start_col: usize,
2349 end_row: usize,
2350 end_col: usize,
2351 context: Option<&Context>,
2352 ) -> Option<Vec<Vec<ResultData>>> {
2353 let is_self = match sheet_opt {
2354 Some(name) => name == &self.name,
2355 None => true,
2356 };
2357 let source: &Sheet = if is_self {
2358 self
2359 } else {
2360 context?.sheets.get(sheet_opt.as_ref()?)?
2361 };
2362 let actual_end_row = if end_row == usize::MAX {
2363 source.row_count().saturating_sub(1)
2364 } else {
2365 end_row
2366 };
2367 let actual_end_col = if end_col == usize::MAX {
2368 source.col_count().saturating_sub(1)
2369 } else {
2370 end_col
2371 };
2372 if actual_end_row < start_row || actual_end_col < start_col {
2373 return Some(Vec::new());
2374 }
2375 let mut grid = Vec::with_capacity(actual_end_row - start_row + 1);
2376 for r in start_row..=actual_end_row {
2377 let mut row = Vec::with_capacity(actual_end_col - start_col + 1);
2378 for c in start_col..=actual_end_col {
2379 row.push(source.get_result_data(&CellRef::new(r, c)));
2380 }
2381 grid.push(row);
2382 }
2383 Some(grid)
2384 }
2385
2386 fn evaluate_database_function(
2400 &self,
2401 func_name: &str,
2402 args: &[crate::core::parser::Expr],
2403 evaluated_args: &[ResultData],
2404 context: Option<&Context>,
2405 ) -> Result<ResultData, EngineError> {
2406 if args.len() < 3 || evaluated_args.len() < 3 {
2407 return Ok(ResultData::Error("#VALUE!".to_string()));
2408 }
2409 let (db_sheet, db_sr, db_sc, db_er, db_ec) = match Self::range_bounds(&args[0]) {
2410 Some(v) => v,
2411 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2412 };
2413 let (crit_sheet, crit_sr, crit_sc, crit_er, crit_ec) = match Self::range_bounds(&args[2]) {
2414 Some(v) => v,
2415 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2416 };
2417 let db = match self.materialize_range(&db_sheet, db_sr, db_sc, db_er, db_ec, context) {
2418 Some(g) => g,
2419 None => return Ok(ResultData::Error("#REF!".to_string())),
2420 };
2421 let crit = match self.materialize_range(
2422 &crit_sheet,
2423 crit_sr,
2424 crit_sc,
2425 crit_er,
2426 crit_ec,
2427 context,
2428 ) {
2429 Some(g) => g,
2430 None => return Ok(ResultData::Error("#REF!".to_string())),
2431 };
2432 if db.len() < 2 || crit.len() < 2 {
2433 return Ok(ResultData::Error("#VALUE!".to_string()));
2434 }
2435
2436 let db_headers: Vec<String> = db[0].iter().map(|v| v.to_string()).collect();
2437 let field_idx: usize = match &evaluated_args[1] {
2438 ResultData::String(s) => {
2439 match db_headers.iter().position(|h| h.eq_ignore_ascii_case(s)) {
2440 Some(idx) => idx,
2441 None => return Ok(ResultData::Error("#VALUE!".to_string())),
2442 }
2443 }
2444 other => match self.to_f64(other) {
2445 Some(n) if n >= 1.0 && (n as usize) <= db_headers.len() => n as usize - 1,
2446 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2447 },
2448 };
2449
2450 let crit_headers: Vec<String> = crit[0].iter().map(|v| v.to_string()).collect();
2451 let crit_to_db: Vec<Option<usize>> = crit_headers
2452 .iter()
2453 .map(|h| db_headers.iter().position(|dh| dh.eq_ignore_ascii_case(h)))
2454 .collect();
2455
2456 let mut matched: Vec<ResultData> = Vec::new();
2457 for row in db.iter().skip(1) {
2458 let row_matches_any_criteria_row = crit.iter().skip(1).any(|crit_row| {
2459 crit_row.iter().enumerate().all(|(ci, cell)| {
2460 if matches!(cell, ResultData::None) {
2461 return true;
2462 }
2463 match crit_to_db.get(ci).copied().flatten() {
2464 Some(db_col) => self.match_criteria(&row[db_col], cell),
2465 None => false,
2466 }
2467 })
2468 });
2469 if row_matches_any_criteria_row {
2470 matched.push(row[field_idx].clone());
2471 }
2472 }
2473
2474 match func_name {
2475 "DGET" => match matched.len() {
2476 0 => Ok(ResultData::Error("#VALUE!".to_string())),
2477 1 => Ok(matched.into_iter().next().unwrap()),
2478 _ => Ok(ResultData::Error("#NUM!".to_string())),
2479 },
2480 "DCOUNT" => Ok(ResultData::Float(
2481 matched
2482 .iter()
2483 .filter(|v| Self::range_numeric(v).is_some())
2484 .count() as f64,
2485 )),
2486 "DCOUNTA" => Ok(ResultData::Float(
2487 matched.iter().map(|v| self.counta_helper(v)).sum::<usize>() as f64,
2488 )),
2489 _ => {
2490 let nums: Vec<f64> = matched.iter().filter_map(Self::range_numeric).collect();
2491 match func_name {
2492 "DSUM" => Ok(ResultData::Float(nums.iter().sum())),
2493 "DPRODUCT" => Ok(ResultData::Float(if nums.is_empty() {
2494 0.0
2495 } else {
2496 nums.iter().product()
2497 })),
2498 "DMAX" => {
2499 let m = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
2500 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2501 }
2502 "DMIN" => {
2503 let m = nums.iter().cloned().fold(f64::INFINITY, f64::min);
2504 Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2505 }
2506 "DAVERAGE" => {
2507 if nums.is_empty() {
2508 Ok(ResultData::Error("#DIV/0!".to_string()))
2509 } else {
2510 Ok(ResultData::Float(
2511 nums.iter().sum::<f64>() / nums.len() as f64,
2512 ))
2513 }
2514 }
2515 "DSTDEV" => match crate::core::stats::stdev_s(&nums) {
2516 Ok(v) => Ok(ResultData::Float(v)),
2517 Err(e) => Ok(ResultData::Error(e)),
2518 },
2519 "DSTDEVP" => match crate::core::stats::stdev_p(&nums) {
2520 Ok(v) => Ok(ResultData::Float(v)),
2521 Err(e) => Ok(ResultData::Error(e)),
2522 },
2523 "DVAR" => match crate::core::stats::var_s(&nums) {
2524 Ok(v) => Ok(ResultData::Float(v)),
2525 Err(e) => Ok(ResultData::Error(e)),
2526 },
2527 "DVARP" => match crate::core::stats::var_p(&nums) {
2528 Ok(v) => Ok(ResultData::Float(v)),
2529 Err(e) => Ok(ResultData::Error(e)),
2530 },
2531 _ => unreachable!(),
2532 }
2533 }
2534 }
2535 }
2536
2537 fn proper(&self, s: &str) -> String {
2538 let mut c_chars = Vec::new();
2539 let mut capitalize_next = true;
2540 for c in s.chars() {
2541 if c.is_alphabetic() {
2542 if capitalize_next {
2543 c_chars.extend(c.to_uppercase());
2544 } else {
2545 c_chars.extend(c.to_lowercase());
2546 }
2547 capitalize_next = false;
2548 } else {
2549 c_chars.push(c);
2550 capitalize_next = true;
2551 }
2552 }
2553 c_chars.into_iter().collect()
2554 }
2555
2556 fn get_ymd_hms(&self) -> ((i32, u32, u32), (u32, u32, u32)) {
2557 let now = web_time::SystemTime::now()
2558 .duration_since(web_time::SystemTime::UNIX_EPOCH)
2559 .unwrap_or_default()
2560 .as_secs();
2561 let secs_in_day = 86400;
2562 let days_since_epoch = (now / secs_in_day) as i32;
2563 let seconds_of_day = (now % secs_in_day) as u32;
2564
2565 let hour = seconds_of_day / 3600;
2566 let minute = (seconds_of_day % 3600) / 60;
2567 let second = seconds_of_day % 60;
2568
2569 let era = (if days_since_epoch >= -719468 {
2570 days_since_epoch + 719468
2571 } else {
2572 days_since_epoch + 719468 - 146096
2573 }) / 146097;
2574 let doe = (days_since_epoch + 719468 - era * 146097) as u32;
2575 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
2576 let y = (yoe as i32) + era * 400;
2577 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2578 let mp = (5 * doy + 2) / 153;
2579 let d = doy - (153 * mp + 2) / 5 + 1;
2580 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2581 let year = if m <= 2 { y + 1 } else { y };
2582
2583 ((year, m, d), (hour, minute, second))
2584 }
2585
2586 fn evaluate_let(
2593 &self,
2594 args: &[crate::core::parser::Expr],
2595 context: Option<&Context>,
2596 row: Option<usize>,
2597 col: Option<usize>,
2598 deps: &mut Vec<Dependency>,
2599 scope: &LetScope<'_>,
2600 ) -> Result<ResultData, EngineError> {
2601 use crate::core::parser::Expr;
2602
2603 if args.is_empty() || args.len().is_multiple_of(2) {
2604 return Ok(ResultData::Error("#VALUE!".to_string()));
2605 }
2606 if args.len() == 1 {
2607 return self.evaluate_ast(&args[0], context, row, col, deps, scope);
2608 }
2609
2610 let name = match &args[0] {
2611 Expr::Identifier(n) => n.as_str(),
2612 _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2613 };
2614 let remaining_pairs = args.len() / 2 - 1;
2615 let is_duplicate = args[2..]
2616 .iter()
2617 .step_by(2)
2618 .take(remaining_pairs)
2619 .any(|a| matches!(a, Expr::Identifier(n2) if n2.eq_ignore_ascii_case(name)));
2620 if is_duplicate {
2621 return Ok(ResultData::Error("#VALUE!".to_string()));
2622 }
2623
2624 let value = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
2625 let inner_scope = LetScope::Bound {
2626 name,
2627 value: &value,
2628 parent: scope,
2629 };
2630 self.evaluate_let(&args[2..], context, row, col, deps, &inner_scope)
2631 }
2632
2633 fn extract_lambda(
2642 expr: &crate::core::parser::Expr,
2643 ) -> Option<(Vec<&str>, &crate::core::parser::Expr)> {
2644 use crate::core::parser::Expr;
2645 let Expr::FunctionCall { name, args } = expr else {
2646 return None;
2647 };
2648 if !name.eq_ignore_ascii_case("LAMBDA") || args.is_empty() {
2649 return None;
2650 }
2651 let (body, params) = args.split_last().unwrap();
2652 let param_names: Vec<&str> = params
2653 .iter()
2654 .filter_map(|p| match p {
2655 Expr::Identifier(n) => Some(n.as_str()),
2656 _ => None,
2657 })
2658 .collect();
2659 if param_names.len() != params.len() {
2660 return None;
2661 }
2662 Some((param_names, body))
2663 }
2664
2665 #[allow(clippy::too_many_arguments)]
2671 fn invoke_lambda<'v>(
2672 &self,
2673 params: &[&str],
2674 values: &'v [ResultData],
2675 body: &crate::core::parser::Expr,
2676 context: Option<&Context>,
2677 row: Option<usize>,
2678 col: Option<usize>,
2679 deps: &mut Vec<Dependency>,
2680 scope: &LetScope<'v>,
2681 ) -> Result<ResultData, EngineError> {
2682 match (params.split_first(), values.split_first()) {
2683 (Some((&pname, prest)), Some((vfirst, vrest))) => {
2684 let inner_scope = LetScope::Bound {
2685 name: pname,
2686 value: vfirst,
2687 parent: scope,
2688 };
2689 self.invoke_lambda(prest, vrest, body, context, row, col, deps, &inner_scope)
2690 }
2691 _ => self.evaluate_ast(body, context, row, col, deps, scope),
2692 }
2693 }
2694
2695 fn eval_as_array(
2699 &self,
2700 expr: &crate::core::parser::Expr,
2701 context: Option<&Context>,
2702 row: Option<usize>,
2703 col: Option<usize>,
2704 deps: &mut Vec<Dependency>,
2705 scope: &LetScope<'_>,
2706 ) -> Result<Vec<ResultData>, EngineError> {
2707 Ok(
2708 match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2709 ResultData::List(items) => Self::flatten_row_major(items).0,
2710 other => vec![other],
2711 },
2712 )
2713 }
2714
2715 fn flatten_row_major(items: Vec<ResultData>) -> (Vec<ResultData>, Option<usize>) {
2725 if !items.is_empty() && items.iter().all(|v| matches!(v, ResultData::List(_))) {
2726 let cols = match &items[0] {
2727 ResultData::List(inner) => inner.len().max(1),
2728 _ => 1,
2729 };
2730 let flat = items
2731 .into_iter()
2732 .flat_map(|v| match v {
2733 ResultData::List(inner) => inner,
2734 other => vec![other],
2735 })
2736 .collect();
2737 (flat, Some(cols))
2738 } else {
2739 (items, None)
2740 }
2741 }
2742
2743 fn array_shape(
2751 &self,
2752 expr: &crate::core::parser::Expr,
2753 context: Option<&Context>,
2754 row: Option<usize>,
2755 col: Option<usize>,
2756 deps: &mut Vec<Dependency>,
2757 scope: &LetScope<'_>,
2758 ) -> Result<(Vec<ResultData>, usize), EngineError> {
2759 use crate::core::parser::Expr;
2760 let items = match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2761 ResultData::List(items) => items,
2762 other => vec![other],
2763 };
2764 let (flat, nested_cols) = Self::flatten_row_major(items);
2765 if let Some(cols) = nested_cols {
2766 return Ok((flat, cols));
2767 }
2768 let num_cols = match expr {
2769 Expr::RangeRef {
2770 start_col, end_col, ..
2771 } => (end_col - start_col + 1).max(1),
2772 Expr::CellRef { .. } => 1,
2773 Expr::FunctionCall { name, args } => self
2774 .function_call_cols(name, args, context, row, col, deps, scope)
2775 .unwrap_or_else(|| flat.len().max(1)),
2776 _ => flat.len().max(1),
2777 };
2778 Ok((flat, num_cols))
2779 }
2780
2781 #[allow(clippy::too_many_arguments)]
2789 fn function_call_cols(
2790 &self,
2791 name: &str,
2792 args: &[crate::core::parser::Expr],
2793 context: Option<&Context>,
2794 row: Option<usize>,
2795 col: Option<usize>,
2796 deps: &mut Vec<Dependency>,
2797 scope: &LetScope<'_>,
2798 ) -> Option<usize> {
2799 let mut upper = name.to_ascii_uppercase();
2800 if let Some(rest) = upper.strip_prefix("_XLFN.") {
2801 upper = rest.to_string();
2802 }
2803 if let Some(rest) = upper.strip_prefix("_XLWS.") {
2804 upper = rest.to_string();
2805 }
2806 match upper.as_str() {
2807 "TRANSPOSE" => {
2808 let (flat, cols) = self
2809 .array_shape(args.first()?, context, row, col, deps, scope)
2810 .ok()?;
2811 Some((flat.len().checked_div(cols).unwrap_or(0)).max(1))
2812 }
2813 "HSTACK" => {
2814 let mut total = 0usize;
2815 for a in args {
2816 total += self.array_shape(a, context, row, col, deps, scope).ok()?.1;
2817 }
2818 Some(total)
2819 }
2820 "VSTACK" => {
2821 let mut max_cols = 0usize;
2822 for a in args {
2823 max_cols =
2824 max_cols.max(self.array_shape(a, context, row, col, deps, scope).ok()?.1);
2825 }
2826 Some(max_cols)
2827 }
2828 "CHOOSEROWS" => Some(
2829 self.array_shape(args.first()?, context, row, col, deps, scope)
2830 .ok()?
2831 .1,
2832 ),
2833 "CHOOSECOLS" => Some(args.len().saturating_sub(1).max(1)),
2834 "DROP" | "TAKE" => {
2835 let (_, cols) = self
2836 .array_shape(args.first()?, context, row, col, deps, scope)
2837 .ok()?;
2838 let is_take = upper == "TAKE";
2839 match args.get(2) {
2840 Some(e) => {
2841 let n = self
2842 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2843 .unwrap_or(0.0) as isize;
2844 let (s, e2) = Self::drop_take_bounds(cols as isize, n, is_take);
2845 Some((e2 - s).max(0) as usize)
2846 }
2847 None => Some(if is_take { cols } else { 0 }),
2848 }
2849 }
2850 "EXPAND" => {
2851 let (_, cols) = self
2852 .array_shape(args.first()?, context, row, col, deps, scope)
2853 .ok()?;
2854 match args.get(2) {
2855 Some(e) => Some(
2856 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2857 .unwrap_or(cols as f64) as usize,
2858 ),
2859 None => Some(cols),
2860 }
2861 }
2862 "TOCOL" => Some(1),
2863 "WRAPROWS" => {
2864 let n = self
2865 .to_f64(
2866 &self
2867 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2868 .ok()?,
2869 )
2870 .unwrap_or(1.0)
2871 .max(1.0) as usize;
2872 Some(n)
2873 }
2874 "WRAPCOLS" => {
2875 let (flat, _) = self
2876 .array_shape(args.first()?, context, row, col, deps, scope)
2877 .ok()?;
2878 let wrap = self
2879 .to_f64(
2880 &self
2881 .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2882 .ok()?,
2883 )
2884 .unwrap_or(1.0)
2885 .max(1.0) as usize;
2886 Some(flat.len().div_ceil(wrap).max(1))
2887 }
2888 "UNIQUE" | "SORT" | "SORTBY" | "FILTER" | "TRIMRANGE" => Some(
2889 self.array_shape(args.first()?, context, row, col, deps, scope)
2890 .ok()?
2891 .1,
2892 ),
2893 "SEQUENCE" => match args.get(1) {
2894 Some(e) => Some(
2895 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2896 .unwrap_or(1.0)
2897 .max(1.0) as usize,
2898 ),
2899 None => Some(1),
2900 },
2901 "MUNIT" => {
2902 let n = self
2903 .to_f64(
2904 &self
2905 .evaluate_ast(args.first()?, context, row, col, deps, scope)
2906 .ok()?,
2907 )
2908 .unwrap_or(1.0)
2909 .max(1.0) as usize;
2910 Some(n)
2911 }
2912 "MAKEARRAY" => match args.get(1) {
2913 Some(e) => Some(
2914 self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2915 .unwrap_or(1.0)
2916 .max(1.0) as usize,
2917 ),
2918 None => Some(1),
2919 },
2920 _ => None,
2921 }
2922 }
2923
2924 fn drop_take_bounds(total: isize, n: isize, is_take: bool) -> (isize, isize) {
2928 let n = n.clamp(-total, total);
2929 if is_take {
2930 if n >= 0 { (0, n) } else { (total + n, total) }
2931 } else if n >= 0 {
2932 (n, total)
2933 } else {
2934 (0, total + n)
2935 }
2936 }
2937
2938 #[allow(clippy::too_many_arguments)]
2947 fn evaluate_lambda_function(
2948 &self,
2949 func_name: &str,
2950 args: &[crate::core::parser::Expr],
2951 context: Option<&Context>,
2952 row: Option<usize>,
2953 col: Option<usize>,
2954 deps: &mut Vec<Dependency>,
2955 scope: &LetScope<'_>,
2956 ) -> Result<ResultData, EngineError> {
2957 use crate::core::parser::Expr;
2958
2959 match func_name {
2960 "MAP" => {
2961 if args.len() < 2 {
2962 return Ok(ResultData::Error("#VALUE!".to_string()));
2963 }
2964 let (lambda_expr, array_exprs) = args.split_last().unwrap();
2965 let Some((params, body)) = Self::extract_lambda(lambda_expr) else {
2966 return Ok(ResultData::Error("#VALUE!".to_string()));
2967 };
2968 if params.len() != array_exprs.len() {
2969 return Ok(ResultData::Error("#VALUE!".to_string()));
2970 }
2971 let arrays: Vec<Vec<ResultData>> = array_exprs
2972 .iter()
2973 .map(|e| self.eval_as_array(e, context, row, col, deps, scope))
2974 .collect::<Result<_, _>>()?;
2975 let len = arrays.iter().map(|a| a.len()).max().unwrap_or(0);
2976 let mut results = Vec::with_capacity(len);
2977 for i in 0..len {
2978 let values: Vec<ResultData> = arrays
2979 .iter()
2980 .map(|a| a.get(i).cloned().unwrap_or(ResultData::None))
2981 .collect();
2982 results.push(
2983 self.invoke_lambda(¶ms, &values, body, context, row, col, deps, scope)?,
2984 );
2985 }
2986 Ok(ResultData::List(results))
2987 }
2988 "BYROW" | "BYCOL" => {
2989 if args.len() != 2 {
2990 return Ok(ResultData::Error("#VALUE!".to_string()));
2991 }
2992 let Some((params, body)) = Self::extract_lambda(&args[1]) else {
2993 return Ok(ResultData::Error("#VALUE!".to_string()));
2994 };
2995 if params.len() != 1 {
2996 return Ok(ResultData::Error("#VALUE!".to_string()));
2997 }
2998 let num_cols = match &args[0] {
2999 Expr::RangeRef {
3000 start_col, end_col, ..
3001 } => (end_col - start_col + 1).max(1),
3002 _ => 1,
3003 };
3004 let flat = self.eval_as_array(&args[0], context, row, col, deps, scope)?;
3005 let num_rows = if num_cols == 0 {
3006 0
3007 } else {
3008 flat.len().div_ceil(num_cols)
3009 };
3010 let mut results = Vec::new();
3011 if func_name == "BYROW" {
3012 for r in 0..num_rows {
3013 let row_vals: Vec<ResultData> = (0..num_cols)
3014 .filter_map(|c| flat.get(r * num_cols + c).cloned())
3015 .collect();
3016 let arg = vec![ResultData::List(row_vals)];
3017 results.push(
3018 self.invoke_lambda(
3019 ¶ms, &arg, body, context, row, col, deps, scope,
3020 )?,
3021 );
3022 }
3023 } else {
3024 for c in 0..num_cols {
3025 let col_vals: Vec<ResultData> = (0..num_rows)
3026 .filter_map(|r| flat.get(r * num_cols + c).cloned())
3027 .collect();
3028 let arg = vec![ResultData::List(col_vals)];
3029 results.push(
3030 self.invoke_lambda(
3031 ¶ms, &arg, body, context, row, col, deps, scope,
3032 )?,
3033 );
3034 }
3035 }
3036 Ok(ResultData::List(results))
3037 }
3038 "REDUCE" | "SCAN" => {
3039 if args.len() != 2 && args.len() != 3 {
3040 return Ok(ResultData::Error("#VALUE!".to_string()));
3041 }
3042 let lambda_idx = args.len() - 1;
3043 let array_idx = args.len() - 2;
3044 let Some((params, body)) = Self::extract_lambda(&args[lambda_idx]) else {
3045 return Ok(ResultData::Error("#VALUE!".to_string()));
3046 };
3047 if params.len() != 2 {
3048 return Ok(ResultData::Error("#VALUE!".to_string()));
3049 }
3050 let array = self.eval_as_array(&args[array_idx], context, row, col, deps, scope)?;
3051 let (mut acc, rest, mut history): (ResultData, &[ResultData], Vec<ResultData>) =
3052 if args.len() == 3 {
3053 let init = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3054 (init, &array[..], Vec::new())
3055 } else {
3056 match array.split_first() {
3057 Some((first, rest)) => (first.clone(), rest, vec![first.clone()]),
3058 None => return Ok(ResultData::Error("#VALUE!".to_string())),
3059 }
3060 };
3061 for item in rest {
3062 let call_args = [acc.clone(), item.clone()];
3063 acc = self
3064 .invoke_lambda(¶ms, &call_args, body, context, row, col, deps, scope)?;
3065 history.push(acc.clone());
3066 }
3067 if func_name == "REDUCE" {
3068 Ok(acc)
3069 } else {
3070 Ok(ResultData::List(history))
3071 }
3072 }
3073 "MAKEARRAY" => {
3074 if args.len() != 3 {
3075 return Ok(ResultData::Error("#VALUE!".to_string()));
3076 }
3077 let Some((params, body)) = Self::extract_lambda(&args[2]) else {
3078 return Ok(ResultData::Error("#VALUE!".to_string()));
3079 };
3080 if params.len() != 2 {
3081 return Ok(ResultData::Error("#VALUE!".to_string()));
3082 }
3083 let rows_val = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
3084 let cols_val = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
3085 let num_rows = self.to_f64(&rows_val).unwrap_or(0.0).max(0.0) as usize;
3086 let num_cols = self.to_f64(&cols_val).unwrap_or(0.0).max(0.0) as usize;
3087 let mut results = Vec::with_capacity(num_rows * num_cols);
3088 for r in 1..=num_rows {
3089 for c in 1..=num_cols {
3090 let call_args = [ResultData::Float(r as f64), ResultData::Float(c as f64)];
3091 results.push(self.invoke_lambda(
3092 ¶ms, &call_args, body, context, row, col, deps, scope,
3093 )?);
3094 }
3095 }
3096 Ok(ResultData::List(results))
3097 }
3098 _ => unreachable!(),
3099 }
3100 }
3101
3102 fn parse_a1_reference(text: &str) -> Option<(Option<String>, usize, usize, usize, usize)> {
3111 let text = text.trim();
3112 let (sheet_part, ref_part) = match text.rfind('!') {
3113 Some(idx) => (Some(&text[..idx]), &text[idx + 1..]),
3114 None => (None, text),
3115 };
3116 let sheet = sheet_part.map(|s| s.trim().trim_matches('\'').to_string());
3117
3118 fn parse_cell(s: &str) -> Option<(usize, usize)> {
3119 let s = s.replace('$', "");
3120 let col_end = s.find(|c: char| c.is_ascii_digit())?;
3121 let (col_str, row_str) = s.split_at(col_end);
3122 if col_str.is_empty() || row_str.is_empty() {
3123 return None;
3124 }
3125 let mut col = 0usize;
3126 for ch in col_str.chars() {
3127 if !ch.is_ascii_alphabetic() {
3128 return None;
3129 }
3130 col = col * 26 + (ch.to_ascii_uppercase() as usize - 'A' as usize + 1);
3131 }
3132 let row: usize = row_str.parse().ok()?;
3133 if row == 0 || col == 0 {
3134 return None;
3135 }
3136 Some((row - 1, col - 1))
3137 }
3138
3139 if let Some((start, end)) = ref_part.split_once(':') {
3140 let (r1, c1) = parse_cell(start)?;
3141 let (r2, c2) = parse_cell(end)?;
3142 Some((sheet, r1.min(r2), c1.min(c2), r1.max(r2), c1.max(c2)))
3143 } else {
3144 let (r, c) = parse_cell(ref_part)?;
3145 Some((sheet, r, c, r, c))
3146 }
3147 }
3148
3149 fn read_cell_with_deps(
3156 &self,
3157 sheet_opt: &Option<String>,
3158 r: usize,
3159 c: usize,
3160 context: Option<&Context>,
3161 deps: &mut Vec<Dependency>,
3162 ) -> ResultData {
3163 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3164 if is_self {
3165 deps.push(Dependency::Local(CellRef::new(r, c)));
3166 self.get_result_data(&CellRef::new(r, c))
3167 } else if let Some(ctx) = context {
3168 let name = sheet_opt.clone().unwrap();
3169 deps.push(Dependency::Remote {
3170 sheet: name.clone(),
3171 cell: CellRef::new(r, c),
3172 });
3173 ctx.sheets
3174 .get(&name)
3175 .map(|s| s.get_result_data(&CellRef::new(r, c)))
3176 .unwrap_or(ResultData::None)
3177 } else {
3178 ResultData::None
3179 }
3180 }
3181
3182 #[allow(clippy::too_many_arguments)]
3191 fn evaluate_range_info_function(
3192 &self,
3193 func_name: &str,
3194 args: &[crate::core::parser::Expr],
3195 context: Option<&Context>,
3196 row: Option<usize>,
3197 col: Option<usize>,
3198 deps: &mut Vec<Dependency>,
3199 scope: &LetScope<'_>,
3200 ) -> Result<ResultData, EngineError> {
3201 use crate::core::parser::Expr;
3202
3203 match func_name {
3204 "ROW" => match args.first() {
3205 Some(arg) => match Self::range_bounds(arg) {
3206 Some((_, start_row, _, end_row, _)) if end_row > start_row => {
3207 Ok(ResultData::List(
3208 (start_row..=end_row)
3209 .map(|r| ResultData::Float((r + 1) as f64))
3210 .collect(),
3211 ))
3212 }
3213 Some((_, start_row, _, _, _)) => Ok(ResultData::Float((start_row + 1) as f64)),
3214 None => Ok(ResultData::Error("#VALUE!".to_string())),
3215 },
3216 None => match row {
3217 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3218 None => Ok(ResultData::Error("#VALUE!".to_string())),
3219 },
3220 },
3221 "COLUMN" => match args.first() {
3222 Some(arg) => match Self::range_bounds(arg) {
3223 Some((_, _, start_col, _, end_col)) if end_col > start_col => {
3224 Ok(ResultData::List(
3225 (start_col..=end_col)
3226 .map(|c| ResultData::Float((c + 1) as f64))
3227 .collect(),
3228 ))
3229 }
3230 Some((_, _, start_col, _, _)) => Ok(ResultData::Float((start_col + 1) as f64)),
3231 None => Ok(ResultData::Error("#VALUE!".to_string())),
3232 },
3233 None => match col {
3234 Some(c) => Ok(ResultData::Float((c + 1) as f64)),
3235 None => Ok(ResultData::Error("#VALUE!".to_string())),
3236 },
3237 },
3238 "ROWS" => {
3239 let Some(arg) = args.first() else {
3240 return Ok(ResultData::Error("#VALUE!".to_string()));
3241 };
3242 let Some((sheet_opt, start_row, _, end_row, _)) = Self::range_bounds(arg) else {
3243 return Ok(ResultData::Error("#VALUE!".to_string()));
3244 };
3245 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3246 let actual_end_row = if end_row == usize::MAX {
3247 if is_self {
3248 self.row_count().saturating_sub(1)
3249 } else {
3250 context
3251 .and_then(|ctx| sheet_opt.as_ref().and_then(|n| ctx.sheets.get(n)))
3252 .map(|s| s.row_count().saturating_sub(1))
3253 .unwrap_or(0)
3254 }
3255 } else {
3256 end_row
3257 };
3258 Ok(ResultData::Float(
3259 (actual_end_row.saturating_sub(start_row) + 1) as f64,
3260 ))
3261 }
3262 "COLUMNS" => {
3263 let Some(arg) = args.first() else {
3264 return Ok(ResultData::Error("#VALUE!".to_string()));
3265 };
3266 match Self::range_bounds(arg) {
3267 Some((_, _, start_col, _, end_col)) => Ok(ResultData::Float(
3268 (end_col.saturating_sub(start_col) + 1) as f64,
3269 )),
3270 None => Ok(ResultData::Error("#VALUE!".to_string())),
3271 }
3272 }
3273 "AREAS" => {
3274 if args.is_empty() {
3275 Ok(ResultData::Error("#VALUE!".to_string()))
3276 } else {
3277 Ok(ResultData::Float(1.0))
3278 }
3279 }
3280 "ISREF" => Ok(ResultData::Boolean(matches!(
3281 args.first(),
3282 Some(Expr::CellRef { .. } | Expr::RangeRef { .. } | Expr::StructuredRef { .. })
3283 ))),
3284 "FORMULATEXT" | "ISFORMULA" => {
3285 let Some(arg) = args.first() else {
3286 return Ok(ResultData::Error("#VALUE!".to_string()));
3287 };
3288 let Some((sheet_opt, r, c, _, _)) = Self::range_bounds(arg) else {
3289 return Ok(ResultData::Error("#VALUE!".to_string()));
3290 };
3291 let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
3292 let src = if is_self {
3293 deps.push(Dependency::Local(CellRef::new(r, c)));
3294 self.get_src_str(&CellRef::new(r, c))
3295 } else if let Some(ctx) = context {
3296 let name = sheet_opt.unwrap();
3297 deps.push(Dependency::Remote {
3298 sheet: name.clone(),
3299 cell: CellRef::new(r, c),
3300 });
3301 ctx.sheets
3302 .get(&name)
3303 .map(|s| s.get_src_str(&CellRef::new(r, c)))
3304 .unwrap_or_default()
3305 } else {
3306 String::new()
3307 };
3308 let is_formula = src.starts_with('=');
3309 if func_name == "ISFORMULA" {
3310 Ok(ResultData::Boolean(is_formula))
3311 } else if is_formula {
3312 Ok(ResultData::String(src))
3313 } else {
3314 Ok(ResultData::Error("#N/A".to_string()))
3315 }
3316 }
3317 "SHEETS" => Ok(ResultData::Float(
3318 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3319 )),
3320 "SHEET" => {
3321 let sheet_name = match args.first() {
3322 None => Some(self.name.clone()),
3323 Some(arg) => match Self::range_bounds(arg) {
3324 Some((sheet_opt, ..)) => {
3325 Some(sheet_opt.unwrap_or_else(|| self.name.clone()))
3326 }
3327 None => self
3328 .evaluate_ast(arg, context, row, col, deps, scope)
3329 .ok()
3330 .map(|v| v.to_string()),
3331 },
3332 };
3333
3334 match sheet_name {
3335 Some(name) => {
3336 let ordinal = context
3337 .and_then(|c| {
3338 c.sheet_order
3339 .iter()
3340 .position(|n| n.eq_ignore_ascii_case(&name))
3341 })
3342 .map(|i| i + 1)
3343 .unwrap_or(1);
3344 Ok(ResultData::Float(ordinal as f64))
3345 }
3346 None => Ok(ResultData::Error("#N/A".to_string())),
3347 }
3348 }
3349 "CELL" => {
3350 if args.is_empty() {
3351 return Ok(ResultData::Error("#VALUE!".to_string()));
3352 }
3353 let info_type = self
3354 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3355 .to_string()
3356 .to_lowercase();
3357 let bounds = args.get(1).and_then(Self::range_bounds);
3358 match info_type.as_str() {
3359 "row" => match bounds.map(|b| b.1).or(row) {
3360 Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3361 None => Ok(ResultData::Error("#VALUE!".to_string())),
3362 },
3363 "col" => match bounds {
3364 Some((_, _, c, _, _)) => Ok(ResultData::Float((c + 1) as f64)),
3365 None => Ok(ResultData::Error("#VALUE!".to_string())),
3366 },
3367 "address" => match bounds {
3368 Some((_, r, c, _, _)) => Ok(ResultData::String(format!(
3369 "${}${}",
3370 crate::core::parser::col_idx_to_letters(c),
3371 r + 1
3372 ))),
3373 None => Ok(ResultData::Error("#VALUE!".to_string())),
3374 },
3375 "contents" => match bounds {
3376 Some((sheet_opt, r, c, _, _)) => {
3377 Ok(self.read_cell_with_deps(&sheet_opt, r, c, context, deps))
3378 }
3379 None => Ok(ResultData::Error("#VALUE!".to_string())),
3380 },
3381 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3382 }
3383 }
3384 "INFO" => {
3385 if args.is_empty() {
3386 return Ok(ResultData::Error("#VALUE!".to_string()));
3387 }
3388 let info_type = self
3389 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3390 .to_string()
3391 .to_lowercase();
3392 match info_type.as_str() {
3393 "numfile" => Ok(ResultData::Float(
3394 context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3395 )),
3396 "release" => Ok(ResultData::String("16.0".to_string())),
3397 "system" => Ok(ResultData::String(
3398 if cfg!(target_os = "macos") {
3399 "mac"
3400 } else {
3401 "pcdos"
3402 }
3403 .to_string(),
3404 )),
3405 _ => Ok(ResultData::Error("#VALUE!".to_string())),
3406 }
3407 }
3408 "INDIRECT" => {
3409 if args.is_empty() {
3410 return Ok(ResultData::Error("#VALUE!".to_string()));
3411 }
3412 let text = self
3413 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3414 .to_string();
3415 let a1_style = match args.get(1) {
3416 Some(a) => self.to_bool(&self.evaluate_ast(a, context, row, col, deps, scope)?),
3417 None => true,
3418 };
3419 if !a1_style {
3420 return Ok(ResultData::Error("#VALUE!".to_string()));
3421 }
3422 match Self::parse_a1_reference(&text) {
3423 Some((sheet_opt, start_row, start_col, end_row, end_col)) => {
3424 if start_row == end_row && start_col == end_col {
3425 Ok(self.read_cell_with_deps(
3426 &sheet_opt, start_row, start_col, context, deps,
3427 ))
3428 } else {
3429 match self.materialize_range(
3430 &sheet_opt, start_row, start_col, end_row, end_col, context,
3431 ) {
3432 Some(grid) => {
3433 Ok(ResultData::List(grid.into_iter().flatten().collect()))
3434 }
3435 None => Ok(ResultData::Error("#REF!".to_string())),
3436 }
3437 }
3438 }
3439 None => Ok(ResultData::Error("#REF!".to_string())),
3440 }
3441 }
3442 "OFFSET" => {
3443 if args.len() < 3 {
3444 return Ok(ResultData::Error("#VALUE!".to_string()));
3445 }
3446 let Some((sheet_opt, base_row, base_col, base_end_row, base_end_col)) =
3447 Self::range_bounds(&args[0])
3448 else {
3449 return Ok(ResultData::Error("#VALUE!".to_string()));
3450 };
3451 let row_offset = self
3452 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3453 .unwrap_or(0.0) as isize;
3454 let col_offset = self
3455 .to_f64(&self.evaluate_ast(&args[2], context, row, col, deps, scope)?)
3456 .unwrap_or(0.0) as isize;
3457 let base_height = (base_end_row.saturating_sub(base_row) + 1) as isize;
3458 let base_width = (base_end_col.saturating_sub(base_col) + 1) as isize;
3459 let height = match args.get(3) {
3460 Some(a) => self
3461 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3462 .unwrap_or(base_height as f64) as isize,
3463 None => base_height,
3464 };
3465 let width = match args.get(4) {
3466 Some(a) => self
3467 .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3468 .unwrap_or(base_width as f64) as isize,
3469 None => base_width,
3470 };
3471 let new_row = base_row as isize + row_offset;
3472 let new_col = base_col as isize + col_offset;
3473 if new_row < 0 || new_col < 0 || height <= 0 || width <= 0 {
3474 return Ok(ResultData::Error("#REF!".to_string()));
3475 }
3476 let (start_row, start_col) = (new_row as usize, new_col as usize);
3477 let (end_row, end_col) = (
3478 start_row + (height - 1) as usize,
3479 start_col + (width - 1) as usize,
3480 );
3481 if start_row == end_row && start_col == end_col {
3482 Ok(self.read_cell_with_deps(&sheet_opt, start_row, start_col, context, deps))
3483 } else {
3484 match self.materialize_range(
3485 &sheet_opt, start_row, start_col, end_row, end_col, context,
3486 ) {
3487 Some(grid) => Ok(ResultData::List(grid.into_iter().flatten().collect())),
3488 None => Ok(ResultData::Error("#REF!".to_string())),
3489 }
3490 }
3491 }
3492 _ => unreachable!(),
3493 }
3494 }
3495
3496 fn evaluate_getpivotdata(
3503 &self,
3504 args: &[crate::core::parser::Expr],
3505 context: Option<&Context>,
3506 row: Option<usize>,
3507 col: Option<usize>,
3508 deps: &mut Vec<Dependency>,
3509 scope: &LetScope<'_>,
3510 ) -> Result<ResultData, EngineError> {
3511 if args.len() < 2 || !(args.len() - 2).is_multiple_of(2) {
3512 return Ok(ResultData::Error("#VALUE!".to_string()));
3513 }
3514
3515 let data_field = self
3516 .evaluate_ast(&args[0], context, row, col, deps, scope)?
3517 .to_string();
3518
3519 let (sheet_opt, target_row, target_col, _, _) = match Self::range_bounds(&args[1]) {
3520 Some(bounds) => bounds,
3521 None => return Ok(ResultData::Error("#REF!".to_string())),
3522 };
3523 self.read_cell_with_deps(&sheet_opt, target_row, target_col, context, deps);
3524
3525 let sheet_id = match &sheet_opt {
3526 None => self.id,
3527 Some(name) if name == &self.name => self.id,
3528 Some(name) => match context.and_then(|c| c.sheets.get(name)) {
3529 Some(s) => s.id,
3530 None => return Ok(ResultData::Error("#REF!".to_string())),
3531 },
3532 };
3533
3534 let pivot_tables = context.map(|c| c.pivot_tables).unwrap_or(&[]);
3535 let pivot = match pivot_tables.iter().find(|p| {
3536 p.dest_sheet_id == sheet_id
3537 && p.last_output_end_row
3538 .is_some_and(|end| target_row >= p.dest_row && target_row <= end)
3539 && p.last_output_end_col
3540 .is_some_and(|end| target_col >= p.dest_col && target_col <= end)
3541 }) {
3542 Some(p) => p,
3543 None => return Ok(ResultData::Error("#REF!".to_string())),
3544 };
3545
3546 let mut criteria: Vec<(String, String)> = Vec::new();
3547 let mut i = 2;
3548 while i < args.len() {
3549 let field = self
3550 .evaluate_ast(&args[i], context, row, col, deps, scope)?
3551 .to_string();
3552 let item = self
3553 .evaluate_ast(&args[i + 1], context, row, col, deps, scope)?
3554 .to_string();
3555 criteria.push((field, item));
3556 i += 2;
3557 }
3558
3559 let mut sheet_refs: Vec<&Sheet> = context
3560 .map(|c| c.sheets.values().copied().collect())
3561 .unwrap_or_default();
3562 sheet_refs.push(self);
3563
3564 match crate::core::pivot::getpivotdata(&sheet_refs, pivot, &data_field, &criteria) {
3565 Ok(v) => Ok(v),
3566 Err(e) => Ok(ResultData::Error(e)),
3567 }
3568 }
3569
3570 #[allow(clippy::too_many_arguments)]
3585 fn evaluate_array_reshape_function(
3586 &self,
3587 func_name: &str,
3588 args: &[crate::core::parser::Expr],
3589 context: Option<&Context>,
3590 row: Option<usize>,
3591 col: Option<usize>,
3592 deps: &mut Vec<Dependency>,
3593 scope: &LetScope<'_>,
3594 ) -> Result<ResultData, EngineError> {
3595 match func_name {
3596 "TRANSPOSE" => {
3597 let Some(arg) = args.first() else {
3598 return Ok(ResultData::Error("#VALUE!".to_string()));
3599 };
3600 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3601 let rows = flat.len().checked_div(cols).unwrap_or(0);
3602 let mut result = Vec::with_capacity(flat.len());
3603 for c in 0..cols {
3604 for r in 0..rows {
3605 result.push(flat[r * cols + c].clone());
3606 }
3607 }
3608 Ok(ResultData::List(result))
3609 }
3610 "HSTACK" | "VSTACK" => {
3611 if args.is_empty() {
3612 return Ok(ResultData::Error("#VALUE!".to_string()));
3613 }
3614 let mut shapes = Vec::with_capacity(args.len());
3615 for a in args {
3616 shapes.push(self.array_shape(a, context, row, col, deps, scope)?);
3617 }
3618 let mut result = Vec::new();
3619 if func_name == "HSTACK" {
3620 let max_rows = shapes
3621 .iter()
3622 .map(|(f, c)| if *c == 0 { 0 } else { f.len() / c })
3623 .max()
3624 .unwrap_or(0);
3625 for r in 0..max_rows {
3626 for (flat, cols) in &shapes {
3627 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3628 for c in 0..*cols {
3629 result.push(if r < rows {
3630 flat[r * cols + c].clone()
3631 } else {
3632 ResultData::Error("#N/A".to_string())
3633 });
3634 }
3635 }
3636 }
3637 } else {
3638 let max_cols = shapes.iter().map(|(_, c)| *c).max().unwrap_or(0);
3639 for (flat, cols) in &shapes {
3640 let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3641 for r in 0..rows {
3642 for c in 0..max_cols {
3643 result.push(if c < *cols {
3644 flat[r * cols + c].clone()
3645 } else {
3646 ResultData::Error("#N/A".to_string())
3647 });
3648 }
3649 }
3650 }
3651 }
3652 Ok(ResultData::List(result))
3653 }
3654 "CHOOSEROWS" | "CHOOSECOLS" => {
3655 if args.len() < 2 {
3656 return Ok(ResultData::Error("#VALUE!".to_string()));
3657 }
3658 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3659 let rows = flat.len().checked_div(cols).unwrap_or(0);
3660 let total = if func_name == "CHOOSEROWS" {
3661 rows
3662 } else {
3663 cols
3664 } as isize;
3665 let mut indices = Vec::with_capacity(args.len() - 1);
3666 for idx_expr in &args[1..] {
3667 let n = self
3668 .to_f64(&self.evaluate_ast(idx_expr, context, row, col, deps, scope)?)
3669 .unwrap_or(0.0) as isize;
3670 let real_idx = if n < 0 { total + n } else { n - 1 };
3671 if real_idx < 0 || real_idx >= total {
3672 return Ok(ResultData::Error("#VALUE!".to_string()));
3673 }
3674 indices.push(real_idx as usize);
3675 }
3676 let mut result = Vec::new();
3677 if func_name == "CHOOSEROWS" {
3678 for r in indices {
3679 for c in 0..cols {
3680 result.push(flat[r * cols + c].clone());
3681 }
3682 }
3683 } else {
3684 for r in 0..rows {
3685 for &c in &indices {
3686 result.push(flat[r * cols + c].clone());
3687 }
3688 }
3689 }
3690 Ok(ResultData::List(result))
3691 }
3692 "DROP" | "TAKE" => {
3693 if args.len() < 2 {
3694 return Ok(ResultData::Error("#VALUE!".to_string()));
3695 }
3696 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3697 let num_rows = flat.len().checked_div(cols).unwrap_or(0) as isize;
3698 let is_take = func_name == "TAKE";
3699 let rows_n = self
3700 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3701 .unwrap_or(0.0) as isize;
3702 let cols_n = match args.get(2) {
3703 Some(e) => self
3704 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3705 .unwrap_or(0.0) as isize,
3706 None => {
3707 if is_take {
3708 cols as isize
3709 } else {
3710 0
3711 }
3712 }
3713 };
3714 let (row_start, row_end) = Self::drop_take_bounds(num_rows, rows_n, is_take);
3715 let (col_start, col_end) = Self::drop_take_bounds(cols as isize, cols_n, is_take);
3716 if row_start >= row_end || col_start >= col_end {
3717 return Ok(ResultData::Error("#CALC!".to_string()));
3718 }
3719 let mut result = Vec::new();
3720 for r in row_start..row_end {
3721 for c in col_start..col_end {
3722 result.push(flat[(r as usize) * cols + (c as usize)].clone());
3723 }
3724 }
3725 Ok(ResultData::List(result))
3726 }
3727 "EXPAND" => {
3728 if args.len() < 2 {
3729 return Ok(ResultData::Error("#VALUE!".to_string()));
3730 }
3731 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3732 let orig_rows = flat.len().checked_div(cols).unwrap_or(0);
3733 let new_rows = self
3734 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3735 .unwrap_or(orig_rows as f64) as usize;
3736 let new_cols = match args.get(2) {
3737 Some(e) => self
3738 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3739 .unwrap_or(cols as f64) as usize,
3740 None => cols,
3741 };
3742 let pad = match args.get(3) {
3743 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3744 None => ResultData::Error("#N/A".to_string()),
3745 };
3746 if new_rows < orig_rows || new_cols < cols {
3747 return Ok(ResultData::Error("#VALUE!".to_string()));
3748 }
3749 let mut result = Vec::with_capacity(new_rows * new_cols);
3750 for r in 0..new_rows {
3751 for c in 0..new_cols {
3752 result.push(if r < orig_rows && c < cols {
3753 flat[r * cols + c].clone()
3754 } else {
3755 pad.clone()
3756 });
3757 }
3758 }
3759 Ok(ResultData::List(result))
3760 }
3761 "TOCOL" | "TOROW" => {
3762 let Some(arg) = args.first() else {
3763 return Ok(ResultData::Error("#VALUE!".to_string()));
3764 };
3765 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3766 let rows = flat.len().checked_div(cols).unwrap_or(0);
3767 let ignore = match args.get(1) {
3768 Some(e) => self
3769 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3770 .unwrap_or(0.0) as i64,
3771 None => 0,
3772 };
3773 let scan_by_col = match args.get(2) {
3774 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3775 None => false,
3776 };
3777 let ordered: Vec<ResultData> = if scan_by_col {
3778 let mut v = Vec::with_capacity(flat.len());
3779 for c in 0..cols {
3780 for r in 0..rows {
3781 v.push(flat[r * cols + c].clone());
3782 }
3783 }
3784 v
3785 } else {
3786 flat
3787 };
3788 let filtered: Vec<ResultData> = ordered
3789 .into_iter()
3790 .filter(|v| match ignore {
3791 1 => !matches!(v, ResultData::None),
3792 2 => !matches!(v, ResultData::Error(_)),
3793 3 => !matches!(v, ResultData::None | ResultData::Error(_)),
3794 _ => true,
3795 })
3796 .collect();
3797 Ok(ResultData::List(filtered))
3798 }
3799 "WRAPROWS" | "WRAPCOLS" => {
3800 if args.len() < 2 {
3801 return Ok(ResultData::Error("#VALUE!".to_string()));
3802 }
3803 let (flat, _cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3804 let wrap = self
3805 .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3806 .unwrap_or(1.0)
3807 .max(1.0) as usize;
3808 let pad = match args.get(2) {
3809 Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3810 None => ResultData::Error("#N/A".to_string()),
3811 };
3812 if func_name == "WRAPROWS" {
3813 let mut result = flat;
3814 let rem = result.len() % wrap;
3815 if rem != 0 {
3816 result.extend(std::iter::repeat_n(pad, wrap - rem));
3817 }
3818 Ok(ResultData::List(result))
3819 } else {
3820 let num_result_cols = flat.len().div_ceil(wrap).max(1);
3821 let total = wrap * num_result_cols;
3822 let mut result = Vec::with_capacity(total);
3823 for i in 0..total {
3824 let col = i / wrap;
3825 let r = i % wrap;
3826 let target = r * num_result_cols + col;
3827 while result.len() <= target {
3828 result.push(pad.clone());
3829 }
3830 if i < flat.len() {
3831 result[target] = flat[i].clone();
3832 }
3833 }
3834 Ok(ResultData::List(result))
3835 }
3836 }
3837 "UNIQUE" => {
3838 let Some(arg) = args.first() else {
3839 return Ok(ResultData::Error("#VALUE!".to_string()));
3840 };
3841 let (flat, _cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3842 let exactly_once = match args.get(2) {
3843 Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3844 None => false,
3845 };
3846 let mut seen: Vec<(String, ResultData, usize)> = Vec::new();
3847 for v in &flat {
3848 let key = match v {
3849 ResultData::None => "blank:".to_string(),
3850 ResultData::Boolean(b) => format!("bool:{b}"),
3851 ResultData::Integer(i) => format!("num:{}", *i as f64),
3852 ResultData::Float(f) => format!("num:{f}"),
3853 ResultData::String(s) => format!("str:{s}"),
3854 ResultData::Error(e) => format!("err:{e}"),
3855 ResultData::List(_) | ResultData::Dict(_) => format!("other:{v}"),
3856 };
3857 match seen.iter_mut().find(|(k, ..)| k == &key) {
3858 Some(entry) => entry.2 += 1,
3859 None => seen.push((key, v.clone(), 1)),
3860 }
3861 }
3862 let result: Vec<ResultData> = seen
3863 .into_iter()
3864 .filter(|(_, _, count)| !exactly_once || *count == 1)
3865 .map(|(_, v, _)| v)
3866 .collect();
3867 Ok(ResultData::List(result))
3868 }
3869 "SORT" => {
3870 let Some(arg) = args.first() else {
3871 return Ok(ResultData::Error("#VALUE!".to_string()));
3872 };
3873 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3874 let rows = flat.len().checked_div(cols).unwrap_or(0);
3875 let sort_index = match args.get(1) {
3876 Some(e) => self
3877 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3878 .unwrap_or(1.0) as usize,
3879 None => 1,
3880 };
3881 let sort_order = match args.get(2) {
3882 Some(e) => self
3883 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3884 .unwrap_or(1.0),
3885 None => 1.0,
3886 };
3887 let col_idx = sort_index.saturating_sub(1).min(cols.saturating_sub(1));
3888 let mut row_indices: Vec<usize> = (0..rows).collect();
3889 row_indices.sort_by(|&a, &b| {
3890 Self::sort_compare_blanks_last(
3891 &flat[a * cols + col_idx],
3892 &flat[b * cols + col_idx],
3893 sort_order,
3894 )
3895 });
3896 let mut result = Vec::with_capacity(flat.len());
3897 for r in row_indices {
3898 for c in 0..cols {
3899 result.push(flat[r * cols + c].clone());
3900 }
3901 }
3902 Ok(ResultData::List(result))
3903 }
3904 "SORTBY" => {
3905 if args.len() < 2 {
3906 return Ok(ResultData::Error("#VALUE!".to_string()));
3907 }
3908 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3909 let rows = flat.len().checked_div(cols).unwrap_or(0);
3910 let by = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
3911 let order = match args.get(2) {
3912 Some(e) => self
3913 .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3914 .unwrap_or(1.0),
3915 None => 1.0,
3916 };
3917 let mut row_indices: Vec<usize> = (0..rows).collect();
3918 row_indices.sort_by(|&a, &b| {
3919 let va = by.get(a).cloned().unwrap_or(ResultData::None);
3920 let vb = by.get(b).cloned().unwrap_or(ResultData::None);
3921 Self::sort_compare_blanks_last(&va, &vb, order)
3922 });
3923 let mut result = Vec::with_capacity(flat.len());
3924 for r in row_indices {
3925 for c in 0..cols {
3926 result.push(flat[r * cols + c].clone());
3927 }
3928 }
3929 Ok(ResultData::List(result))
3930 }
3931 "FILTER" => {
3932 if args.len() < 2 {
3933 return Ok(ResultData::Error("#VALUE!".to_string()));
3934 }
3935 let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3936 let rows = flat.len().checked_div(cols).unwrap_or(0);
3937 let include = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
3938 let mut result = Vec::new();
3939 for r in 0..rows {
3940 let keep = include.get(r).map(|v| self.to_bool(v)).unwrap_or(false);
3941 if keep {
3942 for c in 0..cols {
3943 result.push(flat[r * cols + c].clone());
3944 }
3945 }
3946 }
3947 if result.is_empty() {
3948 match args.get(2) {
3949 Some(e) => Ok(self.evaluate_ast(e, context, row, col, deps, scope)?),
3950 None => Ok(ResultData::Error("#CALC!".to_string())),
3951 }
3952 } else {
3953 Ok(ResultData::List(result))
3954 }
3955 }
3956 "TRIMRANGE" => {
3957 let Some(arg) = args.first() else {
3958 return Ok(ResultData::Error("#VALUE!".to_string()));
3959 };
3960 let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3961 let rows = flat.len().checked_div(cols).unwrap_or(0);
3962 let is_blank = |v: &ResultData| {
3963 matches!(v, ResultData::None)
3964 || matches!(v, ResultData::String(s) if s.is_empty())
3965 };
3966 let row_blank = |r: usize| (0..cols).all(|c| is_blank(&flat[r * cols + c]));
3967 let col_blank = |c: usize| (0..rows).all(|r| is_blank(&flat[r * cols + c]));
3968 let mut r_start = 0;
3969 while r_start < rows && row_blank(r_start) {
3970 r_start += 1;
3971 }
3972 let mut r_end = rows;
3973 while r_end > r_start && row_blank(r_end - 1) {
3974 r_end -= 1;
3975 }
3976 let mut c_start = 0;
3977 while c_start < cols && col_blank(c_start) {
3978 c_start += 1;
3979 }
3980 let mut c_end = cols;
3981 while c_end > c_start && col_blank(c_end - 1) {
3982 c_end -= 1;
3983 }
3984 let mut result = Vec::new();
3985 for r in r_start..r_end {
3986 for c in c_start..c_end {
3987 result.push(flat[r * cols + c].clone());
3988 }
3989 }
3990 Ok(ResultData::List(result))
3991 }
3992 _ => unreachable!(),
3993 }
3994 }
3995
3996 pub fn get_src(&self, cell: &CellRef) -> Option<&String> {
4003 let col = self.columns.get(cell.col);
4004 if let Some(col) = col {
4005 col.src.get(cell.row)
4006 } else {
4007 None
4008 }
4009 }
4010
4011 pub fn get_src_str(&self, cell: &CellRef) -> String {
4014 let col = self.columns.get(cell.col);
4015 if let Some(col) = col {
4016 col.src.get(cell.row).cloned().unwrap_or("".to_string())
4017 } else {
4018 "".to_string()
4019 }
4020 }
4021
4022 pub fn get_src_str_ref(&self, cell: &CellRef) -> Option<&str> {
4024 let col = self.columns.get(cell.col)?;
4025 col.src.get(cell.row).map(|s| s.as_str())
4026 }
4027
4028 pub fn get_word_boundaries(&self, cell: &CellRef, char_offset: usize) -> (usize, usize) {
4032 let text = self.get_src_str(cell);
4033 get_word_boundaries_from_str(&text, char_offset)
4034 }
4035}