feat: finish splitting up parsers
This commit is contained in:
parent
63cdb8aa09
commit
2226747dc1
|
@ -8,6 +8,9 @@ strip = true
|
|||
shared-version = true
|
||||
tag-name = "v{{version}}"
|
||||
|
||||
[workspace.dependencies]
|
||||
insta = { version = "1.30.0", features = ["yaml"] }
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ repository = "https://github.com/aiken-lang/aiken"
|
|||
homepage = "https://github.com/aiken-lang/aiken"
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
"Lucas Rosa <x@rvcas.dev>",
|
||||
"Kasey White <kwhitemsg@gmail.com>",
|
||||
"KtorZ <matthias.benkort@gmail.com>",
|
||||
"Lucas Rosa <x@rvcas.dev>",
|
||||
"Kasey White <kwhitemsg@gmail.com>",
|
||||
"KtorZ <matthias.benkort@gmail.com>",
|
||||
]
|
||||
rust-version = "1.66.1"
|
||||
|
||||
|
@ -30,15 +30,12 @@ num-bigint = "0.4.3"
|
|||
[target.'cfg(not(target_family="wasm"))'.dependencies]
|
||||
chumsky = "0.9.2"
|
||||
[target.'cfg(target_family="wasm")'.dependencies]
|
||||
chumsky = { version = "0.9.2", features = ["ahash", "std"], default-features = false }
|
||||
chumsky = { version = "0.9.2", features = [
|
||||
"ahash",
|
||||
"std",
|
||||
], default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.1"
|
||||
insta = { version = "1.30.0", features = ["yaml"] }
|
||||
insta.workspace = true
|
||||
pretty_assertions = "1.3.0"
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.similar]
|
||||
opt-level = 3
|
||||
|
|
|
@ -13,7 +13,7 @@ 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 crate::ast::{self, Span};
|
||||
use chumsky::{chain::Chain, prelude::*};
|
||||
use error::ParseError;
|
||||
use extra::ModuleExtra;
|
||||
|
@ -96,119 +96,3 @@ pub fn module(
|
|||
|
||||
Ok((module, extra))
|
||||
}
|
||||
|
||||
pub fn when_clause_guard_parser() -> impl Parser<Token, ast::ClauseGuard<()>, Error = ParseError> {
|
||||
recursive(|r| {
|
||||
let var_parser = select! {
|
||||
Token::Name { name } => name,
|
||||
Token::UpName { name } => name,
|
||||
}
|
||||
.map_with_span(|name, span| ast::ClauseGuard::Var {
|
||||
name,
|
||||
tipo: (),
|
||||
location: span,
|
||||
});
|
||||
|
||||
let constant_parser = definitions::constant::value().map(ast::ClauseGuard::Constant);
|
||||
|
||||
let block_parser = r
|
||||
.clone()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen));
|
||||
|
||||
let leaf_parser = choice((var_parser, constant_parser, block_parser)).boxed();
|
||||
|
||||
let unary_op = just(Token::Bang);
|
||||
|
||||
let unary = unary_op
|
||||
.map_with_span(|op, span| (op, span))
|
||||
.repeated()
|
||||
.then(leaf_parser)
|
||||
.foldr(|(_, span), value| ast::ClauseGuard::Not {
|
||||
location: span.union(value.location()),
|
||||
value: Box::new(value),
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let comparison_op = choice((
|
||||
just(Token::EqualEqual).to(BinOp::Eq),
|
||||
just(Token::NotEqual).to(BinOp::NotEq),
|
||||
just(Token::Less).to(BinOp::LtInt),
|
||||
just(Token::Greater).to(BinOp::GtInt),
|
||||
just(Token::LessEqual).to(BinOp::LtEqInt),
|
||||
just(Token::GreaterEqual).to(BinOp::GtEqInt),
|
||||
));
|
||||
|
||||
let comparison = unary
|
||||
.clone()
|
||||
.then(comparison_op.then(unary).repeated())
|
||||
.foldl(|left, (op, right)| {
|
||||
let location = left.location().union(right.location());
|
||||
let left = Box::new(left);
|
||||
let right = Box::new(right);
|
||||
match op {
|
||||
BinOp::Eq => ast::ClauseGuard::Equals {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
BinOp::NotEq => ast::ClauseGuard::NotEquals {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
BinOp::LtInt => ast::ClauseGuard::LtInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
BinOp::GtInt => ast::ClauseGuard::GtInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
BinOp::LtEqInt => ast::ClauseGuard::LtEqInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
BinOp::GtEqInt => ast::ClauseGuard::GtEqInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let and_op = just(Token::AmperAmper);
|
||||
let conjunction = comparison
|
||||
.clone()
|
||||
.then(and_op.then(comparison).repeated())
|
||||
.foldl(|left, (_tok, right)| {
|
||||
let location = left.location().union(right.location());
|
||||
let left = Box::new(left);
|
||||
let right = Box::new(right);
|
||||
ast::ClauseGuard::And {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
}
|
||||
});
|
||||
|
||||
let or_op = just(Token::VbarVbar);
|
||||
conjunction
|
||||
.clone()
|
||||
.then(or_op.then(conjunction).repeated())
|
||||
.foldl(|left, (_tok, right)| {
|
||||
let location = left.location().union(right.location());
|
||||
let left = Box::new(left);
|
||||
let right = Box::new(right);
|
||||
ast::ClauseGuard::Or {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
expr::{FnStyle, UntypedExpr},
|
||||
parser::{error::ParseError, token::Token},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
||||
select! {
|
||||
Token::EqualEqual => ast::BinOp::Eq,
|
||||
Token::NotEqual => ast::BinOp::NotEq,
|
||||
Token::Less => ast::BinOp::LtInt,
|
||||
Token::LessEqual => ast::BinOp::LtEqInt,
|
||||
Token::Greater => ast::BinOp::GtInt,
|
||||
Token::GreaterEqual => ast::BinOp::GtEqInt,
|
||||
Token::VbarVbar => ast::BinOp::Or,
|
||||
Token::AmperAmper => ast::BinOp::And,
|
||||
Token::Plus => ast::BinOp::AddInt,
|
||||
Token::Minus => ast::BinOp::SubInt,
|
||||
Token::Slash => ast::BinOp::DivInt,
|
||||
Token::Star => ast::BinOp::MultInt,
|
||||
Token::Percent => ast::BinOp::ModInt,
|
||||
}
|
||||
.map_with_span(|name, location| {
|
||||
use ast::BinOp::*;
|
||||
|
||||
let arg_annotation = match name {
|
||||
Or | And => Some(ast::Annotation::boolean(location)),
|
||||
Eq | NotEq => None,
|
||||
LtInt | LtEqInt | GtInt | GtEqInt | AddInt | SubInt | MultInt | DivInt | ModInt => {
|
||||
Some(ast::Annotation::int(location))
|
||||
}
|
||||
};
|
||||
|
||||
let return_annotation = match name {
|
||||
Or | And | Eq | NotEq | LtInt | LtEqInt | GtInt | GtEqInt => {
|
||||
Some(ast::Annotation::boolean(location))
|
||||
}
|
||||
AddInt | SubInt | MultInt | DivInt | ModInt => Some(ast::Annotation::int(location)),
|
||||
};
|
||||
|
||||
let arguments = vec![
|
||||
ast::Arg {
|
||||
arg_name: ast::ArgName::Named {
|
||||
name: "left".to_string(),
|
||||
label: "left".to_string(),
|
||||
location,
|
||||
is_validator_param: false,
|
||||
},
|
||||
annotation: arg_annotation.clone(),
|
||||
location,
|
||||
tipo: (),
|
||||
},
|
||||
ast::Arg {
|
||||
arg_name: ast::ArgName::Named {
|
||||
name: "right".to_string(),
|
||||
label: "right".to_string(),
|
||||
location,
|
||||
is_validator_param: false,
|
||||
},
|
||||
annotation: arg_annotation,
|
||||
location,
|
||||
tipo: (),
|
||||
},
|
||||
];
|
||||
|
||||
let body = UntypedExpr::BinOp {
|
||||
location,
|
||||
name,
|
||||
left: Box::new(UntypedExpr::Var {
|
||||
location,
|
||||
name: "left".to_string(),
|
||||
}),
|
||||
right: Box::new(UntypedExpr::Var {
|
||||
location,
|
||||
name: "right".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
UntypedExpr::Fn {
|
||||
arguments,
|
||||
body: Box::new(body),
|
||||
return_annotation,
|
||||
fn_style: FnStyle::BinOp(name),
|
||||
location,
|
||||
}
|
||||
})
|
||||
}
|
|
@ -6,9 +6,9 @@ use crate::{
|
|||
parser::{annotation, error::ParseError, token::Token},
|
||||
};
|
||||
|
||||
pub fn parser<'a>(
|
||||
seq_r: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
||||
pub fn parser(
|
||||
seq_r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
just(Token::Fn)
|
||||
.ignore_then(
|
||||
params()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use chumsky::prelude::*;
|
||||
use vec1::{vec1, Vec1};
|
||||
use vec1::Vec1;
|
||||
|
||||
mod anonymous_binop;
|
||||
pub mod anonymous_function;
|
||||
pub mod assignment;
|
||||
mod block;
|
||||
|
@ -14,7 +15,9 @@ mod sequence;
|
|||
pub mod string;
|
||||
mod tuple;
|
||||
mod var;
|
||||
pub mod when;
|
||||
|
||||
use anonymous_binop::parser as anonymous_binop;
|
||||
pub use anonymous_function::parser as anonymous_function;
|
||||
pub use block::parser as block;
|
||||
pub use bytearray::parser as bytearray;
|
||||
|
@ -27,10 +30,11 @@ pub use sequence::parser as sequence;
|
|||
pub use string::parser as string;
|
||||
pub use tuple::parser as tuple;
|
||||
pub use var::parser as var;
|
||||
pub use when::parser as when;
|
||||
|
||||
use crate::{
|
||||
ast::{self, Span},
|
||||
expr::UntypedExpr,
|
||||
expr::{FnStyle, UntypedExpr},
|
||||
};
|
||||
|
||||
use super::{error::ParseError, token::Token};
|
||||
|
@ -52,171 +56,23 @@ pub fn parser(
|
|||
}),
|
||||
});
|
||||
|
||||
let anon_binop_parser = select! {
|
||||
Token::EqualEqual => BinOp::Eq,
|
||||
Token::NotEqual => BinOp::NotEq,
|
||||
Token::Less => BinOp::LtInt,
|
||||
Token::LessEqual => BinOp::LtEqInt,
|
||||
Token::Greater => BinOp::GtInt,
|
||||
Token::GreaterEqual => BinOp::GtEqInt,
|
||||
Token::VbarVbar => BinOp::Or,
|
||||
Token::AmperAmper => BinOp::And,
|
||||
Token::Plus => BinOp::AddInt,
|
||||
Token::Minus => BinOp::SubInt,
|
||||
Token::Slash => BinOp::DivInt,
|
||||
Token::Star => BinOp::MultInt,
|
||||
Token::Percent => BinOp::ModInt,
|
||||
}
|
||||
.map_with_span(|name, location| {
|
||||
use BinOp::*;
|
||||
|
||||
let arg_annotation = match name {
|
||||
Or | And => Some(ast::Annotation::boolean(location)),
|
||||
Eq | NotEq => None,
|
||||
LtInt | LtEqInt | GtInt | GtEqInt | AddInt | SubInt | MultInt | DivInt | ModInt => {
|
||||
Some(ast::Annotation::int(location))
|
||||
}
|
||||
};
|
||||
|
||||
let return_annotation = match name {
|
||||
Or | And | Eq | NotEq | LtInt | LtEqInt | GtInt | GtEqInt => {
|
||||
Some(ast::Annotation::boolean(location))
|
||||
}
|
||||
AddInt | SubInt | MultInt | DivInt | ModInt => Some(ast::Annotation::int(location)),
|
||||
};
|
||||
|
||||
let arguments = vec![
|
||||
ast::Arg {
|
||||
arg_name: ast::ArgName::Named {
|
||||
name: "left".to_string(),
|
||||
label: "left".to_string(),
|
||||
location,
|
||||
is_validator_param: false,
|
||||
},
|
||||
annotation: arg_annotation.clone(),
|
||||
location,
|
||||
tipo: (),
|
||||
},
|
||||
ast::Arg {
|
||||
arg_name: ast::ArgName::Named {
|
||||
name: "right".to_string(),
|
||||
label: "right".to_string(),
|
||||
location,
|
||||
is_validator_param: false,
|
||||
},
|
||||
annotation: arg_annotation,
|
||||
location,
|
||||
tipo: (),
|
||||
},
|
||||
];
|
||||
|
||||
let body = UntypedExpr::BinOp {
|
||||
location,
|
||||
name,
|
||||
left: Box::new(UntypedExpr::Var {
|
||||
location,
|
||||
name: "left".to_string(),
|
||||
}),
|
||||
right: Box::new(UntypedExpr::Var {
|
||||
location,
|
||||
name: "right".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
UntypedExpr::Fn {
|
||||
arguments,
|
||||
body: Box::new(body),
|
||||
return_annotation,
|
||||
fn_style: FnStyle::BinOp(name),
|
||||
location,
|
||||
}
|
||||
});
|
||||
|
||||
let when_clause_parser = pattern_parser()
|
||||
.then(
|
||||
just(Token::Vbar)
|
||||
.ignore_then(pattern_parser())
|
||||
.repeated()
|
||||
.or_not(),
|
||||
)
|
||||
.then(choice((
|
||||
just(Token::If)
|
||||
.ignore_then(when_clause_guard_parser())
|
||||
.or_not()
|
||||
.then_ignore(just(Token::RArrow)),
|
||||
just(Token::If)
|
||||
.ignore_then(take_until(just(Token::RArrow)))
|
||||
.validate(|_value, span, emit| {
|
||||
emit(ParseError::invalid_when_clause_guard(span));
|
||||
None
|
||||
}),
|
||||
)))
|
||||
// TODO: add hint "Did you mean to wrap a multi line clause in curly braces?"
|
||||
.then(choice((
|
||||
r.clone(),
|
||||
just(Token::Todo)
|
||||
.ignore_then(
|
||||
r.clone()
|
||||
.then_ignore(one_of(Token::RArrow).not().rewind())
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|reason, span| {
|
||||
UntypedExpr::todo(span, reason.map(flexible_string_literal))
|
||||
}),
|
||||
just(Token::ErrorTerm)
|
||||
.ignore_then(
|
||||
r.clone()
|
||||
.then_ignore(just(Token::RArrow).not().rewind())
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|reason, span| {
|
||||
UntypedExpr::error(span, reason.map(flexible_string_literal))
|
||||
}),
|
||||
)))
|
||||
.map_with_span(
|
||||
|(((pattern, alternative_patterns_opt), guard), then), span| {
|
||||
let mut patterns = vec1![pattern];
|
||||
patterns.append(&mut alternative_patterns_opt.unwrap_or_default());
|
||||
ast::UntypedClause {
|
||||
location: span,
|
||||
patterns,
|
||||
guard,
|
||||
then,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let when_parser = just(Token::When)
|
||||
// TODO: If subject is empty we should return ParseErrorType::ExpectedExpr,
|
||||
.ignore_then(r.clone().map(Box::new))
|
||||
.then_ignore(just(Token::Is))
|
||||
.then_ignore(just(Token::LeftBrace))
|
||||
// TODO: If clauses are empty we should return ParseErrorType::NoCaseClause
|
||||
.then(when_clause_parser.repeated())
|
||||
.then_ignore(just(Token::RightBrace))
|
||||
.map_with_span(|(subject, clauses), span| UntypedExpr::When {
|
||||
location: span,
|
||||
subject,
|
||||
clauses,
|
||||
});
|
||||
|
||||
let expr_unit_parser = choice((
|
||||
string(),
|
||||
int(),
|
||||
record_update(r),
|
||||
record(r),
|
||||
record_update(r.clone()),
|
||||
record(r.clone()),
|
||||
field_access_constructor,
|
||||
var(),
|
||||
tuple(r),
|
||||
tuple(r.clone()),
|
||||
bytearray(),
|
||||
list(r),
|
||||
anonymous_function(seq_r),
|
||||
anon_binop_parser,
|
||||
block(seq_r),
|
||||
when_parser,
|
||||
assignment::let_(r),
|
||||
assignment::expect(r),
|
||||
if_else(seq_r, r),
|
||||
list(r.clone()),
|
||||
anonymous_function(seq_r.clone()),
|
||||
anonymous_binop(),
|
||||
block(seq_r.clone()),
|
||||
when(r.clone()),
|
||||
assignment::let_(r.clone()),
|
||||
assignment::expect(r.clone()),
|
||||
if_else(seq_r, r.clone()),
|
||||
));
|
||||
|
||||
// Parsing a function call into the appropriate structure
|
||||
|
@ -262,7 +118,7 @@ pub fn parser(
|
|||
select! { Token::Name { name } => name }
|
||||
.then_ignore(just(Token::Colon))
|
||||
.or_not()
|
||||
.then(r.clone())
|
||||
.then(r)
|
||||
.map_with_span(|(label, value), span| {
|
||||
ParserArg::Arg(Box::new(ast::CallArg {
|
||||
label,
|
||||
|
@ -298,7 +154,8 @@ pub fn parser(
|
|||
.map(|(index, a)| match a {
|
||||
ParserArg::Arg(arg) => *arg,
|
||||
ParserArg::Hole { location, label } => {
|
||||
let name = format!("{CAPTURE_VARIABLE}__{index}");
|
||||
let name = format!("{}__{index}", ast::CAPTURE_VARIABLE);
|
||||
|
||||
holes.push(ast::Arg {
|
||||
location: Span::empty(),
|
||||
annotation: None,
|
||||
|
@ -316,7 +173,7 @@ pub fn parser(
|
|||
location,
|
||||
value: UntypedExpr::Var {
|
||||
location,
|
||||
name: format!("{CAPTURE_VARIABLE}__{index}"),
|
||||
name: format!("{}__{index}", ast::CAPTURE_VARIABLE),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -367,7 +224,7 @@ pub fn parser(
|
|||
|
||||
// Negate
|
||||
let op = choice((
|
||||
just(Token::Bang).to(UnOp::Not),
|
||||
just(Token::Bang).to(ast::UnOp::Not),
|
||||
just(Token::Minus)
|
||||
// NOTE: Prevent conflict with usage for '-' as a standalone binary op.
|
||||
// This will make '-' parse when used as standalone binop in a function call.
|
||||
|
@ -381,7 +238,7 @@ pub fn parser(
|
|||
//
|
||||
// which seems acceptable.
|
||||
.then_ignore(just(Token::Comma).not().rewind())
|
||||
.to(UnOp::Negate),
|
||||
.to(ast::UnOp::Negate),
|
||||
));
|
||||
|
||||
let unary = op
|
||||
|
@ -397,9 +254,9 @@ pub fn parser(
|
|||
|
||||
// Product
|
||||
let op = choice((
|
||||
just(Token::Star).to(BinOp::MultInt),
|
||||
just(Token::Slash).to(BinOp::DivInt),
|
||||
just(Token::Percent).to(BinOp::ModInt),
|
||||
just(Token::Star).to(ast::BinOp::MultInt),
|
||||
just(Token::Slash).to(ast::BinOp::DivInt),
|
||||
just(Token::Percent).to(ast::BinOp::ModInt),
|
||||
));
|
||||
|
||||
let product = unary
|
||||
|
@ -415,8 +272,8 @@ pub fn parser(
|
|||
|
||||
// Sum
|
||||
let op = choice((
|
||||
just(Token::Plus).to(BinOp::AddInt),
|
||||
just(Token::Minus).to(BinOp::SubInt),
|
||||
just(Token::Plus).to(ast::BinOp::AddInt),
|
||||
just(Token::Minus).to(ast::BinOp::SubInt),
|
||||
));
|
||||
|
||||
let sum = product
|
||||
|
@ -432,12 +289,12 @@ pub fn parser(
|
|||
|
||||
// Comparison
|
||||
let op = choice((
|
||||
just(Token::EqualEqual).to(BinOp::Eq),
|
||||
just(Token::NotEqual).to(BinOp::NotEq),
|
||||
just(Token::Less).to(BinOp::LtInt),
|
||||
just(Token::Greater).to(BinOp::GtInt),
|
||||
just(Token::LessEqual).to(BinOp::LtEqInt),
|
||||
just(Token::GreaterEqual).to(BinOp::GtEqInt),
|
||||
just(Token::EqualEqual).to(ast::BinOp::Eq),
|
||||
just(Token::NotEqual).to(ast::BinOp::NotEq),
|
||||
just(Token::Less).to(ast::BinOp::LtInt),
|
||||
just(Token::Greater).to(ast::BinOp::GtInt),
|
||||
just(Token::LessEqual).to(ast::BinOp::LtEqInt),
|
||||
just(Token::GreaterEqual).to(ast::BinOp::GtEqInt),
|
||||
));
|
||||
|
||||
let comparison = sum
|
||||
|
@ -452,7 +309,7 @@ pub fn parser(
|
|||
.boxed();
|
||||
|
||||
// Conjunction
|
||||
let op = just(Token::AmperAmper).to(BinOp::And);
|
||||
let op = just(Token::AmperAmper).to(ast::BinOp::And);
|
||||
let conjunction = comparison
|
||||
.clone()
|
||||
.then(op.then(comparison).repeated())
|
||||
|
@ -465,7 +322,7 @@ pub fn parser(
|
|||
.boxed();
|
||||
|
||||
// Disjunction
|
||||
let op = just(Token::VbarVbar).to(BinOp::Or);
|
||||
let op = just(Token::VbarVbar).to(ast::BinOp::Or);
|
||||
let disjunction = conjunction
|
||||
.clone()
|
||||
.then(op.then(conjunction).repeated())
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
use chumsky::prelude::*;
|
||||
use vec1::vec1;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
expr::UntypedExpr,
|
||||
parser::{error::ParseError, expr::string::flexible, pattern, token::Token},
|
||||
};
|
||||
|
||||
use super::guard;
|
||||
|
||||
pub fn parser(
|
||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, ast::UntypedClause, Error = ParseError> + '_ {
|
||||
pattern()
|
||||
.then(just(Token::Vbar).ignore_then(pattern()).repeated().or_not())
|
||||
.then(choice((
|
||||
just(Token::If)
|
||||
.ignore_then(guard())
|
||||
.or_not()
|
||||
.then_ignore(just(Token::RArrow)),
|
||||
just(Token::If)
|
||||
.ignore_then(take_until(just(Token::RArrow)))
|
||||
.validate(|_value, span, emit| {
|
||||
emit(ParseError::invalid_when_clause_guard(span));
|
||||
None
|
||||
}),
|
||||
)))
|
||||
// TODO: add hint "Did you mean to wrap a multi line clause in curly braces?"
|
||||
.then(choice((
|
||||
r.clone(),
|
||||
just(Token::Todo)
|
||||
.ignore_then(
|
||||
r.clone()
|
||||
.then_ignore(one_of(Token::RArrow).not().rewind())
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|reason, span| UntypedExpr::todo(span, reason.map(flexible))),
|
||||
just(Token::ErrorTerm)
|
||||
.ignore_then(
|
||||
r.clone()
|
||||
.then_ignore(just(Token::RArrow).not().rewind())
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|reason, span| UntypedExpr::error(span, reason.map(flexible))),
|
||||
)))
|
||||
.map_with_span(
|
||||
|(((pattern, alternative_patterns_opt), guard), then), span| {
|
||||
let mut patterns = vec1![pattern];
|
||||
patterns.append(&mut alternative_patterns_opt.unwrap_or_default());
|
||||
ast::UntypedClause {
|
||||
location: span,
|
||||
patterns,
|
||||
guard,
|
||||
then,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
parser::{definitions, error::ParseError, token::Token},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedClauseGuard, Error = ParseError> {
|
||||
recursive(|r| {
|
||||
let var_parser = select! {
|
||||
Token::Name { name } => name,
|
||||
Token::UpName { name } => name,
|
||||
}
|
||||
.map_with_span(|name, span| ast::ClauseGuard::Var {
|
||||
name,
|
||||
tipo: (),
|
||||
location: span,
|
||||
});
|
||||
|
||||
let constant_parser = definitions::constant::value().map(ast::ClauseGuard::Constant);
|
||||
|
||||
let block_parser = r
|
||||
.clone()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen));
|
||||
|
||||
let leaf_parser = choice((var_parser, constant_parser, block_parser)).boxed();
|
||||
|
||||
let unary_op = just(Token::Bang);
|
||||
|
||||
let unary = unary_op
|
||||
.map_with_span(|op, span| (op, span))
|
||||
.repeated()
|
||||
.then(leaf_parser)
|
||||
.foldr(|(_, span), value| ast::ClauseGuard::Not {
|
||||
location: span.union(value.location()),
|
||||
value: Box::new(value),
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let comparison_op = choice((
|
||||
just(Token::EqualEqual).to(ast::BinOp::Eq),
|
||||
just(Token::NotEqual).to(ast::BinOp::NotEq),
|
||||
just(Token::Less).to(ast::BinOp::LtInt),
|
||||
just(Token::Greater).to(ast::BinOp::GtInt),
|
||||
just(Token::LessEqual).to(ast::BinOp::LtEqInt),
|
||||
just(Token::GreaterEqual).to(ast::BinOp::GtEqInt),
|
||||
));
|
||||
|
||||
let comparison = unary
|
||||
.clone()
|
||||
.then(comparison_op.then(unary).repeated())
|
||||
.foldl(|left, (op, right)| {
|
||||
let location = left.location().union(right.location());
|
||||
let left = Box::new(left);
|
||||
let right = Box::new(right);
|
||||
match op {
|
||||
ast::BinOp::Eq => ast::ClauseGuard::Equals {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
ast::BinOp::NotEq => ast::ClauseGuard::NotEquals {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
ast::BinOp::LtInt => ast::ClauseGuard::LtInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
ast::BinOp::GtInt => ast::ClauseGuard::GtInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
ast::BinOp::LtEqInt => ast::ClauseGuard::LtEqInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
ast::BinOp::GtEqInt => ast::ClauseGuard::GtEqInt {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let and_op = just(Token::AmperAmper);
|
||||
let conjunction = comparison
|
||||
.clone()
|
||||
.then(and_op.then(comparison).repeated())
|
||||
.foldl(|left, (_tok, right)| {
|
||||
let location = left.location().union(right.location());
|
||||
let left = Box::new(left);
|
||||
let right = Box::new(right);
|
||||
ast::ClauseGuard::And {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
}
|
||||
});
|
||||
|
||||
let or_op = just(Token::VbarVbar);
|
||||
conjunction
|
||||
.clone()
|
||||
.then(or_op.then(conjunction).repeated())
|
||||
.foldl(|left, (_tok, right)| {
|
||||
let location = left.location().union(right.location());
|
||||
let left = Box::new(left);
|
||||
let right = Box::new(right);
|
||||
ast::ClauseGuard::Or {
|
||||
location,
|
||||
left,
|
||||
right,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
use chumsky::prelude::*;
|
||||
|
||||
mod clause;
|
||||
mod guard;
|
||||
|
||||
pub use clause::parser as clause;
|
||||
pub use guard::parser as guard;
|
||||
|
||||
use crate::{
|
||||
expr::UntypedExpr,
|
||||
parser::{error::ParseError, token::Token},
|
||||
};
|
||||
|
||||
pub fn parser(
|
||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
just(Token::When)
|
||||
// TODO: If subject is empty we should return ParseErrorType::ExpectedExpr,
|
||||
.ignore_then(r.clone().map(Box::new))
|
||||
.then_ignore(just(Token::Is))
|
||||
.then_ignore(just(Token::LeftBrace))
|
||||
// TODO: If clauses are empty we should return ParseErrorType::NoCaseClause
|
||||
.then(clause(r).repeated())
|
||||
.then_ignore(just(Token::RightBrace))
|
||||
.map_with_span(|(subject, clauses), span| UntypedExpr::When {
|
||||
location: span,
|
||||
subject,
|
||||
clauses,
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue