首页 > 解决方案 > 用 nom 解析多行注释

问题描述

我正在尝试编写一个识别多行注释的名词解析器......

/*
yo!
*/

...并消耗/丢弃(同样的事情,对吗?)结果:

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

这几乎可以工作。我知道 take_until 在 之前停止*/,但我不知道该怎么做才能让它包含它。

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

给出结果

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

所以我的问题是,我如何获得完整的评论,包括结尾*/

谢谢!

标签: parsingrustnom

解决方案


我假设您并不真的希望返回的字符串在前面/*和结束时都*/完好无损 - 因为preceded总是丢弃第一个解析器的匹配项,所以您永远不会使用preceded. 我认为您主要关心的是确保*/实际消耗关闭。

为此,您可以使用delimited而不是preceded

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

这通过了这个测试:

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

所以你可以确定关闭*/已经消耗。


推荐阅读