aiken/crates/aiken-lang/src/parser/pattern/constructor.rs

154 lines
4.8 KiB
Rust

use chumsky::prelude::*;
use crate::{
ast::{CallArg, Namespace, Span, UntypedPattern},
parser::{error::ParseError, token::Token},
};
pub fn parser(
pattern: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
choice((
select! { Token::Name { name } => name }
.then(just(Token::Dot).ignore_then(select! {Token::UpName { name } => name}))
.then(
just(Token::Dot).ignore_then(
select! {Token::UpName { name } => name}.then(args(pattern.clone())),
),
)
.map_with_span(
|((module, namespace), (name, (arguments, spread_location, is_record))), span| {
UntypedPattern::Constructor {
is_record,
location: span,
name,
arguments,
module: Some(Namespace::Type(Some(module), namespace)),
constructor: (),
spread_location,
tipo: (),
}
},
),
select! { Token::UpName { name } => name }
.then(
just(Token::Dot).ignore_then(
select! {Token::UpName { name } => name}.then(args(pattern.clone())),
),
)
.map_with_span(
|(namespace, (name, (arguments, spread_location, is_record))), span| {
UntypedPattern::Constructor {
is_record,
location: span,
name,
arguments,
module: Some(Namespace::Type(None, namespace)),
constructor: (),
spread_location,
tipo: (),
}
},
),
select! {Token::UpName { name } => name}
.then(args(pattern))
.map_with_span(
|(name, (arguments, spread_location, is_record)), location| {
UntypedPattern::Constructor {
is_record,
location,
name,
arguments,
module: None,
constructor: (),
spread_location,
tipo: (),
}
},
),
))
}
pub(crate) fn args(
pattern: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, (Vec<CallArg<UntypedPattern>>, Option<Span>, bool), Error = ParseError> + '_
{
let record_constructor_pattern_arg_parser = choice((
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.then(pattern.clone())
.map_with_span(|(name, pattern), span| CallArg {
location: span,
label: Some(name),
value: pattern,
}),
select! {Token::Name{name} => name}.map_with_span(|name, span| CallArg {
location: span,
value: UntypedPattern::Var {
name: name.clone(),
location: span,
},
label: Some(name),
}),
))
.separated_by(just(Token::Comma))
.allow_trailing()
.then(
just(Token::DotDot)
.ignored()
.map_with_span(|_spread, span| span)
.then_ignore(just(Token::Comma).or_not())
.or_not(),
)
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
let tuple_constructor_pattern_arg_parser = pattern
.clone()
.map(|pattern| CallArg {
location: pattern.location(),
value: pattern,
label: None,
})
.separated_by(just(Token::Comma))
.allow_trailing()
.then(
just(Token::DotDot)
.ignored()
.map_with_span(|_spread, span| span)
.then_ignore(just(Token::Comma).or_not())
.or_not(),
)
.delimited_by(just(Token::LeftParen), just(Token::RightParen));
choice((
record_constructor_pattern_arg_parser.map(|a| (a, true)),
tuple_constructor_pattern_arg_parser.map(|a| (a, false)),
))
.or_not()
.map(|opt_args| {
opt_args
.map(|((a, b), c)| (a, b, c))
.unwrap_or_else(|| (vec![], None, false))
})
}
#[cfg(test)]
mod tests {
use crate::assert_pattern;
#[test]
fn constructor_basic() {
assert_pattern!("True");
}
#[test]
fn constructor_module_select() {
assert_pattern!("module.Foo");
}
#[test]
fn constructor_type_select() {
assert_pattern!("Foo.Bar");
}
}