2

我有兴趣使用nom解析器组合器来识别这种标识符:

"a"
"a1"
"a_b"
"aA"
"aB_3_1"

标识符的第一个字符应该是字母小写字符,然后可以是字母数字字符和下划线 (so [a-zA-Z0-9_]*) 的任何组合,但不得出现双(或更多)下划线且下划线不得结束标识符,拒绝这些情况:

"Aa"
"aB_"
"a__a"
"_a"

到目前为止,我已经提出了这个解决方案,但不确定我的方法的正确性:

pub fn identifier(s: &str) -> IResult<&str, &str> {
    let (i, _) = verify(anychar, |c: &char| c.is_lowercase())(s)?;
    let (j, _) = alphanumeric0(i)?;
    let (k, _) = recognize(opt(many1(preceded(underscore, alphanumeric1))))(j)?;
    Ok((k,s))
}

此外,我需要在使用它时环绕recognize这个解析器,如下所示:identifier

pub fn identifier2(s: &str) -> IResult<&str, &str> {
    (recognize(identifier))(s)
}
4

1 回答 1

1

这是我想出的变体。它与您的基本相同;我做了以下更改:

  • 最重要的是,我添加了all_consuming,以确保整个输入匹配。您提出的实现中的错误是“aBa_”将成功匹配标识符“aBa”并且不解析尾随“_”(在输入端返回它)。
  • 仅根据解析器组合器重写,而不是使用?语句。
  • 使下划线匹配可选。Nom 解析器通常是贪婪的,所以这不会导致性能下降。
  • 简化为只有 2 个子句,而不是 3 个。解析器本质上运行“匹配任何小写字符,后跟 0 次或多次运行可选的 _,然后再运行 1 次以上字母数字”。
  • 改为many1many0_count只是因为后者没有分配向量。
  • 使函数对错误类型具有通用性,允许该函数的用户使用他们希望的任何错误类型。
pub fn identifier<'a, E: ParseError<&'a str>>(s: &'a str) -> IResult<&'a str, &'a str, E> {
    recognize(all_consuming(pair(
        verify(anychar, |&c| c.is_lowercase()),
        many0_count(preceded(opt(char('_')), alphanumeric1)),
    )))(s)
}

书面的这个函数通过了你提供的所有测试用例。如果您特别想要all_consuming,可能是因为它被用作更大的解析器集的一部分,您必须手动检查识别的标识符是否以_字符结尾。

于 2020-04-20T18:00:36.137 回答