rustnom

Writing a nom parser combinator that tries various parsers built from a supplied parser and combinators


I'd like to write a nom parser combinator that takes a generic parser inner, builds two different variants of it using combinators that already exist in nom, and then tries them in say an alt combinator. Conceptually, I'd like to do

alt((some_existing_combinator(inner), some_other_existing_combinator(inner)))

Of course, this doesn't work, because inner isn't in general of a type that is Copy (or even Clone). From my understanding, no parser built from nom combinators is Copy or Clone. Thus, I'm clearly barking up the wrong tree here. How does one typically approach this kind of problem?


Solution

  • You basically have two options: wrap the whole delimited thing in its own parsing function, which is Copy; or create a zero-arg closure (or function) that creates one-off delimited parsers each time you need one. The toy example below compiles.

    use nom::{
        branch::alt,
        bytes::complete::tag,
        character::complete::{digit1, multispace0},
        combinator::map_res,
        multi::{many0, many1},
        sequence::delimited,
        IResult, Parser,
    };
    
    fn parser(s: &str) -> IResult<&str, i32> {
        map_res((tag("num="), digit1), |(_, n): (_, &str)| n.parse::<i32>())
            .parse(s)
    }
    
    // a fn, therefore Copy
    fn my_delimited(s: &str) -> IResult<&str, i32> {
        delimited(multispace0, parser, multispace0).parse(s)
    }
    
    fn main() {
        // makes a fresh parser each time it's called
        let make_inner = || delimited(multispace0, parser, multispace0);
    
        let parser_1 = alt((many0(my_delimited), many1(my_delimited)));
        let parser_2 = alt((many0(make_inner()), many1(make_inner())));
    }