我的测试parse_line_repeat在最后一行崩溃(这在这一点上是无效的),而不是像测试本身所示将其作为余数返回.

我试着用tuple代替pair,many0代替many1,第一个用this blogpost on nom解,第二个再读documentation on choosing a combinator.

我想要做的可能被描述为错误恢复,但这article不是这样说的,因此我不知道为什么要搜索.

这是lib.rs美元

use nom::{
    branch::alt,
    bytes::complete::tag,
    character::complete::space0,
    multi::{many1, many_till},
    sequence::pair,
    IResult,
};

fn parse_token(input: &str) -> IResult<&str, bool> {
    let (remaining, token) = alt((tag("0"), tag("1")))(input)?;
    if token == "1" {
        return Ok((remaining, true));
    } else {
        return Ok((remaining, false));
    }
}

#[allow(dead_code)]
fn parse_line(input: &str) -> IResult<&str, Vec<bool>> {
    let (remaining, (tokens_raw, _)) = pair(
        many1(many_till(space0, parse_token)),
        many_till(space0, tag("\n")),
    )(input)?;

    let mut tokens = Vec::new();

    for (_, token) in tokens_raw {
        let token: bool = token;
        tokens.push(token);
    }

    Ok((remaining, tokens))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_token_test() {
        assert_eq!(parse_token("0"), Ok(("", false)));
        assert_eq!(parse_token("1"), Ok(("", true)));
    }

    #[test]
    fn parse_line_test() {
        assert_eq!(parse_line("1 \n"), Ok(("", vec![true])));
        assert_eq!(parse_line("0 \n"), Ok(("", vec![false])));
        assert_eq!(
            parse_line("1 1 1 1\n"),
            Ok(("", vec![true, true, true, true]))
        );
    }

    #[test]
    fn parse_line_repeat() {
        let rtn = parse_line("1 \n 1\n");
        assert_eq!(rtn, Ok((" 1\n", vec![true])));
        let rtn = parse_line(rtn.unwrap().0);
        assert_eq!(rtn, Ok(("", vec![true])));
        let rtn = parse_line("1\n 1\n\n");
        assert_eq!(rtn, Ok((" 1\n\n", vec![true])));
        let rtn = parse_line(rtn.unwrap().0);
        assert_eq!(rtn, Ok(("\n", vec![true])));
        let rtn = parse_line(rtn.unwrap().0);
        assert_eq!(rtn, Ok(("\n", vec![])));
    }    
}

这是Cargo.toml美元

[package]
name = "minimal"
version = "0.1.0"
edition = "2021"

[lib]
name = "minimal"
path = "src/lib.rs"

[dependencies]
nom = "7.1.3"

错误:

$ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.94s
     Running unittests minimal/main.rs

running 3 tests
test tests::parse_line_test ... ok
test tests::parse_token_test ... ok
test tests::parse_line_repeat ... FAILED

failures:

---- tests::parse_line_repeat stdout ----
thread 'tests::parse_line_repeat' panicked at 'assertion failed: `(left == right)`
  left: `Err(Error(Error { input: "\n", code: ManyTill }))`,
 right: `Ok(("\n", []))`', minimal/main.rs:67:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::parse_line_repeat

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

Playground

推荐答案

我认为真正的原因是你的测试/预期的行为是有缺陷的.

用更简单的话来说,您的上一个测试判断parse_line的以下行为:

  • 1 \n 1\n->; 1\n+[true]
  • 1\n->;<empty>+[true]

然后,您可以判断:

  • 1\n 1\n\n->; 1\n\n+[true]
  • 1\n\n->;\n+[true]

最后是:

  • \n->;\n+[].

最后一个对我来说没有多大意义.如果 1\n应该变成<empty>,那么为什么\n不应该也变成<empty>呢?为什么一个使用换行符,而另一个不使用?如果是因为解析器不应该使用没有1的空行,那么它返回错误又有什么错呢?返回错误是不能使用对象的解析器所期望的.

因此,你可以采取的可能方式是:

  • 您的代码可使用的空行(\n)is not.在这种情况下,您的代码已经正确,测试有缺陷,应该会出现错误.
  • 您的代码可使用的空行(\n)is.在这种情况下,您的代码和测试都有缺陷.代码应该是many0而不是many1,测试结束时应该判断Ok(("", vec![])).

您的原始代码似乎表明您希望使用not的空行.在这种情况下,我会将测试修改为:

#[test]
fn parse_line_repeat() {
    let rtn = parse_line("1 \n 1\n");
    assert_eq!(rtn, Ok((" 1\n", vec![true])));
    let rtn = parse_line(rtn.unwrap().0);
    assert_eq!(rtn, Ok(("", vec![true])));
    let rtn = parse_line("1\n 1\n\n");
    assert_eq!(rtn, Ok((" 1\n\n", vec![true])));
    let rtn = parse_line(rtn.unwrap().0);
    assert_eq!(rtn, Ok(("\n", vec![true])));
    let rtn = parse_line(rtn.unwrap().0);
    assert_eq!(
        rtn,
        Err(nom::Err::Error(nom::error::Error {
            input: "\n",
            code: nom::error::ErrorKind::ManyTill
        }))
    );
}

Rust相关问答推荐

PyReadonlyArray2到Vec T<>

通过使用光标拖动角来绕其中心旋转矩形

类型批注需要静态生存期

我们能确定Rust会优化掉Clone()吗?如果它会立即掉落?

Option<&T> 如何实现复制

go 重并堆积MPSC通道消息

我可以在 Rust 中 serde struct camel_case 和 deserde PascalCase

使用 Rust 从 Raspberry Pi Pico 上的 SPI 读取值

使用 lalrpop 在 rust 中解析由 " 引用的字符串

如何递归传递闭包作为参数?

如何刷新 TcpStream

哪些特征通过 `Deref` 而哪些不通过?

是否可以在 Rust 中的特定字符上实现特征?

试图理解 Rust 中的可变闭包

无法把握借来的价值不够长寿,请解释

在 Rust 中退出进程

BigUint 二进制补码

为什么 std::iter::Peekable::peek 可变地borrow self 参数?

加入动态数量的期货

如何在 Rust 中使用特征标志来捕获多行代码?