feat: finish splitting up parsers
This commit is contained in:
59
crates/aiken-lang/src/parser/expr/when/clause.rs
Normal file
59
crates/aiken-lang/src/parser/expr/when/clause.rs
Normal 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,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
122
crates/aiken-lang/src/parser/expr/when/guard.rs
Normal file
122
crates/aiken-lang/src/parser/expr/when/guard.rs
Normal 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,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
30
crates/aiken-lang/src/parser/expr/when/mod.rs
Normal file
30
crates/aiken-lang/src/parser/expr/when/mod.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user