1use crate::formula::{Expr, ParseError, parser};
4
5#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
8pub enum FormulaLocale {
9 #[default]
10 En,
11 Es,
12}
13
14const SPANISH: &[(&str, &str)] = &[
15 ("SUM", "SUMA"),
16 ("AVERAGE", "PROMEDIO"),
17 ("IF", "SI"),
18 ("COUNT", "CONTAR"),
19 ("SUMIF", "SUMAR.SI"),
20 ("COUNTIF", "CONTAR.SI"),
21 ("VLOOKUP", "BUSCARV"),
22 ("TODAY", "HOY"),
23 ("NPV", "VNA"),
25 ("IRR", "TIR"),
26 ("PMT", "PAGO"),
27 ("PV", "VA"),
28 ("FV", "VF"),
29 ("TRUE", "VERDADERO"),
30 ("FALSE", "FALSO"),
31];
32
33impl FormulaLocale {
34 pub fn list_separator(self) -> char {
35 if self == Self::Es { ';' } else { ',' }
36 }
37 pub fn decimal_separator(self) -> char {
38 if self == Self::Es { ',' } else { '.' }
39 }
40 pub fn array_column_separator(self) -> char {
41 if self == Self::Es { '\\' } else { ',' }
42 }
43 pub fn function_name(self, canonical: &str) -> &str {
44 if self == Self::Es {
45 SPANISH.iter().find(|(en, _)| en.eq_ignore_ascii_case(canonical)).map(|(_, es)| *es).unwrap_or(canonical)
46 } else {
47 canonical
48 }
49 }
50 pub fn canonical_name(self, localized: &str) -> &str {
51 if self == Self::Es {
52 SPANISH.iter().find(|(_, es)| es.eq_ignore_ascii_case(localized)).map(|(en, _)| *en).unwrap_or(localized)
53 } else {
54 localized
55 }
56 }
57}
58
59pub fn parse_input(src: &str, locale: FormulaLocale, known_name: impl Fn(&str) -> bool) -> Result<Expr, ParseError> {
62 let mut expr = parser::parse_syntax(src, locale)?;
63 if locale == FormulaLocale::Es {
64 normalize(&mut expr, &known_name, &mut Bindings::default(), 0, false)?;
65 }
66 Ok(expr)
67}
68
69pub fn print_input(expr: &Expr, locale: FormulaLocale, known_name: impl Fn(&str) -> bool) -> Result<String, ParseError> {
72 let mut pending = vec![(expr, 0usize)];
76 while let Some((node, depth)) = pending.pop() {
77 if depth > parser::MAX_DEPTH {
78 return Err(ParseError { msg: "formula is nested too deeply".into(), pos: 0 });
79 }
80 match node {
81 Expr::Unary(_, child) | Expr::Paren(child) => pending.push((child, depth + 1)),
82 Expr::Binary(_, left, right) => {
83 pending.push((left, depth));
84 pending.push((right, depth + 1));
85 }
86 Expr::Call(_, args) => pending.extend(args.iter().map(|e| (e, depth + 1))),
87 Expr::Invoke(callee, args) => {
88 pending.push((callee, depth + 1));
89 pending.extend(args.iter().map(|e| (e, depth + 1)));
90 }
91 Expr::Array(rows) => pending.extend(rows.iter().flatten().map(|e| (e, depth + 1))),
92 _ => {}
93 }
94 }
95 if locale == FormulaLocale::En {
96 return Ok(crate::formula::print(expr));
97 }
98 let mut expr = expr.clone();
99 normalize(&mut expr, &known_name, &mut Bindings::default(), 0, true)?;
100 let canonical = crate::formula::print(&expr);
101 let tokens = crate::formula::lexer::tokenize(&canonical).map_err(|e| ParseError { msg: e.msg, pos: e.pos })?;
102 let mut out = String::new();
103 let mut array_depth = 0usize;
104 for token in tokens {
105 use crate::formula::lexer::Tok;
106 let text = canonical.get(token.start..token.end).unwrap_or("");
107 match token.tok {
108 Tok::LBrace => {
109 array_depth += 1;
110 out.push_str(text);
111 }
112 Tok::RBrace => {
113 array_depth = array_depth.saturating_sub(1);
114 out.push_str(text);
115 }
116 Tok::Number(_) => out.push_str(&text.replace('.', &locale.decimal_separator().to_string())),
117 Tok::Comma => out.push(if array_depth > 0 { locale.array_column_separator() } else { locale.list_separator() }),
118 _ => out.push_str(text),
119 }
120 }
121 Ok(out)
122}
123
124pub(crate) fn boolean_alias(name: &str) -> Option<bool> {
125 if name.eq_ignore_ascii_case("VERDADERO") {
126 Some(true)
127 } else if name.eq_ignore_ascii_case("FALSO") {
128 Some(false)
129 } else {
130 None
131 }
132}
133
134#[derive(Default)]
137struct Bindings {
138 stack: Vec<String>,
139 counts: std::collections::HashMap<String, usize>,
140}
141
142impl Bindings {
143 fn len(&self) -> usize {
144 self.stack.len()
145 }
146
147 fn push(&mut self, name: &str) {
148 let key = crate::formula::fold_name(name);
149 *self.counts.entry(key.clone()).or_default() += 1;
150 self.stack.push(key);
151 }
152
153 fn truncate(&mut self, len: usize) {
154 while self.stack.len() > len {
155 let Some(key) = self.stack.pop() else { break };
156 if let Some(n) = self.counts.get_mut(&key) {
157 *n -= 1;
158 if *n == 0 {
159 self.counts.remove(&key);
160 }
161 }
162 }
163 }
164
165 fn contains(&self, name: &str) -> bool {
166 self.counts.contains_key(&crate::formula::fold_name(name))
167 }
168}
169
170fn normalize(expr: &mut Expr, known_name: &impl Fn(&str) -> bool, bindings: &mut Bindings, depth: usize, display: bool) -> Result<(), ParseError> {
171 if depth > parser::MAX_DEPTH {
172 return Err(ParseError { msg: "formula is nested too deeply".into(), pos: 0 });
173 }
174 let bound = |name: &str, bindings: &Bindings| bindings.contains(name) || known_name(name);
175 match expr {
176 Expr::Bool(value) if display => {
177 let alias = if *value { "VERDADERO" } else { "FALSO" };
178 if !bound(alias, bindings) {
179 *expr = Expr::Name(alias.into());
180 }
181 }
182 Expr::Name(name) if !display && !bound(name, bindings) => {
183 if let Some(value) = boolean_alias(name) {
184 *expr = Expr::Bool(value);
185 }
186 }
187 Expr::Call(name, args) if name == "LET" && args.len() >= 3 && !args.len().is_multiple_of(2) => {
188 let saved = bindings.len();
189 if let Some((body, pairs)) = args.split_last_mut() {
190 for pair in pairs.chunks_mut(2) {
191 if let [declaration, value] = pair {
192 normalize(value, known_name, bindings, depth + 1, display)?;
194 if let Expr::Name(name) = declaration {
195 bindings.push(name);
196 }
197 }
198 }
199 normalize(body, known_name, bindings, depth + 1, display)?;
200 }
201 bindings.truncate(saved);
202 }
203 Expr::Call(name, args) if name == "LAMBDA" => {
204 let saved = bindings.len();
205 if let Some((body, params)) = args.split_last_mut() {
206 for param in params {
207 if let Expr::Name(name) = param {
208 bindings.push(name);
209 }
210 }
211 normalize(body, known_name, bindings, depth + 1, display)?;
212 }
213 bindings.truncate(saved);
214 }
215 Expr::Call(name, args) => {
216 let alias = if display { FormulaLocale::Es.function_name(name) } else { FormulaLocale::Es.canonical_name(name) };
217 if !bound(name, bindings) && (!display || !bound(alias, bindings)) {
218 *name = alias.to_string();
219 }
220 for arg in args {
221 normalize(arg, known_name, bindings, depth + 1, display)?;
222 }
223 }
224 Expr::Invoke(callee, args) => {
225 normalize(callee, known_name, bindings, depth + 1, display)?;
226 for arg in args {
227 normalize(arg, known_name, bindings, depth + 1, display)?;
228 }
229 }
230 Expr::Unary(_, child) | Expr::Paren(child) => normalize(child, known_name, bindings, depth + 1, display)?,
231 Expr::Binary(..) => {
232 let mut node = expr;
235 while let Expr::Binary(_, left, right) = node {
236 normalize(right, known_name, bindings, depth + 1, display)?;
237 node = left;
238 }
239 normalize(node, known_name, bindings, depth, display)?;
240 }
241 Expr::Array(rows) if display => {
242 for row in rows {
243 for child in row {
244 normalize(child, known_name, bindings, depth + 1, display)?;
245 }
246 }
247 }
248 _ => {}
251 }
252 Ok(())
253}