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)]
35pub struct ExprSpan {
36 pub start: Span,
38 pub end: Span,
40}
41
42impl ExprSpan {
43 fn point(span: Span) -> Self {
45 ExprSpan {
46 start: span,
47 end: span,
48 }
49 }
50
51 #[must_use]
70 pub fn for_text(text: &str) -> Self {
71 let tokens: proc_macro2::TokenStream = text
72 .parse()
73 .expect("ExprSpan::for_text: text must be valid tokens");
74 let mut iter = tokens.into_iter();
75 let first = iter
76 .next()
77 .expect("ExprSpan::for_text: text must be non-empty");
78 debug_assert!(
79 iter.next().is_none(),
80 "ExprSpan::for_text: text must tokenize to exactly one token"
81 );
82 let span = first.span();
83 ExprSpan {
84 start: span,
85 end: span,
86 }
87 }
88}
89
90#[derive(Clone, Debug, PartialEq)]
101pub enum Literal {
102 I8(i8),
104 I16(i16),
106 I32(i32),
108 I64(i64),
110 I128(i128),
112 Isize(isize),
114 U8(u8),
116 U16(u16),
118 U32(u32),
120 U64(u64),
122 U128(u128),
124 Usize(usize),
126 F32(f32),
128 F64(f64),
130 Bool(bool),
132 Char(char),
134 Str(String),
136 ByteStr(Vec<u8>),
138 CStr(CString),
140 Unit,
142}
143
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
149pub enum LogicalOp {
150 And,
152 Or,
154}
155
156#[derive(Clone, Debug)]
170pub enum Expr {
171 Struct {
181 name: String,
183 name_span: ExprSpan,
185 fields: Vec<RecordInit>,
187 span: ExprSpan,
189 },
190 Literal {
192 value: Literal,
194 span: ExprSpan,
196 },
197 Ident {
199 name: String,
201 span: ExprSpan,
203 },
204 Op {
206 name: String,
208 operands: Vec<Expr>,
210 span: ExprSpan,
212 },
213 Apply {
215 callee: Box<Expr>,
217 args: Vec<Expr>,
219 span: ExprSpan,
221 },
222 Member {
233 receiver: Box<Expr>,
235 name: String,
237 name_span: ExprSpan,
239 args: Option<Vec<Expr>>,
241 span: ExprSpan,
243 },
244 Tuple {
246 elements: Vec<Expr>,
248 span: ExprSpan,
250 },
251 Array {
259 elements: Vec<Expr>,
261 type_annotation: Option<TypeExpr>,
263 annotation_span: Option<ExprSpan>,
265 span: ExprSpan,
267 },
268 TupleIndex {
271 base: Box<Expr>,
273 index: usize,
275 span: ExprSpan,
277 },
278 If {
280 cond: Box<Expr>,
282 then_branch: Box<Expr>,
284 else_branch: Option<Box<Expr>>,
286 span: ExprSpan,
288 },
289 Logical {
291 op: LogicalOp,
293 lhs: Box<Expr>,
295 rhs: Box<Expr>,
297 span: ExprSpan,
299 },
300 Cast {
304 expr: Box<Expr>,
306 type_name: String,
308 span: ExprSpan,
310 },
311 Closure {
313 params: Vec<ClosureParam>,
315 body: Box<Expr>,
317 span: ExprSpan,
319 },
320}
321
322impl Expr {
323 pub fn span(&self) -> ExprSpan {
333 match self {
334 Expr::Literal { span, .. }
335 | Expr::Struct { span, .. }
336 | Expr::Ident { span, .. }
337 | Expr::Op { span, .. }
338 | Expr::Apply { span, .. }
339 | Expr::Member { span, .. }
340 | Expr::Tuple { span, .. }
341 | Expr::Array { span, .. }
342 | Expr::TupleIndex { span, .. }
343 | Expr::If { span, .. }
344 | Expr::Logical { span, .. }
345 | Expr::Cast { span, .. }
346 | Expr::Closure { span, .. } => *span,
347 }
348 }
349}
350
351#[derive(Clone, Debug)]
363pub struct RecordInit {
364 pub name: String,
366 pub name_span: ExprSpan,
368 pub value: Expr,
370}
371
372#[derive(Clone, Debug)]
383pub struct ClosureParam {
384 pub name: String,
386 pub name_span: ExprSpan,
388 pub type_expr: crate::TypeExpr,
390}
391
392pub(crate) fn closure_parameter_diagnostics(params: &[ClosureParam]) -> Vec<crate::ParseError> {
396 let mut names = std::collections::HashSet::with_capacity(params.len());
397 params
398 .iter()
399 .filter_map(|param| {
400 let message = if matches!(param.name.as_str(), "if" | "else" | "as" | "true" | "false")
401 {
402 "reserved keyword is not a closure parameter name"
403 } else if !names.insert(param.name.as_str()) {
404 "duplicate closure parameter name"
405 } else {
406 return None;
407 };
408 Some(crate::ParseError::new_range(
409 message,
410 param.name_span.start,
411 param.name_span.end,
412 ))
413 })
414 .collect()
415}
416
417fn to_literal<T: 'static>(value: T) -> Literal {
423 let mut value = Some(value);
425 let any = &mut value as &mut dyn Any;
426 macro_rules! map {
427 ($($t:ty => $variant:path),+ $(,)?) => {
428 $( if let Some(x) = any.downcast_mut::<Option<$t>>() {
429 return $variant(x.take().expect("literal is transferred once"));
430 } )+
431 };
432 }
433 map! {
434 i8 => Literal::I8, i16 => Literal::I16, i32 => Literal::I32, i64 => Literal::I64,
435 i128 => Literal::I128, isize => Literal::Isize,
436 u8 => Literal::U8, u16 => Literal::U16, u32 => Literal::U32, u64 => Literal::U64,
437 u128 => Literal::U128, usize => Literal::Usize,
438 f32 => Literal::F32, f64 => Literal::F64,
439 bool => Literal::Bool, char => Literal::Char, String => Literal::Str,
440 Vec<u8> => Literal::ByteStr, CString => Literal::CStr,
441 }
442 if any.is::<Option<()>>() {
443 return Literal::Unit;
444 }
445 unreachable!("push_literal called with an unsupported literal type")
446}
447
448#[derive(Debug, Default)]
463pub struct AstContext {
464 values: Vec<Expr>,
466}
467
468impl AstContext {
469 fn pop(&mut self) -> Expr {
473 self.values
474 .pop()
475 .expect("operand present on AST value stack")
476 }
477
478 fn pop_n(&mut self, n: usize) -> Vec<Expr> {
483 let at = self.values.len() - n;
484 self.values.split_off(at)
485 }
486
487 pub fn into_expr(mut self) -> Expr {
499 debug_assert_eq!(
500 self.values.len(),
501 1,
502 "a successfully parsed expression leaves exactly one node"
503 );
504 self.pop()
505 }
506}
507
508impl ParserContext for AstContext {
509 fn resolves_closure_types() -> bool {
511 false
512 }
513
514 fn new_context() -> Self {
516 AstContext { values: Vec::new() }
517 }
518
519 fn new_fragment(&self) -> Self {
521 AstContext { values: Vec::new() }
522 }
523
524 fn push_literal<T: 'static + Clone>(&mut self, value: T, span: Span) {
526 self.values.push(Expr::Literal {
527 value: to_literal(value),
528 span: ExprSpan::point(span),
529 });
530 }
531
532 fn apply_op(
537 &mut self,
538 _op_lookup: &OpLookup,
539 name: &str,
540 arity: usize,
541 start: Span,
542 end: Span,
543 ) -> crate::Result<()> {
544 let span = ExprSpan { start, end };
545 if arity == 0 {
546 self.values.push(Expr::Ident {
547 name: name.to_string(),
548 span,
549 });
550 } else if name == "()" {
551 let mut operands = self.pop_n(arity); let callee = operands.remove(0);
553 self.values.push(Expr::Apply {
554 callee: Box::new(callee),
555 args: operands,
556 span,
557 });
558 } else {
559 let operands = self.pop_n(arity); self.values.push(Expr::Op {
561 name: name.to_string(),
562 operands,
563 span,
564 });
565 }
566 Ok(())
567 }
568
569 fn apply_member(
573 &mut self,
574 _lookup: &OpLookup,
575 name: &str,
576 name_span: ExprSpan,
577 explicit_args: Option<usize>,
578 start: Span,
579 end: Span,
580 ) -> crate::Result<()> {
581 debug_assert!(self.values.len() > explicit_args.unwrap_or(0));
582 let args = explicit_args.map(|count| self.pop_n(count));
583 let receiver = self.pop();
584 self.values.push(Expr::Member {
585 receiver: Box::new(receiver),
586 name: name.to_string(),
587 name_span,
588 args,
589 span: ExprSpan { start, end },
590 });
591 Ok(())
592 }
593
594 fn make_record(
598 &mut self,
599 _lookup: &OpLookup,
600 name: &str,
601 name_span: ExprSpan,
602 fields: Vec<(String, ExprSpan)>,
603 _ambient_start: usize,
604 start: Span,
605 end: Span,
606 ) -> crate::Result<()> {
607 let values = self.pop_n(fields.len());
608 let fields = fields
609 .into_iter()
610 .zip(values)
611 .map(|((name, name_span), value)| RecordInit {
612 name,
613 name_span,
614 value,
615 })
616 .collect();
617 self.values.push(Expr::Struct {
618 name: name.to_string(),
619 name_span,
620 fields,
621 span: ExprSpan { start, end },
622 });
623 Ok(())
624 }
625
626 fn apply_logical(
630 &mut self,
631 name: &str,
632 mut rhs: Self,
633 start: Span,
634 end: Span,
635 ) -> crate::Result<()> {
636 let op = match name {
637 "||" => LogicalOp::Or,
638 "&&" => LogicalOp::And,
639 other => unreachable!("apply_logical called with unsupported operator `{other}`"),
640 };
641 let lhs = self.pop();
642 debug_assert_eq!(
643 rhs.values.len(),
644 1,
645 "rhs fragment produces exactly one value"
646 );
647 let rhs_expr = rhs.pop();
648 self.values.push(Expr::Logical {
649 op,
650 lhs: Box::new(lhs),
651 rhs: Box::new(rhs_expr),
652 span: ExprSpan { start, end },
653 });
654 Ok(())
655 }
656
657 fn join2(
661 &mut self,
662 mut then_fragment: Self,
663 else_fragment: Option<Self>,
664 start: Span,
665 end: Span,
666 ) -> anyhow::Result<()> {
667 let cond = self.pop();
668 debug_assert_eq!(
669 then_fragment.values.len(),
670 1,
671 "then fragment produces exactly one value"
672 );
673 let then_branch = then_fragment.pop();
674 let else_branch = else_fragment.map(|mut fragment| {
675 debug_assert_eq!(
676 fragment.values.len(),
677 1,
678 "else fragment produces exactly one value"
679 );
680 Box::new(fragment.pop())
681 });
682 self.values.push(Expr::If {
683 cond: Box::new(cond),
684 then_branch: Box::new(then_branch),
685 else_branch,
686 span: ExprSpan { start, end },
687 });
688 Ok(())
689 }
690
691 fn make_tuple(
696 &mut self,
697 n: usize,
698 ambient_start: usize,
699 start: Span,
700 end: Span,
701 ) -> crate::Result<()> {
702 if n == 0 {
703 self.values.push(Expr::Literal {
704 value: Literal::Unit,
705 span: ExprSpan { start, end },
706 });
707 return Ok(());
708 }
709 let elements = self.values.split_off(ambient_start);
710 debug_assert_eq!(
711 elements.len(),
712 n,
713 "make_tuple splits off exactly n elements"
714 );
715 self.values.push(Expr::Tuple {
716 elements,
717 span: ExprSpan { start, end },
718 });
719 Ok(())
720 }
721
722 fn make_array(
727 &mut self,
728 n: usize,
729 ambient_start: usize,
730 start: Span,
731 end: Span,
732 ) -> crate::Result<()> {
733 let elements = self.values.split_off(ambient_start);
734 debug_assert_eq!(
735 elements.len(),
736 n,
737 "make_array splits off exactly n elements"
738 );
739 self.values.push(Expr::Array {
740 elements,
741 type_annotation: None,
742 annotation_span: None,
743 span: ExprSpan { start, end },
744 });
745 Ok(())
746 }
747
748 fn make_annotated_array(
753 &mut self,
754 n: usize,
755 ambient_start: usize,
756 annotation: crate::parser_context::AnnotatedArray<'_>,
757 start: Span,
758 end: Span,
759 ) -> crate::Result<()> {
760 let elements = self.values.split_off(ambient_start);
761 debug_assert_eq!(
762 elements.len(),
763 n,
764 "make_annotated_array splits off exactly n elements"
765 );
766 let (type_annotation, annotation_span) = annotation.into_parts();
767 self.values.push(Expr::Array {
768 elements,
769 type_annotation,
770 annotation_span,
771 span: ExprSpan { start, end },
772 });
773 Ok(())
774 }
775
776 fn peek_tuple_arity(&self) -> Option<usize> {
778 None
779 }
780
781 fn tuple_index(&mut self, index: usize, start: Span, end: Span) -> crate::Result<()> {
785 let base = self.pop();
786 self.values.push(Expr::TupleIndex {
787 base: Box::new(base),
788 index,
789 span: ExprSpan { start, end },
790 });
791 Ok(())
792 }
793
794 fn current_stack_offset(&self) -> usize {
796 self.values.len()
797 }
798
799 fn apply_cast(
804 &mut self,
805 _op_lookup: &OpLookup,
806 type_name: &str,
807 start: Span,
808 end: Span,
809 ) -> crate::Result<()> {
810 let expr = self.pop();
811 self.values.push(Expr::Cast {
812 expr: Box::new(expr),
813 type_name: type_name.to_string(),
814 span: ExprSpan { start, end },
815 });
816 Ok(())
817 }
818
819 fn push_closure(
823 &mut self,
824 param_types: Vec<cel_runtime::ValueType>,
825 params: Vec<ClosureParam>,
826 body: Self,
827 span: Span,
828 ) -> crate::Result<()> {
829 let _ = param_types;
830 let body_expr = body.into_expr();
831 let end = body_expr.span().end;
832 self.values.push(Expr::Closure {
833 params,
834 body: Box::new(body_expr),
835 span: ExprSpan { start: span, end },
836 });
837 Ok(())
838 }
839}
840
841#[cfg(test)]
842mod tests {
843 use super::*;
844 use crate::Parser;
845
846 #[test]
848 fn parameter_diagnostics_use_the_offending_formals_span() {
849 let Expr::Closure { mut params, .. } = Parser::<AstContext>::new(OpLookup::new())
850 .parse_str_ast("|x: i32, y: bool| x")
851 .unwrap()
852 else {
853 panic!("closure")
854 };
855 assert!(closure_parameter_diagnostics(¶ms).is_empty());
856 params[1].name = "x".to_owned();
857 let errors = closure_parameter_diagnostics(¶ms);
858 assert_eq!(errors.len(), 1);
859 assert!(errors[0].message().contains("duplicate"));
860 assert_eq!(errors[0].span().start().column, 9);
861 params[1].name = "as".to_owned();
862 assert!(
863 closure_parameter_diagnostics(¶ms)[0]
864 .message()
865 .contains("reserved")
866 );
867 }
868
869 #[test]
871 fn owned_literal_buffers_transfer_without_copying() {
872 let text = String::from("owned");
873 let pointer = text.as_ptr();
874 let Literal::Str(text) = to_literal(text) else {
875 panic!("string literal")
876 };
877 assert_eq!(text, "owned");
878 assert_eq!(text.as_ptr(), pointer);
879 let bytes = vec![1u8, 2, 3];
880 let pointer = bytes.as_ptr();
881 let Literal::ByteStr(bytes) = to_literal(bytes) else {
882 panic!("byte string literal")
883 };
884 assert_eq!(bytes, [1, 2, 3]);
885 assert_eq!(bytes.as_ptr(), pointer);
886 let text = CString::new("owned").unwrap();
887 let pointer = text.as_ptr();
888 let Literal::CStr(text) = to_literal(text) else {
889 panic!("C string literal")
890 };
891 assert_eq!(text.to_bytes(), b"owned");
892 assert_eq!(text.as_ptr(), pointer);
893 }
894
895 fn distinct_span() -> ExprSpan {
897 let tokens: proc_macro2::TokenStream = "\n outer\n end".parse().unwrap();
898 let mut tokens = tokens.into_iter();
899 ExprSpan {
900 start: tokens.next().unwrap().span(),
901 end: tokens.next().unwrap().span(),
902 }
903 }
904 use proc_macro2::Span;
905
906 #[test]
907 fn span_returns_the_range_stored_on_a_leaf_variant() {
908 let target = distinct_span();
909 let expr = Expr::Ident {
910 name: "x".to_string(),
911 span: target,
912 };
913 assert_eq!(
914 crate::SourceSpan::from_proc_macro2_range(expr.span().start, expr.span().end),
915 crate::SourceSpan::new(2, 1, 3, 4)
916 );
917 }
918
919 #[test]
920 fn span_returns_the_range_stored_on_a_composite_variant() {
921 let target = distinct_span();
922 let expr = Expr::If {
923 cond: Box::new(Expr::Literal {
924 value: Literal::Bool(true),
925 span: ExprSpan::point(Span::call_site()),
926 }),
927 then_branch: Box::new(Expr::Literal {
928 value: Literal::I32(1),
929 span: ExprSpan::point(Span::call_site()),
930 }),
931 else_branch: Some(Box::new(Expr::Literal {
932 value: Literal::I32(2),
933 span: ExprSpan::point(Span::call_site()),
934 })),
935 span: target,
936 };
937 assert_eq!(
938 crate::SourceSpan::from_proc_macro2_range(expr.span().start, expr.span().end),
939 crate::SourceSpan::new(2, 1, 3, 4)
940 );
941 }
942
943 #[test]
944 fn closure_span_is_the_stored_span() {
945 let target = distinct_span();
946 let expr = Expr::Closure {
947 params: Vec::new(),
948 body: Box::new(Expr::Literal {
949 value: Literal::I32(1),
950 span: ExprSpan::point(Span::call_site()),
951 }),
952 span: target,
953 };
954 assert_eq!(
955 crate::SourceSpan::from_proc_macro2_range(expr.span().start, expr.span().end),
956 crate::SourceSpan::new(2, 1, 3, 4)
957 );
958 }
959
960 #[test]
961 fn literal_variants_are_distinguishable_and_comparable() {
962 assert_eq!(Literal::I32(1), Literal::I32(1));
963 assert_ne!(Literal::I32(1), Literal::I32(2));
964 assert_ne!(Literal::I32(1), Literal::U8(1));
965 assert_eq!(Literal::Unit, Literal::Unit);
966 }
967
968 #[test]
969 fn logical_op_equality() {
970 assert_eq!(LogicalOp::And, LogicalOp::And);
971 assert_ne!(LogicalOp::And, LogicalOp::Or);
972 }
973
974 #[test]
975 fn push_literal_dispatches_every_concrete_literal_type() {
976 fn literal_of<T: 'static + Clone>(value: T) -> Literal {
977 let mut ctx = AstContext::new_context();
978 ctx.push_literal(value, Span::call_site());
979 match ctx.into_expr() {
980 Expr::Literal { value, .. } => value,
981 other => panic!("expected Literal, got {other:?}"),
982 }
983 }
984 assert_eq!(literal_of(1i8), Literal::I8(1));
985 assert_eq!(literal_of(1i16), Literal::I16(1));
986 assert_eq!(literal_of(1i32), Literal::I32(1));
987 assert_eq!(literal_of(1i64), Literal::I64(1));
988 assert_eq!(literal_of(1i128), Literal::I128(1));
989 assert_eq!(literal_of(1isize), Literal::Isize(1));
990 assert_eq!(literal_of(1u8), Literal::U8(1));
991 assert_eq!(literal_of(1u16), Literal::U16(1));
992 assert_eq!(literal_of(1u32), Literal::U32(1));
993 assert_eq!(literal_of(1u64), Literal::U64(1));
994 assert_eq!(literal_of(1u128), Literal::U128(1));
995 assert_eq!(literal_of(1usize), Literal::Usize(1));
996 assert_eq!(literal_of(1.0f32), Literal::F32(1.0));
997 assert_eq!(literal_of(1.0f64), Literal::F64(1.0));
998 assert_eq!(literal_of(true), Literal::Bool(true));
999 assert_eq!(literal_of('a'), Literal::Char('a'));
1000 assert_eq!(literal_of("s".to_string()), Literal::Str("s".to_string()));
1001 assert_eq!(literal_of(vec![1u8, 2u8]), Literal::ByteStr(vec![1, 2]));
1002 assert_eq!(
1003 literal_of(CString::new("c").unwrap()),
1004 Literal::CStr(CString::new("c").unwrap())
1005 );
1006 assert_eq!(literal_of(()), Literal::Unit);
1007 }
1008
1009 #[test]
1010 fn apply_op_with_arity_zero_records_an_ident_node() {
1011 let mut ctx = AstContext::new_context();
1012 let lookup = OpLookup::new();
1013 ctx.apply_op(&lookup, "x", 0, Span::call_site(), Span::call_site())
1014 .unwrap();
1015 assert!(matches!(ctx.into_expr(), Expr::Ident { name, .. } if name == "x"));
1016 }
1017
1018 #[test]
1019 fn apply_op_with_the_call_operator_records_an_apply_node() {
1020 let mut ctx = AstContext::new_context();
1021 let lookup = OpLookup::new();
1022 ctx.apply_op(&lookup, "x", 0, Span::call_site(), Span::call_site())
1023 .unwrap(); ctx.push_literal(1i32, Span::call_site()); ctx.apply_op(&lookup, "()", 2, Span::call_site(), Span::call_site())
1026 .unwrap();
1027 match ctx.into_expr() {
1028 Expr::Apply { callee, args, .. } => {
1029 assert!(matches!(*callee, Expr::Ident { ref name, .. } if name == "x"));
1030 assert_eq!(args.len(), 1);
1031 }
1032 other => panic!("expected Apply, got {other:?}"),
1033 }
1034 }
1035
1036 #[test]
1037 fn apply_op_with_arity_two_records_an_op_node() {
1038 let mut ctx = AstContext::new_context();
1039 let lookup = OpLookup::new();
1040 ctx.push_literal(1i32, Span::call_site());
1041 ctx.push_literal(2i32, Span::call_site());
1042 ctx.apply_op(&lookup, "+", 2, Span::call_site(), Span::call_site())
1043 .unwrap();
1044 match ctx.into_expr() {
1045 Expr::Op { name, operands, .. } => {
1046 assert_eq!(name, "+");
1047 assert_eq!(operands.len(), 2);
1048 }
1049 other => panic!("expected Op, got {other:?}"),
1050 }
1051 }
1052
1053 #[test]
1054 fn apply_logical_records_a_logical_node() {
1055 let mut ctx = AstContext::new_context();
1056 ctx.push_literal(true, Span::call_site());
1057 let mut rhs = ctx.new_fragment();
1058 rhs.push_literal(false, Span::call_site());
1059 ctx.apply_logical("||", rhs, Span::call_site(), Span::call_site())
1060 .unwrap();
1061 match ctx.into_expr() {
1062 Expr::Logical { op, lhs, rhs, .. } => {
1063 assert_eq!(op, LogicalOp::Or);
1064 assert!(matches!(
1065 *lhs,
1066 Expr::Literal {
1067 value: Literal::Bool(true),
1068 ..
1069 }
1070 ));
1071 assert!(matches!(
1072 *rhs,
1073 Expr::Literal {
1074 value: Literal::Bool(false),
1075 ..
1076 }
1077 ));
1078 }
1079 other => panic!("expected Logical, got {other:?}"),
1080 }
1081 }
1082
1083 #[test]
1084 fn join2_records_an_if_node() {
1085 let mut ctx = AstContext::new_context();
1086 ctx.push_literal(true, Span::call_site());
1087 let mut then_fragment = ctx.new_fragment();
1088 then_fragment.push_literal(1i32, Span::call_site());
1089 let mut else_fragment = ctx.new_fragment();
1090 else_fragment.push_literal(2i32, Span::call_site());
1091 ctx.join2(
1092 then_fragment,
1093 Some(else_fragment),
1094 Span::call_site(),
1095 Span::call_site(),
1096 )
1097 .unwrap();
1098 match ctx.into_expr() {
1099 Expr::If {
1100 cond,
1101 then_branch,
1102 else_branch,
1103 ..
1104 } => {
1105 assert!(matches!(
1106 *cond,
1107 Expr::Literal {
1108 value: Literal::Bool(true),
1109 ..
1110 }
1111 ));
1112 assert!(matches!(
1113 *then_branch,
1114 Expr::Literal {
1115 value: Literal::I32(1),
1116 ..
1117 }
1118 ));
1119 let else_branch = else_branch.expect("explicit else was given");
1120 assert!(matches!(
1121 *else_branch,
1122 Expr::Literal {
1123 value: Literal::I32(2),
1124 ..
1125 }
1126 ));
1127 }
1128 other => panic!("expected If, got {other:?}"),
1129 }
1130 }
1131
1132 #[test]
1133 fn join2_with_none_records_no_else_branch() {
1134 let mut ctx = AstContext::new_context();
1135 ctx.push_literal(true, Span::call_site());
1136 let mut then_fragment = ctx.new_fragment();
1137 then_fragment.push_literal(1i32, Span::call_site());
1138 ctx.join2(then_fragment, None, Span::call_site(), Span::call_site())
1139 .unwrap();
1140 match ctx.into_expr() {
1141 Expr::If { else_branch, .. } => assert!(else_branch.is_none()),
1142 other => panic!("expected If, got {other:?}"),
1143 }
1144 }
1145
1146 #[test]
1147 fn make_tuple_and_tuple_index_roundtrip() {
1148 let mut ctx = AstContext::new_context();
1149 let ambient_start = ctx.current_stack_offset();
1150 ctx.push_literal(1i32, Span::call_site());
1151 ctx.push_literal(2i32, Span::call_site());
1152 ctx.make_tuple(2, ambient_start, Span::call_site(), Span::call_site())
1153 .unwrap();
1154 assert_eq!(ctx.peek_tuple_arity(), None);
1155 ctx.tuple_index(1, Span::call_site(), Span::call_site())
1156 .unwrap();
1157 match ctx.into_expr() {
1158 Expr::TupleIndex { base, index, .. } => {
1159 assert_eq!(index, 1);
1160 assert!(matches!(*base, Expr::Tuple { ref elements, .. } if elements.len() == 2));
1161 }
1162 other => panic!("expected TupleIndex, got {other:?}"),
1163 }
1164 }
1165
1166 #[test]
1167 fn tuple_arity_is_unknown_during_syntax_only_parsing() {
1168 let mut ctx = AstContext::new_context();
1169 ctx.push_literal(5i32, Span::call_site());
1170 assert_eq!(ctx.peek_tuple_arity(), None);
1171 }
1172
1173 #[test]
1174 fn additive_binds_looser_than_multiplicative() {
1175 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1176 let expr = parser.parse_str_ast("1 + 2 * 3").unwrap();
1177 let Expr::Op { name, operands, .. } = expr else {
1178 panic!("expected Op");
1179 };
1180 assert_eq!(name, "+");
1181 assert!(matches!(
1182 operands[0],
1183 Expr::Literal {
1184 value: Literal::I32(1),
1185 ..
1186 }
1187 ));
1188 let Expr::Op {
1189 name: inner_name,
1190 operands: mul_operands,
1191 ..
1192 } = &operands[1]
1193 else {
1194 panic!("expected nested Op");
1195 };
1196 assert_eq!(inner_name, "*");
1197 assert!(matches!(
1198 mul_operands[0],
1199 Expr::Literal {
1200 value: Literal::I32(2),
1201 ..
1202 }
1203 ));
1204 assert!(matches!(
1205 mul_operands[1],
1206 Expr::Literal {
1207 value: Literal::I32(3),
1208 ..
1209 }
1210 ));
1211 }
1212
1213 #[test]
1214 fn unary_minus_and_not_are_recorded_as_arity_one_op_nodes() {
1215 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1216 let expr = parser.parse_str_ast("-1i32").unwrap();
1217 let Expr::Op { name, operands, .. } = expr else {
1218 panic!("expected Op");
1219 };
1220 assert_eq!(name, "-");
1221 assert_eq!(operands.len(), 1);
1222
1223 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1224 let expr = parser.parse_str_ast("!true").unwrap();
1225 let Expr::Op { name, operands, .. } = expr else {
1226 panic!("expected Op");
1227 };
1228 assert_eq!(name, "!");
1229 assert_eq!(operands.len(), 1);
1230 }
1231
1232 #[test]
1233 fn bare_identifier_is_an_ident_node() {
1234 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1235 let expr = parser.parse_str_ast("x").unwrap();
1236 assert!(matches!(expr, Expr::Ident { name, .. } if name == "x"));
1237 }
1238
1239 #[test]
1240 fn call_with_no_args_and_with_args() {
1241 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1242 let expr = parser.parse_str_ast("f()").unwrap();
1243 let Expr::Apply { callee, args, .. } = expr else {
1244 panic!("expected Apply");
1245 };
1246 assert!(matches!(*callee, Expr::Ident { ref name, .. } if name == "f"));
1247 assert_eq!(args.len(), 0);
1248
1249 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1250 let expr = parser.parse_str_ast("f(1i32, 2i32)").unwrap();
1251 let Expr::Apply { args, .. } = expr else {
1252 panic!("expected Apply");
1253 };
1254 assert_eq!(args.len(), 2);
1255 }
1256
1257 #[test]
1258 fn chained_calls_nest_apply_nodes() {
1259 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1260 let expr = parser.parse_str_ast("f()()").unwrap();
1261 let Expr::Apply { callee, args, .. } = expr else {
1262 panic!("expected outer Apply");
1263 };
1264 assert_eq!(args.len(), 0);
1265 assert!(matches!(*callee, Expr::Apply { .. }));
1266 }
1267
1268 #[test]
1269 fn unit_grouping_and_tuples() {
1270 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1271 assert!(matches!(
1272 parser.parse_str_ast("()").unwrap(),
1273 Expr::Literal {
1274 value: Literal::Unit,
1275 ..
1276 }
1277 ));
1278
1279 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1280 assert!(matches!(
1282 parser.parse_str_ast("(1i32 + 2i32)").unwrap(),
1283 Expr::Op { .. }
1284 ));
1285
1286 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1287 let expr = parser.parse_str_ast("(1i32,)").unwrap();
1288 assert!(matches!(expr, Expr::Tuple { ref elements, .. } if elements.len() == 1));
1289
1290 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1291 let expr = parser.parse_str_ast("(1i32, 2i32, 3i32)").unwrap();
1292 assert!(matches!(expr, Expr::Tuple { ref elements, .. } if elements.len() == 3));
1293 }
1294
1295 #[test]
1296 fn tuple_index_single_and_chained() {
1297 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1298 let expr = parser.parse_str_ast("(1i32, 2i32).1").unwrap();
1299 let Expr::TupleIndex { base, index, .. } = expr else {
1300 panic!("expected TupleIndex");
1301 };
1302 assert_eq!(index, 1);
1303 assert!(matches!(*base, Expr::Tuple { ref elements, .. } if elements.len() == 2));
1304
1305 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1306 let expr = parser.parse_str_ast("((1i32, 2i32), 3i32).0.1").unwrap();
1308 let Expr::TupleIndex { base, index, .. } = expr else {
1309 panic!("expected outer TupleIndex");
1310 };
1311 assert_eq!(index, 1);
1312 assert!(matches!(*base, Expr::TupleIndex { index: 0, .. }));
1313 }
1314
1315 #[test]
1316 fn tuple_index_on_a_call_result_still_builds_a_tree() {
1317 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1321 let expr = parser.parse_str_ast("f().5").unwrap();
1322 assert!(matches!(expr, Expr::TupleIndex { index: 5, .. }));
1323 }
1324
1325 #[test]
1326 fn if_without_else_has_no_else_branch() {
1327 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1328 let expr = parser.parse_str_ast("if true { 1i32 }").unwrap();
1329 let Expr::If {
1330 cond,
1331 then_branch,
1332 else_branch,
1333 ..
1334 } = expr
1335 else {
1336 panic!("expected If");
1337 };
1338 assert!(matches!(
1339 *cond,
1340 Expr::Literal {
1341 value: Literal::Bool(true),
1342 ..
1343 }
1344 ));
1345 assert!(matches!(
1346 *then_branch,
1347 Expr::Literal {
1348 value: Literal::I32(1),
1349 ..
1350 }
1351 ));
1352 assert!(else_branch.is_none());
1353 }
1354
1355 #[test]
1356 fn if_else_and_else_if_chain() {
1357 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1358 let expr = parser
1359 .parse_str_ast("if true { 1i32 } else { 2i32 }")
1360 .unwrap();
1361 let Expr::If { else_branch, .. } = expr else {
1362 panic!("expected If");
1363 };
1364 assert!(matches!(
1365 else_branch.as_deref(),
1366 Some(Expr::Literal {
1367 value: Literal::I32(2),
1368 ..
1369 })
1370 ));
1371
1372 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1373 let expr = parser
1374 .parse_str_ast("if true { 1i32 } else if false { 2i32 } else { 3i32 }")
1375 .unwrap();
1376 let Expr::If { else_branch, .. } = expr else {
1377 panic!("expected outer If");
1378 };
1379 assert!(matches!(else_branch.as_deref(), Some(Expr::If { .. })));
1380 }
1381
1382 #[test]
1383 fn logical_or_is_not_desugared_to_if() {
1384 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1385 let expr = parser.parse_str_ast("a || b").unwrap();
1386 let Expr::Logical { op, lhs, rhs, .. } = expr else {
1387 panic!("expected Logical");
1388 };
1389 assert_eq!(op, LogicalOp::Or);
1390 assert!(matches!(*lhs, Expr::Ident { ref name, .. } if name == "a"));
1391 assert!(matches!(*rhs, Expr::Ident { ref name, .. } if name == "b"));
1392 }
1393
1394 #[test]
1395 fn logical_and_is_not_desugared_to_if() {
1396 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1397 let expr = parser.parse_str_ast("a && b").unwrap();
1398 let Expr::Logical { op, .. } = expr else {
1399 panic!("expected Logical");
1400 };
1401 assert_eq!(op, LogicalOp::And);
1402 }
1403
1404 #[test]
1405 fn logical_mixes_with_comparison_at_the_right_precedence() {
1406 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1407 let expr = parser.parse_str_ast("a == 1i32 && b == 2i32").unwrap();
1409 let Expr::Logical { op, lhs, rhs, .. } = expr else {
1410 panic!("expected Logical");
1411 };
1412 assert_eq!(op, LogicalOp::And);
1413 assert!(matches!(*lhs, Expr::Op { ref name, .. } if name == "=="));
1414 assert!(matches!(*rhs, Expr::Op { ref name, .. } if name == "=="));
1415 }
1416
1417 #[test]
1418 fn for_text_produces_a_span_whose_source_text_matches() {
1419 let span = ExprSpan::for_text("i64");
1420 assert_eq!(span.start.source_text().as_deref(), Some("i64"));
1421 assert_eq!(span.end.source_text().as_deref(), Some("i64"));
1422 }
1423
1424 #[test]
1425 fn for_text_works_for_a_string_literal() {
1426 let span = ExprSpan::for_text("\"hello\"");
1427 assert_eq!(span.start.source_text().as_deref(), Some("\"hello\""));
1428 }
1429
1430 #[test]
1431 fn make_array_pushes_one_array_node_holding_the_elements_in_source_order() {
1432 let mut ctx = AstContext::new_context();
1433 let ambient_start = ctx.current_stack_offset();
1434 ctx.push_literal(1i32, Span::call_site());
1435 ctx.push_literal(2i32, Span::call_site());
1436 ctx.make_array(2, ambient_start, Span::call_site(), Span::call_site())
1437 .unwrap();
1438 match ctx.into_expr() {
1439 Expr::Array { elements, .. } => {
1440 assert!(matches!(
1441 elements[0],
1442 Expr::Literal {
1443 value: Literal::I32(1),
1444 ..
1445 }
1446 ));
1447 assert!(matches!(
1448 elements[1],
1449 Expr::Literal {
1450 value: Literal::I32(2),
1451 ..
1452 }
1453 ));
1454 }
1455 other => panic!("expected Array, got {other:?}"),
1456 }
1457 }
1458
1459 #[test]
1460 fn array_literal_records_elements_in_source_order_with_an_enclosing_span() {
1461 let source = "[0i32, 1i32, 2i32]";
1462 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1463 let expr = parser.parse_str_ast(source).unwrap();
1464 let Expr::Array { elements, span, .. } = expr else {
1465 panic!("expected Array");
1466 };
1467 assert_eq!(elements.len(), 3);
1468 for (index, element) in elements.iter().enumerate() {
1469 let Expr::Literal { value, .. } = element else {
1470 panic!("expected a literal element");
1471 };
1472 assert_eq!(*value, Literal::I32(i32::try_from(index).unwrap()));
1473 }
1474 assert_eq!(span.start.source_text().as_deref(), Some(source));
1475 assert_eq!(span.end.source_text().as_deref(), Some(source));
1476 }
1477
1478 #[test]
1479 fn nested_array_literal_nests_array_nodes() {
1480 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1481 let expr = parser.parse_str_ast("[[0i32], [1i32]]").unwrap();
1482 let Expr::Array { elements, .. } = expr else {
1483 panic!("expected outer Array");
1484 };
1485 assert_eq!(elements.len(), 2);
1486 for element in &elements {
1487 assert!(matches!(element, Expr::Array { elements, .. } if elements.len() == 1));
1488 }
1489 }
1490
1491 #[test]
1492 fn typed_array_literal_still_builds_an_array_node() {
1493 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1494 let expr = parser.parse_str_ast("[0, 1]: [i32]").unwrap();
1495 let Expr::Array {
1496 elements,
1497 type_annotation,
1498 annotation_span,
1499 ..
1500 } = expr
1501 else {
1502 panic!("expected Array");
1503 };
1504 assert_eq!(elements.len(), 2);
1505 assert!(annotation_span.is_some());
1506 match type_annotation {
1507 Some(TypeExpr::Array { element, .. }) => {
1508 assert!(matches!(*element, TypeExpr::Named { ref name, .. } if name == "i32"));
1509 }
1510 other => panic!("expected an array type annotation, got {other:?}"),
1511 }
1512 }
1513
1514 #[test]
1515 fn typed_empty_array_literal_builds_an_empty_array_node() {
1516 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1517 let expr = parser.parse_str_ast("[]: [i32]").unwrap();
1518 assert!(matches!(
1519 expr,
1520 Expr::Array {
1521 ref elements,
1522 type_annotation: Some(_),
1523 annotation_span: Some(_),
1524 ..
1525 } if elements.is_empty()
1526 ));
1527 }
1528
1529 #[test]
1530 fn typed_array_literal_preserves_the_exact_annotation_span() {
1531 let source = "[1.0, 42.5]: [f64]";
1532 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1533 let expr = parser.parse_str_ast(source).unwrap();
1534 let Expr::Array {
1535 type_annotation: Some(_),
1536 annotation_span: Some(annotation_span),
1537 ..
1538 } = expr
1539 else {
1540 panic!("expected Array");
1541 };
1542 let start = annotation_span.start.start();
1543 let end = annotation_span.end.end();
1544 assert_eq!(&source[start.column..end.column], ": [f64]");
1545 }
1546
1547 #[test]
1548 fn array_literals_build_nodes_in_call_argument_and_tuple_positions() {
1549 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1550 let expr = parser.parse_str_ast("f([1i32], (2i32, [3i32]))").unwrap();
1551 let Expr::Apply { args, .. } = expr else {
1552 panic!("expected Apply");
1553 };
1554 assert!(matches!(args[0], Expr::Array { ref elements, .. } if elements.len() == 1));
1555 let Expr::Tuple { elements, .. } = &args[1] else {
1556 panic!("expected a tuple argument");
1557 };
1558 assert!(matches!(elements[1], Expr::Array { .. }));
1559 }
1560
1561 #[test]
1562 fn array_literals_build_nodes_in_if_branch_and_closure_body_positions() {
1563 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1564 let expr = parser
1565 .parse_str_ast("if true { [1i32] } else { [2i32] }")
1566 .unwrap();
1567 let Expr::If {
1568 then_branch,
1569 else_branch,
1570 ..
1571 } = expr
1572 else {
1573 panic!("expected If");
1574 };
1575 assert!(matches!(*then_branch, Expr::Array { .. }));
1576 assert!(matches!(else_branch.as_deref(), Some(Expr::Array { .. })));
1577
1578 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1579 let expr = parser.parse_str_ast("|x: i32| [x]").unwrap();
1580 let Expr::Closure { body, .. } = expr else {
1581 panic!("expected Closure");
1582 };
1583 assert!(matches!(*body, Expr::Array { .. }));
1584 }
1585
1586 #[test]
1587 fn array_literal_with_a_tuple_element_still_builds_a_tree() {
1588 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1590 let expr = parser.parse_str_ast("[(1i32, 2i32)]").unwrap();
1591 assert!(matches!(expr, Expr::Array { ref elements, .. } if elements.len() == 1));
1592 }
1593
1594 #[test]
1595 fn ast_context_still_rejects_an_empty_array_literal() {
1596 let mut parser = Parser::<AstContext>::new(OpLookup::new());
1599 let err = parser
1600 .parse_str_ast("[]")
1601 .expect_err("an empty array literal has no inferable element type");
1602 assert!(
1603 err.message().contains("empty array"),
1604 "got: {}",
1605 err.message()
1606 );
1607 }
1608}