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