1use std::fmt::Write;
4
5use crate::base::{CellRef, number_to_text};
6
7use crate::formula::ast::{Anchor, BinOp, Expr, RefKind, Reference, SheetSel, StructItem, StructRef, UnOp};
8
9pub use crate::base::{quote_sheet, sheet_needs_quotes as needs_quotes};
10
11fn sheet_prefix(s: &SheetSel) -> String {
12 match s {
13 SheetSel::Current => String::new(),
14 SheetSel::Named(n) => format!("{}!", quote_sheet(n)),
15 SheetSel::Span(a, b) => {
16 let joined = format!("{a}:{b}");
17 if needs_quotes(a) || needs_quotes(b) { format!("'{}'!", joined.replace('\'', "''")) } else { format!("{joined}!") }
18 }
19 }
20}
21
22fn push_anchor_a1(s: &mut String, a: &Anchor) {
23 if a.col_abs {
24 s.push('$');
25 }
26 push_col(s, a.col);
27 if a.row_abs {
28 s.push('$');
29 }
30 let _ = write!(s, "{}", a.row as u64 + 1);
31}
32
33fn push_col(s: &mut String, col: u32) {
35 let mut letters = [0u8; 8];
36 let mut i = letters.len();
37 let mut n = col as u64 + 1;
38 while n > 0 && i > 0 {
39 i -= 1;
40 if let Some(l) = letters.get_mut(i) {
41 *l = b'A' + ((n - 1) % 26) as u8;
42 }
43 n = (n - 1) / 26;
44 }
45 for &l in letters.get(i..).unwrap_or_default() {
46 s.push(char::from(l));
47 }
48}
49
50pub fn reference_a1(r: &Reference) -> String {
51 range_a1(r, ":")
52}
53
54fn range_a1(r: &Reference, sep: &str) -> String {
56 let mut s = String::new();
57 push_range_a1(&mut s, r, sep);
58 s
59}
60
61fn push_range_a1(s: &mut String, r: &Reference, sep: &str) {
63 match &r.sheet {
64 SheetSel::Current => {}
65 SheetSel::Named(n) => {
66 s.push_str("e_sheet(n));
67 s.push('!');
68 }
69 sheet @ SheetSel::Span(_, _) => s.push_str(&sheet_prefix(sheet)),
70 }
71 let dollar = |abs: bool| if abs { "$" } else { "" };
72 match &r.kind {
73 RefKind::Cell(a) => push_anchor_a1(s, a),
74 RefKind::Range(a, b) => {
75 push_anchor_a1(s, a);
76 s.push_str(sep);
77 push_anchor_a1(s, b);
78 }
79 RefKind::Rows(r0, a0, r1, a1) => {
80 let _ = write!(s, "{}{}{sep}{}{}", dollar(*a0), r0 + 1, dollar(*a1), r1 + 1);
81 }
82 RefKind::Cols(c0, a0, c1, a1) => {
83 s.push_str(dollar(*a0));
84 push_col(s, *c0);
85 s.push_str(sep);
86 s.push_str(dollar(*a1));
87 push_col(s, *c1);
88 }
89 }
90}
91
92fn r1c1_part(letter: char, v: u32, abs: bool, base: u32) -> String {
93 if abs {
94 format!("{letter}{}", v + 1)
95 } else {
96 let d = v as i64 - base as i64;
97 if d == 0 { letter.to_string() } else { format!("{letter}[{d}]") }
98 }
99}
100
101fn anchor_r1c1(a: &Anchor, at: CellRef) -> String {
102 format!("{}{}", r1c1_part('R', a.row, a.row_abs, at.row), r1c1_part('C', a.col, a.col_abs, at.col))
103}
104
105pub fn reference_r1c1(r: &Reference, at: CellRef) -> String {
106 range_r1c1(r, at, ":")
107}
108
109fn range_r1c1(r: &Reference, at: CellRef, sep: &str) -> String {
112 let body = match &r.kind {
113 RefKind::Cell(a) => anchor_r1c1(a, at),
114 RefKind::Range(a, b) => format!("{}{sep}{}", anchor_r1c1(a, at), anchor_r1c1(b, at)),
115 RefKind::Rows(r0, a0, r1, a1) => {
116 let (x, y) = (r1c1_part('R', *r0, *a0, at.row), r1c1_part('R', *r1, *a1, at.row));
117 if x == y && sep == ":" { x } else { format!("{x}{sep}{y}") }
118 }
119 RefKind::Cols(c0, a0, c1, a1) => {
120 let (x, y) = (r1c1_part('C', *c0, *a0, at.col), r1c1_part('C', *c1, *a1, at.col));
121 if x == y && sep == ":" { x } else { format!("{x}{sep}{y}") }
122 }
123 };
124 format!("{}{}", sheet_prefix(&r.sheet), body)
125}
126
127pub fn struct_ref(s: &StructRef) -> String {
128 write_struct_ref(s, true)
129}
130
131pub fn struct_ref_file(s: &StructRef) -> String {
134 write_struct_ref(s, false)
135}
136
137fn sheet_split(n: &str) -> Option<(&str, &str)> {
140 let mut depth = 0usize;
141 let mut escaped = false;
142 let mut at = None;
143 for (i, c) in n.char_indices() {
144 if escaped {
145 escaped = false;
146 continue;
147 }
148 match c {
149 '\'' if depth > 0 => escaped = true,
150 '[' => depth = depth.saturating_add(1),
151 ']' => depth = depth.saturating_sub(1),
152 '!' if depth == 0 => at = Some(i),
153 _ => {}
154 }
155 }
156 at.and_then(|i| Some((n.get(..i)?, n.get(i + 1..)?)))
157}
158
159fn write_struct_ref(s: &StructRef, at_shorthand: bool) -> String {
160 let esc = |c: &str| c.replace('\'', "''").replace('[', "'[").replace(']', "']").replace('#', "'#");
161 let item = |i: &StructItem| match i {
162 StructItem::All => "#All",
163 StructItem::Data => "#Data",
164 StructItem::Headers => "#Headers",
165 StructItem::Totals => "#Totals",
166 StructItem::ThisRow => "#This Row",
167 };
168 let this_row = at_shorthand && s.specifiers == [StructItem::ThisRow];
169 let mut out = s.table.clone();
170 let simple_col = |c: &str| !c.chars().any(|ch| matches!(ch, ' ' | '[' | ']' | '#' | '\'' | ',' | ':'));
171 match (&s.col_start, &s.col_end) {
172 (None, None) if s.specifiers.is_empty() => out.push_str("[]"),
173 (None, None) if this_row => out.push_str("[@]"),
174 (None, None) if s.specifiers.len() == 1 => {
175 let _ = write!(out, "[{}]", s.specifiers.first().map(item).unwrap_or(""));
176 }
177 (Some(c), None) if s.specifiers.is_empty() => {
178 if c.trim() == c && !c.starts_with('@') {
180 let _ = write!(out, "[{}]", esc(c));
181 } else {
182 let _ = write!(out, "[[{}]]", esc(c));
183 }
184 }
185 (Some(c), None) if this_row => {
186 if simple_col(c) {
187 let _ = write!(out, "[@{}]", esc(c));
188 } else {
189 let _ = write!(out, "[@[{}]]", esc(c));
190 }
191 }
192 _ => {
193 let mut parts: Vec<String> = s.specifiers.iter().map(|i| format!("[{}]", item(i))).collect();
194 match (&s.col_start, &s.col_end) {
195 (Some(a), Some(b)) => parts.push(format!("[{}]:[{}]", esc(a), esc(b))),
196 (Some(a), None) => parts.push(format!("[{}]", esc(a))),
197 _ => {}
198 }
199 let _ = write!(out, "[{}]", parts.join(","));
200 }
201 }
202 out
203}
204
205fn number(n: f64) -> String {
206 let text = number_to_text(n);
207 if text.parse::<f64>().is_ok_and(f64::is_finite) {
209 return text;
210 }
211 let exact = format!("{:E}", n);
212 match exact.split_once('E') {
213 Some((m, e)) if !e.starts_with('-') => format!("{m}E+{e}"),
214 _ => exact,
215 }
216}
217
218pub fn print(e: &Expr) -> String {
220 let mut s = String::new();
221 write_expr(&mut s, e, None);
222 s
223}
224
225pub fn print_r1c1(e: &Expr, at: CellRef) -> String {
227 let mut s = String::new();
228 write_expr(&mut s, e, Some(at));
229 s
230}
231
232fn write_expr(s: &mut String, e: &Expr, r1c1: Option<CellRef>) {
233 match e {
234 Expr::Number(n) => s.push_str(&number(*n)),
235 Expr::Text(t) => {
236 s.push('"');
237 s.push_str(&t.replace('"', "\"\""));
238 s.push('"');
239 }
240 Expr::Bool(b) => s.push_str(if *b { "TRUE" } else { "FALSE" }),
241 Expr::Error(err) => s.push_str(err.as_str()),
242 Expr::Array(rows) => {
243 s.push('{');
244 for (ri, row) in rows.iter().enumerate() {
245 if ri > 0 {
246 s.push(';');
247 }
248 for (ci, el) in row.iter().enumerate() {
249 if ci > 0 {
250 s.push(',');
251 }
252 write_expr(s, el, r1c1);
253 }
254 }
255 s.push('}');
256 }
257 Expr::Ref(r) => match r1c1 {
258 Some(at) => s.push_str(&reference_r1c1(r, at)),
259 None => push_range_a1(s, r, ":"),
260 },
261 Expr::Name(n) => match sheet_split(n) {
262 Some((sheet, name)) => {
264 s.push_str("e_sheet(sheet));
265 s.push('!');
266 s.push_str(name);
267 }
268 _ => s.push_str(n),
269 },
270 Expr::Struct(st) => s.push_str(&struct_ref(st)),
271 Expr::Unary(op, x) => match op {
272 UnOp::Neg => {
273 s.push('-');
274 write_expr(s, x, r1c1);
275 }
276 UnOp::Plus => {
277 s.push('+');
278 write_expr(s, x, r1c1);
279 }
280 UnOp::At => {
281 s.push('@');
282 write_expr(s, x, r1c1);
283 }
284 UnOp::Percent => {
285 write_expr(s, x, r1c1);
286 s.push('%');
287 }
288 UnOp::Spill => {
289 write_expr(s, x, r1c1);
290 if !matches!(**x, Expr::Error(_)) {
292 s.push('#');
293 }
294 }
295 UnOp::Trim(mode) => match &**x {
296 Expr::Ref(r) if !matches!(r.kind, RefKind::Cell(_)) => match r1c1 {
297 Some(at) => s.push_str(&range_r1c1(r, at, mode.symbol())),
298 None => push_range_a1(s, r, mode.symbol()),
299 },
300 Expr::Binary(BinOp::Range, a, b) => {
301 write_expr(s, a, r1c1);
302 s.push_str(mode.symbol());
303 write_expr(s, b, r1c1);
304 }
305 Expr::Error(_) => write_expr(s, x, r1c1),
307 other => {
309 s.push_str("TRIMRANGE(");
310 write_expr(s, other, r1c1);
311 let _ = write!(s, ",{0},{0})", mode.code());
312 }
313 },
314 },
315 Expr::Binary(..) => {
316 let mut rest: Vec<(&BinOp, &Expr)> = Vec::new();
319 let mut first = e;
320 while let Expr::Binary(op, a, b) = first {
321 rest.push((op, b));
322 first = a;
323 }
324 write_expr(s, first, r1c1);
325 for (op, right) in rest.into_iter().rev() {
326 s.push_str(op.symbol());
327 write_expr(s, right, r1c1);
328 }
329 }
330 Expr::Call(name, args) => {
331 s.push_str(name);
332 s.push('(');
333 for (i, a) in args.iter().enumerate() {
334 if i > 0 {
335 s.push(',');
336 }
337 write_expr(s, a, r1c1);
338 }
339 s.push(')');
340 }
341 Expr::Invoke(c, args) => {
342 write_expr(s, c, r1c1);
343 s.push('(');
344 for (i, a) in args.iter().enumerate() {
345 if i > 0 {
346 s.push(',');
347 }
348 write_expr(s, a, r1c1);
349 }
350 s.push(')');
351 }
352 Expr::Missing => {}
353 Expr::Paren(x) => {
354 s.push('(');
355 write_expr(s, x, r1c1);
356 s.push(')');
357 }
358 }
359}