Skip to main content

ravenrs_sheet/formula/
mod.rs

1//! The Excel formula language: tokenizer, parser, syntax tree, printer (A1 and R1C1) and
2//! reference adjustment for copy/fill and structural edits.
3#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
4#![forbid(unsafe_code)]
5
6pub mod adjust;
7pub mod ast;
8mod input;
9pub mod lexer;
10pub mod parser;
11pub mod printer;
12
13pub use ast::{Anchor, BinOp, Expr, RefKind, Reference, SheetSel, StructItem, StructRef, TrimMode, UnOp};
14pub use input::{FormulaLocale, parse_input, print_input};
15pub use parser::{MAX_FORMULA_LEN, ParseError, normalize_function_name, parse, parse_closing_parens, parse_name};
16pub use printer::{print, print_r1c1, quote_sheet};
17
18/// Whether two LET/LAMBDA names are the same: case-insensitive in any script, as in Excel
19/// (`dług` = `DŁUG`, `ß` = `ẞ`). Close to Unicode's default case folding: lower case, plus the
20/// folds lower-casing misses for letters a name may hold (`ß`/`ẞ` -> `ss`, `ſ` -> `s`, `ς` -> `σ`).
21/// Dotless `ı` stays distinct from `i`, as outside Turkic languages.
22pub fn same_name(a: &str, b: &str) -> bool {
23    if a.is_ascii() && b.is_ascii() {
24        return a.eq_ignore_ascii_case(b);
25    }
26    a == b || fold_name(a) == fold_name(b)
27}
28
29/// The case-folded form of a name: two names are [`same_name`] when their folded forms are equal.
30pub fn fold_name(s: &str) -> String {
31    s.chars()
32        .flat_map(char::to_lowercase)
33        .flat_map(|c| match c {
34            'ß' => ['s', 's'].into_iter().take(2),
35            'ſ' => ['s', 's'].into_iter().take(1),
36            'ς' => ['σ', 'σ'].into_iter().take(1),
37            c => [c, c].into_iter().take(1),
38        })
39        .collect()
40}
41
42#[cfg(test)]
43mod tests;