aiken_lang/parser/expr/
anonymous_function.rs

1use crate::{
2    ast,
3    expr::{FnStyle, UntypedExpr},
4    parser::{annotation, error::ParseError, pattern, token::Token},
5};
6use chumsky::prelude::*;
7
8pub fn parser(
9    sequence: Recursive<'_, Token, UntypedExpr, ParseError>,
10) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
11    just(Token::Fn)
12        .ignore_then(
13            params()
14                .separated_by(just(Token::Comma))
15                .allow_trailing()
16                .delimited_by(just(Token::LeftParen), just(Token::RightParen)),
17        )
18        .then(just(Token::RArrow).ignore_then(annotation()).or_not())
19        .then(sequence.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)))
20        .map_with_span(
21            |((arguments, return_annotation), body), span| UntypedExpr::Fn {
22                arguments,
23                body: Box::new(body),
24                location: span,
25                fn_style: FnStyle::Plain,
26                return_annotation,
27            },
28        )
29}
30
31pub fn params() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
32    // TODO: return a better error when a label is provided `UnexpectedLabel`
33    choice((
34        select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
35            ast::ArgBy::ByName(ast::ArgName::Discarded {
36                label: name.clone(),
37                name,
38                location: span,
39            })
40        }),
41        select! {Token::Name {name} => name}.map_with_span(|name, span| {
42            ast::ArgBy::ByName(ast::ArgName::Named {
43                label: name.clone(),
44                name,
45                location: span,
46            })
47        }),
48        pattern().map(ast::ArgBy::ByPattern),
49    ))
50    .then(just(Token::Colon).ignore_then(annotation()).or_not())
51    .map_with_span(|(by, annotation), span| ast::UntypedArg {
52        is_validator_param: false,
53        location: span,
54        annotation,
55        doc: None,
56        by,
57    })
58}
59
60#[cfg(test)]
61mod tests {
62    use crate::assert_expr;
63
64    #[test]
65    fn anonymous_function_basic() {
66        assert_expr!(r#"fn (a: Int) -> Int { a + 1 }"#);
67    }
68
69    #[test]
70    fn anonymous_function_by_pattern_no_annotation() {
71        assert_expr!(r#"fn (Foo { my_field }) { my_field * 2 }"#);
72    }
73
74    #[test]
75    fn anonymous_function_by_pattern_with_annotation() {
76        assert_expr!(r#"fn (Foo { my_field } : Foo) { my_field * 2 }"#);
77    }
78
79    #[test]
80    fn anonymous_function_by_pattern_with_alias() {
81        assert_expr!(r#"fn (Foo { my_field, .. } as x) { my_field * my_other_field }"#);
82    }
83}