aiken_lang/parser/expr/
assignment.rs1use 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}