deskc_parser/
expr.rs

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            // TODO: span for Type::Infer
35            .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}