//! Formula syntax tree. use std::sync::Arc; use gridcraft_core::{CellError, CellRef, MAX_COLS, MAX_ROWS, RangeRef}; use serde::{Deserialize, Serialize}; /// One end of a reference, with `$` anchors. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Anchor { pub row: u32, pub col: u32, pub row_abs: bool, pub col_abs: bool, } impl Anchor { pub fn cell(&self) -> CellRef { CellRef::new(self.row, self.col) } } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum RefKind { Cell(Anchor), Range(Anchor, Anchor), /// Whole rows `3:5` (row, abs) pairs. Rows(u32, bool, u32, bool), /// Whole columns `A:C`. Cols(u32, bool, u32, bool), } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum SheetSel { /// Unqualified: the formula's own sheet. Current, Named(String), /// 3-D span `Sheet1:Sheet3`. Span(String, String), } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Reference { pub sheet: SheetSel, pub kind: RefKind, } impl Reference { pub fn range(&self) -> RangeRef { match &self.kind { RefKind::Cell(a) => RangeRef::cell(a.cell()), RefKind::Range(a, b) => RangeRef::new(a.cell(), b.cell()), RefKind::Rows(r0, _, r1, _) => RangeRef::rows((*r0).min(*r1), (*r0).max(*r1)), RefKind::Cols(c0, _, c1, _) => RangeRef::cols((*c0).min(*c1), (*c0).max(*c1)), } } pub fn sheet_name(&self) -> Option<&str> { match &self.sheet { SheetSel::Named(s) => Some(s), _ => None, } } } /// Structured reference `Table1[[#Headers],[Col1]:[Col3]]`, `[@Col]`. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct StructRef { /// Empty for `[@Col]` inside the table itself. pub table: String, pub specifiers: Vec, pub col_start: Option, pub col_end: Option, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum StructItem { All, Data, Headers, Totals, ThisRow, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum UnOp { Neg, Plus, Percent, /// Implicit intersection `@`. At, /// Spill range operator `A1#`: the whole spill range of the dynamic array anchored at the /// cell (`ANCHORARRAY` in files). Spill, /// Trim reference `A1.:E10` / `A1:.E10` / `A1.:.E10` around a range: the range without its /// blank leading, trailing or outer rows and columns (`TRIMRANGE` with both trim codes set). Trim(TrimMode), } /// Which blank edges a trim reference drops. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum TrimMode { /// `.:` — leading blank rows and columns. Leading, /// `:.` — trailing blank rows and columns. Trailing, /// `.:.` — both. All, } impl TrimMode { pub fn symbol(self) -> &'static str { match self { TrimMode::Leading => ".:", TrimMode::Trailing => ":.", TrimMode::All => ".:.", } } /// The equivalent `TRIMRANGE` trim_rows / trim_cols code. pub fn code(self) -> u8 { match self { TrimMode::Leading => 1, TrimMode::Trailing => 2, TrimMode::All => 3, } } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum BinOp { Add, Sub, Mul, Div, Pow, Concat, Eq, Ne, Lt, Gt, Le, Ge, /// `:` between two references. Range, /// `,` inside parentheses. Union, /// space between two references. Intersect, } impl BinOp { pub fn symbol(&self) -> &'static str { match self { BinOp::Add => "+", BinOp::Sub => "-", BinOp::Mul => "*", BinOp::Div => "/", BinOp::Pow => "^", BinOp::Concat => "&", BinOp::Eq => "=", BinOp::Ne => "<>", BinOp::Lt => "<", BinOp::Gt => ">", BinOp::Le => "<=", BinOp::Ge => ">=", BinOp::Range => ":", BinOp::Union => ",", BinOp::Intersect => " ", } } /// Binding power (higher binds tighter). pub fn precedence(&self) -> u8 { match self { BinOp::Range => 90, BinOp::Intersect => 85, BinOp::Union => 80, BinOp::Pow => 50, BinOp::Mul | BinOp::Div => 40, BinOp::Add | BinOp::Sub => 30, BinOp::Concat => 20, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => 10, } } } /// A formula's syntax tree. /// /// A flat operator chain such as `=A1+A2+…` nests one [`Expr::Binary`] per operator on its /// left, so a formula as long as Excel allows (8,192 characters) is about 4,000 levels deep. /// `Clone`, `PartialEq`, [`Expr::walk`] and [`Expr::map`] therefore follow that left spine in a /// loop and recurse only into right-hand operands and real nesting, which the parser bounds. #[derive(Debug, Serialize, Deserialize)] pub enum Expr { Number(f64), Text(Arc), Bool(bool), Error(CellError), /// Array constant `{1,2;3,4}` (rows of literal expressions). Array(Vec>), Ref(Reference), /// Defined name, table name or LET/LAMBDA parameter. Name(String), Struct(StructRef), Unary(UnOp, Box), Binary(BinOp, Box, Box), /// Function call; the name is upper-case without `_xlfn.`. Call(String, Vec), /// Calling the result of an expression, e.g. `LAMBDA(x,x+1)(2)`. Invoke(Box, Vec), /// An omitted argument `F(1,,3)`. Missing, Paren(Box), } impl Expr { /// Visits every node depth-first, parents before children, left to right. pub fn walk<'a>(&'a self, f: &mut dyn FnMut(&'a Expr)) { // An explicit stack: a long operator chain must not recurse once per operand. let mut pending = vec![self]; while let Some(e) = pending.pop() { f(e); match e { Expr::Array(rows) => pending.extend(rows.iter().flatten().rev()), Expr::Unary(_, e) | Expr::Paren(e) => pending.push(e), Expr::Binary(_, a, b) => { pending.push(b); pending.push(a); } Expr::Call(_, args) => pending.extend(args.iter().rev()), Expr::Invoke(c, args) => { pending.extend(args.iter().rev()); pending.push(c); } _ => {} } } } /// Rewrites every node bottom-up, children left to right before their parent. pub fn map(self, f: &mut dyn FnMut(Expr) -> Expr) -> Expr { // Unwind the left spine of a long operator chain into a list, then rebuild it from the // innermost operand outwards, in the order a recursive rewrite would visit the nodes. let mut spine = Vec::new(); let mut node = self; while let Expr::Binary(op, a, b) = node { spine.push((op, b)); node = *a; } let e = match node { Expr::Array(rows) => Expr::Array(rows.into_iter().map(|r| r.into_iter().map(|e| e.map(f)).collect()).collect()), Expr::Unary(op, e) => Expr::Unary(op, Box::new(e.map(f))), Expr::Paren(e) => Expr::Paren(Box::new(e.map(f))), Expr::Call(n, args) => Expr::Call(n, args.into_iter().map(|e| e.map(f)).collect()), Expr::Invoke(c, args) => Expr::Invoke(Box::new(c.map(f)), args.into_iter().map(|e| e.map(f)).collect()), other => other, }; let mut acc = f(e); while let Some((op, b)) = spine.pop() { let b = b.map(f); acc = f(Expr::Binary(op, Box::new(acc), Box::new(b))); } acc } /// All references in the formula. pub fn references(&self) -> Vec<&Reference> { let mut v = Vec::new(); self.walk(&mut |e| { if let Expr::Ref(r) = e { v.push(r); } }); v } /// Function names used (upper-case). pub fn functions(&self) -> Vec<&str> { let mut v = Vec::new(); self.walk(&mut |e| { if let Expr::Call(n, _) = e { v.push(n.as_str()); } }); v } } impl Clone for Expr { fn clone(&self) -> Self { // Copy the left spine of an operator chain in a loop (see [`Expr`]). let mut spine = Vec::new(); let mut node = self; while let Expr::Binary(op, a, b) = node { spine.push((*op, b)); node = a; } let mut acc = match node { Expr::Number(n) => Expr::Number(*n), Expr::Text(t) => Expr::Text(t.clone()), Expr::Bool(b) => Expr::Bool(*b), Expr::Error(e) => Expr::Error(*e), Expr::Array(rows) => Expr::Array(rows.clone()), Expr::Ref(r) => Expr::Ref(r.clone()), Expr::Name(n) => Expr::Name(n.clone()), Expr::Struct(s) => Expr::Struct(s.clone()), Expr::Unary(op, e) => Expr::Unary(*op, e.clone()), Expr::Binary(op, a, b) => Expr::Binary(*op, a.clone(), b.clone()), Expr::Call(n, args) => Expr::Call(n.clone(), args.clone()), Expr::Invoke(c, args) => Expr::Invoke(c.clone(), args.clone()), Expr::Missing => Expr::Missing, Expr::Paren(e) => Expr::Paren(e.clone()), }; while let Some((op, b)) = spine.pop() { acc = Expr::Binary(op, Box::new(acc), b.clone()); } acc } } impl PartialEq for Expr { fn eq(&self, other: &Self) -> bool { // Compare the left spines of operator chains in a loop (see [`Expr`]). The right-hand // operands are compared only once the whole spine matches, so a mismatch on the left // returns before any (possibly deep) right operand is walked. let (mut x, mut y) = (self, other); let mut right_operands = Vec::new(); let equal = loop { match (x, y) { (Expr::Binary(o1, a1, b1), Expr::Binary(o2, a2, b2)) => { if o1 != o2 { return false; } right_operands.push((b1, b2)); x = a1; y = a2; } (Expr::Number(a), Expr::Number(b)) => break a == b, (Expr::Text(a), Expr::Text(b)) => break a == b, (Expr::Bool(a), Expr::Bool(b)) => break a == b, (Expr::Error(a), Expr::Error(b)) => break a == b, (Expr::Array(a), Expr::Array(b)) => break a == b, (Expr::Ref(a), Expr::Ref(b)) => break a == b, (Expr::Name(a), Expr::Name(b)) => break a == b, (Expr::Struct(a), Expr::Struct(b)) => break a == b, (Expr::Unary(o1, a), Expr::Unary(o2, b)) => break o1 == o2 && a == b, (Expr::Call(n1, a), Expr::Call(n2, b)) => break n1 == n2 && a == b, (Expr::Invoke(c1, a), Expr::Invoke(c2, b)) => break c1 == c2 && a == b, (Expr::Missing, Expr::Missing) => break true, (Expr::Paren(a), Expr::Paren(b)) => break a == b, _ => break false, } }; equal && right_operands.into_iter().rev().all(|(b1, b2)| b1 == b2) } } pub(crate) fn clamp_anchor(row: i64, col: i64) -> Option<(u32, u32)> { if (0..MAX_ROWS as i64).contains(&row) && (0..MAX_COLS as i64).contains(&col) { Some((row as u32, col as u32)) } else { None } }