I've been trying to write parsers with nom v8, but I am getting a bit stuck on how to compose things correctly. The problem I am trying to solve is that I know the expected length of recognized messages, and I want to return the expected length (the match id part below) or the full length of the input if an unknown id is passed.
I was trying to write code like this:
use nom::combinator::{rest_len, success};
use nom::error::Error;
fn parse_len<'a>(id: u32) -> impl Parser<&'a [u8], Error = Error<&'a [u8]>> {
match id {
15 => success(123usize),
21 => success(452usize),
[...]
_ => rest_len
}
}
Now the match arms have incompatible types, as rest_len is defined as
From the example on the nom docs, it seems like you can use a function returning an IResult as a parser itself (without returning impl Parser).
Does it still work for users of parse_len if you directly return IResult from there?
Thanks for your suggestion! The problem is that I would need to take a parameter input: &[u8] then, which I don't have available in the caller. It is my understanding based on this reddit post by nom's author that returning IResult would be the older style (nom v7) and should be transitioned away from. I just wonder how to achieve that
I think you are confusing commentary about internal changes in architecture with migration details for users. From the linked post, the author notes that a major goal of v8 was to:
rewrite the internal architecture to use traits instead of functions, while keeping the breaking changes minimal
The internal changes refer to moving from closure-based parsers to trait-based parsers, hence the introduction of trait Parser. The author then notes:
While that new architecture introduces some complexity, compared to the closures based parsers, I believe it will pay off immensely for production parsers, while keeping breaking changes to a minimum. In practice, updating an existing parser from v7 to v8 is painless, just requiring to use the Parser trait in some places, where combinator(arg)(input) is now written combinator(arg).parse(input)
At my day job, I have some non-trivial parsing code written in Nom, and so I decided to migrate to v8. Sure enough, every place where I used a combinator, I needed to add .parse(input), but that was pretty much it. I can only conclude the introduction of complexity to which the author refers is in the internal complexity of maintaining or adding to the code.
I would recommend you continue to use IResult in your return types, if that's what you were doing before, or map the error case to your custom error type as needed.
Thanks for your detailed thoughts! I was referring to this quote by the author (emphasis mine):
You can keep the function based parsers for as long as you want, they will continue working. impl Parser based ones will tend to work well with one another, because they will be able to transmit the Emit/check modes to each other, and I am planning other nice tools for development that will leverage the trait, so you should move to impl Parser eventually
Just getting nom v8 to work was indeed easy, and I did it like you - I used .parse_complete(input) and that was it. But that already highlights one point: I needed to use parse_complete rather than parse everywhere, which nom v8 could in theory be generic over aiui. So I'd still be looking for a nice way to do this transition