feat: move anon fn, let, and expect

This commit is contained in:
rvcas 2023-06-30 00:20:12 -04:00
parent 9c98fc8026
commit eea94fc9a4
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
5 changed files with 303 additions and 265 deletions

View File

@ -11,6 +11,7 @@ mod utils;
pub use annotation::parser as annotation;
pub use definitions::parser as definitions;
pub use expr::parser as expression;
pub use pattern::parser as pattern;
use crate::ast::{self, BinOp, Span};
use chumsky::{chain::Chain, prelude::*};
@ -96,32 +97,6 @@ pub fn module(
Ok((module, extra))
}
pub fn anon_fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
// TODO: return a better error when a label is provided `UnexpectedLabel`
choice((
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
label: name.clone(),
name,
location: span,
}
}),
select! {Token::Name {name} => name}.map_with_span(|name, span| ast::ArgName::Named {
label: name.clone(),
name,
location: span,
is_validator_param: false,
}),
))
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.map_with_span(|(arg_name, annotation), span| ast::Arg {
location: span,
annotation,
tipo: (),
arg_name,
})
}
pub fn when_clause_guard_parser() -> impl Parser<Token, ast::ClauseGuard<()>, Error = ParseError> {
recursive(|r| {
let var_parser = select! {
@ -237,190 +212,3 @@ pub fn when_clause_guard_parser() -> impl Parser<Token, ast::ClauseGuard<()>, Er
})
})
}
pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> {
recursive(|r| {
let record_constructor_pattern_arg_parser = choice((
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.then(r.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 = r
.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((
select! { Token::Name {name} => name }
.then(
just(Token::Dot)
.ignore_then(constructor_pattern_parser.clone())
.or_not(),
)
.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,
},
),
r.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(r.clone().separated_by(just(Token::Comma)))
.then(choice((
just(Token::Comma)
.ignore_then(just(Token::DotDot).ignore_then(r.clone().or_not()).or_not()),
just(Token::Comma).ignored().or_not().map(|_| None),
)))
.then_ignore(just(Token::RightSquare))
.validate(|(elements, tail), span: 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: 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(
just(Token::As)
.ignore_then(select! { Token::Name {name} => name})
.or_not(),
)
.map_with_span(|(pattern, opt_as), span| {
if let Some(name) = opt_as {
ast::UntypedPattern::Assign {
name,
location: span,
pattern: Box::new(pattern),
}
} else {
pattern
}
})
})
}

View File

@ -0,0 +1,56 @@
use chumsky::prelude::*;
use crate::{
ast,
expr::{FnStyle, UntypedExpr},
parser::{annotation, error::ParseError, token::Token},
};
pub fn parser<'a>(
seq_r: Recursive<'a, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
just(Token::Fn)
.ignore_then(
params()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
)
.then(just(Token::RArrow).ignore_then(annotation()).or_not())
.then(seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)))
.map_with_span(
|((arguments, return_annotation), body), span| UntypedExpr::Fn {
arguments,
body: Box::new(body),
location: span,
fn_style: FnStyle::Plain,
return_annotation,
},
)
}
pub fn params() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
// TODO: return a better error when a label is provided `UnexpectedLabel`
choice((
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
label: name.clone(),
name,
location: span,
}
}),
select! {Token::Name {name} => name}.map_with_span(|name, span| ast::ArgName::Named {
label: name.clone(),
name,
location: span,
is_validator_param: false,
}),
))
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.map_with_span(|(arg_name, annotation), span| ast::Arg {
location: span,
annotation,
tipo: (),
arg_name,
})
}

View File

@ -0,0 +1,45 @@
use chumsky::prelude::*;
use crate::{
ast,
expr::UntypedExpr,
parser::{annotation, error::ParseError, pattern, token::Token},
};
pub fn let_(
r: Recursive<'_, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
assignment(r, Token::Let)
}
pub fn expect(
r: Recursive<'_, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
assignment(r, Token::Expect)
}
fn assignment(
r: Recursive<'_, Token, UntypedExpr, ParseError>,
keyword: Token,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
let kind = if keyword == Token::Let {
ast::AssignmentKind::Let
} else {
ast::AssignmentKind::Expect
};
just(keyword)
.ignore_then(pattern())
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.then_ignore(just(Token::Equal))
.then(r.clone())
.map_with_span(
move |((pattern, annotation), value), span| UntypedExpr::Assignment {
location: span,
value: Box::new(value),
pattern,
kind,
annotation,
},
)
}

View File

@ -1,6 +1,8 @@
use chumsky::prelude::*;
use vec1::{vec1, Vec1};
pub mod anonymous_function;
pub mod assignment;
mod block;
mod bytearray;
mod if_else;
@ -13,6 +15,7 @@ pub mod string;
mod tuple;
mod var;
pub use anonymous_function::parser as anonymous_function;
pub use block::parser as block;
pub use bytearray::parser as bytearray;
pub use if_else::parser as if_else;
@ -49,25 +52,6 @@ pub fn parser(
}),
});
let anon_fn_parser = just(Token::Fn)
.ignore_then(
anon_fn_param_parser()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
)
.then(just(Token::RArrow).ignore_then(annotation()).or_not())
.then(seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)))
.map_with_span(
|((arguments, return_annotation), body), span| UntypedExpr::Fn {
arguments,
body: Box::new(body),
location: span,
fn_style: FnStyle::Plain,
return_annotation,
},
);
let anon_binop_parser = select! {
Token::EqualEqual => BinOp::Eq,
Token::NotEqual => BinOp::NotEq,
@ -216,36 +200,6 @@ pub fn parser(
clauses,
});
let let_parser = just(Token::Let)
.ignore_then(pattern_parser())
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.then_ignore(just(Token::Equal))
.then(r.clone())
.map_with_span(
|((pattern, annotation), value), span| UntypedExpr::Assignment {
location: span,
value: Box::new(value),
pattern,
kind: ast::AssignmentKind::Let,
annotation,
},
);
let expect_parser = just(Token::Expect)
.ignore_then(pattern_parser())
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.then_ignore(just(Token::Equal))
.then(r.clone())
.map_with_span(
|((pattern, annotation), value), span| UntypedExpr::Assignment {
location: span,
value: Box::new(value),
pattern,
kind: ast::AssignmentKind::Expect,
annotation,
},
);
let expr_unit_parser = choice((
string(),
int(),
@ -256,12 +210,12 @@ pub fn parser(
tuple(r),
bytearray(),
list(r),
anon_fn_parser,
anonymous_function(seq_r),
anon_binop_parser,
block(seq_r),
when_parser,
let_parser,
expect_parser,
assignment::let_(r),
assignment::expect(r),
if_else(seq_r, r),
));

View File

@ -0,0 +1,195 @@
use chumsky::prelude::*;
use crate::ast;
use super::{
error::{self, ParseError},
token::Token,
};
pub fn parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> {
recursive(|r| {
let record_constructor_pattern_arg_parser = choice((
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.then(r.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 = r
.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((
select! { Token::Name {name} => name }
.then(
just(Token::Dot)
.ignore_then(constructor_pattern_parser.clone())
.or_not(),
)
.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,
},
),
r.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(r.clone().separated_by(just(Token::Comma)))
.then(choice((
just(Token::Comma)
.ignore_then(just(Token::DotDot).ignore_then(r.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(
just(Token::As)
.ignore_then(select! { Token::Name {name} => name})
.or_not(),
)
.map_with_span(|(pattern, opt_as), span| {
if let Some(name) = opt_as {
ast::UntypedPattern::Assign {
name,
location: span,
pattern: Box::new(pattern),
}
} else {
pattern
}
})
})
}