首页 > 解决方案 > 如何解析 Nom 中的大写字符串?

问题描述

我在 Nom 5 中使用函数而不是宏编写解析器。我的目标是编写一个能够识别完全由大写字符组成的字符串的解析器。理想情况下,它将具有与alpha1相同的返回签名。

use nom::{
    character::complete::{alpha1, char, line_ending, not_line_ending},
    combinator::{cut, map, not, recognize},
    error::{context, ParseError, VerboseError},
    multi::{many0, many1},
    IResult,
};

fn uppercase_char<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
    let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    take_while(move |c| chars.contains(c))(i)
}

// Matches 1 or more consecutive uppercase characters
fn upper1<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
    recognize(many1(uppercase_char))(i)
}

尽管可以编译,但我编写的简单单元测试失败了:

#[test]
fn test_upper_string_ok() {
    let input_text = "ADAM";
    let output = upper1::<VerboseError<&str>>(input_text);
    dbg!(&output);
    let expected = Ok(("ADAM", ""));
    assert_eq!(output, expected);
}

失败输出为

---- parse::tests::test_upper_string_ok stdout ----
[src/parse.rs:110] &output = Err(
    Error(
        VerboseError {
            errors: [
                (
                    "",
                    Nom(
                        Many1,
                    ),
                ),
            ],
        },
    ),
)
thread 'parse::tests::test_upper_string_ok' panicked at 'assertion failed: `(left == right)`
  left: `Err(Error(VerboseError { errors: [("", Nom(Many1))] }))`,
 right: `Ok(("ADAM", ""))`', src/parse.rs:112:9
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

标签: parsingrustnom

解决方案


take_while将识别 0 个或更多字符,因此当many1您在内部使用时,它将首先解析整个"ADAM"字符串。然后当many1再次调用它时,由于take_while可以识别一个空字符串,它会成功,但many0many1一个防止该错误的保护:如果底层解析器没有消耗任何输入,它们将返回一个错误。

对于你所需要的,uppercase_char功能应该足够了,不需要recognizeand many1。虽然您可能想替换take_whiletake_while1


推荐阅读