-
Hi, The docs for
However, I can't figure out a way to do that while separating the parse of the parser use winnow::prelude::*; // 0.5.15
use winnow::ascii::space0;
use winnow::combinator::{eof, preceded, repeat_till0};
use winnow::token::any;
fn parse_test<'i>(i: &mut &'i str) -> PResult<&'i str> {
preceded(
";;; ",
repeat_till0::<_, _, (), _, _, _, _>(any, (space0, ";;;", space0, eof)).recognize(),
)
.parse_next(i)
}
fn main() {
// Value should be "test text"
let _ = dbg!(parse_test.parse(";;; test text ;;;"));
// Value should be "test ;;; with more semicolon"
let _ = dbg!(parse_test.parse(";;; test ;;; with more semicolon ;;; "));
} Is there some way to do this that I'm overlooking, or is this an oversight in the API? Also, is there a more ergonomic way to specify that I'm collecting to a |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
What I tend to do is |
Beta Was this translation helpful? Give feedback.
-
I ended up writing my own replacement for fn lazy_quantifier<
I: Stream,
O,
E: ParserError<I>,
P: ContainsToken<I::Token>,
F: Parser<I, O, E>,
>(
predicate: P,
mut terminator: F,
) -> impl Parser<I, (I::Slice, O), E> {
move |i: &mut I| {
let match_start = i.checkpoint();
loop {
let potential_match_end = i.checkpoint();
let match_length = i.offset_from(&match_start);
match terminator.parse_next(i) {
Ok(o) => {
let final_pos = i.checkpoint();
i.reset(match_start);
let matched = i.next_slice(match_length);
i.reset(final_pos);
return Ok((matched, o));
}
Err(ErrMode::Backtrack(_)) => i.reset(potential_match_end),
Err(e) => return Err(e),
}
let c = i.next_token();
if !c.is_some_and(|c| predicate.contains_token(c)) {
return Err(ErrMode::from_error_kind(i, ErrorKind::Slice));
}
}
}
}
pub fn parse_sub_comment<'i>(i: &mut &'i str) -> PResult<(u16, &'i str)> {
let addr = delimited((";;;", space1, '$'), hex4, (':', space0)).parse_next(i)?;
let (description, ()) =
lazy_quantifier(|_| true, (opt((space0, ";;;")), space0, eof).void()).parse_next(i)?;
Ok((addr, description))
} Would still love to hear if there's a way to do this with one of the built-in combinators. |
Beta Was this translation helpful? Give feedback.
-
Another solution, at the cost of parsing the terminator twice (playground): fn parse_test<'i>(i: &mut &'i str) -> PResult<&'i str> {
let terminator = (space0, ";;;", space0, eof);
preceded(
";;; ",
terminated(repeat_till::<_, _, (), _, _, _, _>(.., any, peek(terminator)).recognize(), terminator),
)
.parse_next(i)
} |
Beta Was this translation helpful? Give feedback.
Oh, right, you want to
recognize
only the accumulated portion and ignore the other part. That wouldn't work with the approach I mentioned.The options I can think of
String
and take the performance hitcheckpoint
at the start of thespace0, ";;;", ...
section and then manuallyrecognize
the preceding stringI
and capture indices for thespace0, ";;;", ...
section and use that to get the preceding string.