1use std::convert::TryFrom;
2use std::error::Error;
3use std::fmt::{self, Display};
4use std::sync::Arc;
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate::types::FormulaDialect;
10
11const TOKEN_ENDERS: &str = ",;}) +-*/^&=><%@";
12
13const fn build_token_enders() -> [bool; 256] {
14 let mut tbl = [false; 256];
15 let bytes = TOKEN_ENDERS.as_bytes();
16 let mut i = 0;
17 while i < bytes.len() {
18 tbl[bytes[i] as usize] = true;
19 i += 1;
20 }
21 tbl
22}
23static TOKEN_ENDERS_TABLE: [bool; 256] = build_token_enders();
24
25#[inline(always)]
26fn is_token_ender(c: u8) -> bool {
27 TOKEN_ENDERS_TABLE[c as usize]
28}
29
30static ERROR_CODES: &[&str] = &[
47 "#GETTING_DATA",
48 "#DIV/0!",
49 "#VALUE!",
50 "#SPILL!",
51 "#NAME?",
52 "#NULL!",
53 "#CALC!",
54 "#NUM!",
55 "#REF!",
56 "#N/A",
57];
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum Associativity {
62 Left,
63 Right,
64}
65
66#[derive(Debug)]
68pub struct TokenizerError {
69 pub message: String,
70 pub pos: usize,
71}
72
73#[non_exhaustive]
75#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub enum RecoveryAction {
78 ResourceLimitExceeded,
80 SkippedUnmatchedCloser,
82 UnterminatedString,
84 UnmatchedBracket,
86 InvalidErrorLiteral,
88 UnmatchedOpener,
90}
91
92#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
94#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct TokenDiagnostic {
96 pub span: TokenSpan,
97 pub message: String,
98 pub recovery: RecoveryAction,
99}
100
101impl TokenDiagnostic {
102 fn resource_limit(message: String) -> Self {
103 Self::new(
104 TokenSpan {
105 token_type: TokenType::Literal,
106 subtype: TokenSubType::None,
107 start: 0,
108 end: 0,
109 },
110 message,
111 RecoveryAction::ResourceLimitExceeded,
112 )
113 }
114 fn new(span: TokenSpan, message: String, recovery: RecoveryAction) -> Self {
115 Self {
116 span,
117 message,
118 recovery,
119 }
120 }
121}
122
123#[derive(Debug, Clone)]
124struct SpanTokenizerError {
125 kind: SpanTokenizerErrorKind,
126 pos: usize,
127 message: String,
128 span_start: Option<usize>,
129 span_end: Option<usize>,
130}
131
132#[derive(Debug, Clone, Copy)]
133enum SpanTokenizerErrorKind {
134 NoMatchingOpener,
135 UnmatchedOpening,
136 UnterminatedString,
137 UnmatchedBracket,
138 MismatchedPair,
139 InvalidErrorLiteral,
140}
141
142impl From<SpanTokenizerError> for TokenizerError {
143 fn from(value: SpanTokenizerError) -> Self {
144 TokenizerError {
145 message: value.message,
146 pos: value.pos,
147 }
148 }
149}
150
151impl fmt::Display for TokenizerError {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "TokenizerError: {}", self.message)
154 }
155}
156
157impl Error for TokenizerError {}
158
159#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
162pub enum TokenType {
163 Literal,
164 Operand,
165 Func,
166 Array,
167 Paren,
168 Sep,
169 OpPrefix,
170 OpInfix,
171 OpPostfix,
172 Whitespace,
173}
174impl Display for TokenType {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(f, "{self:?}")
177 }
178}
179
180#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
183pub enum TokenSubType {
184 None,
185 Text,
186 Number,
187 Logical,
188 Error,
189 Range,
190 Open,
191 Close,
192 Arg,
193 Row,
194}
195impl Display for TokenSubType {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(f, "{self:?}")
198 }
199}
200
201#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
203#[derive(Debug, Clone, PartialEq, Hash)]
204pub struct Token {
205 pub value: String, pub token_type: TokenType,
207 pub subtype: TokenSubType,
208 pub start: usize,
209 pub end: usize,
210}
211
212impl Display for Token {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 write!(
215 f,
216 "<{} subtype: {:?} value: {}>",
217 self.token_type, self.subtype, self.value
218 )
219 }
220}
221
222impl Token {
223 pub fn new(value: String, token_type: TokenType, subtype: TokenSubType) -> Self {
224 Token {
225 value,
226 token_type,
227 subtype,
228 start: 0,
229 end: 0,
230 }
231 }
232
233 pub fn new_with_span(
234 value: String,
235 token_type: TokenType,
236 subtype: TokenSubType,
237 start: usize,
238 end: usize,
239 ) -> Self {
240 Token {
241 value,
242 token_type,
243 subtype,
244 start,
245 end,
246 }
247 }
248
249 fn from_slice(
250 source: &str,
251 token_type: TokenType,
252 subtype: TokenSubType,
253 start: usize,
254 end: usize,
255 ) -> Self {
256 Token {
257 value: source[start..end].to_string(),
258 token_type,
259 subtype,
260 start,
261 end,
262 }
263 }
264
265 pub fn is_operator(&self) -> bool {
266 matches!(
267 self.token_type,
268 TokenType::OpPrefix | TokenType::OpInfix | TokenType::OpPostfix
269 )
270 }
271
272 pub fn get_precedence(&self) -> Option<(u8, Associativity)> {
273 let op = if self.token_type == TokenType::OpPrefix {
275 "u"
276 } else {
277 self.value.as_str()
278 };
279
280 match op {
291 "#" => Some((11, Associativity::Left)),
292 ":" => Some((10, Associativity::Left)),
293 " " => Some((9, Associativity::Left)),
294 "," => Some((8, Associativity::Left)),
295 "%" => Some((7, Associativity::Left)),
296 "u" => Some((6, Associativity::Right)),
297 "^" => Some((5, Associativity::Left)),
298 "*" | "/" => Some((4, Associativity::Left)),
299 "+" | "-" => Some((3, Associativity::Left)),
300 "&" => Some((2, Associativity::Left)),
301 "=" | "<" | ">" | "<=" | ">=" | "<>" => Some((1, Associativity::Left)),
302 _ => None,
303 }
304 }
305
306 pub fn make_operand(value: String) -> Self {
308 let subtype = if value.starts_with('"') {
309 TokenSubType::Text
310 } else if value.starts_with('#') {
311 TokenSubType::Error
312 } else if value.eq_ignore_ascii_case("TRUE") || value.eq_ignore_ascii_case("FALSE") {
313 TokenSubType::Logical
314 } else if value.parse::<f64>().is_ok() {
315 TokenSubType::Number
316 } else {
317 TokenSubType::Range
318 };
319 Token::new(value, TokenType::Operand, subtype)
320 }
321
322 pub fn make_operand_with_span(value: String, start: usize, end: usize) -> Self {
324 let subtype = if value.starts_with('"') {
325 TokenSubType::Text
326 } else if value.starts_with('#') {
327 TokenSubType::Error
328 } else if value.eq_ignore_ascii_case("TRUE") || value.eq_ignore_ascii_case("FALSE") {
329 TokenSubType::Logical
330 } else if value.parse::<f64>().is_ok() {
331 TokenSubType::Number
332 } else {
333 TokenSubType::Range
334 };
335 Token::new_with_span(value, TokenType::Operand, subtype, start, end)
336 }
337
338 fn make_operand_from_slice(source: &str, start: usize, end: usize) -> Self {
339 let value_str = &source[start..end];
340 let subtype = if value_str.starts_with('"') {
341 TokenSubType::Text
342 } else if value_str.starts_with('#') {
343 TokenSubType::Error
344 } else if value_str.eq_ignore_ascii_case("TRUE") || value_str.eq_ignore_ascii_case("FALSE")
345 {
346 TokenSubType::Logical
347 } else if value_str.parse::<f64>().is_ok() {
348 TokenSubType::Number
349 } else {
350 TokenSubType::Range
351 };
352 Token::from_slice(source, TokenType::Operand, subtype, start, end)
353 }
354
355 pub fn make_subexp(value: &str, func: bool) -> Self {
360 let last_char = value.chars().last().expect("Empty token value");
361 assert!(matches!(last_char, '{' | '}' | '(' | ')'));
362 let token_type = if func {
363 TokenType::Func
364 } else if "{}".contains(last_char) {
365 TokenType::Array
366 } else if "()".contains(last_char) {
367 TokenType::Paren
368 } else {
369 TokenType::Func
370 };
371 let subtype = if ")}".contains(last_char) {
372 TokenSubType::Close
373 } else {
374 TokenSubType::Open
375 };
376 Token::new(value.to_string(), token_type, subtype)
377 }
378
379 pub fn make_subexp_with_span(value: &str, func: bool, start: usize, end: usize) -> Self {
381 let last_char = value.chars().last().expect("Empty token value");
382 assert!(matches!(last_char, '{' | '}' | '(' | ')'));
383 let token_type = if func {
384 TokenType::Func
385 } else if "{}".contains(last_char) {
386 TokenType::Array
387 } else if "()".contains(last_char) {
388 TokenType::Paren
389 } else {
390 TokenType::Func
391 };
392 let subtype = if ")}".contains(last_char) {
393 TokenSubType::Close
394 } else {
395 TokenSubType::Open
396 };
397 Token::new_with_span(value.to_string(), token_type, subtype, start, end)
398 }
399
400 fn make_subexp_from_slice(source: &str, func: bool, start: usize, end: usize) -> Self {
401 let value_str = &source[start..end];
402 let last_char = value_str.chars().last().expect("Empty token value");
403 let token_type = if func {
404 TokenType::Func
405 } else if "{}".contains(last_char) {
406 TokenType::Array
407 } else if "()".contains(last_char) {
408 TokenType::Paren
409 } else {
410 TokenType::Func
411 };
412 let subtype = if ")}".contains(last_char) {
413 TokenSubType::Close
414 } else {
415 TokenSubType::Open
416 };
417 Token::from_slice(source, token_type, subtype, start, end)
418 }
419
420 pub fn get_closer(&self) -> Result<Token, TokenizerError> {
422 if self.subtype != TokenSubType::Open {
423 return Err(TokenizerError {
424 message: "Token is not an opener".to_string(),
425 pos: 0,
426 });
427 }
428 let closer_value = if self.token_type == TokenType::Array {
429 "}"
430 } else {
431 ")"
432 };
433 Ok(Token::make_subexp(
434 closer_value,
435 self.token_type == TokenType::Func,
436 ))
437 }
438
439 pub fn make_separator(value: &str) -> Self {
441 assert!(value == "," || value == ";");
442 let subtype = if value == "," {
443 TokenSubType::Arg
444 } else {
445 TokenSubType::Row
446 };
447 Token::new(value.to_string(), TokenType::Sep, subtype)
448 }
449
450 pub fn make_separator_with_span(value: &str, start: usize, end: usize) -> Self {
452 assert!(value == "," || value == ";");
453 let subtype = if value == "," {
454 TokenSubType::Arg
455 } else {
456 TokenSubType::Row
457 };
458 Token::new_with_span(value.to_string(), TokenType::Sep, subtype, start, end)
459 }
460}
461
462#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
463#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
464pub struct TokenSpan {
465 pub token_type: TokenType,
466 pub subtype: TokenSubType,
467 pub start: usize,
468 pub end: usize,
469}
470
471#[derive(Debug, Clone, Copy, PartialEq, Eq)]
472pub struct TokenView<'a> {
473 pub span: &'a TokenSpan,
474 pub value: &'a str,
475}
476
477#[derive(Debug, Clone)]
483pub struct TokenStream {
484 pub(crate) pending_error: Option<String>,
485 source: Arc<str>,
486 pub spans: Vec<TokenSpan>,
487 dialect: FormulaDialect,
488 diagnostics: Vec<TokenDiagnostic>,
489}
490
491fn is_whitespace_intersection_span(source: &str, span: &TokenSpan) -> bool {
492 if span.token_type != TokenType::OpInfix {
493 return false;
494 }
495 let Some(value) = source.get(span.start..span.end) else {
496 return false;
497 };
498 value.as_bytes().contains(&b' ')
499 && value
500 .as_bytes()
501 .iter()
502 .all(|byte| matches!(byte, b' ' | b'\t' | b'\r' | b'\n'))
503}
504
505impl TokenStream {
506 pub fn new(formula: &str) -> Result<Self, TokenizerError> {
507 Self::new_with_dialect(formula, FormulaDialect::Excel)
508 }
509
510 pub fn new_with_dialect(
511 formula: &str,
512 dialect: FormulaDialect,
513 ) -> Result<Self, TokenizerError> {
514 let spans = tokenize_spans_with_dialect(formula, dialect)?;
515 let source: Arc<str> = Arc::from(formula);
516 Ok(TokenStream {
517 pending_error: None,
518 source,
519 spans,
520 dialect,
521 diagnostics: Vec::new(),
522 })
523 }
524
525 pub fn new_best_effort(formula: &str) -> Self {
526 Self::new_best_effort_with_dialect(formula, FormulaDialect::Excel)
527 }
528
529 pub fn new_best_effort_with_dialect(formula: &str, dialect: FormulaDialect) -> Self {
530 if let Err(error) = crate::ParserLimits::default().check_source(formula) {
531 return TokenStream {
532 source: Arc::from(""),
533 spans: Vec::new(),
534 dialect,
535 diagnostics: vec![TokenDiagnostic::resource_limit(error.message.clone())],
536 pending_error: Some(error.message),
537 };
538 }
539 let source: Arc<str> = Arc::from(formula);
540 let mut tokenizer = SpanTokenizer::new(source.as_ref(), dialect);
541 let spans = tokenizer.parse_best_effort();
542 let pending_error = tokenizer.exceeded.then(|| {
543 format!(
544 "Formula token limit exceeded (max {})",
545 tokenizer.token_limit
546 )
547 });
548 let mut diagnostics = tokenizer.diagnostics;
549 if let Some(message) = &pending_error {
550 diagnostics.push(TokenDiagnostic::resource_limit(message.clone()));
551 }
552 TokenStream {
553 pending_error,
554 source,
555 spans,
556 dialect,
557 diagnostics,
558 }
559 }
560
561 pub(crate) fn admission_error(&self, limits: crate::ParserLimits) -> Option<TokenizerError> {
563 if let Some(message) = &self.pending_error {
564 return Some(TokenizerError {
565 message: message.clone(),
566 pos: 0,
567 });
568 }
569 if let Err(error) = limits.check_source(&self.source) {
570 return Some(error);
571 }
572 if self.spans.len() > limits.tokens() {
573 return Some(TokenizerError {
574 message: format!("Formula token limit exceeded (max {})", limits.tokens()),
575 pos: 0,
576 });
577 }
578 let mut previous_end = 0;
579 for span in &self.spans {
580 if span.start < previous_end || self.source.get(span.start..span.end).is_none() {
583 return Some(TokenizerError {
584 message: "Invalid external token span sequence".into(),
585 pos: span.start,
586 });
587 }
588 previous_end = span.end;
589 }
590 None
591 }
592
593 pub fn diagnostics(&self) -> Vec<TokenDiagnostic> {
594 self.diagnostics.clone()
595 }
596
597 pub fn diagnostics_ref(&self) -> &[TokenDiagnostic] {
598 &self.diagnostics
599 }
600
601 pub fn has_errors(&self) -> bool {
602 !self.diagnostics.is_empty()
603 }
604
605 pub fn invalid_spans_iter(&self) -> impl Iterator<Item = &TokenSpan> {
606 self.spans.iter().filter(|span| {
607 self.diagnostics.iter().any(|diag| {
608 diag.span.start == span.start
609 && diag.span.end == span.end
610 && diag.span.token_type == span.token_type
611 })
612 })
613 }
614
615 pub fn invalid_spans(&self) -> Vec<&TokenSpan> {
616 self.invalid_spans_iter().collect()
617 }
618
619 pub fn source(&self) -> &str {
620 &self.source
621 }
622
623 pub fn dialect(&self) -> FormulaDialect {
624 self.dialect
625 }
626
627 pub fn len(&self) -> usize {
628 self.spans.len()
629 }
630
631 pub fn is_empty(&self) -> bool {
632 self.spans.is_empty()
633 }
634
635 pub fn get(&self, index: usize) -> Option<TokenView<'_>> {
636 let span = self.spans.get(index)?;
637 let value = self.source.get(span.start..span.end)?;
638 Some(TokenView { span, value })
639 }
640
641 pub fn to_tokens(&self) -> Vec<Token> {
642 self.spans
643 .iter()
644 .map(|s| {
645 let value = if is_whitespace_intersection_span(&self.source, s) {
646 " ".to_string()
647 } else {
648 self.source
649 .get(s.start..s.end)
650 .unwrap_or_default()
651 .to_string()
652 };
653 Token::new_with_span(value, s.token_type, s.subtype, s.start, s.end)
654 })
655 .collect()
656 }
657
658 pub fn render(&self) -> String {
663 let mut out = String::with_capacity(self.source.len());
664 for span in &self.spans {
665 if let Some(s) = self.source.get(span.start..span.end) {
666 out.push_str(s);
667 }
668 }
669 out
670 }
671
672 pub fn render_formula(&self) -> String {
674 if self.source.as_bytes().first() == Some(&b'=') {
675 format!("={}", self.render())
676 } else {
677 self.render()
678 }
679 }
680}
681
682pub(crate) fn tokenize_spans_with_dialect(
683 formula: &str,
684 dialect: FormulaDialect,
685) -> Result<Vec<TokenSpan>, TokenizerError> {
686 tokenize_spans_with_limits(formula, dialect, crate::ParserLimits::default())
687}
688pub(crate) fn tokenize_spans_with_limits(
689 formula: &str,
690 dialect: FormulaDialect,
691 limits: crate::ParserLimits,
692) -> Result<Vec<TokenSpan>, TokenizerError> {
693 limits.check_source(formula)?;
694 let mut tokenizer = SpanTokenizer::new_with_limit(formula, dialect, limits.tokens());
695 tokenizer.parse()?;
696 Ok(tokenizer.spans)
697}
698
699fn operand_subtype(value_str: &str) -> TokenSubType {
700 if value_str.starts_with('"') {
701 TokenSubType::Text
702 } else if value_str.starts_with('#') {
703 TokenSubType::Error
704 } else if value_str.eq_ignore_ascii_case("TRUE") || value_str.eq_ignore_ascii_case("FALSE") {
705 TokenSubType::Logical
706 } else if value_str.parse::<f64>().is_ok() {
707 TokenSubType::Number
708 } else {
709 TokenSubType::Range
710 }
711}
712
713fn is_cell_reference_like(value: &str) -> bool {
714 let bytes = value.as_bytes();
715 let mut i = 0;
716
717 if i < bytes.len() && bytes[i] == b'$' {
718 i += 1;
719 }
720
721 let col_start = i;
722 while i < bytes.len() && bytes[i].is_ascii_alphabetic() {
723 i += 1;
724 }
725 if i == col_start {
726 return false;
727 }
728
729 if i < bytes.len() && bytes[i] == b'$' {
730 i += 1;
731 }
732
733 let row_start = i;
734 while i < bytes.len() && bytes[i].is_ascii_digit() {
735 i += 1;
736 }
737
738 i == bytes.len() && i > row_start
739}
740
741fn reference_value_contains_range_colon(value: &str) -> bool {
742 let value_part = value
743 .rsplit_once('!')
744 .map_or(value, |(_, value_part)| value_part);
745 value_part.contains(':')
746}
747
748fn value_has_structured_reference_bracket(value: &str) -> bool {
753 value
754 .rsplit_once('!')
755 .map_or(value, |(_, value_part)| value_part)
756 .contains('[')
757}
758
759fn range_colon_before_call(value: &str) -> Option<usize> {
764 let name_start = value.rfind('!').map_or(0, |bang| bang + 1);
765 let colon = name_start + value[name_start..].rfind(':')?;
766 (colon > 0 && colon + 1 < value.len()).then_some(colon)
767}
768
769fn is_a1_range_end(piece: &str) -> bool {
772 let bytes = piece.as_bytes();
773 let mut i = 0;
774 if bytes.get(i) == Some(&b'$') {
775 i += 1;
776 }
777 let letters_start = i;
778 let mut col: u32 = 0;
779 while i < bytes.len() && bytes[i].is_ascii_alphabetic() && i - letters_start < 3 {
780 col = col * 26 + u32::from(bytes[i].to_ascii_uppercase() - b'A' + 1);
781 i += 1;
782 }
783 let has_letters = i > letters_start;
784 if has_letters && (col > 16_384 || bytes.get(i).is_some_and(u8::is_ascii_alphabetic)) {
785 return false;
786 }
787 if has_letters && bytes.get(i) == Some(&b'$') {
788 i += 1;
789 }
790 let digits_start = i;
791 while i < bytes.len() && bytes[i].is_ascii_digit() {
792 i += 1;
793 }
794 let digits = &piece[digits_start..i];
795 let row_ok = digits.is_empty()
796 || (digits.len() <= 7
797 && digits
798 .parse::<u32>()
799 .is_ok_and(|r| (1..=1_048_576).contains(&r)));
800 i == bytes.len() && (has_letters || !digits.is_empty()) && row_ok
801}
802
803fn range_operator_colon(value: &str) -> Option<usize> {
808 let start = value.rfind('!').map_or(0, |bang| bang + 1);
809 let part = &value[start..];
810 if part.contains(['[', '\'', '"', '#']) || part.matches(':').count() != 1 {
811 return None;
812 }
813 let (left, right) = part.split_once(':')?;
814 if left.is_empty() || right.is_empty() || (is_a1_range_end(left) && is_a1_range_end(right)) {
815 return None;
816 }
817 Some(start + left.len())
818}
819
820fn ref_error_starts_at(formula: &str, offset: usize) -> bool {
822 formula
823 .as_bytes()
824 .get(offset..offset + 5)
825 .is_some_and(|s| s.eq_ignore_ascii_case(b"#REF!"))
826}
827
828fn is_reference_operand_value(value: &str) -> bool {
829 operand_subtype(value) == TokenSubType::Range
830 && (reference_value_contains_range_colon(value)
831 || value.contains('!')
832 || value.contains('[')
833 || is_cell_reference_like(value))
834}
835
836fn next_starts_reference_expression(formula: &str, mut offset: usize) -> bool {
837 let bytes = formula.as_bytes();
838 while offset < bytes.len() && matches!(bytes[offset], b' ' | b'\t' | b'\r' | b'\n') {
839 offset += 1;
840 }
841 if offset >= bytes.len() {
842 return false;
843 }
844
845 matches!(bytes[offset], b'(' | b'[' | b'\'' | b'$') || bytes[offset].is_ascii_alphabetic()
846}
847
848fn next_reference_has_sheet_qualifier(formula: &str, mut offset: usize) -> bool {
849 let bytes = formula.as_bytes();
850 while offset < bytes.len() && matches!(bytes[offset], b' ' | b'\t' | b'\r' | b'\n') {
851 offset += 1;
852 }
853
854 let mut in_quote = false;
855 while offset < bytes.len() {
856 match bytes[offset] {
857 b'\'' => {
858 if in_quote && offset + 1 < bytes.len() && bytes[offset + 1] == b'\'' {
859 offset += 2;
860 continue;
861 }
862 in_quote = !in_quote;
863 }
864 b'!' => return true,
865 b':' if !in_quote => return false,
866 b',' | b';' | b'}' | b')' | b' ' | b'\t' | b'\r' | b'\n' | b'+' | b'-' | b'*'
867 | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@'
868 if !in_quote =>
869 {
870 return false;
871 }
872 _ => {}
873 }
874 offset += 1;
875 }
876
877 false
878}
879
880struct SpanTokenizer<'a> {
881 formula: &'a str,
882 spans: Vec<TokenSpan>,
883 token_stack: Vec<TokenSpan>,
884 offset: usize,
885 token_start: usize,
886 token_end: usize,
887 dialect: FormulaDialect,
888 diagnostics: Vec<TokenDiagnostic>,
889 token_limit: usize,
890 exceeded: bool,
891}
892
893impl<'a> SpanTokenizer<'a> {
894 fn new(formula: &'a str, dialect: FormulaDialect) -> Self {
895 Self::new_with_limit(formula, dialect, crate::ParserLimits::default().tokens())
896 }
897 fn new_with_limit(formula: &'a str, dialect: FormulaDialect, token_limit: usize) -> Self {
898 SpanTokenizer {
899 formula,
900 spans: Vec::with_capacity((formula.len() / 2).min(token_limit)),
901 token_stack: Vec::with_capacity(16),
902 offset: 0,
903 token_start: 0,
904 token_end: 0,
905 dialect,
906 diagnostics: Vec::new(),
907 token_limit,
908 exceeded: false,
909 }
910 }
911
912 #[inline]
913 fn current_byte(&self) -> Option<u8> {
914 self.formula.as_bytes().get(self.offset).copied()
915 }
916
917 #[inline]
918 fn has_token(&self) -> bool {
919 self.token_end > self.token_start
920 }
921
922 #[inline]
923 fn start_token(&mut self) {
924 self.token_start = self.offset;
925 self.token_end = self.offset;
926 }
927
928 #[inline]
929 fn extend_token(&mut self) {
930 self.token_end = self.offset;
931 }
932
933 fn push_span(
934 &mut self,
935 token_type: TokenType,
936 subtype: TokenSubType,
937 start: usize,
938 end: usize,
939 ) {
940 if self.spans.len() >= self.token_limit {
941 self.exceeded = true;
942 return;
943 }
944 self.spans.push(TokenSpan {
945 token_type,
946 subtype,
947 start,
948 end,
949 });
950 }
951
952 fn save_token(&mut self) {
953 if self.has_token() {
954 if let Some(colon) =
955 range_operator_colon(&self.formula[self.token_start..self.token_end])
956 {
957 let colon = self.token_start + colon;
958 let left = operand_subtype(&self.formula[self.token_start..colon]);
959 self.push_span(TokenType::Operand, left, self.token_start, colon);
960 self.push_span(TokenType::OpInfix, TokenSubType::None, colon, colon + 1);
961 self.token_start = colon + 1;
962 }
963 let value_str = &self.formula[self.token_start..self.token_end];
964 let subtype = operand_subtype(value_str);
965 self.push_span(
966 TokenType::Operand,
967 subtype,
968 self.token_start,
969 self.token_end,
970 );
971 }
972 }
973
974 fn check_scientific_notation(&mut self) -> bool {
975 if let Some(curr_byte) = self.current_byte() {
976 if (curr_byte == b'+' || curr_byte == b'-')
977 && self.has_token()
978 && self.is_scientific_notation_base()
979 && self
980 .formula
981 .as_bytes()
982 .get(self.offset + 1)
983 .is_some_and(|b| b.is_ascii_digit())
984 {
985 self.offset += 1;
986 self.extend_token();
987 return true;
988 }
989 }
990 false
991 }
992
993 fn is_scientific_notation_base(&self) -> bool {
994 if !self.has_token() {
995 return false;
996 }
997
998 let token_slice = &self.formula.as_bytes()[self.token_start..self.token_end];
999 if token_slice.len() < 2 {
1000 return false;
1001 }
1002
1003 let last = token_slice[token_slice.len() - 1];
1004 if !(last == b'E' || last == b'e') {
1005 return false;
1006 }
1007
1008 let first = token_slice[0];
1009 if !first.is_ascii_digit() {
1010 return false;
1011 }
1012
1013 let mut dot_seen = false;
1014 for &ch in &token_slice[1..token_slice.len() - 1] {
1015 match ch {
1016 b'0'..=b'9' => {}
1017 b'.' if !dot_seen => dot_seen = true,
1018 _ => return false,
1019 }
1020 }
1021 true
1022 }
1023
1024 fn parse(&mut self) -> Result<(), TokenizerError> {
1025 let result = self.parse_with_recovery(false).map_err(Into::into);
1026 if self.exceeded {
1027 return Err(TokenizerError {
1028 message: format!("Formula token limit exceeded (max {})", self.token_limit),
1029 pos: self.offset,
1030 });
1031 }
1032 result
1033 }
1034
1035 pub(crate) fn parse_best_effort(&mut self) -> Vec<TokenSpan> {
1036 let _ = self.parse_with_recovery(true);
1037 self.spans.clone()
1038 }
1039
1040 fn parse_with_recovery(&mut self, best_effort: bool) -> Result<(), SpanTokenizerError> {
1041 if self.formula.is_empty() {
1042 return Ok(());
1043 }
1044
1045 if self.formula.as_bytes()[0] != b'=' {
1046 self.push_span(
1047 TokenType::Literal,
1048 TokenSubType::None,
1049 0,
1050 self.formula.len(),
1051 );
1052 return Ok(());
1053 }
1054
1055 self.offset = 1;
1056 self.start_token();
1057
1058 while self.offset < self.formula.len() {
1059 if self.exceeded {
1060 break;
1061 }
1062 if self.check_scientific_notation() {
1063 continue;
1064 }
1065
1066 let curr_byte = self.formula.as_bytes()[self.offset];
1067
1068 if is_token_ender(curr_byte) && self.has_token() {
1069 self.save_token();
1070 self.start_token();
1071 }
1072
1073 let parse_result = match curr_byte {
1074 b'"' | b'\'' => self.parse_string(),
1075 b'[' => self.parse_brackets(),
1076 b'#' => {
1077 if self.should_emit_hash_postfix() {
1078 self.emit_hash_postfix();
1079 Ok(())
1080 } else {
1081 self.parse_error()
1082 }
1083 }
1084 b' ' | b'\t' | b'\r' | b'\n' => self.parse_whitespace(),
1085 b':' => {
1086 if self.should_emit_colon_infix() {
1087 self.emit_infix_operator(self.offset, self.offset + 1);
1088 Ok(())
1089 } else {
1090 if !self.has_token() {
1091 self.start_token();
1092 }
1093 self.offset += 1;
1094 self.extend_token();
1095 Ok(())
1096 }
1097 }
1098 b'+' | b'-' | b'*' | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@' => {
1099 self.parse_operator()
1100 }
1101 b'{' | b'(' => self.parse_opener(),
1102 b')' | b'}' => self.parse_closer(),
1103 b';' | b',' => self.parse_separator(),
1104 _ => {
1105 if !self.has_token() {
1106 self.start_token();
1107 }
1108 self.offset += 1;
1109 self.extend_token();
1110 Ok(())
1111 }
1112 };
1113
1114 if let Err(err) = parse_result {
1115 if best_effort {
1116 self.recover_from_error(err);
1117 } else {
1118 return Err(err);
1119 }
1120 }
1121 }
1122
1123 if self.exceeded {
1126 return Ok(());
1127 }
1128 if self.has_token() {
1129 self.save_token();
1130 }
1131 if self.exceeded {
1132 return Ok(());
1133 }
1134
1135 if !self.token_stack.is_empty() {
1136 if best_effort {
1137 while let Some(open_token) = self.token_stack.pop() {
1138 if let Some(span) = self.spans.iter().find(|span| {
1139 span.start == open_token.start
1140 && span.end == open_token.end
1141 && span.token_type == open_token.token_type
1142 && span.subtype == open_token.subtype
1143 }) {
1144 self.diagnostics.push(TokenDiagnostic::new(
1145 *span,
1146 "Unmatched opening parenthesis or bracket".to_string(),
1147 RecoveryAction::UnmatchedOpener,
1148 ));
1149 }
1150 }
1151 } else {
1152 return Err(SpanTokenizerError {
1153 kind: SpanTokenizerErrorKind::UnmatchedOpening,
1154 pos: self.offset,
1155 message: "Unmatched opening parenthesis or bracket".to_string(),
1156 span_start: None,
1157 span_end: None,
1158 });
1159 }
1160 }
1161
1162 Ok(())
1163 }
1164
1165 fn recover_from_error(&mut self, error: SpanTokenizerError) {
1166 if self.spans.len() >= self.token_limit {
1167 self.exceeded = true;
1168 return;
1169 }
1170 match error.kind {
1171 SpanTokenizerErrorKind::NoMatchingOpener => {
1172 let span = TokenSpan {
1173 token_type: TokenType::Operand,
1174 subtype: TokenSubType::None,
1175 start: error.pos,
1176 end: error.pos + 1,
1177 };
1178 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1179 self.offset = span.end;
1180 self.start_token();
1181 self.diagnostics.push(TokenDiagnostic::new(
1182 span,
1183 format!("No matching opener for closer at position {}", error.pos),
1184 RecoveryAction::SkippedUnmatchedCloser,
1185 ));
1186 }
1187 SpanTokenizerErrorKind::UnmatchedOpening => {
1188 debug_assert!(
1189 false,
1190 "UnmatchedOpening is handled at end-of-input and should not be routed through recover_from_error"
1191 );
1192 }
1193 SpanTokenizerErrorKind::UnterminatedString => {
1194 let start = error.span_start.unwrap_or(error.pos);
1195 let span = TokenSpan {
1196 token_type: TokenType::Operand,
1197 subtype: TokenSubType::None,
1198 start,
1199 end: self.formula.len(),
1200 };
1201 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1202 self.offset = span.end;
1203 self.start_token();
1204 self.diagnostics.push(TokenDiagnostic::new(
1205 span,
1206 "Reached end of formula while parsing string".to_string(),
1207 RecoveryAction::UnterminatedString,
1208 ));
1209 }
1210 SpanTokenizerErrorKind::UnmatchedBracket => {
1211 let start = error.span_start.unwrap_or(error.pos);
1212 let end = error.span_end.unwrap_or(self.formula.len());
1213 let span = TokenSpan {
1214 token_type: TokenType::Operand,
1215 subtype: TokenSubType::None,
1216 start,
1217 end,
1218 };
1219 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1220 self.offset = span.end;
1221 self.start_token();
1222 self.diagnostics.push(TokenDiagnostic::new(
1223 span,
1224 "Encountered unmatched '['".to_string(),
1225 RecoveryAction::UnmatchedBracket,
1226 ));
1227 }
1228 SpanTokenizerErrorKind::MismatchedPair => {
1229 let span = TokenSpan {
1230 token_type: TokenType::Operand,
1231 subtype: TokenSubType::None,
1232 start: error.pos,
1233 end: error.pos + 1,
1234 };
1235 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1236 self.offset = span.end;
1237 self.start_token();
1238 self.diagnostics.push(TokenDiagnostic::new(
1239 span,
1240 "Mismatched ( and { pair".to_string(),
1241 RecoveryAction::SkippedUnmatchedCloser,
1242 ));
1243 }
1244 SpanTokenizerErrorKind::InvalidErrorLiteral => {
1245 let start = error.span_start.unwrap_or(error.pos);
1246 let end = error.span_end.unwrap_or(error.pos + 1);
1247 let span = TokenSpan {
1248 token_type: TokenType::Operand,
1249 subtype: TokenSubType::None,
1250 start,
1251 end,
1252 };
1253 self.push_span(TokenType::Operand, TokenSubType::None, span.start, span.end);
1254 self.offset = span.end;
1255 self.start_token();
1256 self.diagnostics.push(TokenDiagnostic::new(
1257 span,
1258 "Invalid error code".to_string(),
1259 RecoveryAction::InvalidErrorLiteral,
1260 ));
1261 }
1262 }
1263 }
1264
1265 fn parse_string(&mut self) -> Result<(), SpanTokenizerError> {
1266 let delim = self.formula.as_bytes()[self.offset];
1267 assert!(delim == b'"' || delim == b'\'');
1268
1269 let is_dollar_ref = delim == b'\''
1270 && self.has_token()
1271 && self.token_end - self.token_start == 1
1272 && self.formula.as_bytes()[self.token_start] == b'$';
1273
1274 let glue_to_token = delim == b'\''
1279 && self.has_token()
1280 && self.token_end > 0
1281 && self.formula.as_bytes()[self.token_end - 1] == b':';
1282
1283 if !is_dollar_ref && !glue_to_token && self.has_token() {
1284 self.save_token();
1285 self.start_token();
1286 }
1287
1288 let string_start = if is_dollar_ref {
1289 self.token_start
1290 } else {
1291 self.offset
1292 };
1293 self.offset += 1;
1294
1295 while self.offset < self.formula.len() {
1296 if self.formula.as_bytes()[self.offset] == delim {
1297 self.offset += 1;
1298 if self.offset < self.formula.len() && self.formula.as_bytes()[self.offset] == delim
1299 {
1300 self.offset += 1;
1301 } else {
1302 if delim == b'"' {
1303 let value_str = &self.formula[string_start..self.offset];
1304 let subtype = operand_subtype(value_str);
1305 self.push_span(TokenType::Operand, subtype, string_start, self.offset);
1306 self.start_token();
1307 } else {
1308 self.token_end = self.offset;
1309 }
1310 return Ok(());
1311 }
1312 } else {
1313 self.offset += 1;
1314 }
1315 }
1316
1317 Err(SpanTokenizerError {
1318 kind: SpanTokenizerErrorKind::UnterminatedString,
1319 pos: self.offset,
1320 message: "Reached end of formula while parsing string".to_string(),
1321 span_start: Some(string_start),
1322 span_end: Some(self.formula.len()),
1323 })
1324 }
1325
1326 fn parse_brackets(&mut self) -> Result<(), SpanTokenizerError> {
1327 assert_eq!(self.formula.as_bytes()[self.offset], b'[');
1328
1329 if !self.has_token() {
1330 self.start_token();
1331 }
1332
1333 let bracket_start = self.offset;
1334 let mut open_count = 1;
1335 self.offset += 1;
1336
1337 while self.offset < self.formula.len() {
1338 match self.formula.as_bytes()[self.offset] {
1339 b'\'' => {
1344 if self.offset + 1 < self.formula.len() {
1345 self.offset += 2;
1346 continue;
1347 }
1348 self.offset += 1;
1351 continue;
1352 }
1353 b'[' => open_count += 1,
1354 b']' => {
1355 open_count -= 1;
1356 if open_count == 0 {
1357 self.offset += 1;
1358 self.extend_token();
1359 return Ok(());
1360 }
1361 }
1362 _ => {}
1363 }
1364 self.offset += 1;
1365 }
1366
1367 Err(SpanTokenizerError {
1368 kind: SpanTokenizerErrorKind::UnmatchedBracket,
1369 pos: self.offset,
1370 message: "Encountered unmatched '['".to_string(),
1371 span_start: Some(bracket_start),
1372 span_end: Some(self.formula.len()),
1373 })
1374 }
1375
1376 fn parse_error(&mut self) -> Result<(), SpanTokenizerError> {
1377 let has_sheet_prefix = self.has_token()
1383 && self.token_end > 0
1384 && self.formula.as_bytes()[self.token_end - 1] == b'!';
1385 if has_sheet_prefix {
1386 if self.token_end - self.token_start <= 1 {
1387 return Err(SpanTokenizerError {
1388 kind: SpanTokenizerErrorKind::InvalidErrorLiteral,
1389 pos: self.offset,
1390 message: format!(
1391 "Empty sheet qualifier before error literal at position {}",
1392 self.offset
1393 ),
1394 span_start: Some(self.token_start),
1395 span_end: Some(self.offset),
1396 });
1397 }
1398 self.start_token();
1400 } else if self.has_token() {
1401 self.save_token();
1402 self.start_token();
1403 }
1404
1405 if ref_error_starts_at(self.formula, self.offset)
1410 && ref_error_starts_at(self.formula, self.offset + 5)
1411 {
1412 self.offset += 5;
1413 }
1414
1415 let error_start = self.offset;
1416
1417 for &err_code in ERROR_CODES {
1418 let err_bytes = err_code.as_bytes();
1419 if self.offset + err_bytes.len() <= self.formula.len() {
1420 let slice = &self.formula.as_bytes()[self.offset..self.offset + err_bytes.len()];
1421 if slice.eq_ignore_ascii_case(err_bytes) {
1422 self.push_span(
1423 TokenType::Operand,
1424 TokenSubType::Error,
1425 error_start,
1426 self.offset + err_bytes.len(),
1427 );
1428 self.offset += err_bytes.len();
1429 self.start_token();
1430 return Ok(());
1431 }
1432 }
1433 }
1434
1435 let mut end = self.offset + 1;
1436 while end < self.formula.len() {
1437 let ch = self.formula.as_bytes()[end];
1438 if is_token_ender(ch)
1439 || ch == b' '
1440 || ch == b'\t'
1441 || ch == b'\r'
1442 || ch == b'\n'
1443 || ch == b'('
1444 || ch == b'{'
1445 || ch == b'['
1446 || ch == b'"'
1447 || ch == b'\''
1448 {
1449 break;
1450 }
1451 end += 1;
1452 }
1453
1454 Err(SpanTokenizerError {
1455 kind: SpanTokenizerErrorKind::InvalidErrorLiteral,
1456 pos: self.offset,
1457 message: format!("Invalid error code at position {}", self.offset),
1458 span_start: Some(error_start),
1459 span_end: Some(end),
1460 })
1461 }
1462
1463 fn parse_whitespace(&mut self) -> Result<(), SpanTokenizerError> {
1464 self.save_token();
1465
1466 let ws_start = self.offset;
1467 let mut contains_intersection_space = false;
1468 while self.offset < self.formula.len() {
1469 match self.formula.as_bytes()[self.offset] {
1470 b' ' => {
1471 contains_intersection_space = true;
1472 self.offset += 1;
1473 }
1474 b'\t' | b'\r' | b'\n' => self.offset += 1,
1475 _ => break,
1476 }
1477 }
1478
1479 let token_type = if contains_intersection_space
1482 && self.prev_is_reference_producing()
1483 && next_starts_reference_expression(self.formula, self.offset)
1484 {
1485 TokenType::OpInfix
1486 } else {
1487 TokenType::Whitespace
1488 };
1489 self.push_span(token_type, TokenSubType::None, ws_start, self.offset);
1490 self.start_token();
1491 Ok(())
1492 }
1493
1494 fn prev_is_reference_producing(&self) -> bool {
1495 match self.prev_non_whitespace() {
1496 Some(prev) => match prev.token_type {
1497 TokenType::OpPostfix => true,
1498 TokenType::Paren | TokenType::Func | TokenType::Array
1499 if prev.subtype == TokenSubType::Close =>
1500 {
1501 true
1502 }
1503 TokenType::Operand if prev.subtype == TokenSubType::Range => self
1504 .formula
1505 .get(prev.start..prev.end)
1506 .is_some_and(is_reference_operand_value),
1507 _ => false,
1508 },
1509 None => false,
1510 }
1511 }
1512
1513 fn should_emit_colon_infix(&self) -> bool {
1514 if self.has_token() {
1515 let value = &self.formula[self.token_start..self.token_end];
1516 if value.ends_with('!') {
1517 return false;
1518 }
1519 return reference_value_contains_range_colon(value)
1520 || value_has_structured_reference_bracket(value)
1521 || ref_error_starts_at(self.formula, self.offset + 1)
1524 || (value.contains('!')
1525 && next_reference_has_sheet_qualifier(self.formula, self.offset + 1));
1526 }
1527 self.prev_is_reference_producing()
1528 || self.prev_non_whitespace().is_some_and(|prev| {
1529 prev.subtype == TokenSubType::Error && ref_error_starts_at(self.formula, prev.start)
1530 })
1531 }
1532
1533 fn emit_infix_operator(&mut self, start: usize, end: usize) {
1534 self.save_token();
1535 self.start_token();
1536 self.push_span(TokenType::OpInfix, TokenSubType::None, start, end);
1537 self.offset = end;
1538 self.start_token();
1539 }
1540
1541 fn prev_non_whitespace(&self) -> Option<&TokenSpan> {
1542 self.spans
1543 .iter()
1544 .rev()
1545 .find(|t| t.token_type != TokenType::Whitespace)
1546 }
1547
1548 fn should_emit_hash_postfix(&self) -> bool {
1553 if self.has_token() {
1554 if self.formula.as_bytes()[self.token_end - 1] == b'!' {
1558 return false;
1559 }
1560 let value = &self.formula[self.token_start..self.token_end];
1561 return operand_subtype(value) == TokenSubType::Range;
1562 }
1563 match self.prev_non_whitespace() {
1564 Some(prev) => match prev.token_type {
1565 TokenType::OpPostfix => true,
1566 TokenType::Paren | TokenType::Func | TokenType::Array
1567 if prev.subtype == TokenSubType::Close =>
1568 {
1569 true
1570 }
1571 TokenType::Operand if prev.subtype == TokenSubType::Range => true,
1572 _ => false,
1573 },
1574 None => false,
1575 }
1576 }
1577
1578 fn emit_hash_postfix(&mut self) {
1579 self.save_token();
1580 self.start_token();
1581 self.push_span(
1582 TokenType::OpPostfix,
1583 TokenSubType::None,
1584 self.offset,
1585 self.offset + 1,
1586 );
1587 self.offset += 1;
1588 self.start_token();
1589 }
1590
1591 fn parse_operator(&mut self) -> Result<(), SpanTokenizerError> {
1592 self.save_token();
1593
1594 if self.offset + 1 < self.formula.len() {
1595 let two_char = &self.formula.as_bytes()[self.offset..self.offset + 2];
1596 if two_char == b">=" || two_char == b"<=" || two_char == b"<>" {
1597 self.push_span(
1598 TokenType::OpInfix,
1599 TokenSubType::None,
1600 self.offset,
1601 self.offset + 2,
1602 );
1603 self.offset += 2;
1604 self.start_token();
1605 return Ok(());
1606 }
1607 }
1608
1609 let curr_byte = self.formula.as_bytes()[self.offset];
1610 let token_type = match curr_byte {
1611 b'@' => TokenType::OpPrefix,
1612 b'%' => TokenType::OpPostfix,
1613 b'+' | b'-' => {
1614 if self.spans.is_empty() {
1615 TokenType::OpPrefix
1616 } else {
1617 let prev = self.prev_non_whitespace();
1618 if let Some(p) = prev {
1619 if p.subtype == TokenSubType::Close
1620 || p.token_type == TokenType::OpPostfix
1621 || p.token_type == TokenType::Operand
1622 {
1623 TokenType::OpInfix
1624 } else {
1625 TokenType::OpPrefix
1626 }
1627 } else {
1628 TokenType::OpPrefix
1629 }
1630 }
1631 }
1632 _ => TokenType::OpInfix,
1633 };
1634
1635 self.push_span(token_type, TokenSubType::None, self.offset, self.offset + 1);
1636 self.offset += 1;
1637 self.start_token();
1638 Ok(())
1639 }
1640
1641 fn parse_opener(&mut self) -> Result<(), SpanTokenizerError> {
1642 let curr_byte = self.formula.as_bytes()[self.offset];
1643 assert!(curr_byte == b'(' || curr_byte == b'{');
1644
1645 let token = if curr_byte == b'{' {
1646 self.save_token();
1647 TokenSpan {
1648 token_type: TokenType::Array,
1649 subtype: TokenSubType::Open,
1650 start: self.offset,
1651 end: self.offset + 1,
1652 }
1653 } else if self.has_token() {
1654 if let Some(colon) =
1655 range_colon_before_call(&self.formula[self.token_start..self.token_end])
1656 {
1657 let colon = self.token_start + colon;
1658 let subtype = operand_subtype(&self.formula[self.token_start..colon]);
1659 self.push_span(TokenType::Operand, subtype, self.token_start, colon);
1660 self.push_span(TokenType::OpInfix, TokenSubType::None, colon, colon + 1);
1661 self.token_start = colon + 1;
1662 }
1663 let token = TokenSpan {
1664 token_type: TokenType::Func,
1665 subtype: TokenSubType::Open,
1666 start: self.token_start,
1667 end: self.offset + 1,
1668 };
1669 self.token_start = self.offset + 1;
1670 self.token_end = self.offset + 1;
1671 token
1672 } else {
1673 TokenSpan {
1674 token_type: TokenType::Paren,
1675 subtype: TokenSubType::Open,
1676 start: self.offset,
1677 end: self.offset + 1,
1678 }
1679 };
1680
1681 if self.spans.len() >= self.token_limit {
1682 self.exceeded = true;
1683 return Ok(());
1684 }
1685 self.spans.push(token);
1686 self.token_stack.push(token);
1687 self.offset += 1;
1688 self.start_token();
1689 Ok(())
1690 }
1691
1692 fn parse_closer(&mut self) -> Result<(), SpanTokenizerError> {
1693 self.save_token();
1694
1695 let curr_byte = self.formula.as_bytes()[self.offset];
1696 assert!(curr_byte == b')' || curr_byte == b'}');
1697
1698 if let Some(open_token) = self.token_stack.last().copied() {
1699 let expected = if open_token.token_type == TokenType::Array {
1700 b'}'
1701 } else {
1702 b')'
1703 };
1704 if curr_byte != expected {
1705 return Err(SpanTokenizerError {
1706 kind: SpanTokenizerErrorKind::MismatchedPair,
1707 pos: self.offset,
1708 message: "Mismatched ( and { pair".to_string(),
1709 span_start: Some(self.offset),
1710 span_end: Some(self.offset + 1),
1711 });
1712 }
1713
1714 self.token_stack.pop();
1715 self.push_span(
1716 open_token.token_type,
1717 TokenSubType::Close,
1718 self.offset,
1719 self.offset + 1,
1720 );
1721 } else {
1722 return Err(SpanTokenizerError {
1723 kind: SpanTokenizerErrorKind::NoMatchingOpener,
1724 pos: self.offset,
1725 message: format!("No matching opener for closer at position {}", self.offset),
1726 span_start: Some(self.offset),
1727 span_end: Some(self.offset + 1),
1728 });
1729 }
1730
1731 self.offset += 1;
1732 self.start_token();
1733 Ok(())
1734 }
1735
1736 fn parse_separator(&mut self) -> Result<(), SpanTokenizerError> {
1737 self.save_token();
1738
1739 let curr_byte = self.formula.as_bytes()[self.offset];
1740 assert!(curr_byte == b';' || curr_byte == b',');
1741
1742 let top_token = self.token_stack.last();
1743 let in_function_or_array = matches!(
1744 top_token.map(|t| t.token_type),
1745 Some(TokenType::Func | TokenType::Array)
1746 );
1747 let in_array = matches!(top_token.map(|t| t.token_type), Some(TokenType::Array));
1748
1749 let (token_type, subtype) = match curr_byte {
1750 b',' => {
1751 if in_function_or_array {
1752 (TokenType::Sep, TokenSubType::Arg)
1753 } else {
1754 (TokenType::OpInfix, TokenSubType::None)
1755 }
1756 }
1757 b';' => {
1758 if in_array {
1759 (TokenType::Sep, TokenSubType::Row)
1760 } else if self.dialect == FormulaDialect::OpenFormula && in_function_or_array {
1761 (TokenType::Sep, TokenSubType::Arg)
1762 } else if self.dialect == FormulaDialect::OpenFormula {
1763 (TokenType::OpInfix, TokenSubType::None)
1764 } else {
1765 (TokenType::Sep, TokenSubType::Row)
1766 }
1767 }
1768 _ => (TokenType::OpInfix, TokenSubType::None),
1769 };
1770
1771 self.push_span(token_type, subtype, self.offset, self.offset + 1);
1772 self.offset += 1;
1773 self.start_token();
1774 Ok(())
1775 }
1776}
1777
1778pub struct Tokenizer {
1780 exceeded: bool,
1781 admission_error: Option<TokenizerError>,
1782 formula: String, pub items: Vec<Token>,
1784 token_stack: Vec<Token>,
1785 offset: usize, token_start: usize, token_end: usize, dialect: FormulaDialect,
1789}
1790
1791impl Tokenizer {
1792 pub fn new(formula: &str) -> Result<Self, TokenizerError> {
1794 Self::new_with_dialect(formula, FormulaDialect::Excel)
1795 }
1796
1797 pub fn new_best_effort(formula: &str) -> Self {
1801 Self::new_best_effort_with_dialect(formula, FormulaDialect::Excel)
1802 }
1803
1804 pub fn new_best_effort_with_dialect(formula: &str, dialect: FormulaDialect) -> Self {
1806 let stream = TokenStream::new_best_effort_with_dialect(formula, dialect);
1807 Self::from_token_stream(&stream)
1808 }
1809
1810 pub fn new_with_dialect(
1812 formula: &str,
1813 dialect: FormulaDialect,
1814 ) -> Result<Self, TokenizerError> {
1815 crate::ParserLimits::default().check_source(formula)?;
1816 let mut tokenizer = Tokenizer {
1817 exceeded: false,
1818 admission_error: None,
1819 formula: formula.to_string(),
1820 items: Vec::with_capacity(
1821 (formula.len() / 2).min(crate::ParserLimits::default().tokens()),
1822 ), token_stack: Vec::with_capacity(16),
1824 offset: 0,
1825 token_start: 0,
1826 token_end: 0,
1827 dialect,
1828 };
1829 let result = tokenizer.parse();
1830 if tokenizer.exceeded {
1831 return Err(TokenizerError {
1832 message: format!(
1833 "Formula token limit exceeded (max {})",
1834 crate::ParserLimits::default().tokens()
1835 ),
1836 pos: tokenizer.offset,
1837 });
1838 }
1839 result?;
1840 Ok(tokenizer)
1841 }
1842
1843 pub fn from_token_stream(stream: &TokenStream) -> Self {
1844 let admission_error = stream.admission_error(crate::ParserLimits::default());
1845 let (formula, items) = if admission_error.is_some() {
1846 (String::new(), Vec::new())
1847 } else {
1848 (stream.source.to_string(), stream.to_tokens())
1849 };
1850 Tokenizer {
1851 exceeded: false,
1852 admission_error,
1853 formula,
1854 items,
1855 token_stack: Vec::with_capacity(16),
1856 offset: 0,
1857 token_start: 0,
1858 token_end: 0,
1859 dialect: stream.dialect,
1860 }
1861 }
1862
1863 pub fn admission_error(&self) -> Option<&TokenizerError> {
1865 self.admission_error.as_ref()
1866 }
1867
1868 fn emit(&mut self, token: Token) {
1869 if self.items.len() >= crate::ParserLimits::default().tokens() {
1870 self.exceeded = true;
1871 return;
1872 }
1873 self.items.push(token);
1874 }
1875 #[inline]
1877 fn current_byte(&self) -> Option<u8> {
1878 self.formula.as_bytes().get(self.offset).copied()
1879 }
1880
1881 #[inline]
1883 fn has_token(&self) -> bool {
1884 self.token_end > self.token_start
1885 }
1886
1887 #[inline]
1889 fn start_token(&mut self) {
1890 self.token_start = self.offset;
1891 self.token_end = self.offset;
1892 }
1893
1894 #[inline]
1896 fn extend_token(&mut self) {
1897 self.token_end = self.offset;
1898 }
1899
1900 fn parse(&mut self) -> Result<(), TokenizerError> {
1902 if self.formula.is_empty() {
1903 return Ok(());
1904 }
1905
1906 if self.formula.as_bytes()[0] != b'=' {
1908 self.emit(Token::new_with_span(
1909 self.formula.clone(),
1910 TokenType::Literal,
1911 TokenSubType::None,
1912 0,
1913 self.formula.len(),
1914 ));
1915 return Ok(());
1916 }
1917
1918 self.offset = 1;
1920 self.start_token();
1921
1922 while self.offset < self.formula.len() {
1923 if self.exceeded {
1924 break;
1925 }
1926 if self.check_scientific_notation()? {
1927 continue;
1928 }
1929
1930 let curr_byte = self.formula.as_bytes()[self.offset];
1931
1932 if is_token_ender(curr_byte) && self.has_token() {
1934 self.save_token();
1935 self.start_token();
1936 }
1937
1938 match curr_byte {
1940 b'"' | b'\'' => self.parse_string()?,
1941 b'[' => self.parse_brackets()?,
1942 b'#' => {
1943 if self.should_emit_hash_postfix() {
1944 self.emit_hash_postfix();
1945 } else {
1946 self.parse_error()?
1947 }
1948 }
1949 b' ' | b'\t' | b'\r' | b'\n' => self.parse_whitespace()?,
1950 b':' => {
1951 if self.should_emit_colon_infix() {
1952 self.emit_infix_operator(self.offset, self.offset + 1);
1953 } else {
1954 if !self.has_token() {
1955 self.start_token();
1956 }
1957 self.offset += 1;
1958 self.extend_token();
1959 }
1960 }
1961 b'+' | b'-' | b'*' | b'/' | b'^' | b'&' | b'=' | b'>' | b'<' | b'%' | b'@' => {
1963 self.parse_operator()?
1964 }
1965 b'{' | b'(' => self.parse_opener()?,
1966 b')' | b'}' => self.parse_closer()?,
1967 b';' | b',' => self.parse_separator()?,
1968 _ => {
1969 if !self.has_token() {
1971 self.start_token();
1972 }
1973 self.offset += 1;
1974 self.extend_token();
1975 }
1976 }
1977 }
1978
1979 if self.has_token() {
1981 self.save_token();
1982 }
1983
1984 if !self.token_stack.is_empty() {
1986 return Err(TokenizerError {
1987 message: "Unmatched opening parenthesis or bracket".to_string(),
1988 pos: self.offset,
1989 });
1990 }
1991
1992 Ok(())
1993 }
1994
1995 fn check_scientific_notation(&mut self) -> Result<bool, TokenizerError> {
2003 if let Some(curr_byte) = self.current_byte() {
2004 if (curr_byte == b'+' || curr_byte == b'-')
2005 && self.has_token()
2006 && self.is_scientific_notation_base()
2007 && self
2008 .formula
2009 .as_bytes()
2010 .get(self.offset + 1)
2011 .is_some_and(|b| b.is_ascii_digit())
2012 {
2013 self.offset += 1;
2014 self.extend_token();
2015 return Ok(true);
2016 }
2017 }
2018 Ok(false)
2019 }
2020
2021 fn is_scientific_notation_base(&self) -> bool {
2024 if !self.has_token() {
2025 return false;
2026 }
2027
2028 let token_slice = &self.formula.as_bytes()[self.token_start..self.token_end];
2029 if token_slice.len() < 2 {
2030 return false;
2031 }
2032
2033 let last = token_slice[token_slice.len() - 1];
2034 if !(last == b'E' || last == b'e') {
2035 return false;
2036 }
2037
2038 let first = token_slice[0];
2039 if !first.is_ascii_digit() {
2040 return false;
2041 }
2042
2043 let mut dot_seen = false;
2044 for &ch in &token_slice[1..token_slice.len() - 1] {
2046 match ch {
2047 b'0'..=b'9' => {}
2048 b'.' if !dot_seen => dot_seen = true,
2049 _ => return false,
2050 }
2051 }
2052 true
2053 }
2054
2055 fn save_token(&mut self) {
2057 if self.has_token() {
2058 if let Some(colon) =
2059 range_operator_colon(&self.formula[self.token_start..self.token_end])
2060 {
2061 let colon = self.token_start + colon;
2062 self.emit(Token::make_operand_from_slice(
2063 &self.formula,
2064 self.token_start,
2065 colon,
2066 ));
2067 self.emit(Token::from_slice(
2068 &self.formula,
2069 TokenType::OpInfix,
2070 TokenSubType::None,
2071 colon,
2072 colon + 1,
2073 ));
2074 self.token_start = colon + 1;
2075 }
2076 let token =
2077 Token::make_operand_from_slice(&self.formula, self.token_start, self.token_end);
2078 self.emit(token);
2079 }
2080 }
2081
2082 fn parse_string(&mut self) -> Result<(), TokenizerError> {
2084 let delim = self.formula.as_bytes()[self.offset];
2085 assert!(delim == b'"' || delim == b'\'');
2086
2087 let is_dollar_ref = delim == b'\''
2089 && self.has_token()
2090 && self.token_end - self.token_start == 1
2091 && self.formula.as_bytes()[self.token_start] == b'$';
2092
2093 let glue_to_token = delim == b'\''
2098 && self.has_token()
2099 && self.token_end > 0
2100 && self.formula.as_bytes()[self.token_end - 1] == b':';
2101
2102 if !is_dollar_ref && !glue_to_token && self.has_token() {
2103 self.save_token();
2104 self.start_token();
2105 }
2106
2107 let string_start = if is_dollar_ref {
2108 self.token_start
2109 } else {
2110 self.offset
2111 };
2112 self.offset += 1; while self.offset < self.formula.len() {
2115 if self.formula.as_bytes()[self.offset] == delim {
2116 self.offset += 1;
2117 if self.offset < self.formula.len() && self.formula.as_bytes()[self.offset] == delim
2119 {
2120 self.offset += 1; } else {
2122 if delim == b'"' {
2124 let token = Token::make_operand_from_slice(
2125 &self.formula,
2126 string_start,
2127 self.offset,
2128 );
2129 self.emit(token);
2130 self.start_token();
2131 } else {
2132 self.token_end = self.offset;
2134 }
2135 return Ok(());
2136 }
2137 } else {
2138 self.offset += 1;
2139 }
2140 }
2141
2142 Err(TokenizerError {
2143 message: "Reached end of formula while parsing string".to_string(),
2144 pos: self.offset,
2145 })
2146 }
2147
2148 fn parse_brackets(&mut self) -> Result<(), TokenizerError> {
2150 assert_eq!(self.formula.as_bytes()[self.offset], b'[');
2151
2152 if !self.has_token() {
2153 self.start_token();
2154 }
2155
2156 let mut open_count = 1;
2157 self.offset += 1;
2158
2159 while self.offset < self.formula.len() {
2160 match self.formula.as_bytes()[self.offset] {
2161 b'\'' => {
2166 if self.offset + 1 < self.formula.len() {
2167 self.offset += 2;
2168 continue;
2169 }
2170 self.offset += 1;
2171 continue;
2172 }
2173 b'[' => open_count += 1,
2174 b']' => {
2175 open_count -= 1;
2176 if open_count == 0 {
2177 self.offset += 1;
2178 self.extend_token();
2179 return Ok(());
2180 }
2181 }
2182 _ => {}
2183 }
2184 self.offset += 1;
2185 }
2186
2187 Err(TokenizerError {
2188 message: "Encountered unmatched '['".to_string(),
2189 pos: self.offset,
2190 })
2191 }
2192
2193 fn should_emit_hash_postfix(&self) -> bool {
2195 if self.has_token() {
2196 if self.formula.as_bytes()[self.token_end - 1] == b'!' {
2197 return false;
2198 }
2199 let value = &self.formula[self.token_start..self.token_end];
2200 let is_range = !value.starts_with('"')
2204 && !value.starts_with('#')
2205 && value != "TRUE"
2206 && value != "FALSE"
2207 && value.parse::<f64>().is_err();
2208 return is_range;
2209 }
2210 let prev = self
2211 .items
2212 .iter()
2213 .rev()
2214 .find(|t| t.token_type != TokenType::Whitespace);
2215 match prev {
2216 Some(p) => match p.token_type {
2217 TokenType::OpPostfix => true,
2218 TokenType::Paren | TokenType::Func | TokenType::Array
2219 if p.subtype == TokenSubType::Close =>
2220 {
2221 true
2222 }
2223 TokenType::Operand if p.subtype == TokenSubType::Range => true,
2224 _ => false,
2225 },
2226 None => false,
2227 }
2228 }
2229
2230 fn emit_hash_postfix(&mut self) {
2231 self.save_token();
2232 self.start_token();
2233 self.emit(Token::from_slice(
2234 &self.formula,
2235 TokenType::OpPostfix,
2236 TokenSubType::None,
2237 self.offset,
2238 self.offset + 1,
2239 ));
2240 self.offset += 1;
2241 self.start_token();
2242 }
2243
2244 fn parse_error(&mut self) -> Result<(), TokenizerError> {
2246 let has_sheet_prefix = self.has_token()
2251 && self.token_end > 0
2252 && self.formula.as_bytes()[self.token_end - 1] == b'!';
2253 if has_sheet_prefix {
2254 if self.token_end - self.token_start <= 1 {
2255 return Err(TokenizerError {
2256 message: format!(
2257 "Empty sheet qualifier before error literal at position {}",
2258 self.offset
2259 ),
2260 pos: self.offset,
2261 });
2262 }
2263 self.start_token();
2265 } else if self.has_token() {
2266 self.save_token();
2267 self.start_token();
2268 }
2269
2270 if ref_error_starts_at(&self.formula, self.offset)
2272 && ref_error_starts_at(&self.formula, self.offset + 5)
2273 {
2274 self.offset += 5;
2275 }
2276
2277 let error_start = self.offset;
2278
2279 for &err_code in ERROR_CODES {
2281 let err_bytes = err_code.as_bytes();
2282 if self.offset + err_bytes.len() <= self.formula.len() {
2283 let slice = &self.formula.as_bytes()[self.offset..self.offset + err_bytes.len()];
2284 if slice.eq_ignore_ascii_case(err_bytes) {
2285 let token = Token::make_operand_from_slice(
2286 &self.formula,
2287 error_start,
2288 self.offset + err_bytes.len(),
2289 );
2290 self.emit(token);
2291 self.offset += err_bytes.len();
2292 self.start_token();
2293 return Ok(());
2294 }
2295 }
2296 }
2297
2298 Err(TokenizerError {
2299 message: format!("Invalid error code at position {}", self.offset),
2300 pos: self.offset,
2301 })
2302 }
2303
2304 fn parse_whitespace(&mut self) -> Result<(), TokenizerError> {
2306 self.save_token();
2307
2308 let ws_start = self.offset;
2309 let mut contains_intersection_space = false;
2310 while self.offset < self.formula.len() {
2311 match self.formula.as_bytes()[self.offset] {
2312 b' ' => {
2313 contains_intersection_space = true;
2314 self.offset += 1;
2315 }
2316 b'\t' | b'\r' | b'\n' => self.offset += 1,
2317 _ => break,
2318 }
2319 }
2320
2321 let token_type = if contains_intersection_space
2324 && self.prev_is_reference_producing()
2325 && next_starts_reference_expression(&self.formula, self.offset)
2326 {
2327 TokenType::OpInfix
2328 } else {
2329 TokenType::Whitespace
2330 };
2331
2332 let token = if token_type == TokenType::OpInfix {
2333 Token::new_with_span(
2334 " ".to_string(),
2335 token_type,
2336 TokenSubType::None,
2337 ws_start,
2338 self.offset,
2339 )
2340 } else {
2341 Token::from_slice(
2342 &self.formula,
2343 token_type,
2344 TokenSubType::None,
2345 ws_start,
2346 self.offset,
2347 )
2348 };
2349 self.emit(token);
2350 self.start_token();
2351 Ok(())
2352 }
2353
2354 fn prev_non_whitespace(&self) -> Option<&Token> {
2355 self.items
2356 .iter()
2357 .rev()
2358 .find(|t| t.token_type != TokenType::Whitespace)
2359 }
2360
2361 fn prev_is_reference_producing(&self) -> bool {
2362 match self.prev_non_whitespace() {
2363 Some(prev) => match prev.token_type {
2364 TokenType::OpPostfix => true,
2365 TokenType::Paren | TokenType::Func | TokenType::Array
2366 if prev.subtype == TokenSubType::Close =>
2367 {
2368 true
2369 }
2370 TokenType::Operand if prev.subtype == TokenSubType::Range => {
2371 is_reference_operand_value(&prev.value)
2372 }
2373 _ => false,
2374 },
2375 None => false,
2376 }
2377 }
2378
2379 fn should_emit_colon_infix(&self) -> bool {
2380 if self.has_token() {
2381 let value = &self.formula[self.token_start..self.token_end];
2382 if value.ends_with('!') {
2383 return false;
2384 }
2385 return reference_value_contains_range_colon(value)
2386 || value_has_structured_reference_bracket(value)
2387 || ref_error_starts_at(&self.formula, self.offset + 1)
2390 || (value.contains('!')
2391 && next_reference_has_sheet_qualifier(&self.formula, self.offset + 1));
2392 }
2393 self.prev_is_reference_producing()
2394 || self.prev_non_whitespace().is_some_and(|prev| {
2395 prev.subtype == TokenSubType::Error && prev.value.eq_ignore_ascii_case("#REF!")
2396 })
2397 }
2398
2399 fn emit_infix_operator(&mut self, start: usize, end: usize) {
2400 self.save_token();
2401 self.start_token();
2402 self.emit(Token::from_slice(
2403 &self.formula,
2404 TokenType::OpInfix,
2405 TokenSubType::None,
2406 start,
2407 end,
2408 ));
2409 self.offset = end;
2410 self.start_token();
2411 }
2412
2413 fn parse_operator(&mut self) -> Result<(), TokenizerError> {
2415 self.save_token();
2416
2417 if self.offset + 1 < self.formula.len() {
2419 let two_char = &self.formula.as_bytes()[self.offset..self.offset + 2];
2420 if two_char == b">=" || two_char == b"<=" || two_char == b"<>" {
2421 self.emit(Token::from_slice(
2422 &self.formula,
2423 TokenType::OpInfix,
2424 TokenSubType::None,
2425 self.offset,
2426 self.offset + 2,
2427 ));
2428 self.offset += 2;
2429 self.start_token();
2430 return Ok(());
2431 }
2432 }
2433
2434 let curr_byte = self.formula.as_bytes()[self.offset];
2435 let token_type = match curr_byte {
2436 b'@' => TokenType::OpPrefix,
2437 b'%' => TokenType::OpPostfix,
2438 b'+' | b'-' => {
2439 if self.items.is_empty() {
2441 TokenType::OpPrefix
2442 } else {
2443 let prev = self
2444 .items
2445 .iter()
2446 .rev()
2447 .find(|t| t.token_type != TokenType::Whitespace);
2448 if let Some(p) = prev {
2449 if p.subtype == TokenSubType::Close
2450 || p.token_type == TokenType::OpPostfix
2451 || p.token_type == TokenType::Operand
2452 {
2453 TokenType::OpInfix
2454 } else {
2455 TokenType::OpPrefix
2456 }
2457 } else {
2458 TokenType::OpPrefix
2459 }
2460 }
2461 }
2462 _ => TokenType::OpInfix,
2463 };
2464
2465 self.emit(Token::from_slice(
2466 &self.formula,
2467 token_type,
2468 TokenSubType::None,
2469 self.offset,
2470 self.offset + 1,
2471 ));
2472 self.offset += 1;
2473 self.start_token();
2474 Ok(())
2475 }
2476
2477 fn parse_opener(&mut self) -> Result<(), TokenizerError> {
2479 let curr_byte = self.formula.as_bytes()[self.offset];
2480 assert!(curr_byte == b'(' || curr_byte == b'{');
2481
2482 let token = if curr_byte == b'{' {
2483 self.save_token();
2484 Token::make_subexp_from_slice(&self.formula, false, self.offset, self.offset + 1)
2485 } else if self.has_token() {
2486 if let Some(colon) =
2487 range_colon_before_call(&self.formula[self.token_start..self.token_end])
2488 {
2489 let colon = self.token_start + colon;
2490 self.emit(Token::make_operand_from_slice(
2491 &self.formula,
2492 self.token_start,
2493 colon,
2494 ));
2495 self.emit(Token::from_slice(
2496 &self.formula,
2497 TokenType::OpInfix,
2498 TokenSubType::None,
2499 colon,
2500 colon + 1,
2501 ));
2502 self.token_start = colon + 1;
2503 }
2504 let token = Token::make_subexp_from_slice(
2506 &self.formula,
2507 true,
2508 self.token_start,
2509 self.offset + 1,
2510 );
2511 self.token_start = self.offset + 1;
2512 self.token_end = self.offset + 1;
2513 token
2514 } else {
2515 Token::make_subexp_from_slice(&self.formula, false, self.offset, self.offset + 1)
2516 };
2517
2518 self.emit(token.clone());
2519 self.token_stack.push(token);
2520 self.offset += 1;
2521 self.start_token();
2522 Ok(())
2523 }
2524
2525 fn parse_closer(&mut self) -> Result<(), TokenizerError> {
2527 self.save_token();
2528
2529 let curr_byte = self.formula.as_bytes()[self.offset];
2530 assert!(curr_byte == b')' || curr_byte == b'}');
2531
2532 if let Some(open_token) = self.token_stack.pop() {
2533 let closer = open_token.get_closer()?;
2534 if (curr_byte == b'}' && closer.value != "}")
2535 || (curr_byte == b')' && closer.value != ")")
2536 {
2537 return Err(TokenizerError {
2538 message: "Mismatched ( and { pair".to_string(),
2539 pos: self.offset,
2540 });
2541 }
2542
2543 self.emit(Token::from_slice(
2544 &self.formula,
2545 closer.token_type,
2546 TokenSubType::Close,
2547 self.offset,
2548 self.offset + 1,
2549 ));
2550 } else {
2551 return Err(TokenizerError {
2552 message: format!("No matching opener for closer at position {}", self.offset),
2553 pos: self.offset,
2554 });
2555 }
2556
2557 self.offset += 1;
2558 self.start_token();
2559 Ok(())
2560 }
2561
2562 fn parse_separator(&mut self) -> Result<(), TokenizerError> {
2564 self.save_token();
2565
2566 let curr_byte = self.formula.as_bytes()[self.offset];
2567 assert!(curr_byte == b';' || curr_byte == b',');
2568
2569 let top_token = self.token_stack.last();
2570 let in_function_or_array = matches!(
2571 top_token.map(|t| t.token_type),
2572 Some(TokenType::Func | TokenType::Array)
2573 );
2574 let in_array = matches!(top_token.map(|t| t.token_type), Some(TokenType::Array));
2575
2576 let (token_type, subtype) = match curr_byte {
2577 b',' => {
2578 if in_function_or_array {
2579 (TokenType::Sep, TokenSubType::Arg)
2580 } else {
2581 (TokenType::OpInfix, TokenSubType::None)
2582 }
2583 }
2584 b';' => {
2585 if in_array {
2586 (TokenType::Sep, TokenSubType::Row)
2588 } else if self.dialect == FormulaDialect::OpenFormula && in_function_or_array {
2589 (TokenType::Sep, TokenSubType::Arg)
2591 } else if self.dialect == FormulaDialect::OpenFormula {
2592 (TokenType::OpInfix, TokenSubType::None)
2593 } else {
2594 (TokenType::Sep, TokenSubType::Row)
2595 }
2596 }
2597 _ => (TokenType::OpInfix, TokenSubType::None),
2598 };
2599
2600 self.emit(Token::from_slice(
2601 &self.formula,
2602 token_type,
2603 subtype,
2604 self.offset,
2605 self.offset + 1,
2606 ));
2607
2608 self.offset += 1;
2609 self.start_token();
2610 Ok(())
2611 }
2612
2613 pub fn render(&self) -> String {
2615 if self.items.is_empty() {
2616 "".to_string()
2617 } else if self.items[0].token_type == TokenType::Literal {
2618 self.items[0].value.clone()
2619 } else {
2620 let concatenated: String = self.items.iter().map(|t| t.value.clone()).collect();
2621 format!("={concatenated}")
2622 }
2623 }
2624
2625 pub fn dialect(&self) -> FormulaDialect {
2627 self.dialect
2628 }
2629}
2630
2631impl TryFrom<&str> for Tokenizer {
2632 type Error = TokenizerError;
2633
2634 fn try_from(value: &str) -> Result<Self, Self::Error> {
2635 Tokenizer::new(value)
2636 }
2637}
2638
2639impl TryFrom<String> for Tokenizer {
2640 type Error = TokenizerError;
2641
2642 fn try_from(value: String) -> Result<Self, Self::Error> {
2643 Tokenizer::new(&value)
2644 }
2645}