使用 nom 解析自定义标识符
Parsing custom identifier with nom
我有兴趣使用 nom 解析器组合器来识别此类标识符:
"a"
"a1"
"a_b"
"aA"
"aB_3_1"
标识符的第一个字符应该是字母小写字符,然后可以是字母数字字符和下划线的任意组合(因此 [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)
}
这是我想出的变体。它与您的大部分相同;我做了以下更改:
- 最重要的是,我添加了
all_consuming
,以确保整个输入匹配。您提议的实现中的错误是 "aBa_" 会 成功地 匹配标识符 "aBa" 并且不解析尾随的“_”(在输入端返回它)。
- 完全根据解析器组合器重写,而不是使用
?
语句。
- 使下划线匹配可选。 Nom 解析器通常是贪婪的,所以这不会导致性能下降。
- 简化为只有 2 个子句,而不是 3 个。解析器基本上运行 "match any lower case character, followed by 0 or more runs of an optional _ followed by 1 more more alphanumerics"。
- 将
many1
更改为many0_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
,可能是因为它被用作更大的解析器集的一部分,您将必须手动检查识别的标识符不以 _
字符结尾。
我有兴趣使用 nom 解析器组合器来识别此类标识符:
"a"
"a1"
"a_b"
"aA"
"aB_3_1"
标识符的第一个字符应该是字母小写字符,然后可以是字母数字字符和下划线的任意组合(因此 [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)
}
这是我想出的变体。它与您的大部分相同;我做了以下更改:
- 最重要的是,我添加了
all_consuming
,以确保整个输入匹配。您提议的实现中的错误是 "aBa_" 会 成功地 匹配标识符 "aBa" 并且不解析尾随的“_”(在输入端返回它)。 - 完全根据解析器组合器重写,而不是使用
?
语句。 - 使下划线匹配可选。 Nom 解析器通常是贪婪的,所以这不会导致性能下降。
- 简化为只有 2 个子句,而不是 3 个。解析器基本上运行 "match any lower case character, followed by 0 or more runs of an optional _ followed by 1 more more alphanumerics"。
- 将
many1
更改为many0_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
,可能是因为它被用作更大的解析器集的一部分,您将必须手动检查识别的标识符不以 _
字符结尾。