Refactor several parse methods to use more combinators

This commit is contained in:
Joshua Warner 2022-10-31 19:57:20 -07:00
parent dc2500aac2
commit 5d5b71a083
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
12 changed files with 351 additions and 359 deletions

View file

@ -470,7 +470,6 @@ pub enum EWhen<'a> {
Condition(&'a EExpr<'a>, Position),
Branch(&'a EExpr<'a>, Position),
IndentIs(Position),
IndentCondition(Position),
IndentPattern(Position),
IndentArrow(Position),
@ -869,13 +868,13 @@ where
P1: Parser<'a, Before, E>,
After: 'a,
E: 'a,
F: Fn(&'a Bump, State<'a>, Progress, Before, u32) -> ParseResult<'a, After, E>,
F: Fn(&'a Bump, State<'a>, Progress, Before) -> ParseResult<'a, After, E>,
{
move |arena, state, min_indent| {
parser
.parse(arena, state, min_indent)
.and_then(|(progress, output, next_state)| {
transform(arena, next_state, progress, output, min_indent)
transform(arena, next_state, progress, output)
})
}
}
@ -1376,6 +1375,64 @@ macro_rules! and {
};
}
/// Similar to `and`, but we modify the min_indent of the second parser to be
/// 1 greater than the line_indent() at the start of the first parser.
#[macro_export]
macro_rules! indented_seq {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
let start_indent = state.line_indent();
// TODO: we should account for min_indent here, but this doesn't currently work
// because min_indent is sometimes larger than it really should be, which is in turn
// due to uses of `increment_indent`.
//
// let p1_indent = std::cmp::max(start_indent, min_indent);
let p1_indent = start_indent;
let p2_indent = p1_indent + 1;
// We have to clone this because if the first parser passes and then
// the second one fails, we need to revert back to the original state.
let original_state = state.clone();
match $p1.parse(arena, state, p1_indent) {
Ok((p1, (), state)) => match $p2.parse(arena, state, p2_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)),
Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)),
},
Err((progress, fail, state)) => Err((progress, fail, state)),
}
}
};
}
/// Similar to `and`, but we modify the min_indent of the second parser to be
/// 1 greater than the column() at the start of the first parser.
#[macro_export]
macro_rules! absolute_indented_seq {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
let start_indent = state.column();
let p1_indent = start_indent;
let p2_indent = p1_indent + 1;
// We have to clone this because if the first parser passes and then
// the second one fails, we need to revert back to the original state.
let original_state = state.clone();
match $p1.parse(arena, state, p1_indent) {
Ok((p1, out1, state)) => match $p2.parse(arena, state, p2_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)),
},
Err((progress, fail, state)) => Err((progress, fail, state)),
}
}
};
}
#[macro_export]
macro_rules! one_of {
($p1:expr, $p2:expr) => {
@ -1450,6 +1507,16 @@ where
move |arena, state, min_indent| parser.parse(arena, state, min_indent + 1)
}
pub fn line_min_indent<'a, P, T, X: 'a>(parser: P) -> impl Parser<'a, T, X>
where
P: Parser<'a, T, X>,
{
move |arena, state: State<'a>, min_indent| {
let min_indent = std::cmp::max(state.line_indent(), min_indent);
parser.parse(arena, state, min_indent)
}
}
pub fn absolute_column_min_indent<'a, P, T, X: 'a>(parser: P) -> impl Parser<'a, T, X>
where
P: Parser<'a, T, X>,
@ -1517,28 +1584,25 @@ where
}
}
pub fn parse_word1<'a, ToError, E>(
state: State<'a>,
min_indent: u32,
word: u8,
to_error: ToError,
) -> ParseResult<'a, (), E>
pub fn word1_indent<'a, ToError, E>(word: u8, to_error: ToError) -> impl Parser<'a, (), E>
where
ToError: Fn(Position) -> E,
E: 'a,
{
debug_assert_ne!(word, b'\n');
if min_indent > state.column() {
return Err((NoProgress, to_error(state.pos()), state));
}
match state.bytes().first() {
Some(x) if *x == word => {
let state = state.advance(1);
Ok((MadeProgress, (), state))
move |_arena: &'a Bump, state: State<'a>, min_indent: u32| {
if min_indent > state.column() {
return Err((NoProgress, to_error(state.pos()), state));
}
match state.bytes().first() {
Some(x) if *x == word => {
let state = state.advance(1);
Ok((MadeProgress, (), state))
}
_ => Err((NoProgress, to_error(state.pos()), state)),
}
_ => Err((NoProgress, to_error(state.pos()), state)),
}
}