Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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))] }))
}
like image 453
sshine Avatar asked Nov 02 '25 09:11

sshine


1 Answers

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

like image 182
cafce25 Avatar answered Nov 04 '25 12:11

cafce25



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!