How to propagate Nom fail context out of many0?

444 Views Asked by At

I have a Nom parser that parses strings but fails on keywords.

The parser below correctly fails when given a keyword.

But the error message that says it failed because it encountered a keyword does not propagate.

The problem is that while label fails, many0(label) succeeds with one less result. Subsequently, eof fails because not everything was parsed. This correctly fails, but the error message is lost.

How do I propagate the local error message?

(See below code on playground.)

use nom::bytes::complete::{take_while, take_while1};
use nom::error::{VerboseError};
use nom::multi::{many0};
use nom::{IResult};
use nom::error::{context};
use nom::combinator::{eof, fail};

type ParseResult<'input, Out> = IResult<&'input str, Out, VerboseError<&'input str>>;

fn label(s1: &str) -> ParseResult<&str> {
    let (s2, a) = take_while1(|c: char| c.is_alphabetic())(s1)?;
    let (s3, _) = take_while(|c: char| c.is_whitespace())(s2)?;
    if a == "derp" {
        return context("this message is lost", fail)(s1);
    }
    Ok((s3, a))
}

fn parse(s: &str) -> ParseResult<Vec<&str>> {
    let (s, labels) = many0(label)(s)?;
    let (s, _) = eof(s)?;
    Ok((s, labels))
}

fn main() {
    println!("{:?}", parse("foo bar herp flerp"));
    // Ok(("", ["foo", "bar", "herp", "flerp"]))

    // This fails with Nom(Eof), both Nom(Fail) and context is lost
    println!("{:?}", parse("foo bar herp derp"));
    // Err(Error(VerboseError { errors: [("derp", Nom(Eof))] }))
}
1

There are 1 best solutions below

3
On BEST ANSWER

You can use cut to prevent many0 to explore alternatives (consuming less) in the failure case:

use nom::combinator::cut;
return cut(context("this message is lost", fail))(s1);

Playground