1use super::error::LexError;
27use super::span::{SourcePos, Span};
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum Expression {
32 Literal {
34 value: ExprLiteral,
36 span: Span,
38 },
39 Identifier {
41 name: String,
43 span: Span,
45 },
46 Call {
48 name: String,
50 args: Vec<Expression>,
52 span: Span,
54 },
55 Access {
57 target: Box<Expression>,
59 field: String,
61 span: Span,
63 },
64}
65
66#[derive(Debug, Clone, PartialEq)]
68pub enum ExprLiteral {
69 Int(i64),
71 Float(f64),
73 String(String),
75 Bool(bool),
77}
78
79impl std::fmt::Display for Expression {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 Expression::Literal { value, .. } => write!(f, "{}", value),
83 Expression::Identifier { name, .. } => write!(f, "{}", name),
84 Expression::Call { name, args, .. } => {
85 write!(f, "{}(", name)?;
86 for (i, arg) in args.iter().enumerate() {
87 if i > 0 {
88 write!(f, ", ")?;
89 }
90 write!(f, "{}", arg)?;
91 }
92 write!(f, ")")
93 }
94 Expression::Access { target, field, .. } => {
95 write!(f, "{}.{}", target, field)
96 }
97 }
98 }
99}
100
101impl std::fmt::Display for ExprLiteral {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 ExprLiteral::Int(n) => write!(f, "{}", n),
105 ExprLiteral::Float(n) => write!(f, "{}", n),
106 ExprLiteral::String(s) => {
107 write!(f, "\"")?;
109 for ch in s.chars() {
110 if ch == '"' {
111 write!(f, "\"\"")?;
112 } else {
113 write!(f, "{}", ch)?;
114 }
115 }
116 write!(f, "\"")
117 }
118 ExprLiteral::Bool(b) => write!(f, "{}", b),
119 }
120 }
121}
122
123pub fn parse_expression(s: &str) -> Result<Expression, LexError> {
135 let mut parser = ExprParser::new(s);
136 let expr = parser.parse_expr()?;
137 parser.skip_whitespace();
138 if parser.pos < parser.chars.len() {
139 return Err(LexError::InvalidToken {
140 message: format!(
141 "unexpected character '{}' at position {}",
142 parser.chars[parser.pos], parser.pos
143 ),
144 pos: SourcePos::default(),
145 });
146 }
147 Ok(expr)
148}
149
150pub fn parse_expression_token(s: &str) -> Result<Expression, LexError> {
154 if !s.starts_with("$(") {
155 return Err(LexError::InvalidToken {
156 message: "expression must start with $(".to_string(),
157 pos: SourcePos::default(),
158 });
159 }
160
161 let content = extract_expression_content(s)?;
163 parse_expression(&content)
164}
165
166fn extract_expression_content(s: &str) -> Result<String, LexError> {
168 if !s.starts_with("$(") {
169 return Err(LexError::InvalidToken {
170 message: "expression must start with $(".to_string(),
171 pos: SourcePos::default(),
172 });
173 }
174
175 let mut in_quotes = false;
176 let chars: Vec<char> = s.chars().collect();
177 let mut i = 2; let mut depth = 1;
179 let mut content_end = None;
180
181 while i < chars.len() {
182 let ch = chars[i];
183
184 if ch == '"' {
185 if in_quotes && i + 1 < chars.len() && chars[i + 1] == '"' {
187 i += 2;
188 continue;
189 }
190 in_quotes = !in_quotes;
191 } else if !in_quotes {
192 if ch == '(' {
193 depth += 1;
194 } else if ch == ')' {
195 depth -= 1;
196 if depth == 0 {
197 content_end = Some(i);
198 break;
199 }
200 }
201 }
202
203 i += 1;
204 }
205
206 if depth != 0 {
207 return Err(LexError::UnclosedExpression {
208 pos: SourcePos::default(),
209 });
210 }
211
212 let content_end = content_end.ok_or(LexError::UnclosedExpression {
213 pos: SourcePos::default(),
214 })?;
215 let content: String = chars[2..content_end].iter().collect();
216 Ok(content)
217}
218
219struct ExprParser {
220 chars: Vec<char>,
222 pos: usize,
224 column: usize,
226 line: usize,
228}
229
230impl ExprParser {
231 fn new(s: &str) -> Self {
232 Self {
233 chars: s.chars().collect(),
234 pos: 0,
235 column: 1,
236 line: 1,
237 }
238 }
239
240 fn make_span(&self, start_col: usize) -> Span {
242 Span::new(
243 SourcePos::new(self.line, start_col),
244 SourcePos::new(self.line, self.column),
245 )
246 }
247
248 fn skip_whitespace(&mut self) {
249 while self.pos < self.chars.len() && self.chars[self.pos].is_whitespace() {
250 self.pos += 1;
251 self.column += 1;
252 }
253 }
254
255 fn peek(&self) -> Option<char> {
256 if self.pos < self.chars.len() {
257 Some(self.chars[self.pos])
258 } else {
259 None
260 }
261 }
262
263 fn advance(&mut self) -> Option<char> {
264 if self.pos < self.chars.len() {
265 let ch = self.chars[self.pos];
266 self.pos += 1;
267 self.column += 1;
268 Some(ch)
269 } else {
270 None
271 }
272 }
273
274 fn parse_expr(&mut self) -> Result<Expression, LexError> {
275 self.skip_whitespace();
276 let expr_start = self.column;
277
278 let mut expr = self.parse_atom()?;
279
280 loop {
282 self.skip_whitespace();
283 match self.peek() {
284 Some('(') => {
285 let name = match expr {
287 Expression::Identifier { name, .. } => name,
288 _ => {
289 return Err(LexError::InvalidToken {
290 message: "function call on non-identifier".to_string(),
291 pos: SourcePos::default(),
292 });
293 }
294 };
295 self.advance(); let args = self.parse_args()?;
297 expr = Expression::Call {
298 name,
299 args,
300 span: self.make_span(expr_start),
301 };
302 }
303 Some('.') => {
304 self.advance(); self.skip_whitespace();
307 let field = self.parse_identifier()?;
308 expr = Expression::Access {
309 target: Box::new(expr),
310 field,
311 span: self.make_span(expr_start),
312 };
313 }
314 _ => break,
315 }
316 }
317
318 Ok(expr)
319 }
320
321 fn parse_atom(&mut self) -> Result<Expression, LexError> {
322 self.skip_whitespace();
323 let start = self.column;
324
325 match self.peek() {
326 Some('"') => {
327 let s = self.parse_string()?;
329 Ok(Expression::Literal {
330 value: ExprLiteral::String(s),
331 span: self.make_span(start),
332 })
333 }
334 Some(ch) if ch.is_ascii_digit() || ch == '-' => {
335 self.parse_number_with_start(start)
337 }
338 Some(ch) if ch.is_ascii_alphabetic() || ch == '_' => {
339 let ident = self.parse_identifier()?;
341 let span = self.make_span(start);
342 match ident.as_str() {
343 "true" => Ok(Expression::Literal {
344 value: ExprLiteral::Bool(true),
345 span,
346 }),
347 "false" => Ok(Expression::Literal {
348 value: ExprLiteral::Bool(false),
349 span,
350 }),
351 _ => Ok(Expression::Identifier { name: ident, span }),
352 }
353 }
354 Some('(') => {
355 self.advance(); let expr = self.parse_expr()?;
358 self.skip_whitespace();
359 if self.peek() != Some(')') {
360 return Err(LexError::InvalidToken {
361 message: "expected ')' after parenthesized expression".to_string(),
362 pos: SourcePos::default(),
363 });
364 }
365 self.advance(); Ok(expr)
367 }
368 Some(ch) => Err(LexError::InvalidToken {
369 message: format!("unexpected character '{}' in expression", ch),
370 pos: SourcePos::default(),
371 }),
372 None => Err(LexError::InvalidToken {
373 message: "unexpected end of expression".to_string(),
374 pos: SourcePos::default(),
375 }),
376 }
377 }
378
379 fn parse_identifier(&mut self) -> Result<String, LexError> {
380 let mut ident = String::new();
381
382 match self.peek() {
383 Some(ch) if ch.is_ascii_alphabetic() || ch == '_' => {
384 if let Some(c) = self.advance() {
385 ident.push(c);
386 } else {
387 return Err(LexError::InvalidToken {
388 message: "unexpected end of input while parsing identifier".to_string(),
389 pos: SourcePos::default(),
390 });
391 }
392 }
393 _ => {
394 return Err(LexError::InvalidToken {
395 message: "expected identifier".to_string(),
396 pos: SourcePos::default(),
397 });
398 }
399 }
400
401 while let Some(ch) = self.peek() {
402 if ch.is_ascii_alphanumeric() || ch == '_' {
403 if let Some(c) = self.advance() {
404 ident.push(c);
405 } else {
406 break;
407 }
408 } else {
409 break;
410 }
411 }
412
413 Ok(ident)
414 }
415
416 fn parse_string(&mut self) -> Result<String, LexError> {
417 if self.advance() != Some('"') {
418 return Err(LexError::InvalidToken {
419 message: "expected '\"'".to_string(),
420 pos: SourcePos::default(),
421 });
422 }
423
424 let mut result = String::new();
425
426 loop {
427 match self.advance() {
428 Some('"') => {
429 if self.peek() == Some('"') {
431 self.advance();
432 result.push('"');
433 } else {
434 return Ok(result);
436 }
437 }
438 Some(ch) => result.push(ch),
439 None => {
440 return Err(LexError::UnclosedQuote {
441 pos: SourcePos::default(),
442 })
443 }
444 }
445 }
446 }
447
448 fn parse_number_with_start(&mut self, start_col: usize) -> Result<Expression, LexError> {
449 let mut num_str = String::new();
450 let mut has_dot = false;
451
452 if self.peek() == Some('-') {
454 if let Some(c) = self.advance() {
455 num_str.push(c);
456 } else {
457 return Err(LexError::InvalidToken {
458 message: "unexpected end of input while parsing number".to_string(),
459 pos: SourcePos::default(),
460 });
461 }
462 }
463
464 while let Some(ch) = self.peek() {
466 if ch.is_ascii_digit() {
467 if let Some(c) = self.advance() {
468 num_str.push(c);
469 } else {
470 break;
471 }
472 } else if ch == '.' && !has_dot {
473 let next_pos = self.pos + 1;
476 if next_pos < self.chars.len() && self.chars[next_pos].is_ascii_digit() {
477 has_dot = true;
478 if let Some(c) = self.advance() {
479 num_str.push(c);
480 } else {
481 break;
482 }
483 } else {
484 break;
486 }
487 } else {
488 break;
489 }
490 }
491
492 let span = self.make_span(start_col);
493
494 if has_dot {
495 let f: f64 = num_str.parse().map_err(|_| LexError::InvalidToken {
496 message: format!("invalid float: {}", num_str),
497 pos: SourcePos::default(),
498 })?;
499 Ok(Expression::Literal {
500 value: ExprLiteral::Float(f),
501 span,
502 })
503 } else {
504 let i: i64 = num_str.parse().map_err(|_| LexError::InvalidToken {
505 message: format!("invalid integer: {}", num_str),
506 pos: SourcePos::default(),
507 })?;
508 Ok(Expression::Literal {
509 value: ExprLiteral::Int(i),
510 span,
511 })
512 }
513 }
514
515 fn parse_args(&mut self) -> Result<Vec<Expression>, LexError> {
516 let mut args = Vec::new();
517
518 self.skip_whitespace();
519 if self.peek() == Some(')') {
520 self.advance(); return Ok(args);
522 }
523
524 loop {
525 let arg = self.parse_expr()?;
526 args.push(arg);
527
528 self.skip_whitespace();
529 match self.peek() {
530 Some(',') => {
531 self.advance(); }
533 Some(')') => {
534 self.advance(); return Ok(args);
536 }
537 Some(ch) => {
538 return Err(LexError::InvalidToken {
539 message: format!("expected ',' or ')' in argument list, got '{}'", ch),
540 pos: SourcePos::new(1, 1),
541 });
542 }
543 None => {
544 return Err(LexError::InvalidToken {
545 message: "unexpected end of expression in argument list".to_string(),
546 pos: SourcePos::new(1, 1),
547 });
548 }
549 }
550 }
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557
558 #[test]
559 fn test_parse_identifier() {
560 let expr = parse_expression("foo").unwrap();
561 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "foo"));
562 }
563
564 #[test]
565 fn test_parse_identifier_with_underscore() {
566 let expr = parse_expression("foo_bar").unwrap();
567 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "foo_bar"));
568 }
569
570 #[test]
571 fn test_parse_integer() {
572 let expr = parse_expression("42").unwrap();
573 assert!(matches!(
574 expr,
575 Expression::Literal {
576 value: ExprLiteral::Int(42),
577 ..
578 }
579 ));
580 }
581
582 #[test]
583 fn test_parse_negative_integer() {
584 let expr = parse_expression("-123").unwrap();
585 assert!(matches!(
586 expr,
587 Expression::Literal {
588 value: ExprLiteral::Int(-123),
589 ..
590 }
591 ));
592 }
593
594 #[test]
595 fn test_parse_float() {
596 let expr = parse_expression("3.25").unwrap();
597 assert!(
598 matches!(expr, Expression::Literal { value: ExprLiteral::Float(f), .. } if (f - 3.25).abs() < 0.001)
599 );
600 }
601
602 #[test]
603 fn test_parse_string() {
604 let expr = parse_expression(r#""hello""#).unwrap();
605 assert!(
606 matches!(expr, Expression::Literal { value: ExprLiteral::String(s), .. } if s == "hello")
607 );
608 }
609
610 #[test]
611 fn test_parse_string_with_escaped_quote() {
612 let expr = parse_expression(r#""say ""hello""""#).unwrap();
613 assert!(
614 matches!(expr, Expression::Literal { value: ExprLiteral::String(s), .. } if s == "say \"hello\"")
615 );
616 }
617
618 #[test]
619 fn test_parse_bool_true() {
620 let expr = parse_expression("true").unwrap();
621 assert!(matches!(
622 expr,
623 Expression::Literal {
624 value: ExprLiteral::Bool(true),
625 ..
626 }
627 ));
628 }
629
630 #[test]
631 fn test_parse_bool_false() {
632 let expr = parse_expression("false").unwrap();
633 assert!(matches!(
634 expr,
635 Expression::Literal {
636 value: ExprLiteral::Bool(false),
637 ..
638 }
639 ));
640 }
641
642 #[test]
643 fn test_parse_call_no_args() {
644 let expr = parse_expression("now()").unwrap();
645 assert!(
646 matches!(expr, Expression::Call { name, args, .. } if name == "now" && args.is_empty())
647 );
648 }
649
650 #[test]
651 fn test_parse_call_one_arg() {
652 let expr = parse_expression("upper(x)").unwrap();
653 match expr {
654 Expression::Call { name, args, .. } => {
655 assert_eq!(name, "upper");
656 assert_eq!(args.len(), 1);
657 assert!(matches!(&args[0], Expression::Identifier { name, .. } if name == "x"));
658 }
659 _ => panic!("expected Call"),
660 }
661 }
662
663 #[test]
664 fn test_parse_call_multiple_args() {
665 let expr = parse_expression("concat(a, b, c)").unwrap();
666 match expr {
667 Expression::Call { name, args, .. } => {
668 assert_eq!(name, "concat");
669 assert_eq!(args.len(), 3);
670 }
671 _ => panic!("expected Call"),
672 }
673 }
674
675 #[test]
676 fn test_parse_call_string_args() {
677 let expr = parse_expression(r#"concat("hello", "world")"#).unwrap();
678 match expr {
679 Expression::Call { name, args, .. } => {
680 assert_eq!(name, "concat");
681 assert_eq!(args.len(), 2);
682 assert!(
683 matches!(&args[0], Expression::Literal { value: ExprLiteral::String(s), .. } if s == "hello")
684 );
685 assert!(
686 matches!(&args[1], Expression::Literal { value: ExprLiteral::String(s), .. } if s == "world")
687 );
688 }
689 _ => panic!("expected Call"),
690 }
691 }
692
693 #[test]
694 fn test_parse_nested_call() {
695 let expr = parse_expression("outer(inner(x))").unwrap();
696 match expr {
697 Expression::Call { name, args, .. } => {
698 assert_eq!(name, "outer");
699 assert_eq!(args.len(), 1);
700 match &args[0] {
701 Expression::Call {
702 name: inner_name, ..
703 } => {
704 assert_eq!(inner_name, "inner");
705 }
706 _ => panic!("expected nested Call"),
707 }
708 }
709 _ => panic!("expected Call"),
710 }
711 }
712
713 #[test]
714 fn test_parse_field_access() {
715 let expr = parse_expression("user.name").unwrap();
716 match expr {
717 Expression::Access { target, field, .. } => {
718 assert!(matches!(*target, Expression::Identifier { name, .. } if name == "user"));
719 assert_eq!(field, "name");
720 }
721 _ => panic!("expected Access"),
722 }
723 }
724
725 #[test]
726 fn test_parse_chained_access() {
727 let expr = parse_expression("a.b.c").unwrap();
728 match expr {
729 Expression::Access { target, field, .. } => {
730 assert_eq!(field, "c");
731 match *target {
732 Expression::Access {
733 target: inner,
734 field: inner_field,
735 ..
736 } => {
737 assert!(
738 matches!(*inner, Expression::Identifier { name, .. } if name == "a")
739 );
740 assert_eq!(inner_field, "b");
741 }
742 _ => panic!("expected nested Access"),
743 }
744 }
745 _ => panic!("expected Access"),
746 }
747 }
748
749 #[test]
750 fn test_parse_call_then_access() {
751 let expr = parse_expression("get_user().name").unwrap();
752 match expr {
753 Expression::Access { target, field, .. } => {
754 assert_eq!(field, "name");
755 assert!(matches!(*target, Expression::Call { name, .. } if name == "get_user"));
756 }
757 _ => panic!("expected Access"),
758 }
759 }
760
761 #[test]
762 fn test_parse_expression_token() {
763 let expr = parse_expression_token("$(now())").unwrap();
764 assert!(
765 matches!(expr, Expression::Call { name, args, .. } if name == "now" && args.is_empty())
766 );
767 }
768
769 #[test]
770 fn test_parse_expression_token_nested_parens() {
771 let expr = parse_expression_token("$(concat(a, b))").unwrap();
772 assert!(matches!(expr, Expression::Call { name, .. } if name == "concat"));
773 }
774
775 #[test]
776 fn test_display_identifier() {
777 let expr = Expression::Identifier {
778 name: "foo".to_string(),
779 span: Span::synthetic(),
780 };
781 assert_eq!(format!("{}", expr), "foo");
782 }
783
784 #[test]
785 fn test_display_call() {
786 let expr = Expression::Call {
787 name: "func".to_string(),
788 args: vec![
789 Expression::Identifier {
790 name: "x".to_string(),
791 span: Span::synthetic(),
792 },
793 Expression::Literal {
794 value: ExprLiteral::Int(42),
795 span: Span::synthetic(),
796 },
797 ],
798 span: Span::synthetic(),
799 };
800 assert_eq!(format!("{}", expr), "func(x, 42)");
801 }
802
803 #[test]
804 fn test_display_access() {
805 let expr = Expression::Access {
806 target: Box::new(Expression::Identifier {
807 name: "user".to_string(),
808 span: Span::synthetic(),
809 }),
810 field: "name".to_string(),
811 span: Span::synthetic(),
812 };
813 assert_eq!(format!("{}", expr), "user.name");
814 }
815
816 #[test]
817 fn test_display_string_with_quotes() {
818 let expr = Expression::Literal {
819 value: ExprLiteral::String("say \"hi\"".to_string()),
820 span: Span::synthetic(),
821 };
822 assert_eq!(format!("{}", expr), "\"say \"\"hi\"\"\"");
823 }
824
825 #[test]
826 fn test_whitespace_handling() {
827 let expr = parse_expression(" func( a , b ) ").unwrap();
828 assert!(
829 matches!(expr, Expression::Call { name, args, .. } if name == "func" && args.len() == 2)
830 );
831 }
832
833 #[test]
834 fn test_error_unclosed_paren() {
835 let result = parse_expression("func(x");
836 assert!(result.is_err());
837 }
838
839 #[test]
840 fn test_error_unclosed_string() {
841 let result = parse_expression(r#""unclosed"#);
842 assert!(result.is_err());
843 }
844
845 #[test]
846 fn test_error_unexpected_char() {
847 let result = parse_expression("func@");
848 assert!(result.is_err());
849 }
850
851 #[test]
852 fn test_parenthesized_expr() {
853 let expr = parse_expression("(foo)").unwrap();
854 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "foo"));
855 }
856
857 #[test]
860 fn test_parse_zero() {
861 let expr = parse_expression("0").unwrap();
862 assert!(matches!(
863 expr,
864 Expression::Literal {
865 value: ExprLiteral::Int(0),
866 ..
867 }
868 ));
869 }
870
871 #[test]
872 fn test_parse_large_integer() {
873 let expr = parse_expression("9223372036854775807").unwrap();
874 assert!(matches!(
875 expr,
876 Expression::Literal {
877 value: ExprLiteral::Int(9223372036854775807),
878 ..
879 }
880 ));
881 }
882
883 #[test]
884 fn test_parse_negative_zero() {
885 let expr = parse_expression("-0").unwrap();
887 assert!(matches!(
888 expr,
889 Expression::Literal {
890 value: ExprLiteral::Int(0),
891 ..
892 }
893 ));
894 }
895
896 #[test]
897 fn test_parse_float_zero() {
898 let expr = parse_expression("0.0").unwrap();
899 assert!(
900 matches!(expr, Expression::Literal { value: ExprLiteral::Float(f), .. } if f == 0.0)
901 );
902 }
903
904 #[test]
905 fn test_parse_negative_float() {
906 let expr = parse_expression("-3.5").unwrap();
907 assert!(
908 matches!(expr, Expression::Literal { value: ExprLiteral::Float(f), .. } if (f - (-3.5)).abs() < 0.001)
909 );
910 }
911
912 #[test]
913 fn test_parse_float_small() {
914 let expr = parse_expression("0.001").unwrap();
915 assert!(
916 matches!(expr, Expression::Literal { value: ExprLiteral::Float(f), .. } if (f - 0.001).abs() < 0.0001)
917 );
918 }
919
920 #[test]
921 fn test_parse_string_empty() {
922 let expr = parse_expression(r#""""#).unwrap();
923 assert!(
924 matches!(expr, Expression::Literal { value: ExprLiteral::String(s), .. } if s.is_empty())
925 );
926 }
927
928 #[test]
929 fn test_parse_string_with_spaces() {
930 let expr = parse_expression(r#""hello world""#).unwrap();
931 assert!(
932 matches!(expr, Expression::Literal { value: ExprLiteral::String(s), .. } if s == "hello world")
933 );
934 }
935
936 #[test]
937 fn test_parse_string_with_special_chars() {
938 let expr = parse_expression(r#""hello!@#$%""#).unwrap();
939 assert!(
940 matches!(expr, Expression::Literal { value: ExprLiteral::String(s), .. } if s == "hello!@#$%")
941 );
942 }
943
944 #[test]
945 fn test_parse_string_unicode() {
946 let expr = parse_expression(r#""日本語 🎉""#).unwrap();
947 assert!(
948 matches!(expr, Expression::Literal { value: ExprLiteral::String(s), .. } if s.contains('🎉'))
949 );
950 }
951
952 #[test]
953 fn test_parse_string_multiple_escaped_quotes() {
954 let expr = parse_expression(r#""""""""#).unwrap();
955 assert!(
956 matches!(expr, Expression::Literal { value: ExprLiteral::String(s), .. } if s == "\"\"")
957 );
958 }
959
960 #[test]
963 fn test_parse_identifier_single_char() {
964 let expr = parse_expression("x").unwrap();
965 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "x"));
966 }
967
968 #[test]
969 fn test_parse_identifier_underscore_only() {
970 let expr = parse_expression("_").unwrap();
971 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "_"));
972 }
973
974 #[test]
975 fn test_parse_identifier_leading_underscore() {
976 let expr = parse_expression("_private").unwrap();
977 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "_private"));
978 }
979
980 #[test]
981 fn test_parse_identifier_double_underscore() {
982 let expr = parse_expression("__dunder__").unwrap();
983 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "__dunder__"));
984 }
985
986 #[test]
987 fn test_parse_identifier_with_numbers() {
988 let expr = parse_expression("var123").unwrap();
989 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "var123"));
990 }
991
992 #[test]
995 fn test_parse_call_with_literals() {
996 let expr = parse_expression("func(42, 3.5, true)").unwrap();
997 match expr {
998 Expression::Call { name, args, .. } => {
999 assert_eq!(name, "func");
1000 assert_eq!(args.len(), 3);
1001 assert!(matches!(
1002 args[0],
1003 Expression::Literal {
1004 value: ExprLiteral::Int(42),
1005 ..
1006 }
1007 ));
1008 assert!(matches!(
1009 args[1],
1010 Expression::Literal {
1011 value: ExprLiteral::Float(_),
1012 ..
1013 }
1014 ));
1015 assert!(matches!(
1016 args[2],
1017 Expression::Literal {
1018 value: ExprLiteral::Bool(true),
1019 ..
1020 }
1021 ));
1022 }
1023 _ => panic!("expected Call"),
1024 }
1025 }
1026
1027 #[test]
1028 fn test_parse_deeply_nested_calls() {
1029 let expr = parse_expression("a(b(c(d(e))))").unwrap();
1030 fn count_depth(e: &Expression) -> usize {
1031 match e {
1032 Expression::Call { args, .. } => {
1033 if args.is_empty() {
1034 1
1035 } else {
1036 1 + count_depth(&args[0])
1037 }
1038 }
1039 Expression::Identifier { .. } => 1,
1040 _ => 0,
1041 }
1042 }
1043 assert_eq!(count_depth(&expr), 5);
1044 }
1045
1046 #[test]
1047 fn test_parse_call_with_string_containing_comma() {
1048 let expr = parse_expression(r#"func("a, b", c)"#).unwrap();
1049 match expr {
1050 Expression::Call { name, args, .. } => {
1051 assert_eq!(name, "func");
1052 assert_eq!(args.len(), 2);
1053 assert!(
1054 matches!(&args[0], Expression::Literal { value: ExprLiteral::String(s), .. } if s == "a, b")
1055 );
1056 }
1057 _ => panic!("expected Call"),
1058 }
1059 }
1060
1061 #[test]
1062 fn test_parse_call_with_string_containing_paren() {
1063 let expr = parse_expression(r#"func("(test)")"#).unwrap();
1064 match expr {
1065 Expression::Call { name, args, .. } => {
1066 assert_eq!(name, "func");
1067 assert_eq!(args.len(), 1);
1068 assert!(
1069 matches!(&args[0], Expression::Literal { value: ExprLiteral::String(s), .. } if s == "(test)")
1070 );
1071 }
1072 _ => panic!("expected Call"),
1073 }
1074 }
1075
1076 #[test]
1079 fn test_parse_deeply_chained_access() {
1080 let expr = parse_expression("a.b.c.d.e").unwrap();
1081 fn count_access(e: &Expression) -> usize {
1082 match e {
1083 Expression::Access { target, .. } => 1 + count_access(target),
1084 _ => 0,
1085 }
1086 }
1087 assert_eq!(count_access(&expr), 4);
1088 }
1089
1090 #[test]
1091 fn test_parse_access_with_numbers_in_field() {
1092 let expr = parse_expression("obj.field123").unwrap();
1093 match expr {
1094 Expression::Access { field, .. } => {
1095 assert_eq!(field, "field123");
1096 }
1097 _ => panic!("expected Access"),
1098 }
1099 }
1100
1101 #[test]
1102 fn test_parse_access_then_call_error() {
1103 let result = parse_expression("obj.method()");
1107 assert!(result.is_err());
1108 }
1109
1110 #[test]
1111 fn test_parse_method_call_works() {
1112 let expr = parse_expression("method()").unwrap();
1114 match expr {
1115 Expression::Call { name, args, .. } => {
1116 assert_eq!(name, "method");
1117 assert!(args.is_empty());
1118 }
1119 _ => panic!("expected Call"),
1120 }
1121 }
1122
1123 #[test]
1124 fn test_parse_number_then_access() {
1125 let expr = parse_expression("42.5").unwrap();
1128 assert!(matches!(
1129 expr,
1130 Expression::Literal { value: ExprLiteral::Float(f), .. } if (f - 42.5).abs() < 0.001
1131 ));
1132 }
1133
1134 #[test]
1137 fn test_parse_expression_token_simple() {
1138 let expr = parse_expression_token("$(x)").unwrap();
1139 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "x"));
1140 }
1141
1142 #[test]
1143 fn test_parse_expression_token_complex() {
1144 let expr = parse_expression_token("$(user.profile.name)").unwrap();
1145 match expr {
1146 Expression::Access { field, .. } => {
1147 assert_eq!(field, "name");
1148 }
1149 _ => panic!("expected Access"),
1150 }
1151 }
1152
1153 #[test]
1154 fn test_parse_expression_token_with_quotes() {
1155 let expr = parse_expression_token(r#"$(concat("a", "b"))"#).unwrap();
1156 assert!(matches!(expr, Expression::Call { .. }));
1157 }
1158
1159 #[test]
1160 fn test_parse_expression_token_not_starting_with_dollar() {
1161 let result = parse_expression_token("(x)");
1162 assert!(result.is_err());
1163 }
1164
1165 #[test]
1166 fn test_parse_expression_token_unclosed() {
1167 let result = parse_expression_token("$(x");
1168 assert!(result.is_err());
1169 }
1170
1171 #[test]
1174 fn test_display_int() {
1175 let lit = ExprLiteral::Int(42);
1176 assert_eq!(format!("{}", lit), "42");
1177 }
1178
1179 #[test]
1180 fn test_display_float() {
1181 let lit = ExprLiteral::Float(3.5);
1182 assert!(format!("{}", lit).starts_with("3.5"));
1183 }
1184
1185 #[test]
1186 fn test_display_bool() {
1187 assert_eq!(format!("{}", ExprLiteral::Bool(true)), "true");
1188 assert_eq!(format!("{}", ExprLiteral::Bool(false)), "false");
1189 }
1190
1191 #[test]
1192 fn test_display_nested_call() {
1193 let expr = Expression::Call {
1194 name: "outer".to_string(),
1195 args: vec![Expression::Call {
1196 name: "inner".to_string(),
1197 args: vec![Expression::Literal {
1198 value: ExprLiteral::Int(42),
1199 span: Span::synthetic(),
1200 }],
1201 span: Span::synthetic(),
1202 }],
1203 span: Span::synthetic(),
1204 };
1205 assert_eq!(format!("{}", expr), "outer(inner(42))");
1206 }
1207
1208 #[test]
1209 fn test_display_access_chain() {
1210 let expr = Expression::Access {
1211 target: Box::new(Expression::Access {
1212 target: Box::new(Expression::Identifier {
1213 name: "a".to_string(),
1214 span: Span::synthetic(),
1215 }),
1216 field: "b".to_string(),
1217 span: Span::synthetic(),
1218 }),
1219 field: "c".to_string(),
1220 span: Span::synthetic(),
1221 };
1222 assert_eq!(format!("{}", expr), "a.b.c");
1223 }
1224
1225 #[test]
1228 fn test_error_empty_expression() {
1229 let result = parse_expression("");
1230 assert!(result.is_err());
1231 }
1232
1233 #[test]
1234 fn test_error_only_whitespace() {
1235 let result = parse_expression(" ");
1236 assert!(result.is_err());
1237 }
1238
1239 #[test]
1240 fn test_error_trailing_garbage() {
1241 let result = parse_expression("foo bar");
1242 assert!(result.is_err());
1243 }
1244
1245 #[test]
1246 fn test_error_call_on_literal() {
1247 let result = parse_expression("42()");
1250 assert!(result.is_err());
1251 }
1252
1253 #[test]
1254 fn test_error_unclosed_string_in_call() {
1255 let result = parse_expression(r#"func("unclosed)"#);
1256 assert!(result.is_err());
1257 }
1258
1259 #[test]
1260 fn test_error_missing_comma_in_args() {
1261 let result = parse_expression("func(a b)");
1262 assert!(result.is_err());
1263 }
1264
1265 #[test]
1266 fn test_error_trailing_comma_in_args() {
1267 let result = parse_expression("func(a,)");
1268 assert!(result.is_err());
1269 }
1270
1271 #[test]
1272 fn test_error_unclosed_paren_in_nested() {
1273 let result = parse_expression("outer(inner(x)");
1274 assert!(result.is_err());
1275 }
1276
1277 #[test]
1280 fn test_expression_equality() {
1281 let a = Expression::Identifier {
1282 name: "foo".to_string(),
1283 span: Span::synthetic(),
1284 };
1285 let b = Expression::Identifier {
1286 name: "foo".to_string(),
1287 span: Span::synthetic(),
1288 };
1289 assert_eq!(a, b);
1290 }
1291
1292 #[test]
1293 fn test_expression_clone() {
1294 let original = Expression::Call {
1295 name: "func".to_string(),
1296 args: vec![Expression::Literal {
1297 value: ExprLiteral::Int(42),
1298 span: Span::synthetic(),
1299 }],
1300 span: Span::synthetic(),
1301 };
1302 let cloned = original.clone();
1303 assert_eq!(original, cloned);
1304 }
1305
1306 #[test]
1307 fn test_expr_literal_equality() {
1308 assert_eq!(ExprLiteral::Int(42), ExprLiteral::Int(42));
1309 assert_ne!(ExprLiteral::Int(42), ExprLiteral::Int(43));
1310 assert_eq!(ExprLiteral::Bool(true), ExprLiteral::Bool(true));
1311 assert_eq!(
1312 ExprLiteral::String("test".to_string()),
1313 ExprLiteral::String("test".to_string())
1314 );
1315 }
1316
1317 #[test]
1318 fn test_expr_literal_clone() {
1319 let original = ExprLiteral::String("hello".to_string());
1320 let cloned = original.clone();
1321 assert_eq!(original, cloned);
1322 }
1323
1324 #[test]
1325 fn test_expression_debug() {
1326 let expr = Expression::Call {
1327 name: "func".to_string(),
1328 args: vec![],
1329 span: Span::synthetic(),
1330 };
1331 let debug = format!("{:?}", expr);
1332 assert!(debug.contains("func"));
1333 }
1334
1335 #[test]
1338 fn test_deeply_nested_parens() {
1339 let expr = parse_expression("(((foo)))").unwrap();
1340 assert!(matches!(expr, Expression::Identifier { name, .. } if name == "foo"));
1341 }
1342
1343 #[test]
1344 fn test_parens_around_call() {
1345 let expr = parse_expression("(func(x))").unwrap();
1346 assert!(matches!(expr, Expression::Call { name, .. } if name == "func"));
1347 }
1348
1349 #[test]
1350 fn test_parens_around_access() {
1351 let expr = parse_expression("(a.b)").unwrap();
1352 assert!(matches!(expr, Expression::Access { field, .. } if field == "b"));
1353 }
1354
1355 #[test]
1356 fn test_unclosed_inner_paren() {
1357 let result = parse_expression("((foo)");
1358 assert!(result.is_err());
1359 }
1360
1361 #[test]
1364 fn test_identifier_span() {
1365 let expr = parse_expression("foo").unwrap();
1366 if let Expression::Identifier { span, .. } = expr {
1367 assert!(!span.is_synthetic());
1368 assert_eq!(span.start().line(), 1);
1369 assert_eq!(span.start().column(), 1);
1370 assert_eq!(span.end().column(), 4); } else {
1372 panic!("expected identifier");
1373 }
1374 }
1375
1376 #[test]
1377 fn test_identifier_with_leading_whitespace_span() {
1378 let expr = parse_expression(" bar").unwrap();
1379 if let Expression::Identifier { span, .. } = expr {
1380 assert!(!span.is_synthetic());
1381 assert_eq!(span.start().column(), 3); assert_eq!(span.end().column(), 6);
1383 } else {
1384 panic!("expected identifier");
1385 }
1386 }
1387
1388 #[test]
1389 fn test_integer_span() {
1390 let expr = parse_expression("12345").unwrap();
1391 if let Expression::Literal { span, .. } = expr {
1392 assert!(!span.is_synthetic());
1393 assert_eq!(span.start().column(), 1);
1394 assert_eq!(span.end().column(), 6);
1395 } else {
1396 panic!("expected literal");
1397 }
1398 }
1399
1400 #[test]
1401 fn test_string_span() {
1402 let expr = parse_expression("\"hello\"").unwrap();
1403 if let Expression::Literal { span, .. } = expr {
1404 assert!(!span.is_synthetic());
1405 assert_eq!(span.start().column(), 1);
1406 assert_eq!(span.end().column(), 8); } else {
1408 panic!("expected literal");
1409 }
1410 }
1411
1412 #[test]
1413 fn test_call_span() {
1414 let expr = parse_expression("func(x, y)").unwrap();
1415 if let Expression::Call { span, .. } = expr {
1416 assert!(!span.is_synthetic());
1417 assert_eq!(span.start().column(), 1);
1418 assert_eq!(span.end().column(), 11);
1419 } else {
1420 panic!("expected call");
1421 }
1422 }
1423
1424 #[test]
1425 fn test_access_span() {
1426 let expr = parse_expression("a.b").unwrap();
1427 if let Expression::Access { span, .. } = expr {
1428 assert!(!span.is_synthetic());
1429 assert_eq!(span.start().column(), 1);
1430 assert_eq!(span.end().column(), 4);
1431 } else {
1432 panic!("expected access");
1433 }
1434 }
1435
1436 #[test]
1437 fn test_boolean_span() {
1438 let expr = parse_expression("true").unwrap();
1439 if let Expression::Literal { span, .. } = expr {
1440 assert!(!span.is_synthetic());
1441 assert_eq!(span.start().column(), 1);
1442 assert_eq!(span.end().column(), 5);
1443 } else {
1444 panic!("expected literal");
1445 }
1446 }
1447
1448 #[test]
1449 fn test_negative_number_span() {
1450 let expr = parse_expression("-42").unwrap();
1451 if let Expression::Literal { span, .. } = expr {
1452 assert!(!span.is_synthetic());
1453 assert_eq!(span.start().column(), 1);
1454 assert_eq!(span.end().column(), 4);
1455 } else {
1456 panic!("expected literal");
1457 }
1458 }
1459}