Search code examples
parsingrustnom

Parse multiline comment with nom


I'm trying to write a nom parser that recognizes multiline comments...

/*
yo!
*/

...and consumes/discards (same thing, right?) the result:

use nom::{
  bytes::complete::{tag, take_until},
  error::{ErrorKind, ParseError},
  sequence::preceded,
  IResult,
};

fn multiline_comment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
    preceded(tag("/*"), take_until("*/"))(i)
}

This almost works. I know the take_until stops just before the */, but I don't know what to do to make it include it.

#[test]
fn should_consume_multiline_comments() {
    assert_eq!(
        multiline_comment::<(&str, ErrorKind)>("/*abc\n\ndef*/"),
        Ok(("", "/*abc\n\ndef*/"))
    );
}

gives the result

thread 'should_consume_multiline_comments' panicked at 'assertion failed: `(left == right)`
left: `Ok(("*/", "abc\n\ndef"))`,
right: `Ok(("", "/*abc\n\ndef*/"))`'

So my question is, how do I get the full comment, including the ending */

Thanks!


Solution

  • I am assuming that you don't really want the returned string to have both preceding /* and closing */ intact - since preceded always discards the match from the first parser, you are never going to get that using preceded. I assume your main concern is ensuring that the closing */ is actually consumed.

    For this you can use delimited rather than preceded:

    fn multiline_comment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
        delimited(tag("/*"), is_not("*/"), tag("*/"))(i)
    }
    

    This passes this test:

    assert_eq!(
        multiline_comment1::<(&str, ErrorKind)>("/*abc\n\ndef*/"),
        Ok(("", "abc\n\ndef"))
    );
    

    so you can be sure that the closing */ has been consumed.