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