aiken_lang/parser/expr/
if_else.rs1use super::block;
2use crate::{
3 ast,
4 expr::UntypedExpr,
5 parser::{annotation, error::ParseError, pattern, token::Token},
6};
7use chumsky::prelude::*;
8
9pub fn parser<'a>(
10 sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
11 expression: Recursive<'a, Token, UntypedExpr, ParseError>,
12) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
13 just(Token::If)
14 .ignore_then(if_branch(sequence.clone(), expression.clone()))
15 .then(
16 just(Token::Else)
17 .ignore_then(just(Token::If))
18 .ignore_then(if_branch(sequence.clone(), expression))
19 .repeated(),
20 )
21 .then_ignore(just(Token::Else))
22 .then(block(sequence))
23 .map_with_span(|((first, alternative_branches), final_else), span| {
24 let mut branches = vec1::vec1![first];
25
26 branches.extend(alternative_branches);
27
28 UntypedExpr::If {
29 location: span,
30 branches,
31 final_else: Box::new(final_else),
32 }
33 })
34}
35
36fn if_branch<'a>(
37 sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
38 expression: Recursive<'a, Token, UntypedExpr, ParseError>,
39) -> impl Parser<Token, ast::UntypedIfBranch, Error = ParseError> + 'a {
40 expression
41 .then(
42 just(Token::Is)
43 .ignore_then(
44 pattern()
45 .then_ignore(just(Token::Colon))
46 .or_not()
47 .then(annotation())
48 .map_with_span(|(pattern, annotation), span| (pattern, annotation, span)),
49 )
50 .or_not(),
51 )
52 .then(block(sequence))
53 .map_with_span(|((condition, is), body), span| {
54 let is = is.map(|(pattern, annotation, is_span)| {
55 let pattern = pattern.unwrap_or_else(|| match &condition {
56 UntypedExpr::Var { name, location } => ast::Pattern::Var {
57 name: name.clone(),
58 location: *location,
59 },
60 _ => ast::Pattern::Discard {
61 location: is_span,
62 name: "_".to_string(),
63 },
64 });
65
66 ast::AssignmentPattern {
67 pattern,
68 annotation: Some(annotation),
69 location: is_span,
70 }
71 });
72
73 ast::IfBranch {
74 condition,
75 body,
76 is,
77 location: span,
78 }
79 })
80}
81
82#[cfg(test)]
83mod tests {
84 use crate::assert_expr;
85
86 #[test]
87 fn if_else_basic() {
88 assert_expr!(
89 r#"
90 if True {
91 1 + 1
92 } else if a < 1 {
93 3
94 } else {
95 4
96 }
97 "#
98 );
99 }
100
101 #[test]
102 fn if_else_ambiguous_record() {
103 assert_expr!(
104 r#"
105 if ec1 == Infinity {
106 ec2
107 } else if ec1 == Foo { foo } {
108 ec1
109 } else {
110 Infinity
111 }
112 "#
113 );
114 }
115
116 #[test]
117 fn if_else_with_soft_cast() {
118 assert_expr!(
119 r#"
120 if ec1 is Some(x): Option<Int> {
121 ec2
122 } else if ec1 is Foo { foo }: Foo {
123 ec1
124 } else if ec1 is Option<Int> {
125 let Some(x) = ec1
126
127 x
128 } else {
129 Infinity
130 }
131 "#
132 );
133 }
134
135 #[test]
136 fn if_soft_cast_discard_assign() {
137 assert_expr!(
138 r#"
139 if foo() is Foo {
140 todo
141 } else {
142 todo
143 }
144 "#
145 );
146 }
147
148 #[test]
149 fn if_soft_cast_not_var_condition() {
150 assert_expr!(
151 r#"
152 if foo() is Foo { a }: Foo {
153 todo
154 } else {
155 todo
156 }
157 "#
158 );
159 }
160}