1use crate::ParserLimits;
2use crate::structured_ref;
3use crate::tokenizer::{
4 Associativity, Token, TokenSpan, TokenStream, TokenSubType, TokenType, TokenizerError,
5};
6use crate::types::{FormulaDialect, ParsingError};
7use crate::{ExcelError, LiteralValue};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use crate::hasher::FormulaHasher;
13use formualizer_common::coord::{
14 col_index_from_letters_1based, col_letters_from_1based, parse_a1_1based,
15};
16use formualizer_common::{
17 AxisBound, RelativeCoord, SheetCellRef, SheetLocator, SheetRangeRef, SheetRef,
18};
19use once_cell::sync::Lazy;
20use smallvec::SmallVec;
21use std::error::Error;
22use std::fmt::{self, Display};
23use std::hash::{Hash, Hasher};
24use std::str::FromStr;
25use std::sync::Arc;
26
27type VolatilityFn = dyn Fn(&str) -> bool + Send + Sync + 'static;
28type VolatilityClassifierBox = Box<VolatilityFn>;
29type VolatilityClassifierArc = Arc<VolatilityFn>;
30
31#[derive(Debug)]
33pub struct ParserError {
34 pub message: String,
35 pub position: Option<usize>,
36}
37
38impl Display for ParserError {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 if let Some(pos) = self.position {
41 write!(f, "ParserError at position {}: {}", pos, self.message)
42 } else {
43 write!(f, "ParserError: {}", self.message)
44 }
45 }
46}
47
48impl Error for ParserError {}
49
50static COLUMN_LOOKUP: Lazy<Vec<String>> = Lazy::new(|| {
52 let mut cols = Vec::with_capacity(702);
53 for c in b'A'..=b'Z' {
55 cols.push(String::from(c as char));
56 }
57 for c1 in b'A'..=b'Z' {
59 for c2 in b'A'..=b'Z' {
60 cols.push(format!("{}{}", c1 as char, c2 as char));
61 }
62 }
63 cols
64});
65
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
68#[derive(Debug, Clone, PartialEq, Hash)]
69pub enum TableSpecifier {
70 All,
72 Data,
74 Headers,
76 Totals,
78 Row(TableRowSpecifier),
80 Column(String),
82 ColumnRange(String, String),
84 SpecialItem(SpecialItem),
86 Combination(Vec<Box<TableSpecifier>>),
88}
89
90#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
92#[derive(Debug, Clone, PartialEq, Hash)]
93pub enum TableRowSpecifier {
94 Current,
96 All,
98 Data,
100 Headers,
102 Totals,
104 Index(u32),
106}
107
108#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
110#[derive(Debug, Clone, PartialEq, Hash)]
111pub enum SpecialItem {
112 Headers,
114 Data,
116 Totals,
118 All,
120 ThisRow,
122}
123
124#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
126#[derive(Debug, Clone, PartialEq, Hash)]
127pub struct TableReference {
128 pub name: String,
130 pub specifier: Option<TableSpecifier>,
132}
133
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135#[derive(Debug, Clone, PartialEq, Hash)]
136pub enum ExternalBookRef {
137 Token(String),
138}
139
140impl ExternalBookRef {
141 pub fn token(&self) -> &str {
142 match self {
143 ExternalBookRef::Token(s) => s,
144 }
145 }
146}
147
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
150pub enum ExternalRefKind {
151 Cell {
152 row: u32,
153 col: u32,
154 row_abs: bool,
155 col_abs: bool,
156 },
157 Range {
158 start_row: Option<u32>,
159 start_col: Option<u32>,
160 end_row: Option<u32>,
161 end_col: Option<u32>,
162 start_row_abs: bool,
163 start_col_abs: bool,
164 end_row_abs: bool,
165 end_col_abs: bool,
166 },
167}
168
169impl ExternalRefKind {
170 pub fn cell(row: u32, col: u32) -> Self {
171 Self::Cell {
172 row,
173 col,
174 row_abs: false,
175 col_abs: false,
176 }
177 }
178
179 pub fn cell_with_abs(row: u32, col: u32, row_abs: bool, col_abs: bool) -> Self {
180 Self::Cell {
181 row,
182 col,
183 row_abs,
184 col_abs,
185 }
186 }
187
188 pub fn range(
189 start_row: Option<u32>,
190 start_col: Option<u32>,
191 end_row: Option<u32>,
192 end_col: Option<u32>,
193 ) -> Self {
194 Self::Range {
195 start_row,
196 start_col,
197 end_row,
198 end_col,
199 start_row_abs: false,
200 start_col_abs: false,
201 end_row_abs: false,
202 end_col_abs: false,
203 }
204 }
205
206 #[allow(clippy::too_many_arguments)]
209 pub fn range_with_abs(
210 start_row: Option<u32>,
211 start_col: Option<u32>,
212 end_row: Option<u32>,
213 end_col: Option<u32>,
214 start_row_abs: bool,
215 start_col_abs: bool,
216 end_row_abs: bool,
217 end_col_abs: bool,
218 ) -> Self {
219 Self::Range {
220 start_row,
221 start_col,
222 end_row,
223 end_col,
224 start_row_abs,
225 start_col_abs,
226 end_row_abs,
227 end_col_abs,
228 }
229 }
230}
231
232#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233#[derive(Debug, Clone, PartialEq, Hash)]
234pub struct ExternalReference {
235 pub raw: String,
236 pub book: ExternalBookRef,
237 pub sheet: String,
238 pub kind: ExternalRefKind,
239}
240
241#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
243#[derive(Debug, Clone, PartialEq, Hash)]
244pub enum ReferenceType {
245 Cell {
246 sheet: Option<String>,
247 row: u32,
248 col: u32,
249 row_abs: bool,
250 col_abs: bool,
251 },
252 Range {
253 sheet: Option<String>,
254 start_row: Option<u32>,
255 start_col: Option<u32>,
256 end_row: Option<u32>,
257 end_col: Option<u32>,
258 start_row_abs: bool,
259 start_col_abs: bool,
260 end_row_abs: bool,
261 end_col_abs: bool,
262 },
263 Cell3D {
268 sheet_first: String,
269 sheet_last: String,
270 row: u32,
271 col: u32,
272 row_abs: bool,
273 col_abs: bool,
274 },
275 Range3D {
277 sheet_first: String,
278 sheet_last: String,
279 start_row: Option<u32>,
280 start_col: Option<u32>,
281 end_row: Option<u32>,
282 end_col: Option<u32>,
283 start_row_abs: bool,
284 start_col_abs: bool,
285 end_row_abs: bool,
286 end_col_abs: bool,
287 },
288 External(ExternalReference),
289 Table(TableReference),
290 NamedRange(String),
291}
292
293impl Display for TableSpecifier {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 match self {
296 TableSpecifier::All => write!(f, "#All"),
297 TableSpecifier::Data => write!(f, "#Data"),
298 TableSpecifier::Headers => write!(f, "#Headers"),
299 TableSpecifier::Totals => write!(f, "#Totals"),
300 TableSpecifier::Row(row) => write!(f, "{row}"),
301 TableSpecifier::Column(column) => write!(f, "{column}"),
302 TableSpecifier::ColumnRange(start, end) => write!(f, "{start}:{end}"),
303 TableSpecifier::SpecialItem(item) => write!(f, "{item}"),
304 TableSpecifier::Combination(specs) => {
305 let mut first = true;
311 for spec in specs {
312 if !first {
313 write!(f, ",")?;
314 }
315 first = false;
316 match spec.as_ref() {
317 TableSpecifier::ColumnRange(start, end) => {
318 write!(f, "[{start}]:[{end}]")?;
319 }
320 other => write!(f, "[{other}]")?,
321 }
322 }
323 Ok(())
324 }
325 }
326 }
327}
328
329impl Display for TableRowSpecifier {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 match self {
332 TableRowSpecifier::Current => write!(f, "@"),
333 TableRowSpecifier::All => write!(f, "#All"),
334 TableRowSpecifier::Data => write!(f, "#Data"),
335 TableRowSpecifier::Headers => write!(f, "#Headers"),
336 TableRowSpecifier::Totals => write!(f, "#Totals"),
337 TableRowSpecifier::Index(idx) => write!(f, "{idx}"),
338 }
339 }
340}
341
342impl Display for SpecialItem {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 match self {
345 SpecialItem::Headers => write!(f, "#Headers"),
346 SpecialItem::Data => write!(f, "#Data"),
347 SpecialItem::Totals => write!(f, "#Totals"),
348 SpecialItem::All => write!(f, "#All"),
349 SpecialItem::ThisRow => write!(f, "@"),
350 }
351 }
352}
353
354fn sheet_name_needs_quoting(name: &str) -> bool {
355 formualizer_common::a1_sheet_name_needs_quoting(name)
356}
357
358#[derive(Debug, Clone)]
359struct OpenFormulaRefPart {
360 sheet: Option<String>,
361 coord: String,
362}
363
364type AxisPartWithAbs = Option<(u32, bool)>;
365type RangePartWithAbs = (AxisPartWithAbs, AxisPartWithAbs);
366
367#[derive(Debug, Clone)]
369enum SheetSpec {
370 None,
372 Single(String),
374 Range { first: String, last: String },
376}
377
378impl ReferenceType {
379 pub fn cell(sheet: Option<String>, row: u32, col: u32) -> Self {
381 Self::Cell {
382 sheet,
383 row,
384 col,
385 row_abs: false,
386 col_abs: false,
387 }
388 }
389
390 pub fn cell_with_abs(
392 sheet: Option<String>,
393 row: u32,
394 col: u32,
395 row_abs: bool,
396 col_abs: bool,
397 ) -> Self {
398 Self::Cell {
399 sheet,
400 row,
401 col,
402 row_abs,
403 col_abs,
404 }
405 }
406
407 pub fn range(
409 sheet: Option<String>,
410 start_row: Option<u32>,
411 start_col: Option<u32>,
412 end_row: Option<u32>,
413 end_col: Option<u32>,
414 ) -> Self {
415 Self::Range {
416 sheet,
417 start_row,
418 start_col,
419 end_row,
420 end_col,
421 start_row_abs: false,
422 start_col_abs: false,
423 end_row_abs: false,
424 end_col_abs: false,
425 }
426 }
427
428 #[allow(clippy::too_many_arguments)]
432 pub fn range_with_abs(
433 sheet: Option<String>,
434 start_row: Option<u32>,
435 start_col: Option<u32>,
436 end_row: Option<u32>,
437 end_col: Option<u32>,
438 start_row_abs: bool,
439 start_col_abs: bool,
440 end_row_abs: bool,
441 end_col_abs: bool,
442 ) -> Self {
443 Self::Range {
444 sheet,
445 start_row,
446 start_col,
447 end_row,
448 end_col,
449 start_row_abs,
450 start_col_abs,
451 end_row_abs,
452 end_col_abs,
453 }
454 }
455
456 pub fn from_string(reference: &str) -> Result<Self, ParsingError> {
458 Self::parse_excel_reference(reference)
459 }
460
461 pub fn from_string_with_dialect(
463 reference: &str,
464 dialect: FormulaDialect,
465 ) -> Result<Self, ParsingError> {
466 match dialect {
467 FormulaDialect::Excel => Self::parse_excel_reference(reference),
468 FormulaDialect::OpenFormula => Self::parse_openformula_reference(reference)
469 .or_else(|_| Self::parse_excel_reference(reference)),
470 }
471 }
472
473 pub fn parse_sheet_ref(reference: &str) -> Result<SheetRef<'static>, ParsingError> {
477 Self::parse_sheet_ref_with_dialect(reference, FormulaDialect::Excel)
478 }
479
480 pub fn parse_sheet_ref_with_dialect(
482 reference: &str,
483 dialect: FormulaDialect,
484 ) -> Result<SheetRef<'static>, ParsingError> {
485 match dialect {
486 FormulaDialect::Excel => Self::parse_excel_sheet_ref(reference),
487 FormulaDialect::OpenFormula => Self::parse_openformula_sheet_ref(reference)
488 .or_else(|_| Self::parse_excel_sheet_ref(reference)),
489 }
490 }
491
492 pub fn to_sheet_ref_lossy(&self) -> Option<SheetRef<'_>> {
495 match self {
496 ReferenceType::Cell {
497 sheet,
498 row,
499 col,
500 row_abs,
501 col_abs,
502 } => {
503 let row0 = row.checked_sub(1)?;
504 let col0 = col.checked_sub(1)?;
505 let sheet_loc = match sheet.as_deref() {
506 Some(name) => SheetLocator::from_name(name),
507 None => SheetLocator::Current,
508 };
509 let coord = RelativeCoord::new(row0, col0, *row_abs, *col_abs);
510 Some(SheetRef::Cell(SheetCellRef::new(sheet_loc, coord)))
511 }
512 ReferenceType::Range {
513 sheet,
514 start_row,
515 start_col,
516 end_row,
517 end_col,
518 start_row_abs,
519 start_col_abs,
520 end_row_abs,
521 end_col_abs,
522 } => {
523 let sheet_loc = match sheet.as_deref() {
524 Some(name) => SheetLocator::from_name(name),
525 None => SheetLocator::Current,
526 };
527 let sr = start_row
528 .and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *start_row_abs)));
529 if start_row.is_some() && sr.is_none() {
530 return None;
531 }
532 let sc = start_col
533 .and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *start_col_abs)));
534 if start_col.is_some() && sc.is_none() {
535 return None;
536 }
537 let er =
538 end_row.and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *end_row_abs)));
539 if end_row.is_some() && er.is_none() {
540 return None;
541 }
542 let ec =
543 end_col.and_then(|v| v.checked_sub(1).map(|i| AxisBound::new(i, *end_col_abs)));
544 if end_col.is_some() && ec.is_none() {
545 return None;
546 }
547 let range = SheetRangeRef::from_parts(sheet_loc, sr, sc, er, ec).ok()?;
548 Some(SheetRef::Range(range))
549 }
550 _ => None,
551 }
552 }
553
554 fn parse_excel_sheet_ref(reference: &str) -> Result<SheetRef<'static>, ParsingError> {
555 let (spec, ref_part) = Self::extract_sheet_spec(reference);
556 if matches!(spec, SheetSpec::Range { .. }) {
557 return Err(ParsingError::InvalidReference(
558 "3D references are not supported for SheetRef".to_string(),
559 ));
560 }
561 let sheet = match spec {
562 SheetSpec::None => None,
563 SheetSpec::Single(name) => Some(name),
564 SheetSpec::Range { .. } => unreachable!(),
565 };
566
567 if ref_part.contains('[') {
568 return Err(ParsingError::InvalidReference(
569 "Table references are not supported for SheetRef".to_string(),
570 ));
571 }
572
573 let sheet_loc: SheetLocator<'static> = match sheet {
574 Some(name) => SheetLocator::from_name(name),
575 None => SheetLocator::Current,
576 };
577
578 if ref_part.contains(':') {
579 let mut parts = ref_part.splitn(2, ':');
580 let start = parts.next().unwrap();
581 let end = parts.next().ok_or_else(|| {
582 ParsingError::InvalidReference(format!("Invalid range: {ref_part}"))
583 })?;
584
585 let (start_col, start_row) = Self::parse_range_part_with_abs(start)?;
586 let (end_col, end_row) = Self::parse_range_part_with_abs(end)?;
587
588 let start_col = Self::axis_bound_from_1based(start_col)?;
589 let start_row = Self::axis_bound_from_1based(start_row)?;
590 let end_col = Self::axis_bound_from_1based(end_col)?;
591 let end_row = Self::axis_bound_from_1based(end_row)?;
592
593 let range =
594 SheetRangeRef::from_parts(sheet_loc, start_row, start_col, end_row, end_col)
595 .map_err(|err| ParsingError::InvalidReference(err.to_string()))?;
596 Ok(SheetRef::Range(range))
597 } else {
598 let (row, col, row_abs, col_abs) = parse_a1_1based(&ref_part)
599 .map_err(|err| ParsingError::InvalidReference(err.to_string()))?;
600 let coord = RelativeCoord::new(row - 1, col - 1, row_abs, col_abs);
601 Ok(SheetRef::Cell(SheetCellRef::new(sheet_loc, coord)))
602 }
603 }
604
605 fn parse_openformula_sheet_ref(reference: &str) -> Result<SheetRef<'static>, ParsingError> {
606 Self::parse_excel_sheet_ref(reference)
607 }
608
609 fn axis_bound_from_1based(
610 bound: Option<(u32, bool)>,
611 ) -> Result<Option<AxisBound>, ParsingError> {
612 match bound {
613 Some((index, abs)) => AxisBound::from_excel_1based(index, abs)
614 .map(Some)
615 .map_err(|err| ParsingError::InvalidReference(err.to_string())),
616 None => Ok(None),
617 }
618 }
619
620 fn parse_range_part_with_abs(part: &str) -> Result<RangePartWithAbs, ParsingError> {
621 if let Ok((row, col, row_abs, col_abs)) = parse_a1_1based(part) {
622 return Ok((Some((col, col_abs)), Some((row, row_abs))));
623 }
624
625 let bytes = part.as_bytes();
626 let len = bytes.len();
627 let mut i = 0usize;
628
629 let mut col_abs = false;
630 let mut row_abs = false;
631
632 if i < len && bytes[i] == b'$' {
633 col_abs = true;
634 i += 1;
635 }
636
637 let col_start = i;
638 while i < len && bytes[i].is_ascii_alphabetic() {
639 i += 1;
640 }
641
642 if i > col_start {
643 let col_str = &part[col_start..i];
644 let col1 = Self::column_to_number(col_str)?;
645
646 if i == len {
647 return Ok((Some((col1, col_abs)), None));
648 }
649
650 if i < len && bytes[i] == b'$' {
651 row_abs = true;
652 i += 1;
653 }
654
655 if i >= len {
656 return Err(ParsingError::InvalidReference(format!(
657 "Invalid range part: {part}"
658 )));
659 }
660
661 let row_start = i;
662 while i < len && bytes[i].is_ascii_digit() {
663 i += 1;
664 }
665
666 if row_start == i || i != len {
667 return Err(ParsingError::InvalidReference(format!(
668 "Invalid range part: {part}"
669 )));
670 }
671
672 let row_str = &part[row_start..i];
673 let row1 = row_str
674 .parse::<u32>()
675 .map_err(|_| ParsingError::InvalidReference(format!("Invalid row: {row_str}")))?;
676 if row1 == 0 {
677 return Err(ParsingError::InvalidReference(format!(
678 "Invalid range part: {part}"
679 )));
680 }
681
682 return Ok((Some((col1, col_abs)), Some((row1, row_abs))));
683 }
684
685 i = 0;
686 if i < len && bytes[i] == b'$' {
687 row_abs = true;
688 i += 1;
689 }
690
691 let row_start = i;
692 while i < len && bytes[i].is_ascii_digit() {
693 i += 1;
694 }
695
696 if row_start == i || i != len {
697 return Err(ParsingError::InvalidReference(format!(
698 "Invalid range part: {part}"
699 )));
700 }
701
702 let row_str = &part[row_start..i];
703 let row1 = row_str
704 .parse::<u32>()
705 .map_err(|_| ParsingError::InvalidReference(format!("Invalid row: {row_str}")))?;
706 if row1 == 0 {
707 return Err(ParsingError::InvalidReference(format!(
708 "Invalid range part: {part}"
709 )));
710 }
711
712 Ok((None, Some((row1, row_abs))))
713 }
714
715 fn parse_3d_reference(first: &str, last: &str, ref_part: &str) -> Result<Self, ParsingError> {
716 if first.is_empty() || last.is_empty() {
717 return Err(ParsingError::InvalidReference(format!(
718 "3D reference requires two sheet names: {first}:{last}!{ref_part}"
719 )));
720 }
721 if ref_part.is_empty() {
722 return Err(ParsingError::InvalidReference(format!(
723 "3D reference {first}:{last}! is missing a cell or range"
724 )));
725 }
726 if ref_part.contains('[') {
728 return Err(ParsingError::InvalidReference(format!(
729 "3D reference {first}:{last}!{ref_part} cannot target a table"
730 )));
731 }
732
733 if ref_part.contains(':') {
734 let mut parts = ref_part.splitn(2, ':');
735 let start = parts.next().unwrap();
736 let end = parts.next().ok_or_else(|| {
737 ParsingError::InvalidReference(format!("Invalid range: {ref_part}"))
738 })?;
739 let (start_col, start_row) = Self::parse_range_part_with_abs(start)?;
740 let (end_col, end_row) = Self::parse_range_part_with_abs(end)?;
741
742 let split = |bound: Option<(u32, bool)>| match bound {
743 Some((index, abs)) => (Some(index), abs),
744 None => (None, false),
745 };
746 let (start_col, start_col_abs) = split(start_col);
747 let (start_row, start_row_abs) = split(start_row);
748 let (end_col, end_col_abs) = split(end_col);
749 let (end_row, end_row_abs) = split(end_row);
750
751 Ok(ReferenceType::Range3D {
752 sheet_first: first.to_string(),
753 sheet_last: last.to_string(),
754 start_row,
755 start_col,
756 end_row,
757 end_col,
758 start_row_abs,
759 start_col_abs,
760 end_row_abs,
761 end_col_abs,
762 })
763 } else {
764 let (col, row, col_abs, row_abs) =
765 Self::parse_cell_reference(ref_part).map_err(|_| {
766 ParsingError::InvalidReference(format!(
767 "Invalid 3D reference target: {ref_part}"
768 ))
769 })?;
770 Ok(ReferenceType::Cell3D {
771 sheet_first: first.to_string(),
772 sheet_last: last.to_string(),
773 row,
774 col,
775 row_abs,
776 col_abs,
777 })
778 }
779 }
780
781 fn parse_excel_reference(reference: &str) -> Result<Self, ParsingError> {
782 if let Some(rest) = reference.strip_prefix("[0]")
787 && rest.contains('!')
788 {
789 return Self::parse_excel_reference(rest.strip_prefix('!').unwrap_or(rest));
790 }
791 if let Some(rest) = reference.strip_prefix("'[0]")
792 && rest.contains('!')
793 {
794 return Self::parse_excel_reference(&format!("'{rest}"));
795 }
796
797 if reference.starts_with('[') && reference.ends_with(']') && !reference.contains('!') {
804 return Self::parse_bracketed_structured_reference(reference);
805 }
806
807 let (sheet_spec, ref_part) = Self::extract_sheet_spec(reference);
809
810 if let SheetSpec::Range { first, last, .. } = &sheet_spec {
813 return Self::parse_3d_reference(first, last, &ref_part);
814 }
815
816 let sheet = match sheet_spec {
817 SheetSpec::None => None,
818 SheetSpec::Single(name) => Some(name),
819 SheetSpec::Range { .. } => unreachable!(),
821 };
822
823 if ref_part.contains('[') {
826 if Self::is_r1c1_shape(&ref_part) {
834 return Ok(ReferenceType::NamedRange(reference.to_string()));
835 }
836 return Self::parse_table_reference(&ref_part);
837 }
838
839 let external_sheet = sheet.as_deref().and_then(|s| {
840 let lb = s.rfind('[')?;
844 let rb_rel = s[lb..].find(']')?;
845 let rb = lb + rb_rel;
846 if lb >= rb {
847 return None;
848 }
849
850 let token = &s[..=rb];
851 let sheet_name = &s[rb + 1..];
852 if sheet_name.is_empty() {
853 None
854 } else {
855 Some((token, sheet_name))
856 }
857 });
858
859 if ref_part.contains(':') {
860 let mut parts = ref_part.splitn(2, ':');
862 let start = parts.next().unwrap();
863 let end = parts.next().ok_or_else(|| {
864 ParsingError::InvalidReference(format!("Invalid range: {ref_part}"))
865 })?;
866 let (start_col, start_row) = Self::parse_range_part_with_abs(start)?;
867 let (end_col, end_row) = Self::parse_range_part_with_abs(end)?;
868
869 let split = |bound: Option<(u32, bool)>| match bound {
870 Some((index, abs)) => (Some(index), abs),
871 None => (None, false),
872 };
873 let (start_col, start_col_abs) = split(start_col);
874 let (start_row, start_row_abs) = split(start_row);
875 let (end_col, end_col_abs) = split(end_col);
876 let (end_row, end_row_abs) = split(end_row);
877
878 if let Some((book_token, sheet_name)) = external_sheet {
879 Ok(ReferenceType::External(ExternalReference {
880 raw: reference.to_string(),
881 book: ExternalBookRef::Token(book_token.to_string()),
882 sheet: sheet_name.to_string(),
883 kind: ExternalRefKind::Range {
884 start_row,
885 start_col,
886 end_row,
887 end_col,
888 start_row_abs,
889 start_col_abs,
890 end_row_abs,
891 end_col_abs,
892 },
893 }))
894 } else {
895 Ok(ReferenceType::Range {
896 sheet,
897 start_row,
898 start_col,
899 end_row,
900 end_col,
901 start_row_abs,
902 start_col_abs,
903 end_row_abs,
904 end_col_abs,
905 })
906 }
907 } else {
908 match Self::parse_cell_reference(&ref_part) {
910 Ok((col, row, col_abs, row_abs)) => {
911 if let Some((book_token, sheet_name)) = external_sheet {
912 Ok(ReferenceType::External(ExternalReference {
913 raw: reference.to_string(),
914 book: ExternalBookRef::Token(book_token.to_string()),
915 sheet: sheet_name.to_string(),
916 kind: ExternalRefKind::Cell {
917 row,
918 col,
919 row_abs,
920 col_abs,
921 },
922 }))
923 } else {
924 Ok(ReferenceType::Cell {
925 sheet,
926 row,
927 col,
928 row_abs,
929 col_abs,
930 })
931 }
932 }
933 Err(_) => {
934 Ok(ReferenceType::NamedRange(reference.to_string()))
936 }
937 }
938 }
939 }
940
941 fn parse_cell_reference(reference: &str) -> Result<(u32, u32, bool, bool), ParsingError> {
943 parse_a1_1based(reference)
944 .map(|(row, col, row_abs, col_abs)| (col, row, col_abs, row_abs))
945 .map_err(|_| {
946 ParsingError::InvalidReference(format!("Invalid cell reference: {reference}"))
947 })
948 }
949
950 pub(crate) fn column_to_number(column: &str) -> Result<u32, ParsingError> {
952 col_index_from_letters_1based(column)
953 .map_err(|_| ParsingError::InvalidReference(format!("Invalid column: {column}")))
954 }
955
956 pub(crate) fn number_to_column(num: u32) -> String {
958 if num == 0 {
959 return String::new();
960 }
961 if num > 0 && num <= 702 {
963 return COLUMN_LOOKUP[(num - 1) as usize].clone();
964 }
965
966 col_letters_from_1based(num).unwrap_or_default()
967 }
968
969 fn format_col(col: u32, abs: bool) -> String {
970 if abs {
971 format!("${}", Self::number_to_column(col))
972 } else {
973 Self::number_to_column(col)
974 }
975 }
976
977 fn format_row(row: u32, abs: bool) -> String {
978 if abs {
979 format!("${row}")
980 } else {
981 row.to_string()
982 }
983 }
984}
985
986impl Display for ReferenceType {
987 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988 write!(
989 f,
990 "{}",
991 match self {
992 ReferenceType::Cell {
993 sheet,
994 row,
995 col,
996 row_abs,
997 col_abs,
998 } => {
999 let col_str = Self::format_col(*col, *col_abs);
1000 let row_str = Self::format_row(*row, *row_abs);
1001
1002 if let Some(sheet_name) = sheet {
1003 format!(
1004 "{}!{col_str}{row_str}",
1005 formualizer_common::format_a1_sheet_name(sheet_name)
1006 )
1007 } else {
1008 format!("{col_str}{row_str}")
1009 }
1010 }
1011 ReferenceType::Range {
1012 sheet,
1013 start_row,
1014 start_col,
1015 end_row,
1016 end_col,
1017 start_row_abs,
1018 start_col_abs,
1019 end_row_abs,
1020 end_col_abs,
1021 } => {
1022 let start_ref = match (start_col, start_row) {
1024 (Some(col), Some(row)) => format!(
1025 "{}{}",
1026 Self::format_col(*col, *start_col_abs),
1027 Self::format_row(*row, *start_row_abs)
1028 ),
1029 (Some(col), None) => Self::format_col(*col, *start_col_abs),
1030 (None, Some(row)) => Self::format_row(*row, *start_row_abs),
1031 (None, None) => "".to_string(), };
1033
1034 let end_ref = match (end_col, end_row) {
1036 (Some(col), Some(row)) => format!(
1037 "{}{}",
1038 Self::format_col(*col, *end_col_abs),
1039 Self::format_row(*row, *end_row_abs)
1040 ),
1041 (Some(col), None) => Self::format_col(*col, *end_col_abs),
1042 (None, Some(row)) => Self::format_row(*row, *end_row_abs),
1043 (None, None) => "".to_string(), };
1045
1046 let range_part = format!("{start_ref}:{end_ref}");
1047
1048 if let Some(sheet_name) = sheet {
1049 format!(
1050 "{}!{range_part}",
1051 formualizer_common::format_a1_sheet_name(sheet_name)
1052 )
1053 } else {
1054 range_part
1055 }
1056 }
1057 ReferenceType::Cell3D {
1058 sheet_first,
1059 sheet_last,
1060 row,
1061 col,
1062 row_abs,
1063 col_abs,
1064 } => {
1065 let col_str = Self::format_col(*col, *col_abs);
1066 let row_str = Self::format_row(*row, *row_abs);
1067 let prefix = format_3d_sheet_prefix(sheet_first, sheet_last);
1068 format!("{prefix}!{col_str}{row_str}")
1069 }
1070 ReferenceType::Range3D {
1071 sheet_first,
1072 sheet_last,
1073 start_row,
1074 start_col,
1075 end_row,
1076 end_col,
1077 start_row_abs,
1078 start_col_abs,
1079 end_row_abs,
1080 end_col_abs,
1081 } => {
1082 let start_ref = match (start_col, start_row) {
1083 (Some(col), Some(row)) => format!(
1084 "{}{}",
1085 Self::format_col(*col, *start_col_abs),
1086 Self::format_row(*row, *start_row_abs)
1087 ),
1088 (Some(col), None) => Self::format_col(*col, *start_col_abs),
1089 (None, Some(row)) => Self::format_row(*row, *start_row_abs),
1090 (None, None) => "".to_string(),
1091 };
1092 let end_ref = match (end_col, end_row) {
1093 (Some(col), Some(row)) => format!(
1094 "{}{}",
1095 Self::format_col(*col, *end_col_abs),
1096 Self::format_row(*row, *end_row_abs)
1097 ),
1098 (Some(col), None) => Self::format_col(*col, *end_col_abs),
1099 (None, Some(row)) => Self::format_row(*row, *end_row_abs),
1100 (None, None) => "".to_string(),
1101 };
1102 let range_part = format!("{start_ref}:{end_ref}");
1103 let prefix = format_3d_sheet_prefix(sheet_first, sheet_last);
1104 format!("{prefix}!{range_part}")
1105 }
1106 ReferenceType::External(ext) => ext.raw.clone(),
1107 ReferenceType::Table(table_ref) => {
1108 if let Some(specifier) = &table_ref.specifier {
1109 match specifier {
1112 TableSpecifier::Column(column) => {
1113 format!("{}[{}]", table_ref.name, column.trim())
1114 }
1115 TableSpecifier::ColumnRange(start, end) => {
1116 format!("{}[{}:{}]", table_ref.name, start.trim(), end.trim())
1117 }
1118 _ => {
1119 format!("{}[{}]", table_ref.name, specifier)
1121 }
1122 }
1123 } else {
1124 table_ref.name.clone()
1125 }
1126 }
1127 ReferenceType::NamedRange(name) => name.clone(),
1128 }
1129 )
1130 }
1131}
1132
1133fn format_3d_sheet_prefix(first: &str, last: &str) -> String {
1137 if sheet_name_needs_quoting(first) || sheet_name_needs_quoting(last) {
1138 let escaped = format!("{first}:{last}").replace('\'', "''");
1139 format!("'{escaped}'")
1140 } else {
1141 format!("{first}:{last}")
1142 }
1143}
1144
1145impl TryFrom<&str> for ReferenceType {
1146 type Error = ParsingError;
1147
1148 fn try_from(value: &str) -> Result<Self, Self::Error> {
1149 ReferenceType::from_string(value)
1150 }
1151}
1152
1153impl FromStr for ReferenceType {
1154 type Err = ParsingError;
1155
1156 fn from_str(s: &str) -> Result<Self, Self::Err> {
1157 ReferenceType::from_string(s)
1158 }
1159}
1160
1161impl ReferenceType {
1162 pub fn normalise(&self) -> String {
1164 format!("{self}")
1165 }
1166
1167 fn read_sheet_segment(reference: &str, start: usize) -> Option<(String, usize, bool)> {
1172 let bytes = reference.as_bytes();
1173 if start >= bytes.len() {
1174 return None;
1175 }
1176
1177 if bytes[start] == b'\'' {
1178 let mut i = start + 1;
1180 let body_start = i;
1181 while i < bytes.len() {
1182 if bytes[i] == b'\'' {
1183 if i + 1 < bytes.len() && bytes[i + 1] == b'\'' {
1184 i += 2;
1185 continue;
1186 }
1187 let raw = &reference[body_start..i];
1188 let name = raw.replace("''", "'");
1189 return Some((name, i + 1, true));
1190 }
1191 i += 1;
1192 }
1193 None
1194 } else {
1195 let mut i = start;
1198 while i < bytes.len() {
1199 let b = bytes[i];
1200 match b {
1201 b':' | b'!' | b'\'' | b' ' | b'\t' | b'\n' | b'\r' => break,
1202 _ => i += 1,
1203 }
1204 }
1205 if i == start {
1206 None
1207 } else {
1208 Some((reference[start..i].to_string(), i, false))
1209 }
1210 }
1211 }
1212
1213 fn extract_sheet_spec(reference: &str) -> (SheetSpec, String) {
1216 let Some((first_name, after_first, first_quoted)) = Self::read_sheet_segment(reference, 0)
1217 else {
1218 return Self::extract_sheet_spec_fallback(reference);
1221 };
1222 let bytes = reference.as_bytes();
1223
1224 if after_first < bytes.len() && bytes[after_first] == b':' {
1226 let second_start = after_first + 1;
1227 if let Some((second_name, after_second, _)) =
1228 Self::read_sheet_segment(reference, second_start)
1229 && after_second < bytes.len()
1230 && bytes[after_second] == b'!'
1231 {
1232 let ref_part = reference[after_second + 1..].to_string();
1233 return (
1234 SheetSpec::Range {
1235 first: first_name,
1236 last: second_name,
1237 },
1238 ref_part,
1239 );
1240 }
1241
1242 if second_start < bytes.len() {
1248 if let Some(bang) = reference[second_start..].find('!') {
1249 let ref_part = reference[second_start + bang + 1..].to_string();
1250 return (
1251 SheetSpec::Range {
1252 first: first_name,
1253 last: String::new(),
1254 },
1255 ref_part,
1256 );
1257 }
1258 }
1259 }
1260
1261 if after_first < bytes.len() && bytes[after_first] == b'!' {
1263 let ref_part = reference[after_first + 1..].to_string();
1264 if first_quoted && !first_name.contains('[') {
1269 if let Some((first, last)) = first_name.split_once(':') {
1270 if !first.is_empty() && !last.is_empty() && !last.contains(':') {
1271 return (
1272 SheetSpec::Range {
1273 first: first.to_string(),
1274 last: last.to_string(),
1275 },
1276 ref_part,
1277 );
1278 }
1279 }
1280 }
1281 return (SheetSpec::Single(first_name), ref_part);
1282 }
1283
1284 Self::extract_sheet_spec_fallback(reference)
1287 }
1288
1289 fn extract_sheet_spec_fallback(reference: &str) -> (SheetSpec, String) {
1290 let bytes = reference.as_bytes();
1291 let mut i = 0;
1296 while i < bytes.len() {
1297 if bytes[i] == b'!' && i > 0 {
1298 let sheet = reference[..i].to_string();
1299 let ref_part = reference[i + 1..].to_string();
1300 return (SheetSpec::Single(sheet), ref_part);
1301 }
1302 i += 1;
1303 }
1304
1305 (SheetSpec::None, reference.to_string())
1306 }
1307
1308 fn is_r1c1_shape(s: &str) -> bool {
1318 let bytes = s.as_bytes();
1319 let len = bytes.len();
1320 let mut i = 0usize;
1321 let mut anchored = false;
1322
1323 if i >= len || bytes[i] != b'R' {
1324 return false;
1325 }
1326 i += 1;
1327
1328 let row_digits_start = i;
1329 while i < len && bytes[i].is_ascii_digit() {
1330 i += 1;
1331 }
1332 if i > row_digits_start {
1333 anchored = true;
1334 }
1335
1336 if i < len && bytes[i] == b'[' {
1337 i += 1;
1338 if i < len && bytes[i] == b'-' {
1339 i += 1;
1340 }
1341 let n_start = i;
1342 while i < len && bytes[i].is_ascii_digit() {
1343 i += 1;
1344 }
1345 if i == n_start || i >= len || bytes[i] != b']' {
1346 return false;
1347 }
1348 i += 1;
1349 anchored = true;
1350 }
1351
1352 if i >= len || bytes[i] != b'C' {
1353 return false;
1354 }
1355 i += 1;
1356
1357 let col_digits_start = i;
1358 while i < len && bytes[i].is_ascii_digit() {
1359 i += 1;
1360 }
1361 if i > col_digits_start {
1362 anchored = true;
1363 }
1364
1365 if i < len && bytes[i] == b'[' {
1366 i += 1;
1367 if i < len && bytes[i] == b'-' {
1368 i += 1;
1369 }
1370 let n_start = i;
1371 while i < len && bytes[i].is_ascii_digit() {
1372 i += 1;
1373 }
1374 if i == n_start || i >= len || bytes[i] != b']' {
1375 return false;
1376 }
1377 i += 1;
1378 anchored = true;
1379 }
1380
1381 i == len && anchored
1382 }
1383
1384 fn parse_table_reference(reference: &str) -> Result<Self, ParsingError> {
1390 let bracket_pos = reference.find('[').ok_or_else(|| {
1391 ParsingError::InvalidReference(format!("Missing '[' in table reference: {reference}"))
1392 })?;
1393 let table_name = reference[..bracket_pos].trim();
1394 if table_name.is_empty() {
1395 return Err(ParsingError::InvalidReference(reference.to_string()));
1396 }
1397
1398 let specifier_str = &reference[bracket_pos..];
1399 let specifier = structured_ref::parse_full_specifier(specifier_str)?;
1400
1401 Ok(ReferenceType::Table(TableReference {
1402 name: table_name.to_string(),
1403 specifier,
1404 }))
1405 }
1406
1407 fn parse_bracketed_structured_reference(reference: &str) -> Result<Self, ParsingError> {
1414 debug_assert!(reference.starts_with('[') && reference.ends_with(']'));
1415 let specifier = structured_ref::parse_full_specifier(reference)?;
1416
1417 match specifier {
1418 Some(TableSpecifier::Column(name)) => Ok(ReferenceType::Table(TableReference {
1419 name,
1420 specifier: Some(TableSpecifier::SpecialItem(SpecialItem::Data)),
1421 })),
1422 other => Ok(ReferenceType::Table(TableReference {
1423 name: String::new(),
1424 specifier: other,
1425 })),
1426 }
1427 }
1428
1429 fn parse_openformula_reference(reference: &str) -> Result<Self, ParsingError> {
1430 if reference.starts_with('[') && reference.ends_with(']') {
1431 let inner = &reference[1..reference.len() - 1];
1432 if inner.is_empty() {
1433 return Err(ParsingError::InvalidReference(
1434 "Empty OpenFormula reference".to_string(),
1435 ));
1436 }
1437
1438 let mut parts = inner.splitn(2, ':');
1439 let start_part_str = parts.next().unwrap();
1440 let end_part_str = parts.next();
1441
1442 let start_part = Self::parse_openformula_part(start_part_str)?;
1443 let end_part = if let Some(part) = end_part_str {
1444 Some(Self::parse_openformula_part(part)?)
1445 } else {
1446 None
1447 };
1448
1449 let sheet = match (&start_part.sheet, &end_part) {
1450 (Some(sheet), Some(end)) => {
1451 if let Some(end_sheet) = &end.sheet {
1452 if end_sheet != sheet {
1453 return Err(ParsingError::InvalidReference(format!(
1454 "Mismatched sheets in reference: {sheet} vs {end_sheet}"
1455 )));
1456 }
1457 }
1458 Some(sheet.clone())
1459 }
1460 (Some(sheet), None) => Some(sheet.clone()),
1461 (None, Some(end)) => end.sheet.clone(),
1462 (None, None) => None,
1463 };
1464
1465 let mut excel_like = String::new();
1466 if let Some(sheet_name) = sheet {
1467 if sheet_name_needs_quoting(&sheet_name) {
1468 let escaped = sheet_name.replace('\'', "''");
1469 excel_like.push('\'');
1470 excel_like.push_str(&escaped);
1471 excel_like.push('\'');
1472 } else {
1473 excel_like.push_str(&sheet_name);
1474 }
1475 excel_like.push('!');
1476 }
1477
1478 excel_like.push_str(&start_part.coord);
1479 if let Some(end) = end_part {
1480 excel_like.push(':');
1481 excel_like.push_str(&end.coord);
1482 }
1483
1484 return Self::parse_excel_reference(&excel_like);
1485 }
1486
1487 Err(ParsingError::InvalidReference(format!(
1488 "Unsupported OpenFormula reference: {reference}"
1489 )))
1490 }
1491
1492 fn parse_openformula_part(part: &str) -> Result<OpenFormulaRefPart, ParsingError> {
1493 let trimmed = part.trim();
1494 if trimmed.is_empty() {
1495 return Err(ParsingError::InvalidReference(
1496 "Empty component in OpenFormula reference".to_string(),
1497 ));
1498 }
1499
1500 if trimmed == "." {
1501 return Err(ParsingError::InvalidReference(
1502 "Incomplete OpenFormula reference component".to_string(),
1503 ));
1504 }
1505
1506 if trimmed.starts_with('[') {
1507 return Err(ParsingError::InvalidReference(format!(
1509 "Unexpected '[' in OpenFormula reference component: {trimmed}"
1510 )));
1511 }
1512
1513 let (sheet, coord_slice) = if let Some(stripped) = trimmed.strip_prefix('.') {
1514 (None, stripped.trim())
1515 } else if let Some(dot_idx) = Self::find_openformula_sheet_separator(trimmed) {
1516 let sheet_part = trimmed[..dot_idx].trim();
1517 let coord_part = trimmed[dot_idx + 1..].trim();
1518 if coord_part.is_empty() {
1519 return Err(ParsingError::InvalidReference(format!(
1520 "Missing coordinate in OpenFormula reference component: {trimmed}"
1521 )));
1522 }
1523 let sheet_name = Self::normalise_openformula_sheet(sheet_part)?;
1524 (Some(sheet_name), coord_part)
1525 } else {
1526 (None, trimmed)
1527 };
1528
1529 let coord = coord_slice.trim_start_matches('.').trim().to_string();
1530
1531 if coord.is_empty() {
1532 return Err(ParsingError::InvalidReference(format!(
1533 "Missing coordinate in OpenFormula reference component: {trimmed}"
1534 )));
1535 }
1536
1537 Ok(OpenFormulaRefPart { sheet, coord })
1538 }
1539
1540 fn normalise_openformula_sheet(sheet: &str) -> Result<String, ParsingError> {
1541 let without_abs = sheet.trim().trim_start_matches('$');
1542
1543 if without_abs.starts_with('\'') {
1544 if without_abs.len() < 2 || !without_abs.ends_with('\'') {
1545 return Err(ParsingError::InvalidReference(format!(
1546 "Unterminated sheet name in OpenFormula reference: {sheet}"
1547 )));
1548 }
1549 let inner = &without_abs[1..without_abs.len() - 1];
1550 Ok(inner.replace("''", "'"))
1551 } else {
1552 Ok(without_abs.to_string())
1553 }
1554 }
1555
1556 fn find_openformula_sheet_separator(part: &str) -> Option<usize> {
1557 let bytes = part.as_bytes();
1558 let mut i = 0;
1559 let mut in_quotes = false;
1560
1561 while i < bytes.len() {
1562 match bytes[i] {
1563 b'\'' => {
1564 if i + 1 < bytes.len() && bytes[i + 1] == b'\'' {
1565 i += 2;
1566 continue;
1567 }
1568 in_quotes = !in_quotes;
1569 i += 1;
1570 }
1571 b'.' if !in_quotes => return Some(i),
1572 _ => i += 1,
1573 }
1574 }
1575
1576 None
1577 }
1578
1579 pub fn to_excel_string(&self) -> String {
1586 match self {
1587 ReferenceType::Cell {
1588 sheet,
1589 row,
1590 col,
1591 row_abs,
1592 col_abs,
1593 } => {
1594 let col_str = Self::format_col(*col, *col_abs);
1595 let row_str = Self::format_row(*row, *row_abs);
1596 if let Some(s) = sheet {
1597 if sheet_name_needs_quoting(s) {
1598 let escaped_name = s.replace('\'', "''");
1599 format!("'{}'!{}{}", escaped_name, col_str, row_str)
1600 } else {
1601 format!("{}!{}{}", s, col_str, row_str)
1602 }
1603 } else {
1604 format!("{}{}", col_str, row_str)
1605 }
1606 }
1607 ReferenceType::Range {
1608 sheet,
1609 start_row,
1610 start_col,
1611 end_row,
1612 end_col,
1613 start_row_abs,
1614 start_col_abs,
1615 end_row_abs,
1616 end_col_abs,
1617 } => {
1618 let start_ref = match (start_col, start_row) {
1620 (Some(col), Some(row)) => format!(
1621 "{}{}",
1622 Self::format_col(*col, *start_col_abs),
1623 Self::format_row(*row, *start_row_abs)
1624 ),
1625 (Some(col), None) => Self::format_col(*col, *start_col_abs),
1626 (None, Some(row)) => Self::format_row(*row, *start_row_abs),
1627 (None, None) => "".to_string(), };
1629
1630 let end_ref = match (end_col, end_row) {
1632 (Some(col), Some(row)) => format!(
1633 "{}{}",
1634 Self::format_col(*col, *end_col_abs),
1635 Self::format_row(*row, *end_row_abs)
1636 ),
1637 (Some(col), None) => Self::format_col(*col, *end_col_abs),
1638 (None, Some(row)) => Self::format_row(*row, *end_row_abs),
1639 (None, None) => "".to_string(), };
1641
1642 let range_part = format!("{start_ref}:{end_ref}");
1643
1644 if let Some(s) = sheet {
1645 if sheet_name_needs_quoting(s) {
1646 let escaped_name = s.replace('\'', "''");
1647 format!("'{escaped_name}'!{range_part}")
1648 } else {
1649 format!("{s}!{range_part}")
1650 }
1651 } else {
1652 range_part
1653 }
1654 }
1655 ReferenceType::Cell3D { .. } | ReferenceType::Range3D { .. } => format!("{self}"),
1656 ReferenceType::External(ext) => ext.raw.clone(),
1657 ReferenceType::Table(table_ref) => {
1658 if let Some(specifier) = &table_ref.specifier {
1659 format!("{}[{}]", table_ref.name, specifier)
1660 } else {
1661 table_ref.name.clone()
1662 }
1663 }
1664 ReferenceType::NamedRange(name) => name.clone(),
1665 }
1666 }
1667}
1668
1669#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1671#[derive(Debug, Clone, PartialEq, Hash)]
1672pub enum ASTNodeType {
1673 Literal(LiteralValue),
1674 Omitted,
1679 Reference {
1680 original: String, reference: ReferenceType, },
1683 UnaryOp {
1684 op: String,
1685 expr: Box<ASTNode>,
1686 },
1687 BinaryOp {
1688 op: String,
1689 left: Box<ASTNode>,
1690 right: Box<ASTNode>,
1691 },
1692 Function {
1693 name: String,
1694 args: Vec<ASTNode>, },
1696 Call {
1699 callee: Box<ASTNode>,
1700 args: Vec<ASTNode>,
1701 },
1702 Array(Vec<Vec<ASTNode>>), }
1704
1705impl Display for ASTNodeType {
1706 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1707 match self {
1708 ASTNodeType::Literal(value) => write!(f, "Literal({value})"),
1709 ASTNodeType::Omitted => write!(f, "Omitted"),
1710 ASTNodeType::Reference { reference, .. } => write!(f, "Reference({reference:?})"),
1711 ASTNodeType::UnaryOp { op, expr } => write!(f, "UnaryOp({op}, {expr})"),
1712 ASTNodeType::BinaryOp { op, left, right } => {
1713 write!(f, "BinaryOp({op}, {left}, {right})")
1714 }
1715 ASTNodeType::Function { name, args } => write!(f, "Function({name}, {args:?})"),
1716 ASTNodeType::Call { callee, args } => write!(f, "Call({callee}, {args:?})"),
1717 ASTNodeType::Array(rows) => write!(f, "Array({rows:?})"),
1718 }
1719 }
1720}
1721
1722#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1724#[derive(Debug, Clone, PartialEq)]
1725pub struct ASTNode {
1726 pub node_type: ASTNodeType,
1727 pub source_token: Option<Token>,
1728 pub contains_volatile: bool,
1733}
1734
1735impl ASTNode {
1736 pub fn new(node_type: ASTNodeType, source_token: Option<Token>) -> Self {
1737 ASTNode {
1738 node_type,
1739 source_token,
1740 contains_volatile: false,
1741 }
1742 }
1743
1744 pub fn new_with_volatile(
1746 node_type: ASTNodeType,
1747 source_token: Option<Token>,
1748 contains_volatile: bool,
1749 ) -> Self {
1750 ASTNode {
1751 node_type,
1752 source_token,
1753 contains_volatile,
1754 }
1755 }
1756
1757 pub fn contains_volatile(&self) -> bool {
1759 self.contains_volatile
1760 }
1761
1762 pub fn fingerprint(&self) -> u64 {
1763 self.calculate_hash()
1764 }
1765
1766 pub fn calculate_hash(&self) -> u64 {
1768 let mut hasher = FormulaHasher::new();
1769 self.hash_node(&mut hasher);
1770 hasher.finish()
1771 }
1772
1773 fn hash_node(&self, hasher: &mut FormulaHasher) {
1774 match &self.node_type {
1775 ASTNodeType::Literal(value) => {
1776 hasher.write(&[1]); value.hash(hasher);
1778 }
1779 ASTNodeType::Omitted => hasher.write(&[8]),
1780 ASTNodeType::Reference { reference, .. } => {
1781 hasher.write(&[2]); reference.hash(hasher);
1783 }
1784 ASTNodeType::UnaryOp { op, expr } => {
1785 hasher.write(&[3]); hasher.write(op.as_bytes());
1787 expr.hash_node(hasher);
1788 }
1789 ASTNodeType::BinaryOp { op, left, right } => {
1790 hasher.write(&[4]); hasher.write(op.as_bytes());
1792 left.hash_node(hasher);
1793 right.hash_node(hasher);
1794 }
1795 ASTNodeType::Function { name, args } => {
1796 hasher.write(&[5]); let name_lower = name.to_lowercase();
1799 hasher.write(name_lower.as_bytes());
1800 hasher.write_usize(args.len());
1801 for arg in args {
1802 arg.hash_node(hasher);
1803 }
1804 }
1805 ASTNodeType::Call { callee, args } => {
1806 hasher.write(&[7]); callee.hash_node(hasher);
1808 hasher.write_usize(args.len());
1809 for arg in args {
1810 arg.hash_node(hasher);
1811 }
1812 }
1813 ASTNodeType::Array(rows) => {
1814 hasher.write(&[6]); hasher.write_usize(rows.len());
1816 for row in rows {
1817 hasher.write_usize(row.len());
1818 for item in row {
1819 item.hash_node(hasher);
1820 }
1821 }
1822 }
1823 }
1824 }
1825
1826 pub fn get_dependencies(&self) -> Vec<&ReferenceType> {
1827 let mut dependencies = Vec::new();
1828 self.collect_dependencies(&mut dependencies);
1829 dependencies
1830 }
1831
1832 pub fn get_dependency_strings(&self) -> Vec<String> {
1833 self.get_dependencies()
1834 .into_iter()
1835 .map(|dep| format!("{dep}"))
1836 .collect()
1837 }
1838
1839 fn collect_dependencies<'a>(&'a self, dependencies: &mut Vec<&'a ReferenceType>) {
1840 match &self.node_type {
1841 ASTNodeType::Reference { reference, .. } => {
1842 dependencies.push(reference);
1843 }
1844 ASTNodeType::UnaryOp { expr, .. } => {
1845 expr.collect_dependencies(dependencies);
1846 }
1847 ASTNodeType::BinaryOp { left, right, .. } => {
1848 left.collect_dependencies(dependencies);
1849 right.collect_dependencies(dependencies);
1850 }
1851 ASTNodeType::Function { args, .. } => {
1852 for arg in args {
1853 arg.collect_dependencies(dependencies);
1854 }
1855 }
1856 ASTNodeType::Call { callee, args } => {
1857 callee.collect_dependencies(dependencies);
1858 for arg in args {
1859 arg.collect_dependencies(dependencies);
1860 }
1861 }
1862 ASTNodeType::Array(rows) => {
1863 for row in rows {
1864 for item in row {
1865 item.collect_dependencies(dependencies);
1866 }
1867 }
1868 }
1869 _ => {}
1870 }
1871 }
1872
1873 pub fn refs(&self) -> RefIter<'_> {
1876 RefIter {
1877 stack: smallvec::smallvec![self],
1878 }
1879 }
1880
1881 pub fn visit_refs<V: FnMut(RefView<'_>)>(&self, mut visitor: V) {
1883 let mut stack: Vec<&ASTNode> = Vec::with_capacity(8);
1884 stack.push(self);
1885 while let Some(node) = stack.pop() {
1886 match &node.node_type {
1887 ASTNodeType::Reference { reference, .. } => visitor(RefView::from(reference)),
1888 ASTNodeType::UnaryOp { expr, .. } => stack.push(expr),
1889 ASTNodeType::BinaryOp { left, right, .. } => {
1890 stack.push(right);
1892 stack.push(left);
1893 }
1894 ASTNodeType::Function { args, .. } => {
1895 for a in args.iter().rev() {
1896 stack.push(a);
1897 }
1898 }
1899 ASTNodeType::Call { callee, args } => {
1900 for a in args.iter().rev() {
1901 stack.push(a);
1902 }
1903 stack.push(callee);
1904 }
1905 ASTNodeType::Array(rows) => {
1906 for r in rows.iter().rev() {
1907 for item in r.iter().rev() {
1908 stack.push(item);
1909 }
1910 }
1911 }
1912 ASTNodeType::Literal(_) | ASTNodeType::Omitted => {}
1913 }
1914 }
1915 }
1916
1917 pub fn collect_references(&self, policy: &CollectPolicy) -> SmallVec<[ReferenceType; 4]> {
1919 let mut out: SmallVec<[ReferenceType; 4]> = SmallVec::new();
1920 self.visit_refs(|rv| match rv {
1921 RefView::Cell {
1922 sheet,
1923 row,
1924 col,
1925 row_abs,
1926 col_abs,
1927 } => out.push(ReferenceType::Cell {
1928 sheet: sheet.map(|s| s.to_string()),
1929 row,
1930 col,
1931 row_abs,
1932 col_abs,
1933 }),
1934 RefView::Range {
1935 sheet,
1936 start_row,
1937 start_col,
1938 end_row,
1939 end_col,
1940 start_row_abs,
1941 start_col_abs,
1942 end_row_abs,
1943 end_col_abs,
1944 } => {
1945 if policy.expand_small_ranges {
1947 if let (Some(sr), Some(sc), Some(er), Some(ec)) =
1948 (start_row, start_col, end_row, end_col)
1949 {
1950 let rows = er.saturating_sub(sr) + 1;
1951 let cols = ec.saturating_sub(sc) + 1;
1952 let area = rows.saturating_mul(cols);
1953 if area as usize <= policy.range_expansion_limit {
1954 let row_abs = start_row_abs && end_row_abs;
1955 let col_abs = start_col_abs && end_col_abs;
1956 for r in sr..=er {
1957 for c in sc..=ec {
1958 out.push(ReferenceType::Cell {
1959 sheet: sheet.map(|s| s.to_string()),
1960 row: r,
1961 col: c,
1962 row_abs,
1963 col_abs,
1964 });
1965 }
1966 }
1967 return; }
1969 }
1970 }
1971 out.push(ReferenceType::Range {
1972 sheet: sheet.map(|s| s.to_string()),
1973 start_row,
1974 start_col,
1975 end_row,
1976 end_col,
1977 start_row_abs,
1978 start_col_abs,
1979 end_row_abs,
1980 end_col_abs,
1981 });
1982 }
1983 RefView::Cell3D {
1984 sheet_first,
1985 sheet_last,
1986 row,
1987 col,
1988 row_abs,
1989 col_abs,
1990 } => out.push(ReferenceType::Cell3D {
1991 sheet_first: sheet_first.to_string(),
1992 sheet_last: sheet_last.to_string(),
1993 row,
1994 col,
1995 row_abs,
1996 col_abs,
1997 }),
1998 RefView::Range3D {
1999 sheet_first,
2000 sheet_last,
2001 start_row,
2002 start_col,
2003 end_row,
2004 end_col,
2005 start_row_abs,
2006 start_col_abs,
2007 end_row_abs,
2008 end_col_abs,
2009 } => out.push(ReferenceType::Range3D {
2010 sheet_first: sheet_first.to_string(),
2011 sheet_last: sheet_last.to_string(),
2012 start_row,
2013 start_col,
2014 end_row,
2015 end_col,
2016 start_row_abs,
2017 start_col_abs,
2018 end_row_abs,
2019 end_col_abs,
2020 }),
2021 RefView::External {
2022 raw,
2023 book,
2024 sheet,
2025 kind,
2026 } => out.push(ReferenceType::External(ExternalReference {
2027 raw: raw.to_string(),
2028 book: ExternalBookRef::Token(book.to_string()),
2029 sheet: sheet.to_string(),
2030 kind,
2031 })),
2032 RefView::Table { name, specifier } => out.push(ReferenceType::Table(TableReference {
2033 name: name.to_string(),
2034 specifier: specifier.cloned(),
2035 })),
2036 RefView::NamedRange { name } => {
2037 if policy.include_names {
2038 out.push(ReferenceType::NamedRange(name.to_string()));
2039 }
2040 }
2041 });
2042 out
2043 }
2044 pub fn update_sheet_references(&mut self, target_name: Option<&str>, new_name: &str) {
2050 match &mut self.node_type {
2051 ASTNodeType::Reference {
2052 reference: ReferenceType::Cell { sheet, .. } | ReferenceType::Range { sheet, .. },
2053 ..
2054 } => {
2055 if let Some(current_sheet) = sheet
2056 && (target_name.is_none() || target_name == Some(current_sheet.as_str()))
2057 {
2058 *sheet = Some(new_name.to_string());
2059 }
2060 }
2061 ASTNodeType::Reference {
2062 reference:
2063 ReferenceType::Cell3D {
2064 sheet_first,
2065 sheet_last,
2066 ..
2067 }
2068 | ReferenceType::Range3D {
2069 sheet_first,
2070 sheet_last,
2071 ..
2072 },
2073 ..
2074 } => {
2075 if target_name.is_none() || target_name == Some(sheet_first.as_str()) {
2076 *sheet_first = new_name.to_string();
2077 }
2078 if target_name.is_none() || target_name == Some(sheet_last.as_str()) {
2079 *sheet_last = new_name.to_string();
2080 }
2081 }
2082 ASTNodeType::UnaryOp { expr, .. } => {
2083 expr.update_sheet_references(target_name, new_name);
2084 }
2085 ASTNodeType::BinaryOp { left, right, .. } => {
2086 left.update_sheet_references(target_name, new_name);
2087 right.update_sheet_references(target_name, new_name);
2088 }
2089 ASTNodeType::Function { args, .. } => {
2090 for arg in args {
2091 arg.update_sheet_references(target_name, new_name);
2092 }
2093 }
2094 ASTNodeType::Call { callee, args } => {
2095 callee.update_sheet_references(target_name, new_name);
2096 for arg in args {
2097 arg.update_sheet_references(target_name, new_name);
2098 }
2099 }
2100 ASTNodeType::Array(rows) => {
2101 for row in rows {
2102 for cell in row {
2103 cell.update_sheet_references(target_name, new_name);
2104 }
2105 }
2106 }
2107 _ => {}
2108 }
2109 }
2110}
2111
2112#[derive(Clone, Copy, Debug)]
2114pub enum RefView<'a> {
2115 Cell {
2116 sheet: Option<&'a str>,
2117 row: u32,
2118 col: u32,
2119 row_abs: bool,
2120 col_abs: bool,
2121 },
2122 Range {
2123 sheet: Option<&'a str>,
2124 start_row: Option<u32>,
2125 start_col: Option<u32>,
2126 end_row: Option<u32>,
2127 end_col: Option<u32>,
2128 start_row_abs: bool,
2129 start_col_abs: bool,
2130 end_row_abs: bool,
2131 end_col_abs: bool,
2132 },
2133 Cell3D {
2135 sheet_first: &'a str,
2136 sheet_last: &'a str,
2137 row: u32,
2138 col: u32,
2139 row_abs: bool,
2140 col_abs: bool,
2141 },
2142 Range3D {
2144 sheet_first: &'a str,
2145 sheet_last: &'a str,
2146 start_row: Option<u32>,
2147 start_col: Option<u32>,
2148 end_row: Option<u32>,
2149 end_col: Option<u32>,
2150 start_row_abs: bool,
2151 start_col_abs: bool,
2152 end_row_abs: bool,
2153 end_col_abs: bool,
2154 },
2155 External {
2156 raw: &'a str,
2157 book: &'a str,
2158 sheet: &'a str,
2159 kind: ExternalRefKind,
2160 },
2161 Table {
2162 name: &'a str,
2163 specifier: Option<&'a TableSpecifier>,
2164 },
2165 NamedRange {
2166 name: &'a str,
2167 },
2168}
2169
2170impl<'a> From<&'a ReferenceType> for RefView<'a> {
2171 fn from(r: &'a ReferenceType) -> Self {
2172 match r {
2173 ReferenceType::Cell {
2174 sheet,
2175 row,
2176 col,
2177 row_abs,
2178 col_abs,
2179 } => RefView::Cell {
2180 sheet: sheet.as_deref(),
2181 row: *row,
2182 col: *col,
2183 row_abs: *row_abs,
2184 col_abs: *col_abs,
2185 },
2186 ReferenceType::Range {
2187 sheet,
2188 start_row,
2189 start_col,
2190 end_row,
2191 end_col,
2192 start_row_abs,
2193 start_col_abs,
2194 end_row_abs,
2195 end_col_abs,
2196 } => RefView::Range {
2197 sheet: sheet.as_deref(),
2198 start_row: *start_row,
2199 start_col: *start_col,
2200 end_row: *end_row,
2201 end_col: *end_col,
2202 start_row_abs: *start_row_abs,
2203 start_col_abs: *start_col_abs,
2204 end_row_abs: *end_row_abs,
2205 end_col_abs: *end_col_abs,
2206 },
2207 ReferenceType::Cell3D {
2208 sheet_first,
2209 sheet_last,
2210 row,
2211 col,
2212 row_abs,
2213 col_abs,
2214 } => RefView::Cell3D {
2215 sheet_first: sheet_first.as_str(),
2216 sheet_last: sheet_last.as_str(),
2217 row: *row,
2218 col: *col,
2219 row_abs: *row_abs,
2220 col_abs: *col_abs,
2221 },
2222 ReferenceType::Range3D {
2223 sheet_first,
2224 sheet_last,
2225 start_row,
2226 start_col,
2227 end_row,
2228 end_col,
2229 start_row_abs,
2230 start_col_abs,
2231 end_row_abs,
2232 end_col_abs,
2233 } => RefView::Range3D {
2234 sheet_first: sheet_first.as_str(),
2235 sheet_last: sheet_last.as_str(),
2236 start_row: *start_row,
2237 start_col: *start_col,
2238 end_row: *end_row,
2239 end_col: *end_col,
2240 start_row_abs: *start_row_abs,
2241 start_col_abs: *start_col_abs,
2242 end_row_abs: *end_row_abs,
2243 end_col_abs: *end_col_abs,
2244 },
2245 ReferenceType::External(ext) => RefView::External {
2246 raw: ext.raw.as_str(),
2247 book: ext.book.token(),
2248 sheet: ext.sheet.as_str(),
2249 kind: ext.kind,
2250 },
2251 ReferenceType::Table(tr) => RefView::Table {
2252 name: tr.name.as_str(),
2253 specifier: tr.specifier.as_ref(),
2254 },
2255 ReferenceType::NamedRange(name) => RefView::NamedRange { name },
2256 }
2257 }
2258}
2259
2260pub struct RefIter<'a> {
2262 stack: smallvec::SmallVec<[&'a ASTNode; 8]>,
2263}
2264
2265impl<'a> Iterator for RefIter<'a> {
2266 type Item = RefView<'a>;
2267 fn next(&mut self) -> Option<Self::Item> {
2268 while let Some(node) = self.stack.pop() {
2269 match &node.node_type {
2270 ASTNodeType::Reference { reference, .. } => return Some(RefView::from(reference)),
2271 ASTNodeType::UnaryOp { expr, .. } => self.stack.push(expr),
2272 ASTNodeType::BinaryOp { left, right, .. } => {
2273 self.stack.push(right);
2274 self.stack.push(left);
2275 }
2276 ASTNodeType::Function { args, .. } => {
2277 for a in args.iter().rev() {
2278 self.stack.push(a);
2279 }
2280 }
2281 ASTNodeType::Call { callee, args } => {
2282 for a in args.iter().rev() {
2283 self.stack.push(a);
2284 }
2285 self.stack.push(callee);
2286 }
2287 ASTNodeType::Array(rows) => {
2288 for r in rows.iter().rev() {
2289 for item in r.iter().rev() {
2290 self.stack.push(item);
2291 }
2292 }
2293 }
2294 ASTNodeType::Literal(_) | ASTNodeType::Omitted => {}
2295 }
2296 }
2297 None
2298 }
2299}
2300
2301#[derive(Debug, Clone)]
2303pub struct CollectPolicy {
2304 pub expand_small_ranges: bool,
2305 pub range_expansion_limit: usize,
2306 pub include_names: bool,
2307}
2308
2309impl Default for CollectPolicy {
2310 fn default() -> Self {
2311 Self {
2312 expand_small_ranges: false,
2313 range_expansion_limit: 0,
2314 include_names: true,
2315 }
2316 }
2317}
2318
2319impl Display for ASTNode {
2320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2321 write!(f, "{}", self.node_type)
2322 }
2323}
2324
2325impl std::hash::Hash for ASTNode {
2326 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2327 let hash = self.calculate_hash();
2328 state.write_u64(hash);
2329 }
2330}
2331
2332impl From<TokenizerError> for ParserError {
2333 fn from(err: TokenizerError) -> Self {
2334 ParserError {
2335 message: err.message,
2336 position: Some(err.pos),
2337 }
2338 }
2339}
2340
2341pub struct Parser {
2347 source: Arc<str>,
2348 tokens: Arc<[TokenSpan]>,
2349 position: usize,
2350 volatility_classifier: Option<VolatilityClassifierBox>,
2351 dialect: FormulaDialect,
2352 in_call_args_depth: usize,
2355 depth: usize,
2359 limits: ParserLimits,
2360 nodes: usize,
2361 pending_error: Option<ParserError>,
2362}
2363
2364struct BuiltNode {
2367 node: ASTNode,
2368 height: usize,
2369}
2370#[derive(Default)]
2371struct BuiltArgs {
2372 nodes: Vec<ASTNode>,
2373 height: usize,
2374 volatile: bool,
2375}
2376impl BuiltArgs {
2377 fn push(&mut self, child: BuiltNode) {
2378 self.height = self.height.max(child.height);
2379 self.volatile |= child.node.contains_volatile;
2380 self.nodes.push(child.node);
2381 }
2382}
2383impl std::ops::Deref for BuiltNode {
2384 type Target = ASTNode;
2385 fn deref(&self) -> &ASTNode {
2386 &self.node
2387 }
2388}
2389
2390impl Parser {
2391 pub fn new<T: AsRef<str>>(formula: T) -> Result<Self, TokenizerError> {
2393 Self::new_with_dialect(formula, FormulaDialect::Excel)
2394 }
2395
2396 pub fn try_from_formula(formula: &str) -> Result<Self, TokenizerError> {
2398 Self::new(formula)
2399 }
2400
2401 pub fn new_with_dialect<T: AsRef<str>>(
2403 formula: T,
2404 dialect: FormulaDialect,
2405 ) -> Result<Self, TokenizerError> {
2406 Self::new_with_limits(formula.as_ref(), dialect, ParserLimits::default())
2407 }
2408
2409 fn new_with_limits(
2410 formula: &str,
2411 dialect: FormulaDialect,
2412 limits: ParserLimits,
2413 ) -> Result<Self, TokenizerError> {
2414 limits.check_source(formula)?;
2415 let spans = crate::tokenizer::tokenize_spans_with_limits(formula, dialect, limits)?;
2416 let mut parser = Self::from_source_and_tokens(
2417 Arc::from(formula),
2418 Arc::from(spans.into_boxed_slice()),
2419 dialect,
2420 );
2421 parser.limits = limits;
2422 Ok(parser)
2423 }
2424
2425 pub fn from_token_stream(stream: &TokenStream) -> Self {
2427 let limits = ParserLimits::default();
2428 let error = stream.admission_error(limits).map(ParserError::from);
2429 if let Some(error) = error {
2430 let mut parser =
2431 Self::from_source_and_tokens(Arc::from(""), Arc::from([]), stream.dialect());
2432 parser.pending_error = Some(error);
2433 return parser;
2434 }
2435 Self::from_source_and_tokens(
2436 Arc::from(stream.source()),
2437 Arc::from(stream.spans.clone().into_boxed_slice()),
2438 stream.dialect(),
2439 )
2440 }
2441
2442 fn from_source_and_tokens(
2443 source: Arc<str>,
2444 tokens: Arc<[TokenSpan]>,
2445 dialect: FormulaDialect,
2446 ) -> Self {
2447 Parser {
2448 source,
2449 tokens,
2450 position: 0,
2451 volatility_classifier: None,
2452 dialect,
2453 in_call_args_depth: 0,
2454 depth: 0,
2455 limits: ParserLimits::default(),
2456 nodes: 0,
2457 pending_error: None,
2458 }
2459 }
2460
2461 pub fn with_volatility_classifier<F>(mut self, f: F) -> Self
2464 where
2465 F: Fn(&str) -> bool + Send + Sync + 'static,
2466 {
2467 self.volatility_classifier = Some(Box::new(f));
2468 self
2469 }
2470
2471 fn unary(&mut self, expr: BuiltNode, span: TokenSpan) -> Result<BuiltNode, ParserError> {
2473 let height = 1 + expr.height;
2474 self.admit(height)?;
2475 let token = self.span_to_token(&span);
2476 let volatile = expr.contains_volatile;
2477 Ok(BuiltNode {
2478 height,
2479 node: ASTNode::new_with_volatile(
2480 ASTNodeType::UnaryOp {
2481 op: token.value.clone(),
2482 expr: Box::new(expr.node),
2483 },
2484 Some(token),
2485 volatile,
2486 ),
2487 })
2488 }
2489 fn binary(
2490 &mut self,
2491 left: BuiltNode,
2492 right: BuiltNode,
2493 span: TokenSpan,
2494 ) -> Result<BuiltNode, ParserError> {
2495 let height = 1 + left.height.max(right.height);
2496 self.admit(height)?;
2497 let token = self.span_to_token(&span);
2498 let volatile = left.contains_volatile || right.contains_volatile;
2499 Ok(BuiltNode {
2500 height,
2501 node: ASTNode::new_with_volatile(
2502 ASTNodeType::BinaryOp {
2503 op: token.value.clone(),
2504 left: Box::new(left.node),
2505 right: Box::new(right.node),
2506 },
2507 Some(token),
2508 volatile,
2509 ),
2510 })
2511 }
2512 fn call(&mut self, left: BuiltNode, args: BuiltArgs) -> Result<BuiltNode, ParserError> {
2513 let height = 1 + left.height.max(args.height);
2514 self.admit(height)?;
2515 let volatile = left.contains_volatile || args.volatile;
2516 Ok(BuiltNode {
2517 height,
2518 node: ASTNode::new_with_volatile(
2519 ASTNodeType::Call {
2520 callee: Box::new(left.node),
2521 args: args.nodes,
2522 },
2523 None,
2524 volatile,
2525 ),
2526 })
2527 }
2528 fn skip_whitespace(&mut self) {
2529 while self.position < self.tokens.len()
2530 && self.tokens[self.position].token_type == TokenType::Whitespace
2531 {
2532 self.position += 1;
2533 }
2534 }
2535
2536 fn span_value(&self, span: &TokenSpan) -> &str {
2537 &self.source[span.start..span.end]
2538 }
2539
2540 fn semantic_span_value(&self, span: &TokenSpan) -> &str {
2541 let value = self.span_value(span);
2542 if span.token_type == TokenType::OpInfix
2543 && value.as_bytes().contains(&b' ')
2544 && value
2545 .as_bytes()
2546 .iter()
2547 .all(|byte| matches!(byte, b' ' | b'\t' | b'\r' | b'\n'))
2548 {
2549 " "
2550 } else {
2551 value
2552 }
2553 }
2554
2555 fn span_to_token(&self, span: &TokenSpan) -> Token {
2556 Token::new_with_span(
2557 self.semantic_span_value(span).to_string(),
2558 span.token_type,
2559 span.subtype,
2560 span.start,
2561 span.end,
2562 )
2563 }
2564
2565 fn span_precedence(&self, span: &TokenSpan) -> Option<(u8, Associativity)> {
2566 if !matches!(
2567 span.token_type,
2568 TokenType::OpPrefix | TokenType::OpInfix | TokenType::OpPostfix
2569 ) {
2570 return None;
2571 }
2572
2573 let op = if span.token_type == TokenType::OpPrefix {
2574 "u"
2575 } else {
2576 self.semantic_span_value(span)
2577 };
2578
2579 match op {
2580 "#" => Some((11, Associativity::Left)),
2581 ":" => Some((10, Associativity::Left)),
2582 " " => Some((9, Associativity::Left)),
2583 "," => Some((8, Associativity::Left)),
2584 "%" => Some((7, Associativity::Left)),
2585 "u" => Some((6, Associativity::Right)),
2586 "^" => Some((5, Associativity::Left)),
2587 "*" | "/" => Some((4, Associativity::Left)),
2588 "+" | "-" => Some((3, Associativity::Left)),
2589 "&" => Some((2, Associativity::Left)),
2590 "=" | "<" | ">" | "<=" | ">=" | "<>" => Some((1, Associativity::Left)),
2591 _ => None,
2592 }
2593 }
2594
2595 #[cold]
2596 fn limit_error(&self, budget: &str, limit: usize) -> ParserError {
2597 ParserError {
2598 message: format!("Formula {budget} (max {limit})"),
2599 position: Some(self.position),
2600 }
2601 }
2602 fn admit(&mut self, height: usize) -> Result<(), ParserError> {
2603 if self.nodes >= self.limits.ast_nodes {
2604 return Err(self.limit_error("AST node limit exceeded", self.limits.ast_nodes));
2605 }
2606 if height > self.limits.ast_height {
2607 return Err(self.limit_error("AST height limit exceeded", self.limits.ast_height));
2608 }
2609 self.nodes += 1;
2610 Ok(())
2611 }
2612 fn omitted(&mut self) -> Result<BuiltNode, ParserError> {
2613 self.admit(1)?;
2614 Ok(BuiltNode {
2615 node: ASTNode::new(ASTNodeType::Omitted, None),
2616 height: 1,
2617 })
2618 }
2619 pub fn parse(&mut self) -> Result<ASTNode, ParserError> {
2620 if let Some(error) = &self.pending_error {
2621 return Err(ParserError {
2622 message: error.message.clone(),
2623 position: error.position,
2624 });
2625 }
2626 self.nodes = 0;
2627 if self.tokens.is_empty() {
2628 return Err(ParserError {
2629 message: "No tokens to parse".to_string(),
2630 position: None,
2631 });
2632 }
2633
2634 self.skip_whitespace();
2635 if self.position >= self.tokens.len() {
2636 return Err(ParserError {
2637 message: "No tokens to parse".to_string(),
2638 position: None,
2639 });
2640 }
2641
2642 if self.tokens[self.position].token_type == TokenType::Literal {
2643 let span = self.tokens[self.position];
2644 self.position += 1;
2645 self.skip_whitespace();
2646 if self.position < self.tokens.len() {
2647 return Err(ParserError {
2648 message: format!(
2649 "Unexpected token at position {}: {:?}",
2650 self.position, self.tokens[self.position]
2651 ),
2652 position: Some(self.position),
2653 });
2654 }
2655
2656 let token = self.span_to_token(&span);
2657 self.admit(1)?;
2658 return Ok(ASTNode::new(
2659 ASTNodeType::Literal(LiteralValue::Text(token.value.clone())),
2660 Some(token),
2661 ));
2662 }
2663
2664 let ast = self.parse_expression()?;
2665 self.skip_whitespace();
2666 if self.position < self.tokens.len() {
2667 return Err(ParserError {
2668 message: format!(
2669 "Unexpected token at position {}: {:?}",
2670 self.position, self.tokens[self.position]
2671 ),
2672 position: Some(self.position),
2673 });
2674 }
2675 Ok(ast.node)
2676 }
2677
2678 fn parse_expression(&mut self) -> Result<BuiltNode, ParserError> {
2679 self.parse_bp(0)
2680 }
2681
2682 fn parse_bp(&mut self, min_precedence: u8) -> Result<BuiltNode, ParserError> {
2683 self.depth += 1;
2686 if self.depth > self.limits.pratt_frames {
2687 self.depth -= 1;
2688 return Err(self.limit_error("nesting too deep", self.limits.pratt_frames));
2689 }
2690 let result = self.parse_bp_inner(min_precedence);
2691 self.depth -= 1;
2692 result
2693 }
2694
2695 fn parse_bp_inner(&mut self, min_precedence: u8) -> Result<BuiltNode, ParserError> {
2696 let mut left = self.parse_prefix()?;
2697
2698 loop {
2699 self.skip_whitespace();
2700 if self.position >= self.tokens.len() {
2701 break;
2702 }
2703
2704 if self.tokens[self.position].token_type == TokenType::Paren
2707 && self.tokens[self.position].subtype == TokenSubType::Open
2708 {
2709 self.position += 1;
2710 let args = self.parse_call_arguments()?;
2711 left = self.call(left, args)?;
2712 continue;
2713 }
2714
2715 if self.tokens[self.position].token_type == TokenType::OpPostfix {
2716 let (precedence, _) = self
2717 .span_precedence(&self.tokens[self.position])
2718 .unwrap_or((0, Associativity::Left));
2719 if precedence < min_precedence {
2720 break;
2721 }
2722
2723 let op_span = self.tokens[self.position];
2724 self.position += 1;
2725 left = self.unary(left, op_span)?;
2726 continue;
2727 }
2728
2729 let token = &self.tokens[self.position];
2730 if token.token_type != TokenType::OpInfix {
2731 break;
2732 }
2733
2734 if self.in_call_args_depth > 0 && self.span_value(token) == "," {
2737 break;
2738 }
2739
2740 let (precedence, associativity) = self
2741 .span_precedence(token)
2742 .unwrap_or((0, Associativity::Left));
2743 if precedence < min_precedence {
2744 break;
2745 }
2746
2747 let op_span = self.tokens[self.position];
2748 self.position += 1;
2749
2750 let next_min_precedence = if associativity == Associativity::Left {
2751 precedence + 1
2752 } else {
2753 precedence
2754 };
2755
2756 let right = self.parse_bp(next_min_precedence)?;
2757 left = self.binary(left, right, op_span)?;
2758 }
2759
2760 Ok(left)
2761 }
2762
2763 fn parse_prefix(&mut self) -> Result<BuiltNode, ParserError> {
2764 self.skip_whitespace();
2765 if self.position < self.tokens.len()
2766 && self.tokens[self.position].token_type == TokenType::OpPrefix
2767 {
2768 let op_span = self.tokens[self.position];
2769 self.position += 1;
2770
2771 let (precedence, _) = self
2772 .span_precedence(&op_span)
2773 .unwrap_or((0, Associativity::Right));
2774
2775 let expr = self.parse_bp(precedence)?;
2776 return self.unary(expr, op_span);
2777 }
2778
2779 self.parse_primary()
2780 }
2781
2782 fn parse_primary(&mut self) -> Result<BuiltNode, ParserError> {
2783 self.skip_whitespace();
2784 if self.position >= self.tokens.len() {
2785 return Err(ParserError {
2786 message: "Unexpected end of tokens".to_string(),
2787 position: Some(self.position),
2788 });
2789 }
2790
2791 let token = &self.tokens[self.position];
2792 match token.token_type {
2793 TokenType::Operand => {
2794 let span = self.tokens[self.position];
2795 self.position += 1;
2796 self.parse_operand(span)
2797 }
2798 TokenType::Func => {
2799 let span = self.tokens[self.position];
2800 self.position += 1;
2801 self.parse_function(span)
2802 }
2803 TokenType::Paren if token.subtype == TokenSubType::Open => {
2804 self.position += 1;
2805 let expr = self.parse_expression()?;
2806 self.skip_whitespace();
2807 if self.position >= self.tokens.len()
2808 || self.tokens[self.position].token_type != TokenType::Paren
2809 || self.tokens[self.position].subtype != TokenSubType::Close
2810 {
2811 return Err(ParserError {
2812 message: "Expected closing parenthesis".to_string(),
2813 position: Some(self.position),
2814 });
2815 }
2816 self.position += 1;
2817 Ok(expr)
2818 }
2819 TokenType::Array if token.subtype == TokenSubType::Open => {
2820 self.position += 1;
2821 self.parse_array()
2822 }
2823 _ => Err(ParserError {
2824 message: format!("Unexpected token: {token:?}"),
2825 position: Some(self.position),
2826 }),
2827 }
2828 }
2829
2830 fn parse_operand(&mut self, span: TokenSpan) -> Result<BuiltNode, ParserError> {
2831 self.admit(1)?;
2832 let value = self.span_value(&span);
2833 let token = self.span_to_token(&span);
2834
2835 match span.subtype {
2836 TokenSubType::Number => {
2837 let value = value.parse::<f64>().map_err(|_| ParserError {
2838 message: format!("Invalid number: {value}"),
2839 position: Some(self.position),
2840 })?;
2841 Ok(BuiltNode {
2842 height: 1,
2843 node: ASTNode::new(
2844 ASTNodeType::Literal(LiteralValue::Number(value)),
2845 Some(token),
2846 ),
2847 })
2848 }
2849 TokenSubType::Text => {
2850 let mut text = value.to_string();
2851 if text.starts_with('"') && text.ends_with('"') && text.len() >= 2 {
2852 text = text[1..text.len() - 1].to_string();
2853 text = text.replace("\"\"", "\"");
2854 }
2855 Ok(BuiltNode {
2856 height: 1,
2857 node: ASTNode::new(ASTNodeType::Literal(LiteralValue::Text(text)), Some(token)),
2858 })
2859 }
2860 TokenSubType::Logical => {
2861 let v = value.eq_ignore_ascii_case("TRUE");
2862 Ok(BuiltNode {
2863 height: 1,
2864 node: ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(v)), Some(token)),
2865 })
2866 }
2867 TokenSubType::Error => {
2868 let error = ExcelError::from_error_string(value);
2869 Ok(BuiltNode {
2870 height: 1,
2871 node: ASTNode::new(
2872 ASTNodeType::Literal(LiteralValue::Error(error)),
2873 Some(token),
2874 ),
2875 })
2876 }
2877 TokenSubType::Range => {
2878 let reference = ReferenceType::from_string_with_dialect(value, self.dialect)
2879 .map_err(|e| ParserError {
2880 message: format!("Invalid reference '{value}': {e}"),
2881 position: Some(self.position),
2882 })?;
2883 Ok(BuiltNode {
2884 height: 1,
2885 node: ASTNode::new(
2886 ASTNodeType::Reference {
2887 original: value.to_string(),
2888 reference,
2889 },
2890 Some(token),
2891 ),
2892 })
2893 }
2894 _ => Err(ParserError {
2895 message: format!("Unexpected operand subtype: {:?}", span.subtype),
2896 position: Some(self.position),
2897 }),
2898 }
2899 }
2900
2901 fn parse_function(&mut self, func_span: TokenSpan) -> Result<BuiltNode, ParserError> {
2902 let func_value = self.span_value(&func_span);
2903 if !func_value.ends_with('(') {
2904 return Err(ParserError {
2905 message: "Invalid function token".to_string(),
2906 position: Some(self.position),
2907 });
2908 }
2909 let name = func_value[..func_value.len() - 1].to_string();
2910 let args = self.parse_function_arguments()?;
2911
2912 let this_is_volatile = self
2913 .volatility_classifier
2914 .as_ref()
2915 .map(|f| f(name.as_str()))
2916 .unwrap_or(false);
2917 let args_volatile = args.volatile;
2918
2919 let height = 1 + args.height;
2920 self.admit(height)?;
2921 let func_token = self.span_to_token(&func_span);
2922 Ok(BuiltNode {
2923 height,
2924 node: ASTNode::new_with_volatile(
2925 ASTNodeType::Function {
2926 name,
2927 args: args.nodes,
2928 },
2929 Some(func_token),
2930 this_is_volatile || args_volatile,
2931 ),
2932 })
2933 }
2934
2935 fn parse_call_arguments(&mut self) -> Result<BuiltArgs, ParserError> {
2940 let mut args = BuiltArgs::default();
2941
2942 self.skip_whitespace();
2943 if self.position < self.tokens.len()
2944 && self.tokens[self.position].token_type == TokenType::Paren
2945 && self.tokens[self.position].subtype == TokenSubType::Close
2946 {
2947 self.position += 1;
2948 return Ok(args);
2949 }
2950
2951 self.in_call_args_depth += 1;
2952 let result = (|| -> Result<BuiltArgs, ParserError> {
2953 let mut expecting_argument = true;
2954 let mut saw_argument = false;
2955 loop {
2956 self.skip_whitespace();
2957 if self.position >= self.tokens.len() {
2958 return Err(ParserError {
2959 message: "Unterminated call argument list".to_string(),
2960 position: Some(self.position),
2961 });
2962 }
2963
2964 let token = &self.tokens[self.position];
2965 let is_separator = (token.token_type == TokenType::Sep
2966 && token.subtype == TokenSubType::Arg)
2967 || (token.token_type == TokenType::OpInfix && self.span_value(token) == ",");
2968 let is_close =
2969 token.token_type == TokenType::Paren && token.subtype == TokenSubType::Close;
2970
2971 if expecting_argument {
2972 if is_close {
2973 if saw_argument {
2974 args.push(self.omitted()?);
2975 }
2976 self.position += 1;
2977 return Ok(std::mem::take(&mut args));
2978 }
2979 if is_separator {
2980 args.push(self.omitted()?);
2981 saw_argument = true;
2982 self.position += 1;
2983 } else {
2984 args.push(self.parse_expression()?);
2985 saw_argument = true;
2986 expecting_argument = false;
2987 }
2988 } else if is_separator {
2989 self.position += 1;
2990 expecting_argument = true;
2991 } else if is_close {
2992 self.position += 1;
2993 return Ok(std::mem::take(&mut args));
2994 } else {
2995 return Err(ParserError {
2996 message: format!("Expected ',' or ')' in call arguments, got {token:?}"),
2997 position: Some(self.position),
2998 });
2999 }
3000 }
3001 })();
3002 self.in_call_args_depth -= 1;
3003 result
3004 }
3005
3006 fn parse_function_arguments(&mut self) -> Result<BuiltArgs, ParserError> {
3007 let mut args = BuiltArgs::default();
3008
3009 self.skip_whitespace();
3010 if self.position < self.tokens.len()
3011 && self.tokens[self.position].token_type == TokenType::Func
3012 && self.tokens[self.position].subtype == TokenSubType::Close
3013 {
3014 self.position += 1;
3015 return Ok(args);
3016 }
3017
3018 let mut expecting_argument = true;
3019 let mut saw_argument = false;
3020 loop {
3021 self.skip_whitespace();
3022 if self.position >= self.tokens.len() {
3023 return Err(ParserError {
3024 message: "Unterminated function argument list".to_string(),
3025 position: Some(self.position),
3026 });
3027 }
3028
3029 let token = &self.tokens[self.position];
3030 let is_separator =
3031 token.token_type == TokenType::Sep && token.subtype == TokenSubType::Arg;
3032 let is_close =
3033 token.token_type == TokenType::Func && token.subtype == TokenSubType::Close;
3034
3035 if expecting_argument {
3036 if is_close {
3037 if saw_argument {
3038 args.push(self.omitted()?);
3039 }
3040 self.position += 1;
3041 return Ok(args);
3042 }
3043 if is_separator {
3044 args.push(self.omitted()?);
3045 saw_argument = true;
3046 self.position += 1;
3047 } else {
3048 args.push(self.parse_expression()?);
3049 saw_argument = true;
3050 expecting_argument = false;
3051 }
3052 } else if is_separator {
3053 self.position += 1;
3054 expecting_argument = true;
3055 } else if is_close {
3056 self.position += 1;
3057 return Ok(args);
3058 } else {
3059 return Err(ParserError {
3060 message: format!("Expected ',' or ')' in function arguments, got {token:?}"),
3061 position: Some(self.position),
3062 });
3063 }
3064 }
3065 }
3066
3067 fn append_array_item(
3068 &mut self,
3069 row: &mut Vec<ASTNode>,
3070 height: &mut usize,
3071 volatile: &mut bool,
3072 ) -> Result<(), ParserError> {
3073 let child = self.parse_expression()?;
3074 *height = (*height).max(1 + child.height);
3075 *volatile |= child.contains_volatile;
3076 row.push(child.node);
3077 Ok(())
3078 }
3079 fn finish_array(
3080 &mut self,
3081 rows: Vec<Vec<ASTNode>>,
3082 height: usize,
3083 volatile: bool,
3084 ) -> Result<BuiltNode, ParserError> {
3085 self.admit(height)?;
3086 Ok(BuiltNode {
3087 height,
3088 node: ASTNode::new_with_volatile(ASTNodeType::Array(rows), None, volatile),
3089 })
3090 }
3091 fn parse_array(&mut self) -> Result<BuiltNode, ParserError> {
3092 let mut rows = Vec::new();
3093 let mut current_row = Vec::new();
3094 let mut height = 1;
3095 let mut contains_volatile = false;
3096
3097 self.skip_whitespace();
3098 if self.position < self.tokens.len()
3099 && self.tokens[self.position].token_type == TokenType::Array
3100 && self.tokens[self.position].subtype == TokenSubType::Close
3101 {
3102 self.position += 1;
3103 return self.finish_array(rows, 1, false);
3104 }
3105
3106 self.append_array_item(&mut current_row, &mut height, &mut contains_volatile)?;
3107
3108 while self.position < self.tokens.len() {
3109 self.skip_whitespace();
3110 if self.position >= self.tokens.len() {
3111 break;
3112 }
3113 let token = &self.tokens[self.position];
3114
3115 if token.token_type == TokenType::Sep {
3116 if token.subtype == TokenSubType::Arg {
3117 self.position += 1;
3118 self.append_array_item(&mut current_row, &mut height, &mut contains_volatile)?;
3119 } else if token.subtype == TokenSubType::Row {
3120 self.position += 1;
3121 rows.push(current_row);
3122 current_row = Vec::new();
3123 self.append_array_item(&mut current_row, &mut height, &mut contains_volatile)?;
3124 }
3125 } else if token.token_type == TokenType::Array && token.subtype == TokenSubType::Close {
3126 self.position += 1;
3127 rows.push(current_row);
3128 break;
3129 } else {
3130 return Err(ParserError {
3131 message: format!("Unexpected token in array: {token:?}"),
3132 position: Some(self.position),
3133 });
3134 }
3135 }
3136
3137 if let Some(first) = rows.first() {
3141 if rows.iter().any(|row| row.len() != first.len()) {
3142 return Err(ParserError {
3143 message: "Array rows must have equal length".to_string(),
3144 position: Some(self.position.saturating_sub(1)),
3145 });
3146 }
3147 }
3148
3149 self.finish_array(rows, height, contains_volatile)
3150 }
3151}
3152
3153impl TryFrom<&str> for Parser {
3154 type Error = TokenizerError;
3155
3156 fn try_from(formula: &str) -> Result<Self, Self::Error> {
3157 Self::new(formula)
3158 }
3159}
3160
3161impl TryFrom<String> for Parser {
3162 type Error = TokenizerError;
3163
3164 fn try_from(formula: String) -> Result<Self, Self::Error> {
3165 Self::new(formula)
3166 }
3167}
3168
3169impl From<&TokenStream> for Parser {
3170 fn from(stream: &TokenStream) -> Self {
3171 Self::from_token_stream(stream)
3172 }
3173}
3174
3175impl FromStr for ASTNode {
3176 type Err = ParserError;
3177
3178 fn from_str(formula: &str) -> Result<Self, Self::Err> {
3179 parse(formula)
3180 }
3181}
3182
3183impl TryFrom<&str> for ASTNode {
3184 type Error = ParserError;
3185
3186 fn try_from(formula: &str) -> Result<Self, Self::Error> {
3187 parse(formula)
3188 }
3189}
3190
3191impl TryFrom<String> for ASTNode {
3192 type Error = ParserError;
3193
3194 fn try_from(formula: String) -> Result<Self, Self::Error> {
3195 parse(formula)
3196 }
3197}
3198
3199impl Parser {
3200 pub fn builder() -> ParserBuilder {
3201 ParserBuilder::default()
3202 }
3203}
3204
3205#[derive(Default)]
3206pub struct ParserBuilder {
3207 dialect: FormulaDialect,
3208 volatility_classifier: Option<VolatilityClassifierArc>,
3209 limits: ParserLimits,
3210}
3211
3212impl ParserBuilder {
3213 pub fn limits(mut self, limits: ParserLimits) -> Self {
3214 self.limits = limits;
3215 self
3216 }
3217 pub fn dialect(mut self, dialect: FormulaDialect) -> Self {
3218 self.dialect = dialect;
3219 self
3220 }
3221
3222 pub fn with_volatility_classifier<F>(mut self, f: F) -> Self
3223 where
3224 F: Fn(&str) -> bool + Send + Sync + 'static,
3225 {
3226 self.volatility_classifier = Some(Arc::new(f));
3227 self
3228 }
3229
3230 pub fn build<T: AsRef<str>>(self, formula: T) -> Result<Parser, TokenizerError> {
3231 let mut parser = Parser::new_with_limits(formula.as_ref(), self.dialect, self.limits)?;
3232 if let Some(classifier) = self.volatility_classifier {
3233 parser = parser.with_volatility_classifier(move |name| classifier(name));
3234 }
3235 Ok(parser)
3236 }
3237
3238 pub fn parse<T: AsRef<str>>(self, formula: T) -> Result<ASTNode, ParserError> {
3239 let mut parser = self.build(formula)?;
3240 parser.parse()
3241 }
3242}
3243
3244pub fn normalise_reference(reference: &str) -> Result<String, ParsingError> {
3246 let ref_type = ReferenceType::from_string(reference)?;
3247 Ok(ref_type.to_string())
3248}
3249
3250pub fn parse<T: AsRef<str>>(formula: T) -> Result<ASTNode, ParserError> {
3251 parse_with_dialect(formula, FormulaDialect::Excel)
3252}
3253
3254pub fn parse_with_dialect<T: AsRef<str>>(
3255 formula: T,
3256 dialect: FormulaDialect,
3257) -> Result<ASTNode, ParserError> {
3258 let mut parser = Parser::new_with_dialect(formula, dialect)?;
3259 parser.parse()
3260}
3261
3262pub fn parse_with_volatility_classifier<T, F>(
3265 formula: T,
3266 classifier: F,
3267) -> Result<ASTNode, ParserError>
3268where
3269 T: AsRef<str>,
3270 F: Fn(&str) -> bool + Send + Sync + 'static,
3271{
3272 parse_with_dialect_and_volatility_classifier(formula, FormulaDialect::Excel, classifier)
3273}
3274
3275pub fn parse_with_dialect_and_volatility_classifier<T, F>(
3276 formula: T,
3277 dialect: FormulaDialect,
3278 classifier: F,
3279) -> Result<ASTNode, ParserError>
3280where
3281 T: AsRef<str>,
3282 F: Fn(&str) -> bool + Send + Sync + 'static,
3283{
3284 let mut parser =
3285 Parser::new_with_dialect(formula, dialect)?.with_volatility_classifier(classifier);
3286 parser.parse()
3287}
3288
3289pub struct BatchParser {
3294 include_whitespace: bool,
3295 volatility_classifier: Option<VolatilityClassifierArc>,
3296 token_cache: crate::token_cache::TokenCache,
3297 limits: ParserLimits,
3298 dialect: FormulaDialect,
3299}
3300
3301impl BatchParser {
3302 pub fn builder() -> BatchParserBuilder {
3303 BatchParserBuilder::default()
3304 }
3305
3306 pub fn parse(&mut self, formula: &str) -> Result<ASTNode, ParserError> {
3308 self.limits.check_source(formula)?;
3309 let (source, spans) = if let Some(pair) = self.token_cache.get(formula) {
3310 pair
3311 } else {
3312 let mut spans =
3313 crate::tokenizer::tokenize_spans_with_limits(formula, self.dialect, self.limits)?;
3314 let source: Arc<str> = Arc::from(formula);
3315 if !self.include_whitespace {
3316 spans.retain(|t| t.token_type != TokenType::Whitespace);
3317 }
3318
3319 let spans: Arc<[TokenSpan]> = Arc::from(spans.into_boxed_slice());
3320 self.token_cache
3321 .insert(Arc::clone(&source), Arc::clone(&spans));
3322 (source, spans)
3323 };
3324
3325 let mut parser = Parser::from_source_and_tokens(source, spans, self.dialect);
3326 parser.limits = self.limits;
3327 if let Some(classifier) = self.volatility_classifier.clone() {
3328 parser = parser.with_volatility_classifier(move |name| classifier(name));
3329 }
3330 parser.parse()
3331 }
3332}
3333
3334pub struct BatchParserBuilder {
3335 limits: ParserLimits,
3336 cache_entries: usize,
3337 cache_bytes: usize,
3338 include_whitespace: bool,
3339 volatility_classifier: Option<VolatilityClassifierArc>,
3340 dialect: FormulaDialect,
3341}
3342
3343impl Default for BatchParserBuilder {
3344 fn default() -> Self {
3345 Self {
3346 include_whitespace: false,
3347 volatility_classifier: None,
3348 dialect: FormulaDialect::default(),
3349 limits: ParserLimits::default(),
3350 cache_entries: 16_384,
3351 cache_bytes: 8 * 1024 * 1024,
3352 }
3353 }
3354}
3355impl BatchParserBuilder {
3356 pub fn limits(mut self, limits: ParserLimits) -> Self {
3357 self.limits = limits;
3358 self
3359 }
3360 pub fn cache_capacity(mut self, entries: usize, bytes: usize) -> Self {
3362 self.cache_entries = entries;
3363 self.cache_bytes = bytes;
3364 self
3365 }
3366 pub fn include_whitespace(mut self, include: bool) -> Self {
3367 self.include_whitespace = include;
3368 self
3369 }
3370
3371 pub fn with_volatility_classifier<F>(mut self, f: F) -> Self
3372 where
3373 F: Fn(&str) -> bool + Send + Sync + 'static,
3374 {
3375 self.volatility_classifier = Some(Arc::new(f));
3376 self
3377 }
3378
3379 pub fn dialect(mut self, dialect: FormulaDialect) -> Self {
3380 self.dialect = dialect;
3381 self
3382 }
3383
3384 pub fn build(self) -> BatchParser {
3385 BatchParser {
3386 include_whitespace: self.include_whitespace,
3387 volatility_classifier: self.volatility_classifier,
3388 token_cache: crate::token_cache::TokenCache::new(self.cache_entries, self.cache_bytes),
3389 limits: self.limits,
3390 dialect: self.dialect,
3391 }
3392 }
3393}