2. 标签与字符类

标签 Tag

您可以编写的最简单、最实用的解析器是没有特殊字符的解析器,它只匹配字符串。

在 nom 中,我们将简单的字节集合称为标签(tag)。由于这些字符非常常见,nom 提供一个名为 tag() 的函数。此函数返回给定字符串的解析器。

WARNING

警告:nom有多个不同的定义tag,请确保暂时使用这个!

pub use nom::bytes::complete::tag;

例如,解析字符串的代码 "abc" 可以表示为 tag("abc")

如果你没有使用以函数为值的语言进行编程,那么标记函数的类型签名可能会让你感到惊讶:

pub fn tag<T, Input, Error: ParseError<Input>>(
    tag: T
) -> impl Fn(Input) -> IResult<Input, Input, Error> where
    Input: InputTake + Compare<T>,
    T: InputLength + Clone, 

或者,对于 TInput 均为 &str 的情况,稍微简化一下:

fn tag(tag: &str) -> (impl Fn(&str) -> IResult<&str, Error>)

换句话说,这个函数 tag 返回一个函数。它返回的函数是一个解析器,接受一个 &str 并返回一个 IResult。函数创建解析器并返回它们是 nom 中的常见模式,因此调用它很有用。

下面,我们实现了一个使用 tag 的函数。

fn parse_input(input: &str) -> IResult<&str, &str> {
    // 注意,这里实际上是在创建一个函数,即解析器 "abc"
    //  vvvvv 
    //        这个解析器在这里被调用,返回一个 IResult<&str, &str>
    //         vvvvv
    tag("abc")(input)
}

fn main() -> Result<(), Box<dyn Error>> {
    let (leftover_input, output) = parse_input("abcWorld")?;
    assert_eq!(leftover_input, "World");
    assert_eq!(output, "abc");

    assert!(parse_input("defWorld").is_err());
}

如果您愿意,您还可以使用 tag_no_caseopen in new window 函数检查不区分大小写的标签。

字符类 Character Class

标签非常有用,但也非常受限。Nom 功能的另一端是预先编写的解析器,它允许我们接受一组字符中的任何一个,而不仅仅是接受定义序列中的字符。

以下是其中的一部分:

我们可以使用它们

pub use nom::character::complete::alpha0;
fn parser(input: &str) -> IResult<&str, &str> {
    alpha0(input)
}

fn main() -> Result<(), Box<dyn Error>> {
    let (remaining, letters) = parser("abc123")?;
    assert_eq!(remaining, "123");
    assert_eq!(letters, "abc");
}

需要注意的一点是,由于这些函数的类型签名,最好在返回的函数中使用它们 IResult

如果不这样做,就必须手动指定一些有关函数类型的信息tag,这可能会导致冗长的代码或令人困惑的错误。

Last Updated 2024-11-28 11:45:01