ironcalc_base/expressions/lexer/mod.rs
1//! A tokenizer for spreadsheet formulas.
2//!
3//! This is meant to feed a formula parser.
4//!
5//! You will need to instantiate it with a language and a locale.
6//!
7//! It supports two working modes:
8//!
9//! 1. A1 or display mode
10//! This is for user formulas. References are like `D4`, `D$4` or `F5:T10`
11//! 2. R1C1, internal or runtime mode
12//! A reference like R1C1 refers to $A$1 and R3C4 to $D$4
13//! `R[2]C[5]` refers to a cell two rows below and five columns to the right
14//! It uses the 'en' locale and language.
15//! This is used internally at runtime.
16//!
17//! Formulas look different in different locales:
18//!
19//! =IF(A1, B1, NA()) versus =IF(A1; B1; NA())
20//!
21//! Also numbers are different:
22//!
23//! 1,123.45 versus 1.123,45
24//!
25//! The names of the errors and functions are different in different languages,
26//! but they stay the same in different locales.
27//!
28//! Note that in IronCalc if you are using a locale different from 'en' or a language different from 'en'
29//! you will still need the 'en' locale and language because formulas are stored in that language and locale
30//!
31//! # Examples:
32//! ```
33//! use ironcalc_base::expressions::lexer::{Lexer, LexerMode};
34//! use ironcalc_base::expressions::token::{TokenType, OpCompare};
35//! use ironcalc_base::locale::get_locale;
36//! use ironcalc_base::language::get_language;
37//!
38//! let locale = get_locale("en").unwrap();
39//! let language = get_language("en").unwrap();
40//! let mut lexer = Lexer::new("=A1*SUM(Sheet2!C3:D5)", LexerMode::A1, &locale, &language);
41//! assert_eq!(lexer.next_token(), TokenType::Compare(OpCompare::Equal));
42//! assert!(matches!(lexer.next_token(), TokenType::Reference { .. }));
43//! ```
44
45use std::mem;
46
47use serde::{Deserialize, Serialize};
48
49use crate::expressions::token::{OpCompare, OpProduct, OpSum};
50
51use crate::language::Language;
52use crate::locale::Locale;
53
54use super::token::{Error, TokenType};
55use super::types::*;
56use super::utils;
57
58pub mod util;
59
60#[cfg(test)]
61mod test;
62
63mod ranges;
64mod structured_references;
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub struct LexerError {
68 pub position: usize,
69 pub message: String,
70}
71
72pub(super) type Result<T> = std::result::Result<T, LexerError>;
73
74/// Decides whether `name` should be accepted as an identifier in R1C1 mode, once
75/// the reference-parsing paths have already failed.
76///
77/// This uses the A1 identifier rules (`is_valid_a1_identifier`) rather than
78/// `is_valid_identifier`, so the single-character names "R"/"C" are accepted: they
79/// are valid LAMBDA parameters and LET variables. This matters on reload, because
80/// formulas are stored internally in R1C1 format, so a formula like
81/// `=BYROW(A1:C1, LAMBDA(c, MAX(c)))` is re-parsed in R1C1 mode and must not reject
82/// the `c` parameter.
83///
84/// The one exception is a bare "R"/"C" immediately followed by '[': that is a
85/// malformed R1C1 reference (e.g. `R[`), not an identifier, so it is rejected.
86/// Genuine R1C1 references (`R[n]C[n]`, `RnCn`) are consumed before this fallback.
87fn is_valid_r1c1_identifier(name: &str, next_char: Option<char>) -> bool {
88 if !utils::is_valid_a1_identifier(name) {
89 return false;
90 }
91 // `is_valid_identifier` differs from `is_valid_a1_identifier` only for "R"/"C".
92 // For those, only reject when they start a (malformed) reference, i.e. `R[`.
93 utils::is_valid_identifier(name) || next_char != Some('[')
94}
95
96#[derive(Clone, PartialEq, Eq)]
97pub enum LexerMode {
98 A1,
99 R1C1,
100}
101
102/// Tokenize an input
103#[derive(Clone)]
104pub struct Lexer<'a> {
105 position: usize,
106 next_token_position: Option<usize>,
107 len: usize,
108 chars: Vec<char>,
109 mode: LexerMode,
110 locale: &'a Locale,
111 language: &'a Language,
112}
113
114impl<'a> Lexer<'a> {
115 /// Creates a new `Lexer` that returns the tokens of a formula.
116 pub fn new(
117 formula: &str,
118 mode: LexerMode,
119 locale: &'a Locale,
120 language: &'a Language,
121 ) -> Lexer<'a> {
122 let chars: Vec<char> = formula.chars().collect();
123 let len = chars.len();
124 Lexer {
125 chars,
126 position: 0,
127 next_token_position: None,
128 len,
129 mode,
130 locale,
131 language,
132 }
133 }
134
135 /// Changes the lexer mode
136 pub fn set_lexer_mode(&mut self, mode: LexerMode) {
137 self.mode = mode;
138 }
139
140 /// Sets the locale
141 pub fn set_locale(&mut self, locale: &'a Locale) {
142 self.locale = locale;
143 }
144
145 /// Sets the language
146 pub fn set_language(&mut self, language: &'a Language) {
147 self.language = language;
148 }
149
150 // FIXME: I don't think we should have `is_a1_mode` and `get_formula`.
151 // The caller already knows those two
152
153 /// Returns true if mode is A1
154 pub fn is_a1_mode(&self) -> bool {
155 self.mode == LexerMode::A1
156 }
157
158 /// Returns the formula
159 pub fn get_formula(&self) -> String {
160 self.chars.iter().collect()
161 }
162
163 // FIXME: This is used to get the "marked tokens"
164 // I think a better API would be to return the marked tokens
165 /// Returns the position of the lexer
166 pub fn get_position(&self) -> i32 {
167 self.position as i32
168 }
169
170 /// Resets the formula
171 pub fn set_formula(&mut self, content: &str) {
172 self.chars = content.chars().collect();
173 self.len = self.chars.len();
174 self.position = 0;
175 self.next_token_position = None;
176 }
177
178 /// Returns an error if the token is not the expected one.
179 pub fn expect(&mut self, tk: TokenType) -> Result<()> {
180 let nt = self.next_token();
181 if mem::discriminant(&nt) != mem::discriminant(&tk) {
182 return Err(self.set_error(&format!("Error, expected {tk:?}"), self.position));
183 }
184 Ok(())
185 }
186
187 /// Checks the next token without advancing position
188 /// See also [advance_token](Self::advance_token)
189 pub fn peek_token(&mut self) -> TokenType {
190 let position = self.position;
191 let tk = self.next_token();
192 self.next_token_position = Some(self.position);
193 self.position = position;
194 tk
195 }
196
197 /// Advances position. This is used in conjunction with [`peek_token`](Self::peek_token)
198 /// It is a noop if the has not been a previous peek_token
199 pub fn advance_token(&mut self) {
200 if let Some(position) = self.next_token_position {
201 self.position = position;
202 self.next_token_position = None;
203 }
204 }
205
206 /// Returns the next token
207 pub fn next_token(&mut self) -> TokenType {
208 self.next_token_position = None;
209 self.consume_whitespace();
210
211 match self.read_next_char() {
212 Some(char) => {
213 match char {
214 '+' => TokenType::Addition(OpSum::Add),
215 '-' => TokenType::Addition(OpSum::Minus),
216 '*' => TokenType::Product(OpProduct::Times),
217 '/' => TokenType::Product(OpProduct::Divide),
218 '(' => TokenType::LeftParenthesis,
219 ')' => TokenType::RightParenthesis,
220 '=' => TokenType::Compare(OpCompare::Equal),
221 '{' => TokenType::LeftBrace,
222 '}' => TokenType::RightBrace,
223 '[' => TokenType::LeftBracket,
224 ']' => TokenType::RightBracket,
225 ':' => TokenType::Colon,
226 ';' => TokenType::Semicolon,
227 '@' => TokenType::At,
228 '\\' => TokenType::Backslash,
229 ',' => {
230 if self.locale.numbers.symbols.decimal == "," {
231 match self.consume_number(',') {
232 Ok(number) => TokenType::Number(number),
233 Err(error) => TokenType::Illegal(error),
234 }
235 } else {
236 TokenType::Comma
237 }
238 }
239 '.' => {
240 if self.locale.numbers.symbols.decimal == "." {
241 match self.consume_number('.') {
242 Ok(number) => TokenType::Number(number),
243 Err(error) => TokenType::Illegal(error),
244 }
245 } else {
246 // There is no TokenType::PERIOD
247 TokenType::Illegal(self.set_error("Expecting a number", self.position))
248 }
249 }
250 '!' => TokenType::Bang,
251 '^' => TokenType::Power,
252 '%' => TokenType::Percent,
253 '&' => TokenType::And,
254 '$' => self.consume_absolute_reference(),
255 '<' => {
256 let next_token = self.peek_char();
257 if next_token == Some('=') {
258 self.position += 1;
259 TokenType::Compare(OpCompare::LessOrEqualThan)
260 } else if next_token == Some('>') {
261 self.position += 1;
262 TokenType::Compare(OpCompare::NonEqual)
263 } else {
264 TokenType::Compare(OpCompare::LessThan)
265 }
266 }
267 '>' => {
268 if self.peek_char() == Some('=') {
269 self.position += 1;
270 TokenType::Compare(OpCompare::GreaterOrEqualThan)
271 } else {
272 TokenType::Compare(OpCompare::GreaterThan)
273 }
274 }
275 '#' => self.consume_error(),
276 '"' => match self.consume_string() {
277 Ok(s) => TokenType::String(s),
278 Err(error) => TokenType::Illegal(error),
279 },
280 '\'' => self.consume_quoted_sheet_reference(),
281 '0'..='9' => {
282 let position = self.position - 1;
283 match self.consume_number(char) {
284 Ok(number) => {
285 if self.peek_token() == TokenType::Colon
286 && self.mode == LexerMode::A1
287 {
288 // Its a row range 3:5
289 // FIXME: There are faster ways of parsing this
290 // Like checking that 'number' is integer and that the next token is integer
291 self.position = position;
292 match self.consume_range_a1() {
293 Ok(ParsedRange { left, right }) => {
294 if let Some(right) = right {
295 TokenType::Range {
296 sheet: None,
297 left,
298 right,
299 }
300 } else {
301 TokenType::Illegal(
302 self.set_error("Expecting row range", position),
303 )
304 }
305 }
306 Err(error) => {
307 // Examples:
308 // * 'Sheet 1'!3.4:5
309 // * 'Sheet 1'!3:A2
310 // * 'Sheet 1'!3:
311 TokenType::Illegal(error)
312 }
313 }
314 } else {
315 TokenType::Number(number)
316 }
317 }
318 Err(error) => {
319 // tried to read a number but failed
320 self.position = self.len;
321 TokenType::Illegal(error)
322 }
323 }
324 }
325 _ => {
326 if char.is_alphabetic() || char == '_' {
327 // At this point is one of the following:
328 // 1. A range with sheet: Sheet3!A3:D7
329 // 2. A boolean: TRUE or FALSE (dependent on the language)
330 // 3. A reference like WS34 or R3C5
331 // 4. A range without sheet ER4:ER7
332 // 5. A column range E:E
333 // 6. An identifier like a function name or a defined name
334 // 7. A range operator A1:OFFSET(...)
335 // 8. An Invalid token
336 let position = self.position;
337 self.position -= 1;
338 let name = self.consume_identifier();
339 let position_indent = self.position;
340
341 let peek_char = self.peek_char();
342 let next_char_is_colon = self.peek_char() == Some(':');
343
344 if peek_char == Some('!') {
345 // reference
346 self.position += 1;
347 return self.consume_range(Some(name));
348 } else if peek_char == Some('$') {
349 self.position = position - 1;
350 return self.consume_range(None);
351 }
352 let name_upper = name.to_uppercase();
353 if name_upper == self.language.booleans.r#true {
354 return TokenType::Boolean(true);
355 } else if name_upper == self.language.booleans.r#false {
356 return TokenType::Boolean(false);
357 }
358 if self.peek_char() == Some('(') {
359 return TokenType::Ident(name);
360 }
361 if self.mode == LexerMode::A1 {
362 let parsed_reference = utils::parse_reference_a1(&name_upper);
363 if parsed_reference.is_some()
364 || (utils::is_valid_column(name_upper.trim_start_matches('$'))
365 && next_char_is_colon)
366 {
367 self.position = position - 1;
368 match self.consume_range_a1() {
369 Ok(ParsedRange { left, right }) => {
370 if let Some(right) = right {
371 return TokenType::Range {
372 sheet: None,
373 left,
374 right,
375 };
376 } else {
377 return TokenType::Reference {
378 sheet: None,
379 column: left.column,
380 row: left.row,
381 absolute_row: left.absolute_row,
382 absolute_column: left.absolute_column,
383 };
384 }
385 }
386 Err(error) => {
387 // This could be the range operator: ":"
388 if let Some(r) = parsed_reference {
389 if next_char_is_colon {
390 self.position = position_indent;
391 return TokenType::Reference {
392 sheet: None,
393 row: r.row,
394 column: r.column,
395 absolute_column: r.absolute_column,
396 absolute_row: r.absolute_row,
397 };
398 }
399 }
400 self.position = self.len;
401 return TokenType::Illegal(error);
402 }
403 }
404 } else if utils::is_valid_a1_identifier(&name) {
405 if peek_char == Some('[') {
406 if let Ok(r) = self.consume_structured_reference(&name) {
407 return r;
408 }
409 return TokenType::Illegal(self.set_error(
410 "Invalid structured reference",
411 self.position,
412 ));
413 }
414 return TokenType::Ident(name);
415 } else {
416 return TokenType::Illegal(
417 self.set_error("Invalid identifier (A1)", self.position),
418 );
419 }
420 } else {
421 let pos = self.position;
422 self.position = position - 1;
423 match self.consume_range_r1c1() {
424 // it's a valid R1C1 range
425 // We need to check it's not something like R1C1P
426 Ok(ParsedRange { left, right }) => {
427 if pos > self.position {
428 self.position = pos;
429 if is_valid_r1c1_identifier(&name, self.peek_char()) {
430 return TokenType::Ident(name);
431 } else {
432 self.position = self.len;
433 return TokenType::Illegal(
434 self.set_error(
435 "Invalid identifier (R1C1)",
436 pos,
437 ),
438 );
439 }
440 }
441 if let Some(right) = right {
442 return TokenType::Range {
443 sheet: None,
444 left,
445 right,
446 };
447 } else {
448 return TokenType::Reference {
449 sheet: None,
450 column: left.column,
451 row: left.row,
452 absolute_row: left.absolute_row,
453 absolute_column: left.absolute_column,
454 };
455 }
456 }
457 Err(error) => {
458 self.position = position - 1;
459 if let Ok(r) = self.consume_reference_r1c1() {
460 if self.peek_char() == Some(':') {
461 return TokenType::Reference {
462 sheet: None,
463 row: r.row,
464 column: r.column,
465 absolute_column: r.absolute_column,
466 absolute_row: r.absolute_row,
467 };
468 }
469 }
470 self.position = pos;
471
472 if is_valid_r1c1_identifier(&name, self.peek_char()) {
473 return TokenType::Ident(name);
474 } else {
475 return TokenType::Illegal(self.set_error(
476 &format!("Invalid identifier (R1C1): {name}"),
477 error.position,
478 ));
479 }
480 }
481 }
482 }
483 }
484 TokenType::Illegal(self.set_error("Unknown error", self.position))
485 }
486 }
487 }
488 None => TokenType::EOF,
489 }
490 }
491
492 // Private methods
493
494 fn set_error(&mut self, message: &str, position: usize) -> LexerError {
495 self.position = self.len;
496 LexerError {
497 position,
498 message: message.to_string(),
499 }
500 }
501
502 fn peek_char(&mut self) -> Option<char> {
503 let position = self.position;
504 if position < self.len {
505 Some(self.chars[position])
506 } else {
507 None
508 }
509 }
510
511 fn expect_char(&mut self, ch_expected: char) -> Result<()> {
512 let position = self.position;
513 if position >= self.len {
514 return Err(self.set_error(
515 &format!("Error, expected {} found EOF", ch_expected),
516 self.position,
517 ));
518 } else {
519 let ch = self.chars[position];
520 if ch_expected != ch {
521 return Err(self.set_error(
522 &format!("Error, expected {} found {}", ch_expected, ch),
523 self.position,
524 ));
525 }
526 self.position += 1;
527 }
528 Ok(())
529 }
530
531 fn read_next_char(&mut self) -> Option<char> {
532 let position = self.position;
533 if position < self.len {
534 self.position = position + 1;
535 Some(self.chars[position])
536 } else {
537 None
538 }
539 }
540
541 // Consumes an integer from the input stream
542 fn consume_integer(&mut self, first: char) -> Result<i32> {
543 let mut position = self.position;
544 let len = self.len;
545 let mut chars = first.to_string();
546 while position < len {
547 let next_char = self.chars[position];
548 if next_char.is_ascii_digit() {
549 chars.push(next_char);
550 } else {
551 break;
552 }
553 position += 1;
554 }
555 self.position = position;
556 chars.parse::<i32>().map_err(|_| LexerError {
557 position,
558 message: format!("Failed to parse to int: {chars}"),
559 })
560 }
561
562 // Consumes a number in the current locale.
563 // It only takes into account the decimal separator
564 // Note that we do not parse the thousands separator
565 // Let's say ',' is the thousands separator. Then 1,234 would be an error.
566 // This is ok for most cases:
567 // =IF(A1=1,234, TRUE, FALSE) will not work
568 // If a user introduces a single number in the cell 1,234 we should be able to parse
569 // and format the cell appropriately
570 fn consume_number(&mut self, first: char) -> Result<f64> {
571 let mut position = self.position;
572 let len = self.len;
573 let mut chars = first.to_string();
574 // numbers before the decimal point
575 while position < len {
576 let x = self.chars[position];
577 if x.is_ascii_digit() {
578 chars.push(x);
579 } else {
580 break;
581 }
582 position += 1;
583 }
584 if position < len && self.chars[position].to_string() == self.locale.numbers.symbols.decimal
585 {
586 // numbers after the decimal point
587 chars.push('.');
588 position += 1;
589 while position < len {
590 let x = self.chars[position];
591 if x.is_ascii_digit() {
592 chars.push(x);
593 } else {
594 break;
595 }
596 position += 1;
597 }
598 }
599 if position + 1 < len && (self.chars[position] == 'e' || self.chars[position] == 'E') {
600 // exponential side
601 let x = self.chars[position + 1];
602 if x == '-' || x == '+' || x.is_ascii_digit() {
603 chars.push('e');
604 chars.push(x);
605 position += 2;
606 while position < len {
607 let x = self.chars[position];
608 if x.is_ascii_digit() {
609 chars.push(x);
610 } else {
611 break;
612 }
613 position += 1;
614 }
615 }
616 }
617 self.position = position;
618 match chars.parse::<f64>() {
619 Err(_) => Err(self.set_error(&format!("Failed to parse to double: {chars}"), position)),
620 Ok(v) => Ok(v),
621 }
622 }
623
624 // Consumes an identifier from the input stream
625 fn consume_identifier(&mut self) -> String {
626 let mut position = self.position;
627 while position < self.len {
628 let next_char = self.chars[position];
629 if next_char.is_alphanumeric() || next_char == '_' || next_char == '.' {
630 position += 1;
631 } else {
632 break;
633 }
634 }
635 let chars = self.chars[self.position..position].iter().collect();
636 self.position = position;
637 chars
638 }
639
640 fn consume_string(&mut self) -> Result<String> {
641 let mut position = self.position;
642 let len = self.len;
643 let mut chars = "".to_string();
644 let mut terminated = false;
645 while position < len {
646 let x = self.chars[position];
647 position += 1;
648 if x != '"' {
649 chars.push(x);
650 } else if position < len && self.chars[position] == '"' {
651 chars.push(x);
652 chars.push(self.chars[position]);
653 position += 1;
654 } else {
655 terminated = true;
656 break;
657 }
658 }
659 self.position = position;
660
661 if !terminated {
662 return Err(self.set_error("Expected closing '\"' but found end of input", position));
663 }
664 Ok(chars)
665 }
666
667 // Consumes a quoted string from input
668 // 'This is a quoted string'
669 // ' Also is a ''quoted'' string'
670 // Returns an error if it does not find a closing quote
671 fn consume_single_quote_string(&mut self) -> Result<String> {
672 let mut position = self.position;
673 let len = self.len;
674 let mut success = false;
675 let mut needs_escape = false;
676 while position < len {
677 let next_char = self.chars[position];
678 position += 1;
679 if next_char == '\'' {
680 if position == len {
681 success = true;
682 break;
683 }
684 if self.chars[position] != '\'' {
685 success = true;
686 break;
687 } else {
688 // In Excel we escape "'" with "''"
689 needs_escape = true;
690 position += 1;
691 }
692 }
693 }
694 if !success {
695 // We reached the end without the closing quote
696 return Err(self.set_error("Expected closing \"'\" but found end of input", position));
697 }
698 let chars: String = self.chars[self.position..position - 1].iter().collect();
699 self.position = position;
700 if needs_escape {
701 // In most cases we will not needs escaping so this would be an overkill
702 return Ok(chars.replace("''", "'"));
703 }
704
705 Ok(chars)
706 }
707
708 // Reads an error from the input stream
709 fn consume_error(&mut self) -> TokenType {
710 let errors = &self.language.errors;
711 let rest_of_formula: String = self.chars[self.position - 1..self.len].iter().collect();
712 if rest_of_formula.starts_with(&errors.r#ref) {
713 self.position += errors.r#ref.chars().count() - 1;
714 return TokenType::Error(Error::REF);
715 } else if rest_of_formula.starts_with(&errors.name) {
716 self.position += errors.name.chars().count() - 1;
717 return TokenType::Error(Error::NAME);
718 } else if rest_of_formula.starts_with(&errors.value) {
719 self.position += errors.value.chars().count() - 1;
720 return TokenType::Error(Error::VALUE);
721 } else if rest_of_formula.starts_with(&errors.div) {
722 self.position += errors.div.chars().count() - 1;
723 return TokenType::Error(Error::DIV);
724 } else if rest_of_formula.starts_with(&errors.na) {
725 self.position += errors.na.chars().count() - 1;
726 return TokenType::Error(Error::NA);
727 } else if rest_of_formula.starts_with(&errors.num) {
728 self.position += errors.num.chars().count() - 1;
729 return TokenType::Error(Error::NUM);
730 } else if rest_of_formula.starts_with(&errors.error) {
731 self.position += errors.error.chars().count() - 1;
732 return TokenType::Error(Error::ERROR);
733 } else if rest_of_formula.starts_with(&errors.nimpl) {
734 self.position += errors.nimpl.chars().count() - 1;
735 return TokenType::Error(Error::NIMPL);
736 } else if rest_of_formula.starts_with(&errors.spill) {
737 self.position += errors.spill.chars().count() - 1;
738 return TokenType::Error(Error::SPILL);
739 } else if rest_of_formula.starts_with(&errors.calc) {
740 self.position += errors.calc.chars().count() - 1;
741 return TokenType::Error(Error::CALC);
742 } else if rest_of_formula.starts_with(&errors.null) {
743 self.position += errors.null.chars().count() - 1;
744 return TokenType::Error(Error::NULL);
745 } else if rest_of_formula.starts_with(&errors.circ) {
746 self.position += errors.circ.chars().count() - 1;
747 return TokenType::Error(Error::CIRC);
748 }
749 // If it is not an error it _might_ be a spill operator.
750 TokenType::Spill
751 }
752
753 fn consume_whitespace(&mut self) {
754 let mut position = self.position;
755 let len = self.len;
756 while position < len {
757 let x = self.chars[position];
758 if !x.is_whitespace() {
759 break;
760 }
761 position += 1;
762 }
763 self.position = position;
764 }
765
766 fn consume_absolute_reference(&mut self) -> TokenType {
767 // This is an absolute reference.
768 // $A$4
769 if self.mode == LexerMode::R1C1 {
770 return TokenType::Illegal(
771 self.set_error("Cannot parse A1 reference in R1C1 mode", self.position),
772 );
773 }
774 self.position -= 1;
775 self.consume_range(None)
776 }
777
778 fn consume_quoted_sheet_reference(&mut self) -> TokenType {
779 // This is a reference:
780 // 'First Sheet'!A34
781 let sheet_name = match self.consume_single_quote_string() {
782 Ok(v) => v,
783 Err(error) => {
784 return TokenType::Illegal(error);
785 }
786 };
787 if self.next_token() != TokenType::Bang {
788 return TokenType::Illegal(self.set_error("Expected '!'", self.position));
789 }
790 self.consume_range(Some(sheet_name))
791 }
792
793 fn consume_range(&mut self, sheet: Option<String>) -> TokenType {
794 let m = if self.mode == LexerMode::A1 {
795 self.consume_range_a1()
796 } else {
797 self.consume_range_r1c1()
798 };
799 match m {
800 Ok(ParsedRange { left, right }) => {
801 if let Some(right) = right {
802 TokenType::Range { sheet, left, right }
803 } else {
804 TokenType::Reference {
805 sheet,
806 column: left.column,
807 row: left.row,
808 absolute_row: left.absolute_row,
809 absolute_column: left.absolute_column,
810 }
811 }
812 }
813 Err(error) => TokenType::Illegal(error),
814 }
815 }
816}