1pub mod ast;
2pub mod refs;
3pub mod tokens;
4
5pub use ast::{Expr, Span};
6pub use refs::{CellAddr, Ref};
7use ast::{BinaryOp, UnaryOp};
8use crate::types::ParseError;
9use nom::{IResult, character::complete::multispace0};
10use tokens::{bool_literal, dollar_cell_ref, identifier, number_literal, offset, string_literal};
11
12fn cell_ref_text(i: &str) -> IResult<&str, &str> {
21 dollar_cell_ref(i).or_else(|_| identifier(i))
22}
23
24struct Parser<'a> {
25 full: &'a str,
26}
27
28impl<'a> Parser<'a> {
29 fn new(full: &'a str) -> Self {
30 Self { full }
31 }
32
33 fn span(&self, before: &str, after: &str) -> Span {
34 let start = offset(self.full, before);
35 let end = offset(self.full, after);
36 Span::new(start, end - start)
37 }
38
39 fn parse_primary(&self, i: &'a str) -> IResult<&'a str, Expr> {
42 let i = multispace0(i)?.0;
43
44 if let Ok((rest, n)) = number_literal(i) {
46 return Ok((rest, Expr::Number(n, self.span(i, rest))));
47 }
48
49 if let Ok((rest, text)) = string_literal(i) {
51 return Ok((rest, Expr::Text(text, self.span(i, rest))));
52 }
53
54 if let Some(inner) = i.strip_prefix('{') {
56 let (rest, elems) = self.parse_array_elements(inner)?;
57 let rest = multispace0(rest)?.0;
58 if let Some(after) = rest.strip_prefix('}') {
59 return Ok((after, Expr::Array(elems, self.span(i, after))));
60 }
61 return Err(nom::Err::Error(nom::error::Error::new(
62 rest,
63 nom::error::ErrorKind::Char,
64 )));
65 }
66
67 if let Some(inner) = i.strip_prefix('(') {
69 let inner = multispace0(inner)?.0;
74 let (rest, expr) = self.parse_comparison(inner)?;
75 let rest = multispace0(rest)?.0;
76 if let Some(after) = rest.strip_prefix(')') {
77 return Ok((after, expr));
78 }
79 return Err(nom::Err::Error(nom::error::Error::new(
80 rest,
81 nom::error::ErrorKind::Char,
82 )));
83 }
84
85 if let Ok((rest, b)) = bool_literal(i) {
87 return Ok((rest, Expr::Bool(b, self.span(i, rest))));
88 }
89
90 if i.starts_with('\'') {
92 return self.parse_quoted_sheet_ref(i);
93 }
94
95 if let Ok((rest, span)) = dollar_cell_ref(i) {
101 let rest_ws = multispace0(rest)?.0;
102 if let Some(after_colon) = rest_ws.strip_prefix(':') {
103 if let Ok((rest2, end_span)) = cell_ref_text(after_colon) {
104 if CellAddr::parse(end_span).is_some() {
105 let range_name = format!("{}:{}", span, end_span);
106 return Ok((rest2, Expr::Variable(range_name, self.span(i, rest2))));
107 }
108 }
109 }
110 return Ok((rest, Expr::Variable(span.to_string(), self.span(i, rest))));
111 }
112
113 if let Ok((rest, name)) = identifier(i) {
115 if let Some(after_bang) = rest.strip_prefix('!') {
118 return self.parse_ref_body(i, name.to_string(), after_bang);
119 }
120 let rest_ws = multispace0(rest)?.0;
121 if let Some(args_input) = rest_ws.strip_prefix('(') {
122 let (rest2, args) = self.parse_arg_list(args_input)?;
124 let rest2 = multispace0(rest2)?.0;
125 if let Some(after_close) = rest2.strip_prefix(')') {
126 let func_expr = Expr::FunctionCall {
127 name: name.to_uppercase(),
128 args,
129 span: self.span(i, after_close),
130 };
131 let after_ws = multispace0(after_close)?.0;
133 if let Some(call_input) = after_ws.strip_prefix('(') {
134 let (rest3, call_args) = self.parse_arg_list(call_input)?;
135 let rest3 = multispace0(rest3)?.0;
136 if let Some(after) = rest3.strip_prefix(')') {
137 return Ok((after, Expr::Apply {
138 func: Box::new(func_expr),
139 call_args,
140 span: self.span(i, after),
141 }));
142 }
143 return Err(nom::Err::Error(nom::error::Error::new(
144 rest3,
145 nom::error::ErrorKind::Char,
146 )));
147 }
148 return Ok((after_close, func_expr));
149 }
150 return Err(nom::Err::Error(nom::error::Error::new(
151 rest2,
152 nom::error::ErrorKind::Char,
153 )));
154 }
155 if CellAddr::parse(name).is_some() {
159 if let Some(after_colon) = rest_ws.strip_prefix(':') {
160 if let Ok((rest2, name2)) = cell_ref_text(after_colon) {
161 if CellAddr::parse(name2).is_some() {
162 let range_name = format!("{}:{}", name, name2);
163 return Ok((rest2, Expr::Variable(range_name, self.span(i, rest2))));
164 }
165 }
166 }
167 }
168 return Ok((rest, Expr::Variable(name.to_string(), self.span(i, rest))));
169 }
170
171 Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Alt)))
172 }
173
174 fn parse_ref_body(&self, start: &'a str, sheet: String, i: &'a str) -> IResult<&'a str, Expr> {
180 let err = || nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Tag));
181 let (rest, cell_text) = cell_ref_text(i).map_err(|_| err())?;
182 let addr = CellAddr::parse(cell_text).ok_or_else(err)?;
183 let rest_ws = multispace0(rest)?.0;
185 if let Some(after_colon) = rest_ws.strip_prefix(':') {
186 if let Ok((rest2, end_text)) = cell_ref_text(after_colon) {
187 if let Some(end) = CellAddr::parse(end_text) {
188 let r = Ref::Range { sheet: Some(sheet), start: addr, end };
189 return Ok((rest2, Expr::Reference(r, self.span(start, rest2))));
190 }
191 }
192 }
193 let r = Ref::Cell { sheet: Some(sheet), addr };
194 Ok((rest, Expr::Reference(r, self.span(start, rest))))
195 }
196
197 fn parse_quoted_sheet_ref(&self, i: &'a str) -> IResult<&'a str, Expr> {
200 let inner = &i[1..];
201 let mut sheet = String::new();
202 let mut idx = 0;
203 loop {
204 match inner[idx..].find('\'') {
205 None => {
207 return Err(nom::Err::Error(nom::error::Error::new(
208 i,
209 nom::error::ErrorKind::Char,
210 )));
211 }
212 Some(q) => {
213 sheet.push_str(&inner[idx..idx + q]);
214 let after = idx + q + 1;
215 if inner[after..].starts_with('\'') {
216 sheet.push('\'');
217 idx = after + 1;
218 } else {
219 idx = after;
220 break;
221 }
222 }
223 }
224 }
225 let rest = &inner[idx..];
226 if sheet.is_empty() {
227 return Err(nom::Err::Error(nom::error::Error::new(
228 i,
229 nom::error::ErrorKind::Char,
230 )));
231 }
232 match rest.strip_prefix('!') {
233 Some(after_bang) => self.parse_ref_body(i, sheet, after_bang),
234 None => Err(nom::Err::Error(nom::error::Error::new(
235 rest,
236 nom::error::ErrorKind::Char,
237 ))),
238 }
239 }
240
241 fn parse_arg_list(&self, i: &'a str) -> IResult<&'a str, Vec<Expr>> {
242 let mut args = Vec::new();
243 let mut rest = multispace0(i)?.0;
244
245 if rest.starts_with(')') {
246 return Ok((rest, args));
247 }
248
249 let ws = multispace0(rest)?.0;
251 if ws.starts_with(',') || ws.starts_with(')') {
252 args.push(Expr::Variable(String::new(), Span::new(0, 0)));
254 } else {
255 let (r, first) = self.parse_comparison(rest)?;
256 args.push(first);
257 rest = r;
258 }
259
260 loop {
261 rest = multispace0(rest)?.0;
262 if let Some(after_comma) = rest.strip_prefix(',') {
263 let after_ws = multispace0(after_comma)?.0;
264 if after_ws.starts_with(',') || after_ws.starts_with(')') {
265 args.push(Expr::Variable(String::new(), Span::new(0, 0)));
267 rest = after_comma;
268 } else {
269 let (r, arg) = self.parse_comparison(after_ws)?;
276 args.push(arg);
277 rest = r;
278 }
279 } else {
280 break;
281 }
282 }
283
284 Ok((rest, args))
285 }
286
287 fn parse_array_elements(&self, i: &'a str) -> IResult<&'a str, Vec<Expr>> {
288 let mut rows: Vec<Vec<Expr>> = Vec::new();
289 let mut current_row: Vec<Expr> = Vec::new();
290 let mut rest = multispace0(i)?.0;
291 if rest.starts_with('}') {
292 return Ok((rest, Vec::new())); }
294 let (r, first) = self.parse_comparison(rest)?;
295 current_row.push(first);
296 rest = r;
297 loop {
298 rest = multispace0(rest)?.0;
299 if let Some(after_comma) = rest.strip_prefix(',') {
300 let after_ws = multispace0(after_comma)?.0;
307 let (r, elem) = self.parse_comparison(after_ws)?;
308 current_row.push(elem);
309 rest = r;
310 } else if let Some(after_semi) = rest.strip_prefix(';') {
311 rows.push(std::mem::take(&mut current_row));
312 let after_ws = multispace0(after_semi)?.0;
315 let (r, elem) = self.parse_comparison(after_ws)?;
316 current_row.push(elem);
317 rest = r;
318 } else {
319 break;
320 }
321 }
322 rows.push(current_row);
323 if rows.len() == 1 {
325 return Ok((rest, rows.into_iter().next().unwrap()));
326 }
327 let row_exprs: Vec<Expr> = rows
333 .into_iter()
334 .map(|row_elems| {
335 let s = match (row_elems.first(), row_elems.last()) {
336 (Some(first), Some(last)) => {
337 let start = first.span().offset;
338 let end = last.span().offset + last.span().length;
339 Span::new(start, end - start)
340 }
341 _ => self.span(i, rest),
346 };
347 Expr::Array(row_elems, s)
348 })
349 .collect();
350 Ok((rest, row_exprs))
351 }
352
353 fn parse_postfix(&self, i: &'a str) -> IResult<&'a str, Expr> {
356 let (rest, expr) = self.parse_primary(i)?;
357 let rest_ws = multispace0(rest)?.0;
358 if let Some(after) = rest_ws.strip_prefix('%') {
359 return Ok((after, Expr::UnaryOp {
360 op: UnaryOp::Percent,
361 operand: Box::new(expr),
362 span: self.span(i, after),
363 }));
364 }
365 Ok((rest, expr))
366 }
367
368 fn parse_unary(&self, i: &'a str) -> IResult<&'a str, Expr> {
371 let i_ws = multispace0(i)?.0;
372 if let Some(after_minus) = i_ws.strip_prefix('-') {
373 let (rest, operand) = self.parse_unary(after_minus)?;
374 return Ok((rest, Expr::UnaryOp {
375 op: UnaryOp::Neg,
376 operand: Box::new(operand),
377 span: self.span(i_ws, rest),
378 }));
379 }
380 self.parse_postfix(i)
381 }
382
383 fn parse_power(&self, i: &'a str) -> IResult<&'a str, Expr> {
386 let (rest, left) = self.parse_unary(i)?;
387 let rest_ws = multispace0(rest)?.0;
388 if let Some(after_op) = rest_ws.strip_prefix('^') {
389 let (rest2, right) = self.parse_power(after_op)?;
390 return Ok((rest2, Expr::BinaryOp {
391 op: BinaryOp::Pow,
392 left: Box::new(left),
393 right: Box::new(right),
394 span: self.span(i, rest2),
395 }));
396 }
397 Ok((rest, left))
398 }
399
400 fn parse_multiplicative(&self, i: &'a str) -> IResult<&'a str, Expr> {
403 let (mut rest, mut left) = self.parse_power(i)?;
404 loop {
405 let ws = multispace0(rest)?.0;
406 let op = ws.strip_prefix('*').map(|after| (BinaryOp::Mul, after))
407 .or_else(|| ws.strip_prefix('/').map(|after| (BinaryOp::Div, after)));
408 match op {
409 None => break,
410 Some((op, after)) => {
411 let (r, right) = self.parse_power(after)?;
412 left = Expr::BinaryOp {
413 op,
414 span: self.span(i, r),
415 left: Box::new(left),
416 right: Box::new(right),
417 };
418 rest = r;
419 }
420 }
421 }
422 Ok((rest, left))
423 }
424
425 fn parse_additive(&self, i: &'a str) -> IResult<&'a str, Expr> {
428 let (mut rest, mut left) = self.parse_multiplicative(i)?;
429 loop {
430 let ws = multispace0(rest)?.0;
431 let op = ws.strip_prefix('+').map(|after| (BinaryOp::Add, after))
432 .or_else(|| ws.strip_prefix('-').map(|after| (BinaryOp::Sub, after)));
433 match op {
434 None => break,
435 Some((op, after)) => {
436 let (r, right) = self.parse_multiplicative(after)?;
437 left = Expr::BinaryOp {
438 op,
439 span: self.span(i, r),
440 left: Box::new(left),
441 right: Box::new(right),
442 };
443 rest = r;
444 }
445 }
446 }
447 Ok((rest, left))
448 }
449
450 fn parse_concat(&self, i: &'a str) -> IResult<&'a str, Expr> {
453 let (mut rest, mut left) = self.parse_additive(i)?;
454 loop {
455 let ws = multispace0(rest)?.0;
456 if let Some(after) = ws.strip_prefix('&') {
457 let (r, right) = self.parse_additive(after)?;
458 left = Expr::BinaryOp {
459 op: BinaryOp::Concat,
460 span: self.span(i, r),
461 left: Box::new(left),
462 right: Box::new(right),
463 };
464 rest = r;
465 } else {
466 break;
467 }
468 }
469 Ok((rest, left))
470 }
471
472 fn parse_comparison(&self, i: &'a str) -> IResult<&'a str, Expr> {
475 let (rest, left) = self.parse_concat(i)?;
476 let ws = multispace0(rest)?.0;
477
478 let op_result: Option<(BinaryOp, &'a str)> = if let Some(after) = ws.strip_prefix("<>") {
480 Some((BinaryOp::Ne, after))
481 } else if let Some(after) = ws.strip_prefix("<=") {
482 Some((BinaryOp::Le, after))
483 } else if let Some(after) = ws.strip_prefix(">=") {
484 Some((BinaryOp::Ge, after))
485 } else if let Some(after) = ws.strip_prefix('<') {
486 Some((BinaryOp::Lt, after))
487 } else if let Some(after) = ws.strip_prefix('>') {
488 Some((BinaryOp::Gt, after))
489 } else if let Some(after) = ws.strip_prefix('=') {
490 Some((BinaryOp::Eq, after))
491 } else {
492 None
493 };
494
495 if let Some((op, after)) = op_result {
496 let (r, right) = self.parse_concat(after)?;
497 return Ok((r, Expr::BinaryOp {
498 op,
499 span: self.span(i, r),
500 left: Box::new(left),
501 right: Box::new(right),
502 }));
503 }
504
505 Ok((rest, left))
506 }
507}
508
509#[deprecated(since = "0.7.0", note = "use Engine::sheets()/Engine::excel() and engine.parse() — engine flavor is required; see ADR 2026-04-27; removal target: 0.7.0 coordinated release")]
516pub fn parse(formula: &str) -> Result<Expr, ParseError> {
517 parse_formula(formula)
518}
519
520pub(crate) fn parse_formula(formula: &str) -> Result<Expr, ParseError> {
522 let input = formula.strip_prefix('=').unwrap_or(formula).trim();
523 let p = Parser::new(formula);
524 match p.parse_comparison(input) {
525 Ok((rest, expr)) => {
526 let rest = rest.trim();
527 if rest.is_empty() {
528 Ok(expr)
529 } else {
530 Err(ParseError {
531 message: format!("Unexpected input '{}'", rest),
532 position: offset(formula, rest),
533 })
534 }
535 }
536 Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(ParseError {
537 message: "Parse error".into(),
538 position: offset(formula, e.input),
539 }),
540 Err(nom::Err::Incomplete(_)) => Err(ParseError {
541 message: "Incomplete input".into(),
542 position: formula.len(),
543 }),
544 }
545}
546
547#[deprecated(since = "0.7.0", note = "use Engine::sheets()/Engine::excel() and engine.validate() — engine flavor is required; see ADR 2026-04-27; removal target: 0.7.0 coordinated release")]
549pub fn validate(formula: &str) -> Result<(), ParseError> {
550 parse_formula(formula).map(|_| ())
551}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556 use super::parse_formula as parse;
557 use crate::parser::ast::{BinaryOp, Expr, UnaryOp};
558
559 #[test]
560 fn parse_number_literal() {
561 let expr = parse("=42").unwrap();
562 assert!(matches!(expr, Expr::Number(n, _) if n == 42.0));
563 }
564
565 #[test]
566 fn parse_binary_add() {
567 let expr = parse("=1+2").unwrap();
568 assert!(matches!(expr, Expr::BinaryOp { op: BinaryOp::Add, .. }));
569 }
570
571 #[test]
572 fn parse_precedence() {
573 let expr = parse("=2+3*4").unwrap();
575 match expr {
576 Expr::BinaryOp { op: BinaryOp::Add, right, .. } => {
577 assert!(matches!(*right, Expr::BinaryOp { op: BinaryOp::Mul, .. }));
578 }
579 _ => panic!("Expected Add at top"),
580 }
581 }
582
583 #[test]
584 fn parse_function_call() {
585 let expr = parse("=SUM(1,2,3)").unwrap();
586 match expr {
587 Expr::FunctionCall { name, args, .. } => {
588 assert_eq!(name, "SUM");
589 assert_eq!(args.len(), 3);
590 }
591 _ => panic!("Expected FunctionCall"),
592 }
593 }
594
595 #[test]
596 fn parse_function_call_arg_spans_no_leading_space() {
597 let src = "=SUM(X1*2, X2*3, X3*4)";
600 let expr = parse(src).unwrap();
601 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
602 match expr {
603 Expr::FunctionCall { name, args, .. } => {
604 assert_eq!(name, "SUM");
605 assert_eq!(args.len(), 3);
606 assert_eq!(slice(args[0].span()), "X1*2");
607 assert_eq!(slice(args[1].span()), "X2*3");
608 assert_eq!(slice(args[2].span()), "X3*4");
609 }
610 _ => panic!("Expected FunctionCall"),
611 }
612 }
613
614 #[test]
615 fn parse_function_call_arg_spans_no_space_after_comma_unaffected() {
616 let src = "=SUM(X1*2,X2*3)";
620 let expr = parse(src).unwrap();
621 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
622 match expr {
623 Expr::FunctionCall { name, args, .. } => {
624 assert_eq!(name, "SUM");
625 assert_eq!(args.len(), 2);
626 assert_eq!(slice(args[0].span()), "X1*2");
627 assert_eq!(slice(args[1].span()), "X2*3");
628 }
629 _ => panic!("Expected FunctionCall"),
630 }
631 }
632
633 #[test]
634 fn parse_parenthesised_expr_span_no_leading_space() {
635 let src = "=( A1 + B1 )";
638 let expr = parse(src).unwrap();
639 assert!(matches!(expr, Expr::BinaryOp { .. }));
640 let sp = expr.span();
641 assert_eq!(&src[sp.offset..sp.offset + sp.length], "A1 + B1");
642 let src2 = "=( A1*B1 )";
644 let expr2 = parse(src2).unwrap();
645 let sp2 = expr2.span();
646 assert_eq!(&src2[sp2.offset..sp2.offset + sp2.length], "A1*B1");
647 }
648
649 #[test]
650 fn parse_function_call_nested_and_unary_arg_spans_unaffected() {
651 let src = "=SUM(1, MAX(2,3), 4)";
654 let expr = parse(src).unwrap();
655 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
656 match expr {
657 Expr::FunctionCall { name, args, .. } => {
658 assert_eq!(name, "SUM");
659 assert_eq!(args.len(), 3);
660 assert_eq!(slice(args[0].span()), "1");
661 assert_eq!(slice(args[1].span()), "MAX(2,3)");
662 assert_eq!(slice(args[2].span()), "4");
663 }
664 _ => panic!("Expected FunctionCall"),
665 }
666
667 let src2 = "=SUM(1, -2)";
668 let expr2 = parse(src2).unwrap();
669 let slice2 = |s: &Span| &src2[s.offset..s.offset + s.length];
670 match expr2 {
671 Expr::FunctionCall { name, args, .. } => {
672 assert_eq!(name, "SUM");
673 assert_eq!(args.len(), 2);
674 assert_eq!(slice2(args[0].span()), "1");
675 assert_eq!(slice2(args[1].span()), "-2");
676 }
677 _ => panic!("Expected FunctionCall"),
678 }
679 }
680
681 #[test]
682 fn parse_percent() {
683 let expr = parse("=50%").unwrap();
684 assert!(matches!(expr, Expr::UnaryOp { op: UnaryOp::Percent, .. }));
685 }
686
687 #[test]
688 fn parse_string_literal() {
689 let expr = parse("=\"hello\"").unwrap();
690 assert!(matches!(expr, Expr::Text(ref s, _) if s == "hello"));
691 }
692
693 #[test]
694 fn parse_concat_op() {
695 let expr = parse("=\"a\"&\"b\"").unwrap();
696 assert!(matches!(expr, Expr::BinaryOp { op: BinaryOp::Concat, .. }));
697 }
698
699 #[test]
700 #[allow(deprecated)]
701 fn validate_incomplete_fails() {
702 let err = validate("=SUM(1,").unwrap_err();
703 assert!(!err.message.is_empty());
704 }
705
706 #[test]
707 fn parse_nested() {
708 assert!(parse("=ROUND(SUM(1,2)*1.1, 1)").is_ok());
709 }
710
711 #[test]
712 fn parse_boolean() {
713 let expr = parse("=TRUE").unwrap();
714 assert!(matches!(expr, Expr::Bool(true, _)));
715 }
716
717 #[test]
718 fn parse_variable() {
719 let expr = parse("=myVar").unwrap();
720 assert!(matches!(expr, Expr::Variable(ref n, _) if n == "myVar"));
721 }
722
723 #[test]
724 fn parse_array_literal_numbers() {
725 let expr = parse("={1,2,3}").unwrap();
726 match expr {
727 Expr::Array(elems, _) => assert_eq!(elems.len(), 3),
728 _ => panic!("Expected Array"),
729 }
730 }
731
732 #[test]
733 fn parse_array_literal_mixed() {
734 let expr = parse("={1,\"hello\",TRUE}").unwrap();
735 assert!(matches!(expr, Expr::Array(_, _)));
736 }
737
738 #[test]
739 fn parse_array_literal_empty() {
740 let expr = parse("={}").unwrap();
741 assert!(matches!(expr, Expr::Array(ref e, _) if e.is_empty()));
742 }
743
744 #[test]
745 fn parse_array_literal_row_spans() {
746 let src = "={1,2;3,4}";
749 let expr = parse(src).unwrap();
750 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
751
752 let (rows, outer_span) = match &expr {
753 Expr::Array(rows, span) => (rows, span),
754 _ => panic!("Expected outer Array"),
755 };
756 assert_eq!(slice(outer_span), "{1,2;3,4}");
757 assert_eq!(rows.len(), 2);
758
759 let (row0_elems, row0_span) = match &rows[0] {
760 Expr::Array(elems, span) => (elems, span),
761 _ => panic!("Expected row 0 to be an Array"),
762 };
763 assert_eq!(slice(row0_span), "1,2");
764 assert_eq!(row0_elems.len(), 2);
765 assert_eq!(slice(row0_elems[0].span()), "1");
766 assert_eq!(slice(row0_elems[1].span()), "2");
767
768 let (row1_elems, row1_span) = match &rows[1] {
769 Expr::Array(elems, span) => (elems, span),
770 _ => panic!("Expected row 1 to be an Array"),
771 };
772 assert_eq!(slice(row1_span), "3,4");
773 assert_eq!(row1_elems.len(), 2);
774 assert_eq!(slice(row1_elems[0].span()), "3");
775 assert_eq!(slice(row1_elems[1].span()), "4");
776 }
777
778 #[test]
779 fn parse_array_literal_single_row_unaffected() {
780 let src = "={1,2,3}";
784 let expr = parse(src).unwrap();
785 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
786 match &expr {
787 Expr::Array(elems, span) => {
788 assert_eq!(slice(span), "{1,2,3}");
789 assert_eq!(elems.len(), 3);
790 assert_eq!(slice(elems[0].span()), "1");
791 assert_eq!(slice(elems[1].span()), "2");
792 assert_eq!(slice(elems[2].span()), "3");
793 for e in elems {
794 assert!(!matches!(e, Expr::Array(_, _)), "single-row elements must not be row-wrapped");
795 }
796 }
797 _ => panic!("Expected Array"),
798 }
799 }
800
801 #[test]
802 fn parse_array_literal_element_no_leading_space() {
803 let src = "={1; 2+3}";
808 let expr = parse(src).unwrap();
809 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
810
811 let rows = match &expr {
812 Expr::Array(rows, _) => rows,
813 _ => panic!("Expected outer Array"),
814 };
815 assert_eq!(rows.len(), 2);
816 let row1_elems = match &rows[1] {
817 Expr::Array(elems, _) => elems,
818 _ => panic!("Expected row 1 to be an Array"),
819 };
820 assert_eq!(row1_elems.len(), 1);
821 assert_eq!(slice(row1_elems[0].span()), "2+3");
822 }
823
824 #[test]
825 fn parse_array_literal_multi_row_element_no_leading_space() {
826 let src = "={1, 2+3; 4, 5*6}";
830 let expr = parse(src).unwrap();
831 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
832
833 let rows = match &expr {
834 Expr::Array(rows, _) => rows,
835 _ => panic!("Expected outer Array"),
836 };
837 assert_eq!(rows.len(), 2);
838
839 let row0_elems = match &rows[0] {
840 Expr::Array(elems, _) => elems,
841 _ => panic!("Expected row 0 to be an Array"),
842 };
843 assert_eq!(slice(row0_elems[0].span()), "1");
844 assert_eq!(slice(row0_elems[1].span()), "2+3");
845
846 let row1_elems = match &rows[1] {
847 Expr::Array(elems, _) => elems,
848 _ => panic!("Expected row 1 to be an Array"),
849 };
850 assert_eq!(slice(row1_elems[0].span()), "4");
851 assert_eq!(slice(row1_elems[1].span()), "5*6");
852 }
853
854 #[test]
855 fn parse_array_literal_single_element_rows() {
856 let src = "={1;2}";
859 let expr = parse(src).unwrap();
860 let slice = |s: &Span| &src[s.offset..s.offset + s.length];
861
862 let rows = match &expr {
863 Expr::Array(rows, span) => {
864 assert_eq!(slice(span), "{1;2}");
865 rows
866 }
867 _ => panic!("Expected outer Array"),
868 };
869 assert_eq!(rows.len(), 2);
870
871 let (row0_elems, row0_span) = match &rows[0] {
872 Expr::Array(elems, span) => (elems, span),
873 _ => panic!("Expected row 0 to be an Array"),
874 };
875 assert_eq!(slice(row0_span), "1");
876 assert_eq!(row0_elems.len(), 1);
877 assert_eq!(slice(row0_elems[0].span()), "1");
878
879 let (row1_elems, row1_span) = match &rows[1] {
880 Expr::Array(elems, span) => (elems, span),
881 _ => panic!("Expected row 1 to be an Array"),
882 };
883 assert_eq!(slice(row1_span), "2");
884 assert_eq!(row1_elems.len(), 1);
885 assert_eq!(slice(row1_elems[0].span()), "2");
886 }
887
888 #[test]
889 fn parse_array_in_function_call() {
890 let expr = parse("=SUM({1,2,3})").unwrap();
891 match expr {
892 Expr::FunctionCall { name, args, .. } => {
893 assert_eq!(name, "SUM");
894 assert_eq!(args.len(), 1);
895 assert!(matches!(args[0], Expr::Array(_, _)));
896 }
897 _ => panic!("Expected FunctionCall"),
898 }
899 }
900
901 #[test]
902 fn parse_power_right_assoc() {
903 let expr = parse("=2^3^2").unwrap();
905 match expr {
906 Expr::BinaryOp { op: BinaryOp::Pow, right, .. } => {
907 assert!(matches!(*right, Expr::BinaryOp { op: BinaryOp::Pow, .. }));
908 }
909 _ => panic!("Expected Pow at top"),
910 }
911 }
912}