Closed epage closed 9 months ago
In trivial parsers,
Input
is just a fat pointer. Once you start addingLocated
andRecoverable
(see #96), it grows dramatically and part of what you are seeing is a side effect of work to make those parsers more performant. In some cases, adding a couple ofusize
s to theInput
, without any other change, slowed parsing down by 30%. The way we solved that is by switching to&mut Input
. That has a risk of not doing all the bookkeeping right when reverting (and adding overhead in the happy/success path for that bookkeeping). The realization I had is we could get away without being transactional by letting errors stay pointing to where the error occurred. This also allows errors to not need to carry around the error-side'sInput
any more, allowing them to be shrunk as well, offering more performance benefits (e.g. the performance hit for enablingContextError
ingix
is about 5%, dramatically less than the oldVerboseError
).Another reason you are running into is the parsers I work with don't need the input for each
Context
, leading me to design the default case that way.The final reason you are running into this is
TreeError
was a late edition to the API that I made to help with the port ofgix
fromnom
towinnow
and mostly exists for debugging purposes because the errors literally dump the parser state and can't really be design for humans in mind and has a major performance hit.The way to solve this would be, in a breaking change (slowly building up to being ready to make v0.6) is to take a
Checkpoint
at the start ofcontext
and pass that toadd_context
to let the error type decide what it wants to do with it (TreeError
can decide to do clones, resets, etc). We should evaluate the other error methods to see if they should accept aCheckpoint
as well.
Discussed in https://github.com/winnow-rs/winnow/discussions/373
nom version
```rust use nom::branch::alt; use nom::bytes::complete::{tag, take_while1}; use nom::combinator::{all_consuming, cut, map_res}; use nom::error::{context, VerboseError}; use nom::sequence::preceded; use nom::IResult; fn number(input: &str) -> IResult<&str, u64, VerboseError<&str>> { context("number", alt((hex, decimal)))(input) } fn hex(input: &str) -> IResult<&str, u64, VerboseError<&str>> { context( "hex number", preceded( tag("0x"), cut(context( "hex digits", map_res(take_while1(|c: char| c.is_ascii_hexdigit()), |s: &str| { u64::from_str_radix(s, 16) }), )), ), )(input) } fn decimal(input: &str) -> IResult<&str, u64, VerboseError<&str>> { context( "decimal number", map_res(take_while1(|c: char| c.is_ascii_digit()), |s: &str| { u64::from_str_radix(s, 10) }), )(input) } fn main() { let test = "0xZ123"; // println!("{:#?}", number(test)); match all_consuming(number)(test) { Ok((_, n)) => println!("number: {n}"), Err(nom::Err::Error(e) | nom::Err::Failure(e)) => eprintln!("{e}"), Err(err) => eprintln!("{err}"), } } ```winnow version
```rust use winnow::combinator::{alt, cut_err, preceded}; use winnow::error::{StrContext, TreeError}; use winnow::token::take_while; use winnow::{PResult, Parser}; fn number<'i>(input: &mut &'i str) -> PResult