feat: finish splitting up parsers

This commit is contained in:
rvcas
2023-06-30 13:40:53 -04:00
parent 63cdb8aa09
commit 2226747dc1
9 changed files with 351 additions and 310 deletions

View File

@@ -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,
}
},
)
}

View File

@@ -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,
}
})
})
}

View File

@@ -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,
})
}