deskc_parser/
ty.rs

1use ast::{
2    expr::Expr,
3    span::Spanned,
4    ty::{CommentPosition, Effect, EffectExpr, Type},
5};
6use chumsky::prelude::*;
7use tokens::Token;
8
9use crate::common::{parse_attr, parse_collection, parse_comment, parse_ident};
10
11use super::common::{parse_function, parse_op, ParserExt};
12
13pub fn effect_parser(
14    parser: impl Parser<Token, Spanned<Type>, Error = Simple<Token>> + Clone,
15) -> impl Parser<Token, Effect, Error = Simple<Token>> + Clone {
16    parser
17        .clone()
18        .then_ignore(just(Token::EArrow))
19        .then(parser)
20        .map(|(input, output)| Effect { input, output })
21}
22
23pub fn parser(
24    // Needs this for parse attributes
25    expr: impl Parser<Token, Spanned<Expr>, Error = Simple<Token>> + Clone + 'static,
26) -> impl Parser<Token, Spanned<Type>, Error = Simple<Token>> + Clone {
27    recursive(|type_| {
28        let infer = just(Token::Infer).to(Type::Infer);
29        let this = just(Token::This).to(Type::This);
30        let number = just(Token::NumberType).to(Type::Number);
31        let string = just(Token::StringType).to(Type::String);
32        let trait_ = just(Token::Trait).ignore_then(
33            type_
34                .clone()
35                .separated_by_comma_at_least_one()
36                .map(Type::Trait),
37        );
38        let alias = just(Token::A).ignore_then(parse_ident().map(Type::Alias));
39        let effectful = just(Token::Perform)
40            .ignore_then(type_.clone())
41            .then(parse_effects(type_.clone()))
42            .map(|(ty, effects)| Type::Effectful {
43                ty: Box::new(ty),
44                effects,
45            });
46        let product = parse_op(just(Token::Product), type_.clone()).map(Type::Product);
47        let sum = parse_op(just(Token::Sum), type_.clone()).map(Type::Sum);
48        let array = type_
49            .clone()
50            .delimited_by(just(Token::ArrayBegin), just(Token::ArrayEnd))
51            .map(Box::new)
52            .map(Type::Array);
53        let set = type_
54            .clone()
55            .delimited_by(just(Token::SetBegin), just(Token::SetEnd))
56            .map(Box::new)
57            .map(Type::Set);
58        let function = parse_function(
59            just(Token::Lambda),
60            type_.clone(),
61            just(Token::Arrow),
62            type_.clone(),
63        )
64        .map(|(parameters, body)| Type::Function {
65            parameters,
66            body: Box::new(body),
67        });
68        let attribute = parse_attr(expr, type_.clone()).map(|(attr, ty)| Type::Attribute {
69            attr: Box::new(attr),
70            ty: Box::new(ty),
71        });
72        let brand = filter_map(|span, input| {
73            if let Token::Brand(ident) = input {
74                Ok(ident)
75            } else {
76                Err(Simple::custom(span, "Expected brand"))
77            }
78        })
79        .then(type_.clone())
80        .map(|(brand, ty)| Type::Brand {
81            brand,
82            item: Box::new(ty),
83        });
84        let variable = parse_ident().map(Type::Variable);
85        let bound = parse_ident()
86            .then_ignore(just(Token::TypeAnnotation))
87            .then(type_.clone())
88            .map(|(identifier, bound)| Type::BoundedVariable {
89                bound: Box::new(bound),
90                identifier,
91            });
92        let let_in = just(Token::Let)
93            .ignore_then(parse_ident())
94            .in_()
95            .then(type_.clone())
96            .map(|(definition, expression)| Type::Let {
97                variable: definition,
98                body: Box::new(expression),
99            });
100
101        let prefix_comment =
102            parse_comment()
103                .then(type_.clone())
104                .map(|(text, item)| Type::Comment {
105                    position: CommentPosition::Prefix,
106                    text,
107                    item: Box::new(item),
108                });
109
110        infer
111            .or(prefix_comment)
112            .or(this)
113            .or(number)
114            .or(string)
115            .or(trait_)
116            .or(alias)
117            .or(effectful)
118            .or(product)
119            .or(sum)
120            .or(array)
121            .or(set)
122            .or(function)
123            .or(brand)
124            .or(attribute)
125            .or(bound)
126            .or(variable)
127            .or(let_in)
128            .map_with_span(|t, span| (t, span))
129            .then(parse_comment().or_not())
130            .map_with_span(|(ty, comment), span| {
131                if let Some(comment) = comment {
132                    (
133                        Type::Comment {
134                            position: CommentPosition::Suffix,
135                            text: comment,
136                            item: Box::new(ty),
137                        },
138                        span,
139                    )
140                } else {
141                    ty
142                }
143            })
144    })
145}
146
147fn parse_effects(
148    type_: impl Parser<Token, Spanned<Type>, Error = Simple<Token>> + Clone + 'static,
149) -> impl Parser<Token, Spanned<EffectExpr>, Error = Simple<Token>> + Clone {
150    recursive(|expr| {
151        let effects = parse_collection(
152            Token::SetBegin,
153            type_
154                .clone()
155                .then_ignore(just(Token::EArrow))
156                .then(type_.clone())
157                .map(|(input, output)| Effect { input, output })
158                .map_with_span(|expr, span| (expr, span)),
159            Token::SetEnd,
160        )
161        .map(EffectExpr::Effects);
162        let apply = just(Token::Apply)
163            .ignore_then(type_.clone())
164            .in_()
165            .then(type_.clone().separated_by_comma())
166            .map(|(func, arguments)| EffectExpr::Apply {
167                function: Box::new(func),
168                arguments,
169            });
170
171        let add = parse_op(just(Token::Sum), expr.clone()).map(EffectExpr::Add);
172        let sub = just(Token::Minus)
173            .ignore_then(expr.clone())
174            .then_ignore(just(Token::Comma))
175            .then(expr.clone())
176            .map(|(minuend, subtrahend)| EffectExpr::Sub {
177                minuend: Box::new(minuend),
178                subtrahend: Box::new(subtrahend),
179            });
180        effects
181            .labelled("effects")
182            .or(apply.labelled("effects apply"))
183            .or(add.labelled("effects add"))
184            .or(sub.labelled("effects sub"))
185            .map_with_span(|expr, span| (expr, span))
186    })
187}
188
189#[cfg(test)]
190mod tests {
191    use ast::expr::{Expr, Literal};
192    use chumsky::Stream;
193    use lexer::lexer;
194
195    use crate::expr;
196
197    use super::*;
198
199    fn parse(input: &str) -> Result<Spanned<Type>, Vec<Simple<Token>>> {
200        parser(expr::parser())
201            .then_ignore(end())
202            .parse(Stream::from_iter(
203                input.len()..input.len() + 1,
204                lexer().then_ignore(end()).parse(input).unwrap().into_iter(),
205            ))
206    }
207
208    #[test]
209    fn test_parser() {
210        assert_eq!(parse("'number").unwrap().0, Type::Number);
211    }
212
213    #[test]
214    fn parse_trait() {
215        let trait_ = parse("% 'number, _.").unwrap().0;
216
217        if let Type::Trait(trait_) = trait_ {
218            assert_eq!(trait_.len(), 2);
219            assert_eq!(trait_[0].0, Type::Number);
220            assert_eq!(trait_[1].0, Type::Infer);
221        } else {
222            panic!("Expected trait");
223        }
224    }
225
226    #[test]
227    fn parse_type_alias() {
228        assert_eq!(
229            parse("'a something").unwrap().0,
230            Type::Alias("something".into())
231        );
232    }
233
234    #[test]
235    fn parse_single_token() {
236        assert_eq!(parse("_").unwrap().0, Type::Infer);
237        assert_eq!(parse("'this").unwrap().0, Type::This);
238    }
239
240    #[test]
241    fn parse_product_and_sum() {
242        assert_eq!(
243            parse("* + 'number, _., *").unwrap().0,
244            Type::Product(vec![
245                (
246                    Type::Sum(vec![(Type::Number, 4..11), (Type::Infer, 13..14)]),
247                    2..15
248                ),
249                (Type::Product(vec![]), 17..18)
250            ])
251        );
252    }
253
254    #[test]
255    fn parse_function() {
256        assert_eq!(
257            parse(r#"\ 'number, 'number -> _"#).unwrap().0,
258            Type::Function {
259                parameters: vec![(Type::Number, 2..9), (Type::Number, 11..18)],
260                body: Box::new((Type::Infer, 22..23)),
261            }
262        );
263    }
264
265    #[test]
266    fn parse_array() {
267        assert_eq!(
268            parse("['number]").unwrap().0,
269            Type::Array(Box::new((Type::Number, 1..8)))
270        );
271    }
272
273    #[test]
274    fn parse_set() {
275        assert_eq!(
276            parse("{'number}").unwrap().0,
277            Type::Set(Box::new((Type::Number, 1..8),))
278        );
279    }
280
281    #[test]
282    fn parse_bound() {
283        assert_eq!(
284            parse("a: 'a bound").unwrap().0,
285            Type::BoundedVariable {
286                identifier: "a".into(),
287                bound: Box::new((Type::Alias("bound".into()), 3..11)),
288            }
289        );
290    }
291
292    #[test]
293    fn parse_effectful() {
294        assert_eq!(
295            parse("! result + {'number => 'string}, - >a _, {'string => 'number}")
296                .unwrap()
297                .0,
298            Type::Effectful {
299                ty: Box::new((Type::Variable("result".into()), 2..8)),
300                effects: (
301                    EffectExpr::Add(vec![
302                        (
303                            EffectExpr::Effects(vec![(
304                                Effect {
305                                    input: (Type::Number, 12..19),
306                                    output: (Type::String, 23..30),
307                                },
308                                12..30
309                            )]),
310                            11..31
311                        ),
312                        (
313                            EffectExpr::Sub {
314                                minuend: Box::new((
315                                    EffectExpr::Apply {
316                                        function: Box::new((Type::Variable("a".into()), 36..37)),
317                                        arguments: vec![(Type::Infer, 38..39)],
318                                    },
319                                    35..39
320                                )),
321                                subtrahend: Box::new((
322                                    EffectExpr::Effects(vec![(
323                                        Effect {
324                                            input: (Type::String, 42..49),
325                                            output: (Type::Number, 53..60),
326                                        },
327                                        42..60
328                                    ),],),
329                                    41..61
330                                ))
331                            },
332                            33..61
333                        ),
334                    ]),
335                    9..61
336                ),
337            }
338        );
339    }
340
341    #[test]
342    fn parse_brand() {
343        assert_eq!(
344            parse("@added 'number").unwrap().0,
345            Type::Brand {
346                brand: "added".into(),
347                item: Box::new((Type::Number, 7..14)),
348            }
349        );
350    }
351
352    #[test]
353    fn parse_attribute() {
354        assert_eq!(
355            parse("#1 ~ 'number").unwrap().0,
356            Type::Attribute {
357                attr: Box::new((Expr::Literal(Literal::Int(1)), 1..2)),
358                ty: Box::new((Type::Number, 5..12)),
359            }
360        );
361    }
362
363    #[test]
364    fn parse_let() {
365        assert_eq!(
366            parse("$ x ~ x").unwrap().0,
367            Type::Let {
368                variable: "x".into(),
369                body: Box::new((Type::Variable("x".into()), 6..7))
370            }
371        );
372    }
373
374    #[test]
375    fn parse_comment() {
376        assert_eq!(
377            parse("(a)*(b)").unwrap().0,
378            Type::Comment {
379                position: CommentPosition::Prefix,
380                text: "(a)".into(),
381                item: Box::new((
382                    Type::Comment {
383                        position: CommentPosition::Suffix,
384                        text: "(b)".into(),
385                        item: Box::new((Type::Product(vec![]), 3..4)),
386                    },
387                    3..7
388                )),
389            }
390        );
391    }
392}