Use resume_after_cut instead of resume_after to simplify code

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-05 19:58:19 +01:00
parent b07bef7904
commit 4177649202
2 changed files with 67 additions and 12 deletions

View file

@ -10,6 +10,7 @@ use winnow::Parser;
use winnow::RecoverableParser;
use winnow::ascii::multispace1;
use winnow::combinator::alt;
use winnow::combinator::cut_err;
use winnow::combinator::eof;
use winnow::combinator::peek;
use winnow::combinator::repeat_till;
@ -265,16 +266,18 @@ fn parse_whitespace<'input>(input: &mut Input<'input>) -> PResult<'input, Templa
}
fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken<'input>> {
let ident = ident.map(TemplateToken::Ident).parse_next(input)?;
ident_terminator_check
resume_after_cut(
terminated(
ident.map(TemplateToken::Ident),
cut_err(ident_terminator_check),
)
.context(
ParseError::ctx()
.msg("Invalid variable identifier")
.help("valid variable identifiers are alphanumeric"),
),
bad_ident,
)
.value(ident)
.resume_after(bad_ident)
.with_taken()
.map(|(val, taken)| val.unwrap_or(TemplateToken::Invalid(taken)))
.parse_next(input)
@ -303,6 +306,58 @@ fn ident_terminator<'input>(input: &mut Input<'input>) -> PResult<'input, ()> {
.parse_next(input)
}
// This is just like the standard .resume_after(), except we only resume on Cut errors.
fn resume_after_cut<Input, Output, Error, ParseNext, ParseRecover>(
mut parser: ParseNext,
mut recover: ParseRecover,
) -> impl Parser<Input, Option<Output>, Error>
where
Input: Stream + winnow::stream::Recover<Error>,
Error: ParserError<Input> + FromRecoverableError<Input, Error>,
ParseNext: Parser<Input, Output, Error>,
ParseRecover: Parser<Input, (), Error>,
{
trace("resume_after_cut", move |input: &mut Input| {
resume_after_cut_inner(&mut parser, &mut recover, input)
})
}
fn resume_after_cut_inner<P, R, I, O, E>(
parser: &mut P,
recover: &mut R,
i: &mut I,
) -> winnow::Result<Option<O>, E>
where
P: Parser<I, O, E>,
R: Parser<I, (), E>,
I: Stream,
I: winnow::stream::Recover<E>,
E: ParserError<I> + FromRecoverableError<I, E>,
{
let token_start = i.checkpoint();
let mut err = match parser.parse_next(i) {
Ok(o) => {
return Ok(Some(o));
}
Err(e) if e.is_incomplete() || e.is_backtrack() => {
return Err(e);
}
Err(err) => err,
};
let err_start = i.checkpoint();
if recover.parse_next(i).is_ok() {
if let Err(err_) = i.record_err(&token_start, &err_start, err) {
err = err_;
} else {
return Ok(None);
}
}
i.reset(&err_start);
err = E::from_recoverable_error(&token_start, &err_start, i, err);
Err(err)
}
#[cfg(test)]
mod tests {
use crate::parser::parse;
@ -377,10 +432,10 @@ mod tests {
),
span: Some(
SourceSpan {
range: 25..28,
range: 22..28,
},
),
is_fatal: false,
is_fatal: true,
},
],
},

View file

@ -5,6 +5,6 @@ expression: error.to_report()
error: Invalid variable identifier
 ╭▸ 
1 │ Hello {{ the2re }} {{ the@re }}
│ ━━━
│ ━━━━━━
│
╰ help: valid variable identifiers are alphanumeric