Skip to main content

aiken_lang/parser/expr/
assignment.rs

1use crate::{
2    ast::{self, Span},
3    expr::UntypedExpr,
4    parser::{annotation, error::ParseError, pattern, token::Token},
5};
6use chumsky::prelude::*;
7
8pub fn let_(
9    r: Recursive<'_, Token, UntypedExpr, ParseError>,
10) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
11    just(Token::Let)
12        .ignore_then(assignment_patterns())
13        .then(choice((just(Token::Equal), just(Token::LArrow))))
14        .then(r.clone())
15        .validate(move |((patterns, kind), value), span, emit| {
16            if matches!(value, UntypedExpr::Assignment { .. }) {
17                emit(ParseError::invalid_assignment_right_hand_side(span))
18            }
19
20            let patterns = patterns
21                .try_into()
22                .expect("We use at_least(1) so this should never be empty");
23
24            UntypedExpr::Assignment {
25                location: span,
26                value: Box::new(value),
27                patterns,
28                kind: ast::AssignmentKind::Let {
29                    backpassing: kind == Token::LArrow,
30                },
31                comment: None,
32            }
33        })
34}
35
36fn assignment_patterns() -> impl Parser<Token, Vec<ast::AssignmentPattern>, Error = ParseError> {
37    assignment_pattern()
38        .separated_by(just(Token::Comma))
39        .allow_trailing()
40        .at_least(1)
41}
42
43pub fn assignment_pattern() -> impl Parser<Token, ast::AssignmentPattern, Error = ParseError> {
44    pattern()
45        .then(just(Token::Colon).ignore_then(annotation()).or_not())
46        .map_with_span(|(pattern, annotation), span| ast::AssignmentPattern {
47            pattern,
48            annotation,
49            location: span,
50        })
51}
52
53pub fn expect(
54    r: Recursive<'_, Token, UntypedExpr, ParseError>,
55) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
56    select! {Token::ExpectComment(comment) => comment}
57        .or_not()
58        .then(
59            just(Token::Expect)
60                .map_with_span(|_, span| span)
61                .then(
62                    assignment_patterns()
63                        .then(choice((just(Token::Equal), just(Token::LArrow))))
64                        .or_not(),
65                )
66                .then(r.clone()),
67        )
68        .validate(
69            move |(comment, ((expect_span, opt_pattern), value)), span, emit| {
70                let location = span.map(|_, end| (expect_span.start, end));
71
72                if matches!(value, UntypedExpr::Assignment { .. }) {
73                    emit(ParseError::invalid_assignment_right_hand_side(location))
74                }
75
76                let (patterns, kind) = opt_pattern.unwrap_or_else(|| {
77                    let filler_true = ast::AssignmentPattern::new(
78                        ast::UntypedPattern::true_(location),
79                        None,
80                        Span::empty(),
81                    );
82
83                    (vec![filler_true], Token::Equal)
84                });
85
86                let patterns = patterns
87                    .try_into()
88                    .expect("We use at_least(1) so this should never be empty");
89
90                UntypedExpr::Assignment {
91                    location,
92                    patterns,
93                    value: Box::new(value),
94                    kind: ast::AssignmentKind::Expect {
95                        backpassing: kind == Token::LArrow,
96                    },
97                    comment,
98                }
99            },
100        )
101}
102
103#[cfg(test)]
104mod tests {
105    use crate::assert_expr;
106
107    #[test]
108    fn let_bindings() {
109        assert_expr!("let thing = [ 1, 2, a ]");
110    }
111
112    #[test]
113    fn expect() {
114        assert_expr!("expect Some(x) = something.field");
115    }
116
117    #[test]
118    fn expect_bool_sugar() {
119        assert_expr!("expect something.field == wow");
120    }
121
122    #[test]
123    fn expect_trace_if_false() {
124        assert_expr!("expect foo?");
125    }
126
127    #[test]
128    fn expect_comment() {
129        assert_expr!(
130            r#"
131            /// Some user-defined custom comment
132            expect Some(x) = something.field
133            "#
134        );
135    }
136
137    #[test]
138    fn expect_multiline_comment() {
139        assert_expr!(
140            r#"
141            /// Some user-defined custom comment
142            /// over multiple lines
143            expect Some(x) = something.field
144            "#
145        );
146    }
147
148    #[test]
149    fn expect_unfinished_let() {
150        assert_expr!(
151            "
152            let a =
153            // foo
154            let b = 42
155            "
156        );
157    }
158
159    #[test]
160    fn expect_let_in_let() {
161        assert_expr!("let a = { let b = 42 }");
162    }
163
164    #[test]
165    fn expect_let_in_let_return() {
166        assert_expr!(
167            "
168            let a = {
169              let b = 42
170              b
171            }
172            "
173        );
174    }
175
176    #[test]
177    fn expect_let_in_let_parens() {
178        assert_expr!("let a = ( let b = 42 )");
179    }
180
181    #[test]
182    fn expect_expect_let() {
183        assert_expr!("expect { let a = 42 } = foo");
184    }
185}