aiken_lang/parser/expr/
if_else.rs

1use 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}