feat: finish splitting up parsers
This commit is contained in:
parent
63cdb8aa09
commit
2226747dc1
|
@ -8,6 +8,9 @@ strip = true
|
||||||
shared-version = true
|
shared-version = true
|
||||||
tag-name = "v{{version}}"
|
tag-name = "v{{version}}"
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
insta = { version = "1.30.0", features = ["yaml"] }
|
||||||
|
|
||||||
[profile.dev.package.insta]
|
[profile.dev.package.insta]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
|
|
|
@ -30,15 +30,12 @@ num-bigint = "0.4.3"
|
||||||
[target.'cfg(not(target_family="wasm"))'.dependencies]
|
[target.'cfg(not(target_family="wasm"))'.dependencies]
|
||||||
chumsky = "0.9.2"
|
chumsky = "0.9.2"
|
||||||
[target.'cfg(target_family="wasm")'.dependencies]
|
[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]
|
[dev-dependencies]
|
||||||
indoc = "2.0.1"
|
indoc = "2.0.1"
|
||||||
insta = { version = "1.30.0", features = ["yaml"] }
|
insta.workspace = true
|
||||||
pretty_assertions = "1.3.0"
|
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 expr::parser as expression;
|
||||||
pub use pattern::parser as pattern;
|
pub use pattern::parser as pattern;
|
||||||
|
|
||||||
use crate::ast::{self, BinOp, Span};
|
use crate::ast::{self, Span};
|
||||||
use chumsky::{chain::Chain, prelude::*};
|
use chumsky::{chain::Chain, prelude::*};
|
||||||
use error::ParseError;
|
use error::ParseError;
|
||||||
use extra::ModuleExtra;
|
use extra::ModuleExtra;
|
||||||
|
@ -96,119 +96,3 @@ pub fn module(
|
||||||
|
|
||||||
Ok((module, extra))
|
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},
|
parser::{annotation, error::ParseError, token::Token},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parser<'a>(
|
pub fn parser(
|
||||||
seq_r: Recursive<'a, Token, UntypedExpr, ParseError>,
|
seq_r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||||
just(Token::Fn)
|
just(Token::Fn)
|
||||||
.ignore_then(
|
.ignore_then(
|
||||||
params()
|
params()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
use vec1::{vec1, Vec1};
|
use vec1::Vec1;
|
||||||
|
|
||||||
|
mod anonymous_binop;
|
||||||
pub mod anonymous_function;
|
pub mod anonymous_function;
|
||||||
pub mod assignment;
|
pub mod assignment;
|
||||||
mod block;
|
mod block;
|
||||||
|
@ -14,7 +15,9 @@ mod sequence;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
mod tuple;
|
mod tuple;
|
||||||
mod var;
|
mod var;
|
||||||
|
pub mod when;
|
||||||
|
|
||||||
|
use anonymous_binop::parser as anonymous_binop;
|
||||||
pub use anonymous_function::parser as anonymous_function;
|
pub use anonymous_function::parser as anonymous_function;
|
||||||
pub use block::parser as block;
|
pub use block::parser as block;
|
||||||
pub use bytearray::parser as bytearray;
|
pub use bytearray::parser as bytearray;
|
||||||
|
@ -27,10 +30,11 @@ pub use sequence::parser as sequence;
|
||||||
pub use string::parser as string;
|
pub use string::parser as string;
|
||||||
pub use tuple::parser as tuple;
|
pub use tuple::parser as tuple;
|
||||||
pub use var::parser as var;
|
pub use var::parser as var;
|
||||||
|
pub use when::parser as when;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{self, Span},
|
ast::{self, Span},
|
||||||
expr::UntypedExpr,
|
expr::{FnStyle, UntypedExpr},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{error::ParseError, token::Token};
|
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((
|
let expr_unit_parser = choice((
|
||||||
string(),
|
string(),
|
||||||
int(),
|
int(),
|
||||||
record_update(r),
|
record_update(r.clone()),
|
||||||
record(r),
|
record(r.clone()),
|
||||||
field_access_constructor,
|
field_access_constructor,
|
||||||
var(),
|
var(),
|
||||||
tuple(r),
|
tuple(r.clone()),
|
||||||
bytearray(),
|
bytearray(),
|
||||||
list(r),
|
list(r.clone()),
|
||||||
anonymous_function(seq_r),
|
anonymous_function(seq_r.clone()),
|
||||||
anon_binop_parser,
|
anonymous_binop(),
|
||||||
block(seq_r),
|
block(seq_r.clone()),
|
||||||
when_parser,
|
when(r.clone()),
|
||||||
assignment::let_(r),
|
assignment::let_(r.clone()),
|
||||||
assignment::expect(r),
|
assignment::expect(r.clone()),
|
||||||
if_else(seq_r, r),
|
if_else(seq_r, r.clone()),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Parsing a function call into the appropriate structure
|
// Parsing a function call into the appropriate structure
|
||||||
|
@ -262,7 +118,7 @@ pub fn parser(
|
||||||
select! { Token::Name { name } => name }
|
select! { Token::Name { name } => name }
|
||||||
.then_ignore(just(Token::Colon))
|
.then_ignore(just(Token::Colon))
|
||||||
.or_not()
|
.or_not()
|
||||||
.then(r.clone())
|
.then(r)
|
||||||
.map_with_span(|(label, value), span| {
|
.map_with_span(|(label, value), span| {
|
||||||
ParserArg::Arg(Box::new(ast::CallArg {
|
ParserArg::Arg(Box::new(ast::CallArg {
|
||||||
label,
|
label,
|
||||||
|
@ -298,7 +154,8 @@ pub fn parser(
|
||||||
.map(|(index, a)| match a {
|
.map(|(index, a)| match a {
|
||||||
ParserArg::Arg(arg) => *arg,
|
ParserArg::Arg(arg) => *arg,
|
||||||
ParserArg::Hole { location, label } => {
|
ParserArg::Hole { location, label } => {
|
||||||
let name = format!("{CAPTURE_VARIABLE}__{index}");
|
let name = format!("{}__{index}", ast::CAPTURE_VARIABLE);
|
||||||
|
|
||||||
holes.push(ast::Arg {
|
holes.push(ast::Arg {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
annotation: None,
|
annotation: None,
|
||||||
|
@ -316,7 +173,7 @@ pub fn parser(
|
||||||
location,
|
location,
|
||||||
value: UntypedExpr::Var {
|
value: UntypedExpr::Var {
|
||||||
location,
|
location,
|
||||||
name: format!("{CAPTURE_VARIABLE}__{index}"),
|
name: format!("{}__{index}", ast::CAPTURE_VARIABLE),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,7 +224,7 @@ pub fn parser(
|
||||||
|
|
||||||
// Negate
|
// Negate
|
||||||
let op = choice((
|
let op = choice((
|
||||||
just(Token::Bang).to(UnOp::Not),
|
just(Token::Bang).to(ast::UnOp::Not),
|
||||||
just(Token::Minus)
|
just(Token::Minus)
|
||||||
// NOTE: Prevent conflict with usage for '-' as a standalone binary op.
|
// NOTE: Prevent conflict with usage for '-' as a standalone binary op.
|
||||||
// This will make '-' parse when used as standalone binop in a function call.
|
// This will make '-' parse when used as standalone binop in a function call.
|
||||||
|
@ -381,7 +238,7 @@ pub fn parser(
|
||||||
//
|
//
|
||||||
// which seems acceptable.
|
// which seems acceptable.
|
||||||
.then_ignore(just(Token::Comma).not().rewind())
|
.then_ignore(just(Token::Comma).not().rewind())
|
||||||
.to(UnOp::Negate),
|
.to(ast::UnOp::Negate),
|
||||||
));
|
));
|
||||||
|
|
||||||
let unary = op
|
let unary = op
|
||||||
|
@ -397,9 +254,9 @@ pub fn parser(
|
||||||
|
|
||||||
// Product
|
// Product
|
||||||
let op = choice((
|
let op = choice((
|
||||||
just(Token::Star).to(BinOp::MultInt),
|
just(Token::Star).to(ast::BinOp::MultInt),
|
||||||
just(Token::Slash).to(BinOp::DivInt),
|
just(Token::Slash).to(ast::BinOp::DivInt),
|
||||||
just(Token::Percent).to(BinOp::ModInt),
|
just(Token::Percent).to(ast::BinOp::ModInt),
|
||||||
));
|
));
|
||||||
|
|
||||||
let product = unary
|
let product = unary
|
||||||
|
@ -415,8 +272,8 @@ pub fn parser(
|
||||||
|
|
||||||
// Sum
|
// Sum
|
||||||
let op = choice((
|
let op = choice((
|
||||||
just(Token::Plus).to(BinOp::AddInt),
|
just(Token::Plus).to(ast::BinOp::AddInt),
|
||||||
just(Token::Minus).to(BinOp::SubInt),
|
just(Token::Minus).to(ast::BinOp::SubInt),
|
||||||
));
|
));
|
||||||
|
|
||||||
let sum = product
|
let sum = product
|
||||||
|
@ -432,12 +289,12 @@ pub fn parser(
|
||||||
|
|
||||||
// Comparison
|
// Comparison
|
||||||
let op = choice((
|
let op = choice((
|
||||||
just(Token::EqualEqual).to(BinOp::Eq),
|
just(Token::EqualEqual).to(ast::BinOp::Eq),
|
||||||
just(Token::NotEqual).to(BinOp::NotEq),
|
just(Token::NotEqual).to(ast::BinOp::NotEq),
|
||||||
just(Token::Less).to(BinOp::LtInt),
|
just(Token::Less).to(ast::BinOp::LtInt),
|
||||||
just(Token::Greater).to(BinOp::GtInt),
|
just(Token::Greater).to(ast::BinOp::GtInt),
|
||||||
just(Token::LessEqual).to(BinOp::LtEqInt),
|
just(Token::LessEqual).to(ast::BinOp::LtEqInt),
|
||||||
just(Token::GreaterEqual).to(BinOp::GtEqInt),
|
just(Token::GreaterEqual).to(ast::BinOp::GtEqInt),
|
||||||
));
|
));
|
||||||
|
|
||||||
let comparison = sum
|
let comparison = sum
|
||||||
|
@ -452,7 +309,7 @@ pub fn parser(
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
// Conjunction
|
// Conjunction
|
||||||
let op = just(Token::AmperAmper).to(BinOp::And);
|
let op = just(Token::AmperAmper).to(ast::BinOp::And);
|
||||||
let conjunction = comparison
|
let conjunction = comparison
|
||||||
.clone()
|
.clone()
|
||||||
.then(op.then(comparison).repeated())
|
.then(op.then(comparison).repeated())
|
||||||
|
@ -465,7 +322,7 @@ pub fn parser(
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
// Disjunction
|
// Disjunction
|
||||||
let op = just(Token::VbarVbar).to(BinOp::Or);
|
let op = just(Token::VbarVbar).to(ast::BinOp::Or);
|
||||||
let disjunction = conjunction
|
let disjunction = conjunction
|
||||||
.clone()
|
.clone()
|
||||||
.then(op.then(conjunction).repeated())
|
.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