From 41776492029757c6a0d27f6b6a91626f0fa5819a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Thu, 5 Mar 2026 19:58:19 +0100 Subject: [PATCH] Use resume_after_cut instead of resume_after to simplify code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/parser/mod.rs | 77 ++++++++++++++++--- ...arser__tests__parse_interpolate_bad-2.snap | 2 +- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ff187f5..eff81e2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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,19 +266,21 @@ 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"), - ) - .value(ident) - .resume_after(bad_ident) - .with_taken() - .map(|(val, taken)| val.unwrap_or(TemplateToken::Invalid(taken))) - .parse_next(input) + ), + bad_ident, + ) + .with_taken() + .map(|(val, taken)| val.unwrap_or(TemplateToken::Invalid(taken))) + .parse_next(input) } fn ident<'input>(input: &mut Input<'input>) -> PResult<'input, &'input str> { @@ -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( + mut parser: ParseNext, + mut recover: ParseRecover, +) -> impl Parser, Error> +where + Input: Stream + winnow::stream::Recover, + Error: ParserError + FromRecoverableError, + ParseNext: Parser, + ParseRecover: Parser, +{ + trace("resume_after_cut", move |input: &mut Input| { + resume_after_cut_inner(&mut parser, &mut recover, input) + }) +} + +fn resume_after_cut_inner( + parser: &mut P, + recover: &mut R, + i: &mut I, +) -> winnow::Result, E> +where + P: Parser, + R: Parser, + I: Stream, + I: winnow::stream::Recover, + E: ParserError + FromRecoverableError, +{ + 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, }, ], }, diff --git a/src/parser/snapshots/temple__parser__tests__parse_interpolate_bad-2.snap b/src/parser/snapshots/temple__parser__tests__parse_interpolate_bad-2.snap index de2d41d..9dd6238 100644 --- a/src/parser/snapshots/temple__parser__tests__parse_interpolate_bad-2.snap +++ b/src/parser/snapshots/temple__parser__tests__parse_interpolate_bad-2.snap @@ -5,6 +5,6 @@ expression: error.to_report() error: Invalid variable identifier  ╭▸  1 │ Hello {{ the2re }} {{ the@re }} - │ ━━━ + │ ━━━━━━ │ ╰ help: valid variable identifiers are alphanumeric