Search code examples
rustnom

How to implement a take_until_and_consume-like parser combinator that does not skip the tag?


I would like to write a nom parser combinator that takes as many bytes up to and including a tag sequence. I tried using take_until_and_consume!, but I found that the generated parser combinator discards the tag sequence:

#[macro_use]
extern crate nom;

named!(up_to_and_including_backslash, take_until_and_consume!("\\"));

fn main() {
    let res = up_to_and_including_backslash(b"    \\");
    println!("{:?}", res);
}

Results in:

Done([], [32, 32, 32, 32])

What I would like is for the tag sequence (in this case, the backslash character) to be included in the result:

Done([], [32, 32, 32, 32, 92])

How can I accomplish this?


Solution

  • update:
    You want to use recognize! on take_until_and_consume!("\\") to add everything it consumed to the Output.

    You would write your parser function like this:

    #[macro_use]
    extern crate nom;
    
    named!(up_to_and_including_backslash, recognize!( take_until_and_consume!("\\") ));
    
    fn main() {
        let res = up_to_and_including_backslash(b"    \\");
        println!("{:?}", res);
    }
    

    In case you needed to include the consumed symbols of multiple parsers to your Output you could put them all inside a do_parse! within recognize!. Like so:

    recognize!( do_parse!( tag!("\\") >> take_until_and_consume!("\\") >> take!(4) >> () ) )
    

    old:
    The only way I got this to work was this ugly abomination.

    named!(up_to_and_including_backslash,
        do_parse!(
            line: take_until_and_consume!("\\") >>
            (
                { 
                    let mut complete_line:Vec<u8> = line.to_vec();
                    complete_line.extend_from_slice(b"\\");
                    &*complete_line
                }
            )
        )
    );