Split pattern parser into individual modules.

This commit is contained in:
KtorZ 2023-07-06 09:55:12 +02:00 committed by Lucas
parent 0650d6152d
commit 5a4a2faa4d
7 changed files with 253 additions and 174 deletions

View File

@ -0,0 +1,85 @@
use chumsky::prelude::*;
use crate::{
ast::{CallArg, UntypedPattern},
parser::{error::ParseError, token::Token},
};
pub fn parser(
expression: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
select! {Token::UpName { name } => name}
.then(args(expression))
.map_with_span(|(name, (arguments, with_spread, is_record)), location| {
UntypedPattern::Constructor {
is_record,
location,
name,
arguments,
module: None,
constructor: (),
with_spread,
tipo: (),
}
})
}
pub(crate) fn args(
expression: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, (Vec<CallArg<UntypedPattern>>, bool, bool), Error = ParseError> + '_ {
let record_constructor_pattern_arg_parser = choice((
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.then(expression.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)
.then_ignore(just(Token::Comma).or_not())
.ignored()
.or_not(),
)
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
let tuple_constructor_pattern_arg_parser = expression
.clone()
.map(|pattern| CallArg {
location: pattern.location(),
value: pattern,
label: None,
})
.separated_by(just(Token::Comma))
.allow_trailing()
.then(
just(Token::DotDot)
.then_ignore(just(Token::Comma).or_not())
.ignored()
.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.is_some(), c))
.unwrap_or_else(|| (vec![], false, false))
})
}

View File

@ -0,0 +1,11 @@
use chumsky::prelude::*;
use crate::{
ast::UntypedPattern,
parser::{error::ParseError, token::Token},
};
pub fn parser() -> impl Parser<Token, UntypedPattern, Error = ParseError> {
select! {Token::DiscardName {name} => name}
.map_with_span(|name, location| UntypedPattern::Discard { name, location })
}

View File

@ -0,0 +1,16 @@
use chumsky::prelude::*;
use crate::{
ast::UntypedPattern,
parser::{error::ParseError, token::Token},
};
pub fn parser() -> impl Parser<Token, UntypedPattern, Error = ParseError> {
select! {Token::Int {value, base} => (value, base)}.map_with_span(|(value, base), location| {
UntypedPattern::Int {
location,
value,
base,
}
})
}

View File

@ -0,0 +1,58 @@
use chumsky::prelude::*;
use crate::{
ast::{self, UntypedPattern},
parser::{
error::{self, ParseError},
token::Token,
},
};
pub fn parser(
expression: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
just(Token::LeftSquare)
.ignore_then(expression.clone().separated_by(just(Token::Comma)))
.then(choice((
just(Token::Comma).ignore_then(
just(Token::DotDot)
.ignore_then(expression.clone().or_not())
.or_not(),
),
just(Token::Comma).ignored().or_not().map(|_| None),
)))
.then_ignore(just(Token::RightSquare))
.validate(|(elements, tail), span: ast::Span, emit| {
let tail = match tail {
// There is a tail and it has a Pattern::Var or Pattern::Discard
Some(Some(pat @ (UntypedPattern::Var { .. } | UntypedPattern::Discard { .. }))) => {
Some(pat)
}
Some(Some(pat)) => {
emit(ParseError::expected_input_found(
pat.location(),
None,
Some(error::Pattern::Match),
));
Some(pat)
}
// There is a tail but it has no content, implicit discard
Some(None) => Some(UntypedPattern::Discard {
location: ast::Span {
start: span.end - 1,
end: span.end,
},
name: "_".to_string(),
}),
// No tail specified
None => None,
};
UntypedPattern::List {
location: span,
elements,
tail: tail.map(Box::new),
}
})
}

View File

@ -1,183 +1,33 @@
use chumsky::prelude::*; use chumsky::prelude::*;
use crate::ast; mod constructor;
mod discard;
mod int;
mod list;
mod tuple;
mod var;
use super::{ pub use constructor::parser as constructor;
error::{self, ParseError}, pub use discard::parser as discard;
token::Token, pub use int::parser as int;
pub use list::parser as list;
pub use tuple::parser as tuple;
pub use var::parser as var;
use crate::{
ast::UntypedPattern,
parser::{error::ParseError, token::Token},
}; };
pub fn parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> { pub fn parser() -> impl Parser<Token, UntypedPattern, Error = ParseError> {
recursive(|expression| { recursive(|expression| {
let record_constructor_pattern_arg_parser = choice((
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.then(expression.clone())
.map_with_span(|(name, pattern), span| ast::CallArg {
location: span,
label: Some(name),
value: pattern,
}),
select! {Token::Name{name} => name}.map_with_span(|name, span| ast::CallArg {
location: span,
value: ast::UntypedPattern::Var {
name: name.clone(),
location: span,
},
label: Some(name),
}),
))
.separated_by(just(Token::Comma))
.allow_trailing()
.then(
just(Token::DotDot)
.then_ignore(just(Token::Comma).or_not())
.ignored()
.or_not(),
)
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
let tuple_constructor_pattern_arg_parser = expression
.clone()
.map(|pattern| ast::CallArg {
location: pattern.location(),
value: pattern,
label: None,
})
.separated_by(just(Token::Comma))
.allow_trailing()
.then(
just(Token::DotDot)
.then_ignore(just(Token::Comma).or_not())
.ignored()
.or_not(),
)
.delimited_by(just(Token::LeftParen), just(Token::RightParen));
let constructor_pattern_args_parser = 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.is_some(), c))
.unwrap_or_else(|| (vec![], false, false))
});
let constructor_pattern_parser =
select! {Token::UpName { name } => name}.then(constructor_pattern_args_parser);
choice(( choice((
select! { Token::Name {name} => name } var(expression.clone()),
.then( constructor(expression.clone()),
just(Token::Dot) discard(),
.ignore_then(constructor_pattern_parser.clone()) int(),
.or_not(), tuple(expression.clone()),
) list(expression),
.map_with_span(|(name, opt_pattern), span| {
if let Some((c_name, (arguments, with_spread, is_record))) = opt_pattern {
ast::UntypedPattern::Constructor {
is_record,
location: span,
name: c_name,
arguments,
module: Some(name),
constructor: (),
with_spread,
tipo: (),
}
} else {
ast::UntypedPattern::Var {
location: span,
name,
}
}
}),
constructor_pattern_parser.map_with_span(
|(name, (arguments, with_spread, is_record)), span| {
ast::UntypedPattern::Constructor {
is_record,
location: span,
name,
arguments,
module: None,
constructor: (),
with_spread,
tipo: (),
}
},
),
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::UntypedPattern::Discard {
name,
location: span,
}
}),
select! {Token::Int {value, base} => (value, base)}.map_with_span(
|(value, base), span| ast::UntypedPattern::Int {
location: span,
value,
base,
},
),
expression
.clone()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
just(Token::RightParen),
)
.map_with_span(|elems, span| ast::UntypedPattern::Tuple {
location: span,
elems,
}),
just(Token::LeftSquare)
.ignore_then(expression.clone().separated_by(just(Token::Comma)))
.then(choice((
just(Token::Comma).ignore_then(
just(Token::DotDot)
.ignore_then(expression.clone().or_not())
.or_not(),
),
just(Token::Comma).ignored().or_not().map(|_| None),
)))
.then_ignore(just(Token::RightSquare))
.validate(|(elements, tail), span: ast::Span, emit| {
let tail = match tail {
// There is a tail and it has a Pattern::Var or Pattern::Discard
Some(Some(
pat @ (ast::UntypedPattern::Var { .. }
| ast::UntypedPattern::Discard { .. }),
)) => Some(pat),
Some(Some(pat)) => {
emit(ParseError::expected_input_found(
pat.location(),
None,
Some(error::Pattern::Match),
));
Some(pat)
}
// There is a tail but it has no content, implicit discard
Some(None) => Some(ast::UntypedPattern::Discard {
location: ast::Span {
start: span.end - 1,
end: span.end,
},
name: "_".to_string(),
}),
// No tail specified
None => None,
};
ast::UntypedPattern::List {
location: span,
elements,
tail: tail.map(Box::new),
}
}),
)) ))
.then( .then(
just(Token::As) just(Token::As)
@ -186,7 +36,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> {
) )
.map_with_span(|(pattern, opt_as), span| { .map_with_span(|(pattern, opt_as), span| {
if let Some(name) = opt_as { if let Some(name) = opt_as {
ast::UntypedPattern::Assign { UntypedPattern::Assign {
name, name,
location: span, location: span,
pattern: Box::new(pattern), pattern: Box::new(pattern),

View File

@ -0,0 +1,20 @@
use chumsky::prelude::*;
use crate::{
ast::UntypedPattern,
parser::{error::ParseError, token::Token},
};
pub fn parser(
expression: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
expression
.clone()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
just(Token::RightParen),
)
.map_with_span(|elems, location| UntypedPattern::Tuple { location, elems })
}

View File

@ -0,0 +1,39 @@
use chumsky::prelude::*;
use super::constructor;
use crate::{
ast::UntypedPattern,
parser::{error::ParseError, token::Token},
};
pub fn parser(
expression: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
select! { Token::Name {name} => name }
.then(
just(Token::Dot)
.ignore_then(
select! {Token::UpName { name } => name}.then(constructor::args(expression)),
)
.or_not(),
)
.map_with_span(|(name, opt_pattern), span| {
if let Some((c_name, (arguments, with_spread, is_record))) = opt_pattern {
UntypedPattern::Constructor {
is_record,
location: span,
name: c_name,
arguments,
module: Some(name),
constructor: (),
with_spread,
tipo: (),
}
} else {
UntypedPattern::Var {
location: span,
name,
}
}
})
}