1use crate::messages::{raise, Refusal};
27use crate::refers::{self, is_ident_char, FormulaRef, Kind, Shape};
28use crate::sheet::{MAX_COLUMN, MAX_ROW};
29
30use super::value::ErrorKind;
31
32#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct RefExpr {
36 pub sheet: Option<String>,
38 pub last_sheet: Option<String>,
40 pub top: u32,
41 pub left: u32,
42 pub bottom: u32,
43 pub right: u32,
44 pub written: String,
46 pub shape: Shape,
51 pub corners: Corners,
52 pub found: Option<(u8, u8)>,
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct Corners {
72 pub row1: u32,
73 pub col1: u32,
74 pub row2: u32,
75 pub col2: u32,
76 pub row_abs1: bool,
77 pub col_abs1: bool,
78 pub row_abs2: bool,
79 pub col_abs2: bool,
80}
81
82impl RefExpr {
83 pub fn is_cell(&self) -> bool {
85 self.top == self.bottom && self.left == self.right && self.last_sheet.is_none()
86 }
87
88 pub fn moved(&self, d_row: i64, d_col: i64) -> Option<(u32, u32, u32, u32)> {
97 moved_at(
98 self.shape,
99 &self.corners,
100 (self.top, self.left, self.bottom, self.right),
101 d_row,
102 d_col,
103 )
104 }
105}
106
107#[inline(always)]
115pub(crate) fn moved_at(
116 shape: Shape,
117 c: &Corners,
118 rect: (u32, u32, u32, u32),
119 d_row: i64,
120 d_col: i64,
121) -> Option<(u32, u32, u32, u32)> {
122 if d_row == 0 && d_col == 0 {
123 return Some(rect);
124 }
125 let row = |n: u32, abs: bool| shifted(n, abs, d_row, MAX_ROW);
126 let col = |n: u32, abs: bool| shifted(n, abs, d_col, MAX_COLUMN);
127 match shape {
128 Shape::Cell => {
129 let k = col(c.col1, c.col_abs1)?;
130 let r = row(c.row1, c.row_abs1)?;
131 Some((r, k, r, k))
132 }
133 Shape::Range => {
134 let k1 = col(c.col1, c.col_abs1)?;
135 let r1 = row(c.row1, c.row_abs1)?;
136 let k2 = col(c.col2, c.col_abs2)?;
137 let r2 = row(c.row2, c.row_abs2)?;
138 Some((r1.min(r2), k1.min(k2), r1.max(r2), k1.max(k2)))
139 }
140 Shape::Column => {
141 let k1 = col(c.col1, c.col_abs1)?;
142 let k2 = col(c.col2, c.col_abs2)?;
143 Some((1, k1.min(k2), MAX_ROW, k1.max(k2)))
144 }
145 Shape::Row => {
146 let r1 = row(c.row1, c.row_abs1)?;
147 let r2 = row(c.row2, c.row_abs2)?;
148 Some((r1.min(r2), 1, r1.max(r2), MAX_COLUMN))
149 }
150 Shape::None => None,
151 }
152}
153
154fn shifted(n: u32, is_abs: bool, d: i64, max: u32) -> Option<u32> {
157 if is_abs {
158 return Some(n);
159 }
160 let m = i64::from(n) + d;
161 (m >= 1 && m <= i64::from(max)).then_some(m as u32)
162}
163
164#[derive(Clone, Copy, Debug, PartialEq, Eq)]
166pub enum BinOp {
167 Add,
168 Sub,
169 Mul,
170 Div,
171 Pow,
172 Concat,
173 Eq,
174 Ne,
175 Lt,
176 Gt,
177 Le,
178 Ge,
179}
180
181#[derive(Clone, Debug, PartialEq)]
183pub enum Expr {
184 Number(f64),
185 Text(String),
186 Bool(bool),
187 Error(ErrorKind),
188 Empty,
190 Ref(RefExpr),
191 Name {
193 name: String,
194 sheet: Option<String>,
195 },
196 Call(String, Vec<Expr>),
198 Neg(Box<Expr>),
199 Pos(Box<Expr>),
200 Percent(Box<Expr>),
201 Binary(BinOp, Box<Expr>, Box<Expr>),
202}
203
204#[derive(Clone, Copy, Debug, PartialEq)]
207pub enum Read<'e> {
208 Ref(&'e RefExpr),
209 Name {
210 name: &'e str,
211 sheet: Option<&'e str>,
212 },
213}
214
215impl Expr {
216 pub fn each_read<'e>(&'e self, f: &mut dyn FnMut(Read<'e>)) {
220 let mut stack = vec![self];
221 while let Some(e) = stack.pop() {
222 match e {
223 Expr::Ref(r) => f(Read::Ref(r)),
224 Expr::Name { name, sheet } => f(Read::Name {
225 name,
226 sheet: sheet.as_deref(),
227 }),
228 other => other.push_children(&mut stack),
229 }
230 }
231 }
232
233 pub fn each_call<'e>(&'e self, f: &mut dyn FnMut(&'e str)) {
236 let mut stack = vec![self];
237 while let Some(e) = stack.pop() {
238 if let Expr::Call(name, _) = e {
239 f(name);
240 }
241 e.push_children(&mut stack);
242 }
243 }
244
245 fn push_children<'e>(&'e self, stack: &mut Vec<&'e Expr>) {
251 match self {
252 Expr::Call(_, args) => stack.extend(args.iter().rev()),
253 Expr::Neg(x) | Expr::Pos(x) | Expr::Percent(x) => stack.push(x),
254 Expr::Binary(_, a, b) => {
255 stack.push(b);
256 stack.push(a);
257 }
258 Expr::Ref(_)
259 | Expr::Name { .. }
260 | Expr::Number(_)
261 | Expr::Text(_)
262 | Expr::Bool(_)
263 | Expr::Error(_)
264 | Expr::Empty => {}
265 }
266 }
267}
268
269#[derive(Clone, Debug, PartialEq)]
270enum Tok {
271 Num(f64),
272 Str(String),
273 Bool(bool),
274 Err(ErrorKind),
275 Ref(RefExpr),
276 Name {
277 name: String,
278 sheet: Option<String>,
279 },
280 Func(String),
283 Op(BinOp),
284 Percent,
285 LParen,
286 RParen,
287 Comma,
288 Other(String),
290}
291
292pub fn ref_expr(r: &FormulaRef) -> Option<RefExpr> {
296 if !matches!(
297 r.kind,
298 Kind::Cell | Kind::Range | Kind::Column | Kind::Row | Kind::ThreeD
299 ) {
300 return None;
301 }
302 let (top, left, bottom, right) = match r.shape {
303 Shape::Cell => (r.row1, r.col1, r.row1, r.col1),
304 Shape::Range => (
305 r.row1.min(r.row2),
306 r.col1.min(r.col2),
307 r.row1.max(r.row2),
308 r.col1.max(r.col2),
309 ),
310 Shape::Column => (1, r.col1.min(r.col2), MAX_ROW, r.col1.max(r.col2)),
311 Shape::Row => (r.row1.min(r.row2), 1, r.row1.max(r.row2), MAX_COLUMN),
312 Shape::None => return None,
313 };
314 let (sheet, last_sheet) = if r.sheet_name.is_empty() {
315 (None, None)
316 } else {
317 match r.sheet_name.split_once(':') {
318 Some((first, last)) => (Some(first.to_string()), Some(last.to_string())),
319 None => (Some(r.sheet_name.clone()), None),
320 }
321 };
322 Some(RefExpr {
323 sheet,
324 last_sheet,
325 top,
326 left,
327 bottom,
328 right,
329 written: r.written.clone(),
330 shape: r.shape,
331 corners: Corners {
332 row1: r.row1,
333 col1: r.col1,
334 row2: r.row2,
335 col2: r.col2,
336 row_abs1: r.row_abs1,
337 col_abs1: r.col_abs1,
338 row_abs2: r.row_abs2,
339 col_abs2: r.col_abs2,
340 },
341 found: None,
342 })
343}
344
345fn tokenize(text: &str) -> Vec<Tok> {
346 let s: Vec<char> = text.chars().collect();
347 let refs: Vec<FormulaRef> = refers::scan(text)
348 .into_iter()
349 .filter(|r| r.kind != Kind::Unreadable)
350 .collect();
351 let mut toks = Vec::new();
352 let mut i = usize::from(s.first() == Some(&'='));
353 let mut next_ref = 0;
354 while i < s.len() {
355 while next_ref < refs.len() && refs[next_ref].pos < i {
356 next_ref += 1;
357 }
358 if next_ref < refs.len() && refs[next_ref].pos == i {
359 let r = &refs[next_ref];
360 next_ref += 1;
361 toks.push(match r.kind {
362 Kind::Cell | Kind::Range | Kind::Column | Kind::Row | Kind::ThreeD => {
363 match ref_expr(r) {
364 Some(re) => Tok::Ref(re),
365 None => Tok::Other(r.written.clone()),
366 }
367 }
368 Kind::Name => Tok::Name {
369 name: r.part.clone(),
370 sheet: (!r.sheet_name.is_empty()).then(|| r.sheet_name.clone()),
371 },
372 Kind::Broken => Tok::Err(ErrorKind::Ref),
373 Kind::Structured | Kind::External | Kind::Spill | Kind::Unreadable => {
374 Tok::Other(r.written.clone())
375 }
376 });
377 i += r.span.max(1);
378 continue;
379 }
380 let c = s[i];
381 if c.is_whitespace() {
382 i += 1;
383 } else if c == '"' {
384 let (text, end) = read_string(&s, i);
385 toks.push(Tok::Str(text));
386 i = end;
387 } else if c.is_ascii_digit()
388 || (c == '.' && s.get(i + 1).is_some_and(|d| d.is_ascii_digit()))
389 {
390 let (tok, end) = read_number(&s, i);
391 toks.push(tok);
392 i = end;
393 } else if c == '#' {
394 let mut k = i + 1;
395 while k < s.len() && (s[k].is_ascii_alphanumeric() || s[k] == '/' || s[k] == '_') {
396 k += 1;
397 }
398 if matches!(s.get(k), Some('!') | Some('?')) {
399 k += 1;
400 }
401 let tok: String = s[i..k].iter().collect();
402 toks.push(match ErrorKind::parse(&tok) {
403 Some(kind) => Tok::Err(kind),
404 None => Tok::Other(tok),
405 });
406 i = k;
407 } else if c.is_ascii_alphabetic() || c == '_' {
408 let mut k = i;
409 while k < s.len() && is_ident_char(s[k]) {
410 k += 1;
411 }
412 let tok: String = s[i..k].iter().collect();
413 if s.get(k) == Some(&'(') {
414 toks.push(Tok::Func(tok.to_ascii_uppercase()));
415 } else {
416 match tok.to_ascii_lowercase().as_str() {
417 "true" => toks.push(Tok::Bool(true)),
418 "false" => toks.push(Tok::Bool(false)),
419 _ => toks.push(Tok::Other(tok)),
420 }
421 }
422 i = k;
423 } else {
424 let two: String = s[i..(i + 2).min(s.len())].iter().collect();
425 let (tok, len) = match two.as_str() {
426 "<>" => (Tok::Op(BinOp::Ne), 2),
427 "<=" => (Tok::Op(BinOp::Le), 2),
428 ">=" => (Tok::Op(BinOp::Ge), 2),
429 _ => match c {
430 '<' => (Tok::Op(BinOp::Lt), 1),
431 '>' => (Tok::Op(BinOp::Gt), 1),
432 '=' => (Tok::Op(BinOp::Eq), 1),
433 '+' => (Tok::Op(BinOp::Add), 1),
434 '-' => (Tok::Op(BinOp::Sub), 1),
435 '*' => (Tok::Op(BinOp::Mul), 1),
436 '/' => (Tok::Op(BinOp::Div), 1),
437 '^' => (Tok::Op(BinOp::Pow), 1),
438 '&' => (Tok::Op(BinOp::Concat), 1),
439 '%' => (Tok::Percent, 1),
440 '(' => (Tok::LParen, 1),
441 ')' => (Tok::RParen, 1),
442 ',' => (Tok::Comma, 1),
443 other => (Tok::Other(other.to_string()), 1),
444 },
445 };
446 toks.push(tok);
447 i += len;
448 }
449 }
450 toks
451}
452
453fn read_string(s: &[char], i: usize) -> (String, usize) {
457 let mut out = String::new();
458 let mut k = i + 1;
459 while k < s.len() {
460 if s[k] == '"' {
461 if s.get(k + 1) == Some(&'"') {
462 out.push('"');
463 k += 2;
464 } else {
465 return (out, k + 1);
466 }
467 } else {
468 out.push(s[k]);
469 k += 1;
470 }
471 }
472 (out, k)
473}
474
475fn read_number(s: &[char], i: usize) -> (Tok, usize) {
478 let mut k = i;
479 while k < s.len() && s[k].is_ascii_digit() {
480 k += 1;
481 }
482 if k < s.len() && s[k] == '.' {
483 k += 1;
484 while k < s.len() && s[k].is_ascii_digit() {
485 k += 1;
486 }
487 }
488 if k < s.len() && (s[k] == 'e' || s[k] == 'E') {
489 let mut j = k + 1;
490 if j < s.len() && (s[j] == '+' || s[j] == '-') {
491 j += 1;
492 }
493 let first = j;
494 while j < s.len() && s[j].is_ascii_digit() {
495 j += 1;
496 }
497 if j > first {
498 k = j;
499 }
500 }
501 let text: String = s[i..k].iter().collect();
502 match text.parse::<f64>() {
503 Ok(v) if v.is_finite() => (Tok::Num(v), k),
504 _ => (Tok::Err(ErrorKind::Num), k),
505 }
506}
507
508pub const NESTING_LIMIT: u32 = 64;
516
517pub const LENGTH_LIMIT: usize = 8192;
521
522pub const TOO_DEEP: &str = "nesting deeper than 64";
524
525pub const TOO_LONG: &str = "longer than 8,192 characters";
527
528pub fn unread_refusal(cell: &str, construct: &str) -> Refusal {
533 match construct {
534 TOO_DEEP => raise(
535 "calc-formula-too-deep",
536 &[("cell", cell), ("limit", &NESTING_LIMIT.to_string())],
537 ),
538 TOO_LONG => raise(
539 "calc-formula-too-long",
540 &[("cell", cell), ("limit", "8,192")],
541 ),
542 _ => raise(
543 "calc-construct-not-read",
544 &[("cell", cell), ("construct", construct)],
545 ),
546 }
547}
548
549struct Parser {
550 toks: Vec<Tok>,
551 at: usize,
552 depth: u32,
554}
555
556impl Parser {
557 fn nested<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, String>) -> Result<T, String> {
559 if self.depth >= NESTING_LIMIT {
560 return Err(TOO_DEEP.to_string());
561 }
562 self.depth += 1;
563 let r = f(self);
564 self.depth -= 1;
565 r
566 }
567
568 fn peek(&self) -> Option<&Tok> {
569 self.toks.get(self.at)
570 }
571
572 fn take(&mut self) -> Option<Tok> {
573 let t = self.toks.get(self.at).cloned();
574 if t.is_some() {
575 self.at += 1;
576 }
577 t
578 }
579
580 fn peek_op(&self) -> Option<BinOp> {
581 match self.peek() {
582 Some(Tok::Op(op)) => Some(*op),
583 _ => None,
584 }
585 }
586
587 fn label(t: &Tok) -> String {
589 match t {
590 Tok::Other(s) => s.clone(),
591 Tok::Op(op) => match op {
592 BinOp::Add => "+",
593 BinOp::Sub => "-",
594 BinOp::Mul => "*",
595 BinOp::Div => "/",
596 BinOp::Pow => "^",
597 BinOp::Concat => "&",
598 BinOp::Eq => "=",
599 BinOp::Ne => "<>",
600 BinOp::Lt => "<",
601 BinOp::Gt => ">",
602 BinOp::Le => "<=",
603 BinOp::Ge => ">=",
604 }
605 .to_string(),
606 Tok::Percent => "%".to_string(),
607 Tok::LParen => "(".to_string(),
608 Tok::RParen => ")".to_string(),
609 Tok::Comma => ",".to_string(),
610 Tok::Num(v) => format!("{v}"),
611 Tok::Str(s) => format!("\"{s}\""),
612 Tok::Bool(b) => if *b { "TRUE" } else { "FALSE" }.to_string(),
613 Tok::Err(k) => k.text().to_string(),
614 Tok::Ref(r) => r.written.clone(),
615 Tok::Name { name, .. } => name.clone(),
616 Tok::Func(name) => name.clone(),
617 }
618 }
619
620 fn starts_operand(t: &Tok) -> bool {
621 matches!(
622 t,
623 Tok::Num(_)
624 | Tok::Str(_)
625 | Tok::Bool(_)
626 | Tok::Err(_)
627 | Tok::Ref(_)
628 | Tok::Name { .. }
629 | Tok::Func(_)
630 | Tok::LParen
631 )
632 }
633
634 fn comparison(&mut self) -> Result<Expr, String> {
635 let mut left = self.concat()?;
636 while let Some(op) = self.peek_op() {
637 if !matches!(
638 op,
639 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge
640 ) {
641 break;
642 }
643 self.at += 1;
644 let right = self.concat()?;
645 left = Expr::Binary(op, Box::new(left), Box::new(right));
646 }
647 Ok(left)
648 }
649
650 fn concat(&mut self) -> Result<Expr, String> {
651 let mut left = self.additive()?;
652 while self.peek_op() == Some(BinOp::Concat) {
653 self.at += 1;
654 let right = self.additive()?;
655 left = Expr::Binary(BinOp::Concat, Box::new(left), Box::new(right));
656 }
657 Ok(left)
658 }
659
660 fn additive(&mut self) -> Result<Expr, String> {
661 let mut left = self.term()?;
662 while let Some(op @ (BinOp::Add | BinOp::Sub)) = self.peek_op() {
663 self.at += 1;
664 let right = self.term()?;
665 left = Expr::Binary(op, Box::new(left), Box::new(right));
666 }
667 Ok(left)
668 }
669
670 fn term(&mut self) -> Result<Expr, String> {
671 let mut left = self.power()?;
672 while let Some(op @ (BinOp::Mul | BinOp::Div)) = self.peek_op() {
673 self.at += 1;
674 let right = self.power()?;
675 left = Expr::Binary(op, Box::new(left), Box::new(right));
676 }
677 Ok(left)
678 }
679
680 fn power(&mut self) -> Result<Expr, String> {
681 let mut left = self.unary()?;
682 while self.peek_op() == Some(BinOp::Pow) {
683 self.at += 1;
684 let right = self.unary()?;
685 left = Expr::Binary(BinOp::Pow, Box::new(left), Box::new(right));
686 }
687 Ok(left)
688 }
689
690 fn unary(&mut self) -> Result<Expr, String> {
691 match self.peek_op() {
692 Some(BinOp::Sub) => {
693 self.at += 1;
694 Ok(Expr::Neg(Box::new(self.nested(Self::unary)?)))
695 }
696 Some(BinOp::Add) => {
697 self.at += 1;
698 Ok(Expr::Pos(Box::new(self.nested(Self::unary)?)))
699 }
700 _ => self.postfix(),
701 }
702 }
703
704 fn postfix(&mut self) -> Result<Expr, String> {
705 let mut e = self.primary()?;
706 let mut percents = 0;
707 while self.peek() == Some(&Tok::Percent) {
708 if self.depth + percents >= NESTING_LIMIT {
711 return Err(TOO_DEEP.to_string());
712 }
713 percents += 1;
714 self.at += 1;
715 e = Expr::Percent(Box::new(e));
716 }
717 if let Some(t) = self.peek() {
718 if Self::starts_operand(t) {
719 return Err("intersection".to_string());
722 }
723 }
724 Ok(e)
725 }
726
727 fn primary(&mut self) -> Result<Expr, String> {
728 let Some(t) = self.take() else {
729 return Err("end of formula".to_string());
730 };
731 match t {
732 Tok::Num(v) => Ok(Expr::Number(v)),
733 Tok::Str(s) => Ok(Expr::Text(s)),
734 Tok::Bool(b) => Ok(Expr::Bool(b)),
735 Tok::Err(k) => Ok(Expr::Error(k)),
736 Tok::Ref(r) => Ok(Expr::Ref(r)),
737 Tok::Name { name, sheet } => Ok(Expr::Name { name, sheet }),
738 Tok::Func(name) => {
739 if self.take() != Some(Tok::LParen) {
740 return Err(name);
741 }
742 self.nested(|p| p.call(name))
743 }
744 Tok::LParen => {
745 let e = self.nested(Self::comparison)?;
746 match self.take() {
747 Some(Tok::RParen) => Ok(e),
748 Some(Tok::Comma) => Err("union".to_string()),
749 Some(other) => Err(Self::label(&other)),
750 None => Err("end of formula".to_string()),
751 }
752 }
753 other => Err(Self::label(&other)),
754 }
755 }
756
757 fn call(&mut self, name: String) -> Result<Expr, String> {
759 let mut args = Vec::new();
760 if self.peek() == Some(&Tok::RParen) {
761 self.at += 1;
762 return Ok(Expr::Call(name, args));
763 }
764 loop {
765 match self.peek() {
766 Some(Tok::Comma) | Some(Tok::RParen) => args.push(Expr::Empty),
767 _ => args.push(self.comparison()?),
768 }
769 match self.take() {
770 Some(Tok::Comma) => continue,
771 Some(Tok::RParen) => break,
772 Some(other) => return Err(Self::label(&other)),
773 None => return Err("end of formula".to_string()),
774 }
775 }
776 Ok(Expr::Call(name, args))
777 }
778}
779
780pub fn parse(text: &str) -> Result<Expr, String> {
784 if text.strip_prefix('=').unwrap_or(text).chars().count() > LENGTH_LIMIT {
785 return Err(TOO_LONG.to_string());
786 }
787 let mut p = Parser {
788 toks: tokenize(text),
789 at: 0,
790 depth: 0,
791 };
792 if p.toks.is_empty() {
793 return Err("end of formula".to_string());
794 }
795 let e = p.comparison()?;
796 match p.peek() {
797 None => Ok(e),
798 Some(t) => Err(Parser::label(t)),
799 }
800}
801
802pub fn calls(text: &str) -> Vec<String> {
806 tokenize(text)
807 .into_iter()
808 .filter_map(|t| match t {
809 Tok::Func(name) => Some(name),
810 _ => None,
811 })
812 .collect()
813}
814
815#[cfg(test)]
816mod tests {
817 use super::*;
818
819 fn cell(written: &str, row: u32, col: u32) -> Expr {
820 Expr::Ref(RefExpr {
821 sheet: None,
822 last_sheet: None,
823 top: row,
824 left: col,
825 bottom: row,
826 right: col,
827 written: written.to_string(),
828 shape: Shape::Cell,
829 corners: Corners {
830 row1: row,
831 col1: col,
832 row2: 0,
833 col2: 0,
834 row_abs1: false,
835 col_abs1: false,
836 row_abs2: false,
837 col_abs2: false,
838 },
839 found: None,
840 })
841 }
842
843 #[test]
844 fn literals_and_references_are_read() {
845 assert_eq!(parse("=5"), Ok(Expr::Number(5.0)));
846 assert_eq!(parse("1.5e3"), Ok(Expr::Number(1500.0)));
847 assert_eq!(parse(".5"), Ok(Expr::Number(0.5)));
848 assert_eq!(parse("=\"a\"\"b\""), Ok(Expr::Text("a\"b".to_string())));
849 assert_eq!(parse("=TRUE"), Ok(Expr::Bool(true)));
850 assert_eq!(parse("=false"), Ok(Expr::Bool(false)));
851 assert_eq!(parse("=#N/A"), Ok(Expr::Error(ErrorKind::NA)));
852 assert_eq!(parse("=#REF!"), Ok(Expr::Error(ErrorKind::Ref)));
853 assert_eq!(parse("=B2"), Ok(cell("B2", 2, 2)));
854 assert_eq!(
855 parse("=Rate"),
856 Ok(Expr::Name {
857 name: "Rate".to_string(),
858 sheet: None
859 })
860 );
861 assert_eq!(
862 parse("=Model!Local"),
863 Ok(Expr::Name {
864 name: "Local".to_string(),
865 sheet: Some("Model".to_string())
866 })
867 );
868 let r = match parse("='Q1 Data'!A:A").unwrap() {
869 Expr::Ref(r) => r,
870 other => panic!("{other:?}"),
871 };
872 assert_eq!(r.sheet.as_deref(), Some("Q1 Data"));
873 assert_eq!((r.top, r.left, r.bottom, r.right), (1, 1, MAX_ROW, 1));
874 let span = match parse("=Model:Scratch!B1").unwrap() {
875 Expr::Ref(r) => r,
876 other => panic!("{other:?}"),
877 };
878 assert_eq!(span.sheet.as_deref(), Some("Model"));
879 assert_eq!(span.last_sheet.as_deref(), Some("Scratch"));
880 assert_eq!((span.top, span.left, span.bottom, span.right), (1, 2, 1, 2));
881 assert!(
882 !span.is_cell(),
883 "a span is one cell on each of its sheets, not one cell"
884 );
885 }
886
887 #[test]
888 fn precedence_is_excel_s() {
889 assert_eq!(
891 parse("=-2^2"),
892 Ok(Expr::Binary(
893 BinOp::Pow,
894 Box::new(Expr::Neg(Box::new(Expr::Number(2.0)))),
895 Box::new(Expr::Number(2.0))
896 ))
897 );
898 assert_eq!(
900 parse("=1+2*3"),
901 Ok(Expr::Binary(
902 BinOp::Add,
903 Box::new(Expr::Number(1.0)),
904 Box::new(Expr::Binary(
905 BinOp::Mul,
906 Box::new(Expr::Number(2.0)),
907 Box::new(Expr::Number(3.0))
908 ))
909 ))
910 );
911 assert_eq!(
912 parse("=2^3^2"),
913 Ok(Expr::Binary(
914 BinOp::Pow,
915 Box::new(Expr::Binary(
916 BinOp::Pow,
917 Box::new(Expr::Number(2.0)),
918 Box::new(Expr::Number(3.0))
919 )),
920 Box::new(Expr::Number(2.0))
921 ))
922 );
923 assert_eq!(
925 parse("=1&2=\"12\""),
926 Ok(Expr::Binary(
927 BinOp::Eq,
928 Box::new(Expr::Binary(
929 BinOp::Concat,
930 Box::new(Expr::Number(1.0)),
931 Box::new(Expr::Number(2.0))
932 )),
933 Box::new(Expr::Text("12".to_string()))
934 ))
935 );
936 assert_eq!(
937 parse("=50%"),
938 Ok(Expr::Percent(Box::new(Expr::Number(50.0))))
939 );
940 assert_eq!(
941 parse("=(1+2)*3").map(|e| matches!(e, Expr::Binary(BinOp::Mul, ..))),
942 Ok(true)
943 );
944 }
945
946 #[test]
947 fn calls_take_their_arguments_empty_ones_included() {
948 assert_eq!(
949 parse("=IF(B2>3,\"big\",\"small\")"),
950 Ok(Expr::Call(
951 "IF".to_string(),
952 vec![
953 Expr::Binary(
954 BinOp::Gt,
955 Box::new(cell("B2", 2, 2)),
956 Box::new(Expr::Number(3.0))
957 ),
958 Expr::Text("big".to_string()),
959 Expr::Text("small".to_string()),
960 ]
961 ))
962 );
963 assert_eq!(
964 parse("=sum(A1,,2)"),
965 Ok(Expr::Call(
966 "SUM".to_string(),
967 vec![cell("A1", 1, 1), Expr::Empty, Expr::Number(2.0)]
968 ))
969 );
970 assert_eq!(parse("=PI()"), Ok(Expr::Call("PI".to_string(), vec![])));
971 assert_eq!(parse("=row()"), Ok(Expr::Call("ROW".to_string(), vec![])));
972 assert_eq!(
973 calls("=IF(SUM(A1:A3)>0,INDIRECT(\"B1\"),offset(A1,1,0))"),
974 vec!["IF", "SUM", "INDIRECT", "OFFSET"]
975 );
976 assert_eq!(calls("=\"SUM(\"&A1"), Vec::<String>::new());
977 }
978
979 #[test]
980 fn a_construct_this_slice_does_not_read_is_named_as_written() {
981 assert_eq!(parse("={1,2;3,4}"), Err("{".to_string()));
982 assert_eq!(parse("=@A1:A3"), Err("@".to_string()));
983 assert_eq!(parse("=A1 B1"), Err("intersection".to_string()));
984 assert_eq!(parse("=SUM((A1,B2))"), Err("union".to_string()));
985 assert_eq!(parse("=A1#"), Err("A1#".to_string()));
986 assert_eq!(
987 parse("=SUM(Sales[Amount])"),
988 Err("Sales[Amount]".to_string())
989 );
990 assert_eq!(
991 parse("=[Rates.xlsx]Sheet1!A1"),
992 Err("[Rates.xlsx]Sheet1!A1".to_string())
993 );
994 assert_eq!(parse("=#SPILL!"), Err("#SPILL!".to_string()));
995 assert_eq!(parse("="), Err("end of formula".to_string()));
996 assert_eq!(parse("=1+"), Err("end of formula".to_string()));
997 assert_eq!(parse("=)"), Err(")".to_string()));
998 }
999
1000 #[test]
1001 fn excel_s_two_limits_are_labels_of_their_own() {
1002 let wrap =
1005 |n: usize, open: &str, close: &str| format!("={}1{}", open.repeat(n), close.repeat(n));
1006 for (open, close) in [("(", ")"), ("ABS(", ")"), ("-", ""), ("+", ""), ("", "%")] {
1007 assert!(
1008 parse(&wrap(64, open, close)).is_ok(),
1009 "{open}...{close} at 64"
1010 );
1011 assert_eq!(
1012 parse(&wrap(65, open, close)),
1013 Err(TOO_DEEP.to_string()),
1014 "{open}...{close} at 65"
1015 );
1016 }
1017 let chain = format!("={}11", "1+".repeat(4095));
1020 assert_eq!(chain.len() - 1, LENGTH_LIMIT);
1021 assert!(parse(&chain).is_ok());
1022 assert_eq!(parse(&format!("{chain}1")), Err(TOO_LONG.to_string()));
1023 }
1024}