Skip to main content

aiken_lang/parser/pattern/
list.rs

1use chumsky::prelude::*;
2
3use crate::{
4    ast::{self, UntypedPattern},
5    parser::{
6        error::{self, ParseError},
7        token::Token,
8    },
9};
10
11pub fn parser(
12    expression: Recursive<'_, Token, UntypedPattern, ParseError>,
13) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
14    just(Token::LeftSquare)
15        .ignore_then(expression.clone().separated_by(just(Token::Comma)))
16        .then(choice((
17            just(Token::Comma).ignore_then(
18                just(Token::DotDot)
19                    .ignore_then(expression.clone().or_not())
20                    .or_not(),
21            ),
22            just(Token::Comma).ignored().or_not().map(|_| None),
23        )))
24        .then_ignore(just(Token::RightSquare))
25        .validate(|(elements, tail), span: ast::Span, emit| {
26            // A leading comma without a preceding element (e.g. `[, ..rest]`) is malformed.
27            if elements.is_empty() && tail.is_some() {
28                emit(ParseError::expected_input_found(
29                    span,
30                    None,
31                    Some(error::Pattern::SpreadNoSubject),
32                ));
33            }
34
35            let tail = match tail {
36                // There is a tail and it has a Pattern::Var or Pattern::Discard
37                Some(Some(pat @ (UntypedPattern::Var { .. } | UntypedPattern::Discard { .. }))) => {
38                    Some(pat)
39                }
40                Some(Some(pat)) => {
41                    emit(ParseError::expected_input_found(
42                        pat.location(),
43                        None,
44                        Some(error::Pattern::Match),
45                    ));
46
47                    Some(pat)
48                }
49                // There is a tail but it has no content, implicit discard
50                Some(None) => Some(UntypedPattern::Discard {
51                    location: ast::Span {
52                        start: span.end - 1,
53                        end: span.end,
54                    },
55                    name: "_".to_string(),
56                }),
57                // No tail specified
58                None => None,
59            };
60
61            UntypedPattern::List {
62                location: span,
63                elements,
64                tail: tail.map(Box::new),
65            }
66        })
67}