1use std::any::Any;
13use std::ffi::CString;
14
15use proc_macro2::Span;
16
17use crate::op_table::OpLookup;
18use crate::parser_context::ParserContext;
19use crate::type_expr::TypeExpr;
20
21#[derive(Clone, Copy, Debug)]
32pub struct ExprSpan {
33 pub start: Span,
35 pub end: Span,
37}
38
39impl ExprSpan {
40 fn point(span: Span) -> Self {
42 ExprSpan {
43 start: span,
44 end: span,
45 }
46 }
47
48 #[must_use]
67 pub fn for_text(text: &str) -> Self {
68 let tokens: proc_macro2::TokenStream = text
69 .parse()
70 .expect("ExprSpan::for_text: text must be valid tokens");
71 let mut iter = tokens.into_iter();
72 let first = iter
73 .next()
74 .expect("ExprSpan::for_text: text must be non-empty");
75 debug_assert!(
76 iter.next().is_none(),
77 "ExprSpan::for_text: text must tokenize to exactly one token"
78 );
79 let span = first.span();
80 ExprSpan {
81 start: span,
82 end: span,
83 }
84 }
85}
86
87#[derive(Clone, Debug, PartialEq)]
95pub enum Literal {
96 I8(i8),
98 I16(i16),
100 I32(i32),
102 I64(i64),
104 I128(i128),
106 Isize(isize),
108 U8(u8),
110 U16(u16),
112 U32(u32),
114 U64(u64),
116 U128(u128),
118 Usize(usize),
120 F32(f32),
122 F64(f64),
124 Bool(bool),
126 Char(char),
128 Str(String),
130 ByteStr(Vec<u8>),
132 CStr(CString),
134 Unit,
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub enum LogicalOp {
141 And,
143 Or,
145}
146
147#[derive(Clone, Debug)]
158pub enum Expr {
159 Struct {
169 name: String,
171 name_span: ExprSpan,
173 fields: Vec<RecordInit>,
175 span: ExprSpan,
177 },
178 Literal {
180 value: Literal,
182 span: ExprSpan,
184 },
185 Ident {
187 name: String,
189 span: ExprSpan,
191 },
192 Op {
194 name: String,
196 operands: Vec<Expr>,
198 span: ExprSpan,
200 },
201 Apply {
203 callee: Box<Expr>,
205 args: Vec<Expr>,
207 span: ExprSpan,
209 },
210 Member {
221 receiver: Box<Expr>,
223 name: String,
225 name_span: ExprSpan,
227 args: Option<Vec<Expr>>,
229 span: ExprSpan,
231 },
232 Tuple {
234 elements: Vec<Expr>,
236 span: ExprSpan,
238 },
239 Array {
247 elements: Vec<Expr>,
249 type_annotation: Option<TypeExpr>,
251 annotation_span: Option<ExprSpan>,
253 span: ExprSpan,
255 },
256 TupleIndex {
259 base: Box<Expr>,
261 index: usize,
263 span: ExprSpan,
265 },
266 If {
268 cond: Box<Expr>,
270 then_branch: Box<Expr>,
272 else_branch: Option<Box<Expr>>,
274 span: ExprSpan,
276 },
277 Logical {
279 op: LogicalOp,
281 lhs: Box<Expr>,
283 rhs: Box<Expr>,
285 span: ExprSpan,
287 },
288 Cast {
292 expr: Box<Expr>,
294 type_name: String,
296 span: ExprSpan,
298 },
299 Closure {
301 params: Vec<ClosureParam>,
303 body: Box<Expr>,
305 span: ExprSpan,
307 },
308}
309
310impl Expr {
311 pub fn span(&self) -> ExprSpan {
313 match self {
314 Expr::Literal { span, .. }
315 | Expr::Struct { span, .. }
316 | Expr::Ident { span, .. }
317 | Expr::Op { span, .. }
318 | Expr::Apply { span, .. }
319 | Expr::Member { span, .. }
320 | Expr::Tuple { span, .. }
321 | Expr::Array { span, .. }
322 | Expr::TupleIndex { span, .. }
323 | Expr::If { span, .. }
324 | Expr::Logical { span, .. }
325 | Expr::Cast { span, .. }
326 | Expr::Closure { span, .. } => *span,
327 }
328 }
329}
330
331#[derive(Clone, Debug)]
343pub struct RecordInit {
344 pub name: String,
346 pub name_span: ExprSpan,
348 pub value: Expr,
350}
351
352#[derive(Clone, Debug)]
355pub struct ClosureParam {
356 pub name: String,
358 pub name_span: ExprSpan,
360 pub type_expr: crate::TypeExpr,
362}
363
364fn to_literal<T: 'static + Clone>(value: &T) -> Literal {
370 let any = value as &dyn Any;
371 macro_rules! map {
372 ($($t:ty => $variant:path),+ $(,)?) => {
373 $( if let Some(x) = any.downcast_ref::<$t>() { return $variant(x.clone()); } )+
374 };
375 }
376 map! {
377 i8 => Literal::I8, i16 => Literal::I16, i32 => Literal::I32, i64 => Literal::I64,
378 i128 => Literal::I128, isize => Literal::Isize,
379 u8 => Literal::U8, u16 => Literal::U16, u32 => Literal::U32, u64 => Literal::U64,
380 u128 => Literal::U128, usize => Literal::Usize,
381 f32 => Literal::F32, f64 => Literal::F64,
382 bool => Literal::Bool, char => Literal::Char, String => Literal::Str,
383 Vec<u8> => Literal::ByteStr, CString => Literal::CStr,
384 }
385 if any.is::<()>() {
386 return Literal::Unit;
387 }
388 unreachable!("push_literal called with an unsupported literal type")
389}
390
391#[derive(Debug, Default)]
406pub struct AstContext {
407 values: Vec<Expr>,
409}
410
411impl AstContext {
412 fn pop(&mut self) -> Expr {
416 self.values
417 .pop()
418 .expect("operand present on AST value stack")
419 }
420
421 fn pop_n(&mut self, n: usize) -> Vec<Expr> {
425 let at = self.values.len() - n;
426 self.values.split_off(at)
427 }
428
429 pub fn into_expr(mut self) -> Expr {
433 debug_assert_eq!(
434 self.values.len(),
435 1,
436 "a successfully parsed expression leaves exactly one node"
437 );
438 self.pop()
439 }
440}
441
442impl ParserContext for AstContext {
443 fn resolves_closure_types() -> bool {
444 false
445 }
446
447 fn new_context() -> Self {
448 AstContext { values: Vec::new() }
449 }
450
451 fn new_fragment(&self) -> Self {
452 AstContext { values: Vec::new() }
453 }
454
455 fn push_literal<T: 'static + Clone>(&mut self, value: T, span: Span) {
456 self.values.push(Expr::Literal {
457 value: to_literal(&value),
458 span: ExprSpan::point(span),
459 });
460 }
461
462 fn apply_op(
463 &mut self,
464 _op_lookup: &OpLookup,
465 name: &str,
466 arity: usize,
467 start: Span,
468 end: Span,
469 ) -> crate::Result<()> {
470 let span = ExprSpan { start, end };
471 if arity == 0 {
472 self.values.push(Expr::Ident {
473 name: name.to_string(),
474 span,
475 });
476 } else if name == "()" {
477 let mut operands = self.pop_n(arity); let callee = operands.remove(0);
479 self.values.push(Expr::Apply {
480 callee: Box::new(callee),
481 args: operands,
482 span,
483 });
484 } else {
485 let operands = self.pop_n(arity); self.values.push(Expr::Op {
487 name: name.to_string(),
488 operands,
489 span,
490 });
491 }
492 Ok(())
493 }
494
495 fn apply_member(
499 &mut self,
500 _lookup: &OpLookup,
501 name: &str,
502 name_span: ExprSpan,
503 explicit_args: Option<usize>,
504 start: Span,
505 end: Span,
506 ) -> crate::Result<()> {
507 debug_assert!(self.values.len() > explicit_args.unwrap_or(0));
508 let args = explicit_args.map(|count| self.pop_n(count));
509 let receiver = self.pop();
510 self.values.push(Expr::Member {
511 receiver: Box::new(receiver),
512 name: name.to_string(),
513 name_span,
514 args,
515 span: ExprSpan { start, end },
516 });
517 Ok(())
518 }
519
520 fn make_record(
524 &mut self,
525 _lookup: &OpLookup,
526 name: &str,
527 name_span: ExprSpan,
528 fields: Vec<(String, ExprSpan)>,
529 _ambient_start: usize,
530 start: Span,
531 end: Span,
532 ) -> crate::Result<()> {
533 let values = self.pop_n(fields.len());
534 let fields = fields
535 .into_iter()
536 .zip(values)
537 .map(|((name, name_span), value)| RecordInit {
538 name,
539 name_span,
540 value,
541 })
542 .collect();
543 self.values.push(Expr::Struct {
544 name: name.to_string(),
545 name_span,
546 fields,
547 span: ExprSpan { start, end },
548 });
549 Ok(())
550 }
551
552 fn apply_logical(
553 &mut self,
554 name: &str,
555 mut rhs: Self,
556 start: Span,
557 end: Span,
558 ) -> crate::Result<()> {
559 let op = match name {
560 "||" => LogicalOp::Or,
561 "&&" => LogicalOp::And,
562 other => unreachable!("apply_logical called with unsupported operator `{other}`"),
563 };
564 let lhs = self.pop();
565 debug_assert_eq!(
566 rhs.values.len(),
567 1,
568 "rhs fragment produces exactly one value"
569 );
570 let rhs_expr = rhs.pop();
571 self.values.push(Expr::Logical {
572 op,
573 lhs: Box::new(lhs),
574 rhs: Box::new(rhs_expr),
575 span: ExprSpan { start, end },
576 });
577 Ok(())
578 }
579
580 fn join2(
581 &mut self,
582 mut then_fragment: Self,
583 else_fragment: Option<Self>,
584 start: Span,
585 end: Span,
586 ) -> anyhow::Result<()> {
587 let cond = self.pop();
588 debug_assert_eq!(
589 then_fragment.values.len(),
590 1,
591 "then fragment produces exactly one value"
592 );
593 let then_branch = then_fragment.pop();
594 let else_branch = else_fragment.map(|mut fragment| {
595 debug_assert_eq!(
596 fragment.values.len(),
597 1,
598 "else fragment produces exactly one value"
599 );
600 Box::new(fragment.pop())
601 });
602 self.values.push(Expr::If {
603 cond: Box::new(cond),
604 then_branch: Box::new(then_branch),
605 else_branch,
606 span: ExprSpan { start, end },
607 });
608 Ok(())
609 }
610
611 fn make_tuple(
612 &mut self,
613 n: usize,
614 ambient_start: usize,
615 start: Span,
616 end: Span,
617 ) -> crate::Result<()> {
618 if n == 0 {
619 self.values.push(Expr::Literal {
620 value: Literal::Unit,
621 span: ExprSpan { start, end },
622 });
623 return Ok(());
624 }
625 let elements = self.values.split_off(ambient_start);
626 debug_assert_eq!(
627 elements.len(),
628 n,
629 "make_tuple splits off exactly n elements"
630 );
631 self.values.push(Expr::Tuple {
632 elements,
633 span: ExprSpan { start, end },
634 });
635 Ok(())
636 }
637
638 fn make_array(
639 &mut self,
640 n: usize,
641 ambient_start: usize,
642 start: Span,
643 end: Span,
644 ) -> crate::Result<()> {
645 let elements = self.values.split_off(ambient_start);
646 debug_assert_eq!(
647 elements.len(),
648 n,
649 "make_array splits off exactly n elements"
650 );
651 self.values.push(Expr::Array {
652 elements,
653 type_annotation: None,
654 annotation_span: None,
655 span: ExprSpan { start, end },
656 });
657 Ok(())
658 }
659
660 fn make_annotated_array(
661 &mut self,
662 n: usize,
663 ambient_start: usize,
664 annotation: crate::parser_context::AnnotatedArray<'_>,
665 start: Span,
666 end: Span,
667 ) -> crate::Result<()> {
668 let elements = self.values.split_off(ambient_start);
669 debug_assert_eq!(
670 elements.len(),
671 n,
672 "make_annotated_array splits off exactly n elements"
673 );
674 let (type_annotation, annotation_span) = annotation.into_parts();
675 self.values.push(Expr::Array {
676 elements,
677 type_annotation,
678 annotation_span,
679 span: ExprSpan { start, end },
680 });
681 Ok(())
682 }
683
684 fn peek_tuple_arity(&self) -> Option<usize> {
685 Some(usize::MAX)
691 }
692
693 fn tuple_index(&mut self, index: usize, start: Span, end: Span) -> crate::Result<()> {
694 let base = self.pop();
695 self.values.push(Expr::TupleIndex {
696 base: Box::new(base),
697 index,
698 span: ExprSpan { start, end },
699 });
700 Ok(())
701 }
702
703 fn current_stack_offset(&self) -> usize {
704 self.values.len()
705 }
706
707 fn apply_cast(
708 &mut self,
709 _op_lookup: &OpLookup,
710 type_name: &str,
711 start: Span,
712 end: Span,
713 ) -> crate::Result<()> {
714 let expr = self.pop();
715 self.values.push(Expr::Cast {
716 expr: Box::new(expr),
717 type_name: type_name.to_string(),
718 span: ExprSpan { start, end },
719 });
720 Ok(())
721 }
722
723 fn push_closure(
724 &mut self,
725 param_types: Vec<cel_runtime::ValueType>,
726 params: Vec<ClosureParam>,
727 body: Self,
728 span: Span,
729 ) -> crate::Result<()> {
730 let _ = param_types;
731 let body_expr = body.into_expr();
732 let end = body_expr.span().end;
733 self.values.push(Expr::Closure {
734 params,
735 body: Box::new(body_expr),
736 span: ExprSpan { start: span, end },
737 });
738 Ok(())
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745 use crate::Parser;
746 use proc_macro2::Span;
747
748 #[test]
749 fn span_returns_the_range_stored_on_a_leaf_variant() {
750 let target = ExprSpan {
751 start: Span::call_site(),
752 end: Span::call_site(),
753 };
754 let expr = Expr::Ident {
755 name: "x".to_string(),
756 span: target,
757 };
758 assert_eq!(format!("{:?}", expr.span()), format!("{target:?}"));
759 }
760
761 #[test]
762 fn span_returns_the_range_stored_on_a_composite_variant() {
763 let target = ExprSpan {
764 start: Span::call_site(),
765 end: Span::call_site(),
766 };
767 let expr = Expr::If {
768 cond: Box::new(Expr::Literal {
769 value: Literal::Bool(true),
770 span: target,
771 }),
772 then_branch: Box::new(Expr::Literal {
773 value: Literal::I32(1),
774 span: target,
775 }),
776 else_branch: Some(Box::new(Expr::Literal {
777 value: Literal::I32(2),
778 span: target,
779 })),
780 span: target,
781 };
782 assert_eq!(format!("{:?}", expr.span()), format!("{target:?}"));
783 }
784
785 #[test]
786 fn closure_span_is_the_stored_span() {
787 let target = ExprSpan {
788 start: Span::call_site(),
789 end: Span::call_site(),
790 };
791 let expr = Expr::Closure {
792 params: Vec::new(),
793 body: Box::new(Expr::Literal {
794 value: Literal::I32(1),
795 span: target,
796 }),
797 span: target,
798 };
799 assert_eq!(format!("{:?}", expr.span()), format!("{target:?}"));
800 }
801
802 #[test]
803 fn literal_variants_are_distinguishable_and_comparable() {
804 assert_eq!(Literal::I32(1), Literal::I32(1));
805 assert_ne!(Literal::I32(1), Literal::I32(2));
806 assert_ne!(Literal::I32(1), Literal::U8(1));
807 assert_eq!(Literal::Unit, Literal::Unit);
808 }
809
810 #[test]
811 fn logical_op_equality() {
812 assert_eq!(LogicalOp::And, LogicalOp::And);
813 assert_ne!(LogicalOp::And, LogicalOp::Or);
814 }
815
816 #[test]
817 fn push_literal_dispatches_every_concrete_literal_type() {
818 fn literal_of<T: 'static + Clone>(value: T) -> Literal {
819 let mut ctx = AstContext::new_context();
820 ctx.push_literal(value, Span::call_site());
821 match ctx.into_expr() {
822 Expr::Literal { value, .. } => value,
823 other => panic!("expected Literal, got {other:?}"),
824 }
825 }
826 assert_eq!(literal_of(1i8), Literal::I8(1));
827 assert_eq!(literal_of(1i16), Literal::I16(1));
828 assert_eq!(literal_of(1i32), Literal::I32(1));
829 assert_eq!(literal_of(1i64), Literal::I64(1));
830 assert_eq!(literal_of(1i128), Literal::I128(1));
831 assert_eq!(literal_of(1isize), Literal::Isize(1));
832 assert_eq!(literal_of(1u8), Literal::U8(1));
833 assert_eq!(literal_of(1u16), Literal::U16(1));
834 assert_eq!(literal_of(1u32), Literal::U32(1));
835 assert_eq!(literal_of(1u64), Literal::U64(1));
836 assert_eq!(literal_of(1u128), Literal::U128(1));
837 assert_eq!(literal_of(1usize), Literal::Usize(1));
838 assert_eq!(literal_of(1.0f32), Literal::F32(1.0));
839 assert_eq!(literal_of(1.0f64), Literal::F64(1.0));
840 assert_eq!(literal_of(true), Literal::Bool(true));
841 assert_eq!(literal_of('a'), Literal::Char('a'));
842 assert_eq!(literal_of("s".to_string()), Literal::Str("s".to_string()));
843 assert_eq!(literal_of(vec![1u8, 2u8]), Literal::ByteStr(vec![1, 2]));
844 assert_eq!(
845 literal_of(CString::new("c").unwrap()),
846 Literal::CStr(CString::new("c").unwrap())
847 );
848 assert_eq!(literal_of(()), Literal::Unit);
849 }
850
851 #[test]
852 fn apply_op_with_arity_zero_records_an_ident_node() {
853 let mut ctx = AstContext::new_context();
854 let lookup = OpLookup::new();
855 ctx.apply_op(&lookup, "x", 0, Span::call_site(), Span::call_site())
856 .unwrap();
857 assert!(matches!(ctx.into_expr(), Expr::Ident { name, .. } if name == "x"));
858 }
859
860 #[test]
861 fn apply_op_with_the_call_operator_records_an_apply_node() {
862 let mut ctx = AstContext::new_context();
863 let lookup = OpLookup::new();
864 ctx.apply_op(&lookup, "x", 0, Span::call_site(), Span::call_site())
865 .unwrap(); ctx.push_literal(1i32, Span::call_site()); ctx.apply_op(&lookup, "()", 2, Span::call_site(), Span::call_site())
868 .unwrap();
869 match ctx.into_expr() {
870 Expr::Apply { callee, args, .. } => {
871 assert!(matches!(*callee, Expr::Ident { ref name, .. } if name == "x"));
872 assert_eq!(args.len(), 1);
873 }
874 other => panic!("expected Apply, got {other:?}"),
875 }
876 }
877
878 #[test]
879 fn apply_op_with_arity_two_records_an_op_node() {
880 let mut ctx = AstContext::new_context();
881 let lookup = OpLookup::new();
882 ctx.push_literal(1i32, Span::call_site());
883 ctx.push_literal(2i32, Span::call_site());
884 ctx.apply_op(&lookup, "+", 2, Span::call_site(), Span::call_site())
885 .unwrap();
886 match ctx.into_expr() {
887 Expr::Op { name, operands, .. } => {
888 assert_eq!(name, "+");
889 assert_eq!(operands.len(), 2);
890 }
891 other => panic!("expected Op, got {other:?}"),
892 }
893 }
894
895 #[test]
896 fn apply_logical_records_a_logical_node() {
897 let mut ctx = AstContext::new_context();
898 ctx.push_literal(true, Span::call_site());
899 let mut rhs = ctx.new_fragment();
900 rhs.push_literal(false, Span::call_site());
901 ctx.apply_logical("||", rhs, Span::call_site(), Span::call_site())
902 .unwrap();
903 match ctx.into_expr() {
904 Expr::Logical { op, lhs, rhs, .. } => {
905 assert_eq!(op, LogicalOp::Or);
906 assert!(matches!(
907 *lhs,
908 Expr::Literal {
909 value: Literal::Bool(true),
910 ..
911 }
912 ));
913 assert!(matches!(
914 *rhs,
915 Expr::Literal {
916 value: Literal::Bool(false),
917 ..
918 }
919 ));
920 }
921 other => panic!("expected Logical, got {other:?}"),
922 }
923 }
924
925 #[test]
926 fn join2_records_an_if_node() {
927 let mut ctx = AstContext::new_context();
928 ctx.push_literal(true, Span::call_site());
929 let mut then_fragment = ctx.new_fragment();
930 then_fragment.push_literal(1i32, Span::call_site());
931 let mut else_fragment = ctx.new_fragment();
932 else_fragment.push_literal(2i32, Span::call_site());
933 ctx.join2(
934 then_fragment,
935 Some(else_fragment),
936 Span::call_site(),
937 Span::call_site(),
938 )
939 .unwrap();
940 match ctx.into_expr() {
941 Expr::If {
942 cond,
943 then_branch,
944 else_branch,
945 ..
946 } => {
947 assert!(matches!(
948 *cond,
949 Expr::Literal {
950 value: Literal::Bool(true),
951 ..
952 }
953 ));
954 assert!(matches!(
955 *then_branch,
956 Expr::Literal {
957 value: Literal::I32(1),
958 ..
959 }
960 ));
961 let else_branch = else_branch.expect("explicit else was given");
962 assert!(matches!(
963 *else_branch,
964 Expr::Literal {
965 value: Literal::I32(2),
966 ..
967 }
968 ));
969 }
970 other => panic!("expected If, got {other:?}"),
971 }
972 }
973
974 #[test]
975 fn join2_with_none_records_no_else_branch() {
976 let mut ctx = AstContext::new_context();
977 ctx.push_literal(true, Span::call_site());
978 let mut then_fragment = ctx.new_fragment();
979 then_fragment.push_literal(1i32, Span::call_site());
980 ctx.join2(then_fragment, None, Span::call_site(), Span::call_site())
981 .unwrap();
982 match ctx.into_expr() {
983 Expr::If { else_branch, .. } => assert!(else_branch.is_none()),
984 other => panic!("expected If, got {other:?}"),
985 }
986 }
987
988 #[test]
989 fn make_tuple_and_tuple_index_roundtrip() {
990 let mut ctx = AstContext::new_context();
991 let ambient_start = ctx.current_stack_offset();
992 ctx.push_literal(1i32, Span::call_site());
993 ctx.push_literal(2i32, Span::call_site());
994 ctx.make_tuple(2, ambient_start, Span::call_site(), Span::call_site())
995 .unwrap();
996 assert_eq!(ctx.peek_tuple_arity(), Some(usize::MAX));
997 ctx.tuple_index(1, Span::call_site(), Span::call_site())
998 .unwrap();
999 match ctx.into_expr() {
1000 Expr::TupleIndex { base, index, .. } => {
1001 assert_eq!(index, 1);
1002 assert!(matches!(*base, Expr::Tuple { ref elements, .. } if elements.len() == 2));
1003 }
1004 other => panic!("expected TupleIndex, got {other:?}"),
1005 }
1006 }
1007
1008 #[test]
1009 fn peek_tuple_arity_is_always_some_since_types_are_unresolved_during_parsing() {
1010 let mut ctx = AstContext::new_context();
1011 ctx.push_literal(5i32, Span::call_site());
1012 assert_eq!(ctx.peek_tuple_arity(), Some(usize::MAX));
1013 }
1014
1015 #[test]
1016 fn additive_binds_looser_than_multiplicative() {
1017 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1018 let expr = parser.parse_str_ast("1 + 2 * 3").unwrap();
1019 let Expr::Op { name, operands, .. } = expr else {
1020 panic!("expected Op");
1021 };
1022 assert_eq!(name, "+");
1023 assert!(matches!(
1024 operands[0],
1025 Expr::Literal {
1026 value: Literal::I32(1),
1027 ..
1028 }
1029 ));
1030 let Expr::Op {
1031 name: inner_name,
1032 operands: mul_operands,
1033 ..
1034 } = &operands[1]
1035 else {
1036 panic!("expected nested Op");
1037 };
1038 assert_eq!(inner_name, "*");
1039 assert!(matches!(
1040 mul_operands[0],
1041 Expr::Literal {
1042 value: Literal::I32(2),
1043 ..
1044 }
1045 ));
1046 assert!(matches!(
1047 mul_operands[1],
1048 Expr::Literal {
1049 value: Literal::I32(3),
1050 ..
1051 }
1052 ));
1053 }
1054
1055 #[test]
1056 fn unary_minus_and_not_are_recorded_as_arity_one_op_nodes() {
1057 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1058 let expr = parser.parse_str_ast("-1i32").unwrap();
1059 let Expr::Op { name, operands, .. } = expr else {
1060 panic!("expected Op");
1061 };
1062 assert_eq!(name, "-");
1063 assert_eq!(operands.len(), 1);
1064
1065 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1066 let expr = parser.parse_str_ast("!true").unwrap();
1067 let Expr::Op { name, operands, .. } = expr else {
1068 panic!("expected Op");
1069 };
1070 assert_eq!(name, "!");
1071 assert_eq!(operands.len(), 1);
1072 }
1073
1074 #[test]
1075 fn bare_identifier_is_an_ident_node() {
1076 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1077 let expr = parser.parse_str_ast("x").unwrap();
1078 assert!(matches!(expr, Expr::Ident { name, .. } if name == "x"));
1079 }
1080
1081 #[test]
1082 fn call_with_no_args_and_with_args() {
1083 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1084 let expr = parser.parse_str_ast("f()").unwrap();
1085 let Expr::Apply { callee, args, .. } = expr else {
1086 panic!("expected Apply");
1087 };
1088 assert!(matches!(*callee, Expr::Ident { ref name, .. } if name == "f"));
1089 assert_eq!(args.len(), 0);
1090
1091 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1092 let expr = parser.parse_str_ast("f(1i32, 2i32)").unwrap();
1093 let Expr::Apply { args, .. } = expr else {
1094 panic!("expected Apply");
1095 };
1096 assert_eq!(args.len(), 2);
1097 }
1098
1099 #[test]
1100 fn chained_calls_nest_apply_nodes() {
1101 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1102 let expr = parser.parse_str_ast("f()()").unwrap();
1103 let Expr::Apply { callee, args, .. } = expr else {
1104 panic!("expected outer Apply");
1105 };
1106 assert_eq!(args.len(), 0);
1107 assert!(matches!(*callee, Expr::Apply { .. }));
1108 }
1109
1110 #[test]
1111 fn unit_grouping_and_tuples() {
1112 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1113 assert!(matches!(
1114 parser.parse_str_ast("()").unwrap(),
1115 Expr::Literal {
1116 value: Literal::Unit,
1117 ..
1118 }
1119 ));
1120
1121 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1122 assert!(matches!(
1124 parser.parse_str_ast("(1i32 + 2i32)").unwrap(),
1125 Expr::Op { .. }
1126 ));
1127
1128 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1129 let expr = parser.parse_str_ast("(1i32,)").unwrap();
1130 assert!(matches!(expr, Expr::Tuple { ref elements, .. } if elements.len() == 1));
1131
1132 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1133 let expr = parser.parse_str_ast("(1i32, 2i32, 3i32)").unwrap();
1134 assert!(matches!(expr, Expr::Tuple { ref elements, .. } if elements.len() == 3));
1135 }
1136
1137 #[test]
1138 fn tuple_index_single_and_chained() {
1139 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1140 let expr = parser.parse_str_ast("(1i32, 2i32).1").unwrap();
1141 let Expr::TupleIndex { base, index, .. } = expr else {
1142 panic!("expected TupleIndex");
1143 };
1144 assert_eq!(index, 1);
1145 assert!(matches!(*base, Expr::Tuple { ref elements, .. } if elements.len() == 2));
1146
1147 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1148 let expr = parser.parse_str_ast("((1i32, 2i32), 3i32).0.1").unwrap();
1150 let Expr::TupleIndex { base, index, .. } = expr else {
1151 panic!("expected outer TupleIndex");
1152 };
1153 assert_eq!(index, 1);
1154 assert!(matches!(*base, Expr::TupleIndex { index: 0, .. }));
1155 }
1156
1157 #[test]
1158 fn tuple_index_on_a_call_result_still_builds_a_tree() {
1159 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1163 let expr = parser.parse_str_ast("f().5").unwrap();
1164 assert!(matches!(expr, Expr::TupleIndex { index: 5, .. }));
1165 }
1166
1167 #[test]
1168 fn if_without_else_has_no_else_branch() {
1169 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1170 let expr = parser.parse_str_ast("if true { 1i32 }").unwrap();
1171 let Expr::If {
1172 cond,
1173 then_branch,
1174 else_branch,
1175 ..
1176 } = expr
1177 else {
1178 panic!("expected If");
1179 };
1180 assert!(matches!(
1181 *cond,
1182 Expr::Literal {
1183 value: Literal::Bool(true),
1184 ..
1185 }
1186 ));
1187 assert!(matches!(
1188 *then_branch,
1189 Expr::Literal {
1190 value: Literal::I32(1),
1191 ..
1192 }
1193 ));
1194 assert!(else_branch.is_none());
1195 }
1196
1197 #[test]
1198 fn if_else_and_else_if_chain() {
1199 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1200 let expr = parser
1201 .parse_str_ast("if true { 1i32 } else { 2i32 }")
1202 .unwrap();
1203 let Expr::If { else_branch, .. } = expr else {
1204 panic!("expected If");
1205 };
1206 assert!(matches!(
1207 else_branch.as_deref(),
1208 Some(Expr::Literal {
1209 value: Literal::I32(2),
1210 ..
1211 })
1212 ));
1213
1214 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1215 let expr = parser
1216 .parse_str_ast("if true { 1i32 } else if false { 2i32 } else { 3i32 }")
1217 .unwrap();
1218 let Expr::If { else_branch, .. } = expr else {
1219 panic!("expected outer If");
1220 };
1221 assert!(matches!(else_branch.as_deref(), Some(Expr::If { .. })));
1222 }
1223
1224 #[test]
1225 fn logical_or_is_not_desugared_to_if() {
1226 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1227 let expr = parser.parse_str_ast("a || b").unwrap();
1228 let Expr::Logical { op, lhs, rhs, .. } = expr else {
1229 panic!("expected Logical");
1230 };
1231 assert_eq!(op, LogicalOp::Or);
1232 assert!(matches!(*lhs, Expr::Ident { ref name, .. } if name == "a"));
1233 assert!(matches!(*rhs, Expr::Ident { ref name, .. } if name == "b"));
1234 }
1235
1236 #[test]
1237 fn logical_and_is_not_desugared_to_if() {
1238 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1239 let expr = parser.parse_str_ast("a && b").unwrap();
1240 let Expr::Logical { op, .. } = expr else {
1241 panic!("expected Logical");
1242 };
1243 assert_eq!(op, LogicalOp::And);
1244 }
1245
1246 #[test]
1247 fn logical_mixes_with_comparison_at_the_right_precedence() {
1248 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1249 let expr = parser.parse_str_ast("a == 1i32 && b == 2i32").unwrap();
1251 let Expr::Logical { op, lhs, rhs, .. } = expr else {
1252 panic!("expected Logical");
1253 };
1254 assert_eq!(op, LogicalOp::And);
1255 assert!(matches!(*lhs, Expr::Op { ref name, .. } if name == "=="));
1256 assert!(matches!(*rhs, Expr::Op { ref name, .. } if name == "=="));
1257 }
1258
1259 #[test]
1260 fn for_text_produces_a_span_whose_source_text_matches() {
1261 let span = ExprSpan::for_text("i64");
1262 assert_eq!(span.start.source_text().as_deref(), Some("i64"));
1263 assert_eq!(span.end.source_text().as_deref(), Some("i64"));
1264 }
1265
1266 #[test]
1267 fn for_text_works_for_a_string_literal() {
1268 let span = ExprSpan::for_text("\"hello\"");
1269 assert_eq!(span.start.source_text().as_deref(), Some("\"hello\""));
1270 }
1271
1272 #[test]
1273 fn make_array_pushes_one_array_node_holding_the_elements_in_source_order() {
1274 let mut ctx = AstContext::new_context();
1275 let ambient_start = ctx.current_stack_offset();
1276 ctx.push_literal(1i32, Span::call_site());
1277 ctx.push_literal(2i32, Span::call_site());
1278 ctx.make_array(2, ambient_start, Span::call_site(), Span::call_site())
1279 .unwrap();
1280 match ctx.into_expr() {
1281 Expr::Array { elements, .. } => {
1282 assert!(matches!(
1283 elements[0],
1284 Expr::Literal {
1285 value: Literal::I32(1),
1286 ..
1287 }
1288 ));
1289 assert!(matches!(
1290 elements[1],
1291 Expr::Literal {
1292 value: Literal::I32(2),
1293 ..
1294 }
1295 ));
1296 }
1297 other => panic!("expected Array, got {other:?}"),
1298 }
1299 }
1300
1301 #[test]
1302 fn array_literal_records_elements_in_source_order_with_an_enclosing_span() {
1303 let source = "[0i32, 1i32, 2i32]";
1304 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1305 let expr = parser.parse_str_ast(source).unwrap();
1306 let Expr::Array { elements, span, .. } = expr else {
1307 panic!("expected Array");
1308 };
1309 assert_eq!(elements.len(), 3);
1310 for (index, element) in elements.iter().enumerate() {
1311 let Expr::Literal { value, .. } = element else {
1312 panic!("expected a literal element");
1313 };
1314 assert_eq!(*value, Literal::I32(i32::try_from(index).unwrap()));
1315 }
1316 assert_eq!(span.start.source_text().as_deref(), Some(source));
1317 assert_eq!(span.end.source_text().as_deref(), Some(source));
1318 }
1319
1320 #[test]
1321 fn nested_array_literal_nests_array_nodes() {
1322 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1323 let expr = parser.parse_str_ast("[[0i32], [1i32]]").unwrap();
1324 let Expr::Array { elements, .. } = expr else {
1325 panic!("expected outer Array");
1326 };
1327 assert_eq!(elements.len(), 2);
1328 for element in &elements {
1329 assert!(matches!(element, Expr::Array { elements, .. } if elements.len() == 1));
1330 }
1331 }
1332
1333 #[test]
1334 fn typed_array_literal_still_builds_an_array_node() {
1335 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1336 let expr = parser.parse_str_ast("[0, 1]: [i32]").unwrap();
1337 let Expr::Array {
1338 elements,
1339 type_annotation,
1340 annotation_span,
1341 ..
1342 } = expr
1343 else {
1344 panic!("expected Array");
1345 };
1346 assert_eq!(elements.len(), 2);
1347 assert!(annotation_span.is_some());
1348 match type_annotation {
1349 Some(TypeExpr::Array { element, .. }) => {
1350 assert!(matches!(*element, TypeExpr::Named { ref name, .. } if name == "i32"));
1351 }
1352 other => panic!("expected an array type annotation, got {other:?}"),
1353 }
1354 }
1355
1356 #[test]
1357 fn typed_empty_array_literal_builds_an_empty_array_node() {
1358 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1359 let expr = parser.parse_str_ast("[]: [i32]").unwrap();
1360 assert!(matches!(
1361 expr,
1362 Expr::Array {
1363 ref elements,
1364 type_annotation: Some(_),
1365 annotation_span: Some(_),
1366 ..
1367 } if elements.is_empty()
1368 ));
1369 }
1370
1371 #[test]
1372 fn typed_array_literal_preserves_the_exact_annotation_span() {
1373 let source = "[1.0, 42.5]: [f64]";
1374 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1375 let expr = parser.parse_str_ast(source).unwrap();
1376 let Expr::Array {
1377 type_annotation: Some(_),
1378 annotation_span: Some(annotation_span),
1379 ..
1380 } = expr
1381 else {
1382 panic!("expected Array");
1383 };
1384 let start = annotation_span.start.start();
1385 let end = annotation_span.end.end();
1386 assert_eq!(&source[start.column..end.column], ": [f64]");
1387 }
1388
1389 #[test]
1390 fn array_literals_build_nodes_in_call_argument_and_tuple_positions() {
1391 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1392 let expr = parser.parse_str_ast("f([1i32], (2i32, [3i32]))").unwrap();
1393 let Expr::Apply { args, .. } = expr else {
1394 panic!("expected Apply");
1395 };
1396 assert!(matches!(args[0], Expr::Array { ref elements, .. } if elements.len() == 1));
1397 let Expr::Tuple { elements, .. } = &args[1] else {
1398 panic!("expected a tuple argument");
1399 };
1400 assert!(matches!(elements[1], Expr::Array { .. }));
1401 }
1402
1403 #[test]
1404 fn array_literals_build_nodes_in_if_branch_and_closure_body_positions() {
1405 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1406 let expr = parser
1407 .parse_str_ast("if true { [1i32] } else { [2i32] }")
1408 .unwrap();
1409 let Expr::If {
1410 then_branch,
1411 else_branch,
1412 ..
1413 } = expr
1414 else {
1415 panic!("expected If");
1416 };
1417 assert!(matches!(*then_branch, Expr::Array { .. }));
1418 assert!(matches!(else_branch.as_deref(), Some(Expr::Array { .. })));
1419
1420 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1421 let expr = parser.parse_str_ast("|x: i32| [x]").unwrap();
1422 let Expr::Closure { body, .. } = expr else {
1423 panic!("expected Closure");
1424 };
1425 assert!(matches!(*body, Expr::Array { .. }));
1426 }
1427
1428 #[test]
1429 fn array_literal_with_a_tuple_element_still_builds_a_tree() {
1430 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1432 let expr = parser.parse_str_ast("[(1i32, 2i32)]").unwrap();
1433 assert!(matches!(expr, Expr::Array { ref elements, .. } if elements.len() == 1));
1434 }
1435
1436 #[test]
1437 fn ast_context_still_rejects_an_empty_array_literal() {
1438 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1441 let err = parser
1442 .parse_str_ast("[]")
1443 .expect_err("an empty array literal has no inferable element type");
1444 assert!(
1445 err.message().contains("empty array"),
1446 "got: {}",
1447 err.message()
1448 );
1449 }
1450}