1use ast::{
2 expr::{Expr, Handler, LinkName, Literal, MatchCase},
3 span::Spanned,
4 ty::{CommentPosition, Type},
5};
6use chumsky::prelude::*;
7use tokens::Token;
8
9use crate::common::{parse_attr, parse_comment, parse_ident, parse_uuid};
10
11use super::common::{parse_collection, parse_function, parse_op, parse_typed, ParserExt};
12
13pub fn parser() -> impl Parser<Token, Spanned<Expr>, Error = Simple<Token>> + Clone {
14 recursive(|expr| {
15 let hole = just(Token::Hole).to(Expr::Hole);
16 let int64 = filter_map(|span, token| match token {
17 Token::Int(int) => Ok(int),
18 _ => Err(Simple::custom(span, "expected int literal")),
19 });
20 let rational = int64
21 .then_ignore(just(Token::Divide))
22 .then(int64)
23 .map(|(a, b)| Expr::Literal(Literal::Rational(a, b)));
24 let string = filter_map(|span, token| match token {
25 Token::Str(string) => Ok(Expr::Literal(Literal::String(string))),
26 _ => Err(Simple::custom(span, "expected string literal")),
27 });
28 let literal = rational
29 .or(int64.map(|int| Expr::Literal(Literal::Int(int))))
30 .or(string);
31 let type_ = super::ty::parser(expr.clone());
32 let let_in = just(Token::Let)
33 .ignore_then(expr.clone())
34 .then(
36 just(Token::TypeAnnotation)
37 .ignore_then(type_.clone())
38 .or_not()
39 .map(|ty| ty.unwrap_or((Type::Infer, 0..0))),
40 )
41 .in_()
42 .then(expr.clone())
43 .map(|((definition, ty), expression)| Expr::Let {
44 ty,
45 definition: Box::new(definition),
46 body: Box::new(expression),
47 });
48 let perform = just(Token::Perform)
49 .ignore_then(expr.clone())
50 .then_ignore(just(Token::EArrow))
51 .then(type_.clone())
52 .map(|(effect, output)| Expr::Perform {
53 input: Box::new(effect),
54 output,
55 });
56 let continue_ = just(Token::Continue)
57 .ignore_then(expr.clone())
58 .then(just(Token::EArrow).ignore_then(type_.clone()).or_not())
59 .map(|(expr, output)| Expr::Continue {
60 input: Box::new(expr),
61 output,
62 });
63 let handle = just(Token::Handle)
64 .ignore_then(expr.clone())
65 .in_()
66 .then(
67 type_
68 .clone()
69 .then_ignore(just(Token::EArrow))
70 .then(type_.clone())
71 .then_ignore(just(Token::Arrow).or_not())
72 .then(expr.clone())
73 .map(|((input, output), handler)| Handler {
74 input,
75 output,
76 handler,
77 })
78 .separated_by_comma_at_least_one(),
79 )
80 .map(|(expr, handlers)| Expr::Handle {
81 expr: Box::new(expr),
82 handlers,
83 });
84 let card_uuid = just(Token::Card)
85 .ignore_then(parse_uuid())
86 .map(LinkName::Card);
87 let apply = just(Token::Apply)
88 .ignore_then(type_.clone())
89 .then(card_uuid.clone().or_not())
90 .in_()
91 .then(expr.clone().separated_by_comma_at_least_one())
92 .map(|((function, card), arguments)| Expr::Apply {
93 function,
94 link_name: card,
95 arguments,
96 });
97 let reference = just(Token::Reference)
98 .ignore_then(type_.clone())
99 .then(card_uuid.or_not())
100 .map(|(reference, card)| Expr::Apply {
101 function: reference,
102 link_name: card,
103 arguments: vec![],
104 });
105 let product = parse_op(just(Token::Product), expr.clone()).map(Expr::Product);
106 let function = parse_function(
107 just(Token::Lambda),
108 type_.clone(),
109 just(Token::Arrow),
110 expr.clone(),
111 )
112 .map(|(parameters, body)| Expr::Function {
113 parameters,
114 body: Box::new(body),
115 });
116 let array =
117 parse_collection(Token::ArrayBegin, expr.clone(), Token::ArrayEnd).map(Expr::Array);
118 let set = parse_collection(Token::SetBegin, expr.clone(), Token::SetEnd).map(Expr::Set);
119 let typed = parse_typed(expr.clone(), type_.clone()).map(|(expr, ty)| Expr::Typed {
120 ty,
121 item: Box::new(expr),
122 });
123 let attribute =
124 parse_attr(expr.clone(), expr.clone()).map(|(attr, expr)| Expr::Attribute {
125 attr: Box::new(attr),
126 item: Box::new(expr),
127 });
128 let brand = just(Token::Brands)
129 .ignore_then(parse_ident().separated_by_comma())
130 .in_()
131 .then(expr.clone())
132 .map(|(brands, expr)| Expr::Brand {
133 brands,
134 item: Box::new(expr),
135 });
136 let match_ = just(Token::Sum)
137 .ignore_then(expr.clone())
138 .in_()
139 .then(
140 type_
141 .clone()
142 .then_ignore(just(Token::Arrow))
143 .then(expr.clone())
144 .map(|(ty, expr)| MatchCase { ty, expr })
145 .separated_by_comma_at_least_one(),
146 )
147 .map(|(of, cases)| Expr::Match {
148 of: Box::new(of),
149 cases,
150 });
151 let include = just(Token::Include)
152 .ignore_then(parse_ident())
153 .map(Expr::Include);
154 let label = filter_map(|span, input| {
155 if let Token::Brand(ident) = input {
156 Ok(ident)
157 } else {
158 Err(Simple::custom(span, "Expected brand"))
159 }
160 })
161 .then(expr.clone())
162 .map(|(label, expr)| Expr::Label {
163 label,
164 item: Box::new(expr),
165 });
166 let newtype = just(Token::Type)
167 .ignore_then(parse_ident())
168 .then(type_.clone())
169 .in_()
170 .then(expr.clone())
171 .map(|((ident, ty), expr)| Expr::NewType {
172 ident,
173 ty,
174 expr: Box::new(expr),
175 });
176 let prefix_comment = parse_comment()
177 .then(expr.clone())
178 .map(|(text, expr)| Expr::Comment {
179 position: CommentPosition::Prefix,
180 text,
181 item: Box::new(expr),
182 });
183 let card = just(Token::Card)
184 .ignore_then(parse_uuid())
185 .then(expr.clone())
186 .in_()
187 .then(expr.clone().or_not())
188 .map(|((uuid, item), next)| Expr::Card {
189 uuid,
190 item: Box::new(item),
191 next: next.map(Box::new),
192 });
193
194 hole.or(prefix_comment)
195 .or(literal.labelled("literal"))
196 .or(let_in.labelled("let-in"))
197 .or(perform.labelled("perform"))
198 .or(continue_.labelled("continue"))
199 .or(product.labelled("product"))
200 .or(array.labelled("array"))
201 .or(set.labelled("set"))
202 .or(typed.labelled("typed"))
203 .or(attribute.labelled("attribute"))
204 .or(brand.labelled("brand"))
205 .or(match_.labelled("match"))
206 .or(include.labelled("include"))
207 .or(function.labelled("function"))
208 .or(apply.labelled("apply"))
209 .or(reference.labelled("reference"))
210 .or(handle.labelled("handle"))
211 .or(label.labelled("label"))
212 .or(newtype.labelled("newtype"))
213 .or(card.labelled("card"))
214 .map_with_span(|token, span| (token, span))
215 .then(parse_comment().or_not())
216 .map_with_span(|(expr, comment), span| {
217 if let Some(comment) = comment {
218 (
219 Expr::Comment {
220 position: CommentPosition::Suffix,
221 text: comment,
222 item: Box::new(expr),
223 },
224 span,
225 )
226 } else {
227 expr
228 }
229 })
230 })
231}
232
233#[cfg(test)]
234mod tests {
235 use ast::{expr::MatchCase, ty::Type};
236 use lexer::scan;
237
238 use crate::ParserError;
239
240 use super::*;
241
242 fn parse(input: &str) -> Result<Spanned<Expr>, ParserError> {
243 crate::parse(scan(input).unwrap())
244 }
245
246 #[test]
247 fn parse_literal_int() {
248 assert_eq!(parse("-12").unwrap().0, Expr::Literal(Literal::Int(-12)));
249 }
250
251 #[test]
252 fn parse_literal_rational() {
253 assert_eq!(
254 parse("1/2").unwrap().0,
255 Expr::Literal(Literal::Rational(1, 2))
256 );
257 }
258
259 #[test]
260 fn parse_literal_string() {
261 assert_eq!(
262 parse(r#""abc""#).unwrap().0,
263 Expr::Literal(Literal::String("abc".into()))
264 );
265 }
266
267 #[test]
268 fn parse_let() {
269 assert_eq!(
270 parse("$ 3: 'number ~ ?").unwrap().0,
271 Expr::Let {
272 ty: (Type::Number, 5..12),
273 definition: Box::new((Expr::Literal(Literal::Int(3)), 2..3)),
274 body: Box::new((Expr::Hole, 15..16)),
275 }
276 );
277 }
278
279 #[test]
280 fn parse_perform() {
281 assert_eq!(
282 parse("! ? => 'string").unwrap().0,
283 Expr::Perform {
284 input: Box::new((Expr::Hole, 2..3)),
285 output: (Type::String, 7..14),
286 }
287 );
288 }
289
290 #[test]
291 fn parse_handle() {
292 let trait_ = parse(r#"'handle ? ~ 'number => 'string -> 3"#).unwrap().0;
293 assert_eq!(
294 trait_,
295 Expr::Handle {
296 expr: Box::new((Expr::Hole, 8..9)),
297 handlers: vec![Handler {
298 input: (Type::Number, 12..19),
299 output: (Type::String, 23..30),
300 handler: (Expr::Literal(Literal::Int(3)), 34..35),
301 }],
302 }
303 );
304 }
305
306 #[test]
307 fn parse_call() {
308 assert_eq!(
309 parse("> 'a add ~ 1, 2.").unwrap().0,
310 Expr::Apply {
311 function: (Type::Alias("add".into()), 2..8),
312 link_name: None,
313 arguments: vec![
314 (Expr::Literal(Literal::Int(1)), 11..12),
315 (Expr::Literal(Literal::Int(2)), 14..15)
316 ],
317 }
318 );
319 }
320
321 #[test]
322 fn parse_reference() {
323 assert_eq!(
324 parse("& 'a x").unwrap().0,
325 Expr::Apply {
326 function: (Type::Alias("x".into()), 2..6),
327 link_name: None,
328 arguments: vec![],
329 }
330 );
331 }
332
333 #[test]
334 fn parse_product() {
335 assert_eq!(
336 parse("* 1, ?").unwrap().0,
337 Expr::Product(vec![
338 (Expr::Literal(Literal::Int(1)), 2..3),
339 (Expr::Hole, 5..6),
340 ])
341 );
342 }
343
344 #[test]
345 fn parse_function() {
346 assert_eq!(
347 parse(r#"\ 'number, _ -> ?"#).unwrap().0,
348 Expr::Function {
349 parameters: vec![(Type::Number, 2..9), (Type::Infer, 11..12)],
350 body: Box::new((Expr::Hole, 16..17)),
351 },
352 );
353 }
354
355 #[test]
356 fn parse_array() {
357 assert_eq!(
358 parse("[1, ?, ?]").unwrap().0,
359 Expr::Array(vec![
360 (Expr::Literal(Literal::Int(1)), 1..2),
361 (Expr::Hole, 4..5),
362 (Expr::Hole, 7..8),
363 ])
364 );
365 }
366
367 #[test]
368 fn parse_set() {
369 assert_eq!(
370 parse("{1, ?, ?}").unwrap().0,
371 Expr::Set(vec![
372 (Expr::Literal(Literal::Int(1)), 1..2),
373 (Expr::Hole, 4..5),
374 (Expr::Hole, 7..8),
375 ])
376 );
377 }
378
379 #[test]
380 fn parse_type_annotation() {
381 assert_eq!(
382 parse("^?: 'number").unwrap().0,
383 Expr::Typed {
384 item: Box::new((Expr::Hole, 1..2)),
385 ty: (Type::Number, 4..11),
386 }
387 );
388 }
389
390 #[test]
391 fn parse_attribute() {
392 assert_eq!(
393 parse("# 3 ~ ?").unwrap().0,
394 Expr::Attribute {
395 attr: Box::new((Expr::Literal(Literal::Int(3)), 2..3)),
396 item: Box::new((Expr::Hole, 6..7)),
397 }
398 );
399 }
400
401 #[test]
402 fn parse_brand() {
403 assert_eq!(
404 parse("'brand a, b. ~ ?").unwrap().0,
405 Expr::Brand {
406 brands: vec!["a".into(), "b".into()],
407 item: Box::new((Expr::Hole, 15..16)),
408 }
409 );
410 }
411
412 #[test]
413 fn parse_match() {
414 assert_eq!(
415 parse(
416 r#"
417 + ? ~
418 'number -> "number",
419 'string -> "string".
420 "#
421 )
422 .unwrap()
423 .0,
424 Expr::Match {
425 of: Box::new((Expr::Hole, 15..16)),
426 cases: vec![
427 MatchCase {
428 ty: (Type::Number, 31..38),
429 expr: (Expr::Literal(Literal::String("number".into())), 42..50),
430 },
431 MatchCase {
432 ty: (Type::String, 64..71),
433 expr: (Expr::Literal(Literal::String("string".into())), 75..83),
434 },
435 ]
436 }
437 );
438 }
439
440 #[test]
441 fn parse_match_without_in() {
442 assert_eq!(
443 parse(
444 r#"
445 + & 'a x
446 'number -> "number",
447 'string -> "string".
448 "#
449 )
450 .unwrap()
451 .0,
452 Expr::Match {
453 of: Box::new((
454 Expr::Apply {
455 function: (Type::Alias("x".into()), 17..21),
456 link_name: None,
457 arguments: vec![]
458 },
459 15..21
460 )),
461 cases: vec![
462 MatchCase {
463 ty: (Type::Number, 34..41),
464 expr: (Expr::Literal(Literal::String("number".into())), 45..53),
465 },
466 MatchCase {
467 ty: (Type::String, 67..74),
468 expr: (Expr::Literal(Literal::String("string".into())), 78..86),
469 },
470 ]
471 }
472 );
473 }
474
475 #[test]
476 fn parse_label() {
477 assert_eq!(
478 parse("@true *").unwrap().0,
479 Expr::Label {
480 label: "true".into(),
481 item: Box::new((Expr::Product(vec![]), 6..7)),
482 }
483 );
484 }
485
486 #[test]
487 fn parse_comment() {
488 assert_eq!(
489 parse("(a)*(b)").unwrap().0,
490 Expr::Comment {
491 position: CommentPosition::Prefix,
492 text: "(a)".into(),
493 item: Box::new((
494 Expr::Comment {
495 position: CommentPosition::Suffix,
496 text: "(b)".into(),
497 item: Box::new((Expr::Product(vec![]), 3..4)),
498 },
499 3..7
500 )),
501 }
502 );
503 }
504}