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 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}