Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to propagate Nom fail context out of many0?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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))] }))
}

>Solution :

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading