为什么我的nom解析器不使用整个输入,而使最后一个未解析?

时间:2018-10-09 11:33:30

标签: rust nom

我正在尝试在空格和逗号之间分割一条日志行,以创建VectorToken的{​​{1}}个Field,如代码所示在下面。

我的问题是nom似乎并没有消耗掉整个日志行,它使最后一部分没有得到解析-在这种情况下为Separator

main.rs

08:33:58)

Cargo.toml

#![feature(rust_2018_preview)]

#[macro_use] extern crate nom;

#[derive(Debug, PartialEq)]
pub enum Token<'a> {
    Separator(&'a [u8]),
    Field(&'a [u8]),    
}

named!(separator, is_a!(" ,"));

named!(not_sep, is_not!(" ,"));

named!(
    token<Token>,
    alt_complete!(
        separator => { |s| Token::Separator(s) } |
        not_sep =>   { |n| Token::Field(n) }
    )
);

named!(sequence<Vec<Token>>, many1!(token));


pub fn scan(input: &[u8]) -> Vec<Token> {
    let (_, seq) = sequence(input).unwrap();

    seq
}

fn main() {
}

#[cfg(test)]
mod tests {
    use std::str;
    use crate::Token;
    use crate::scan;

    #[test]
    fn parse_stuff() {

        let log = &b"docker INFO 2019-10-01 08:33:58,878 [1] schedule:run Running job Every 1 hour do _precache_systems_streaks() (last run: 2018-09-21 07:33:58, next run: 2018-09-21 08:33:58)";

        let seq = scan(&log[..]);

        for t in seq {
            let text = match t {
                Token::Field(data) => format!("f[{}]", str::from_utf8(data).unwrap()),
                Token::Separator(data) => format!("s[{}]", str::from_utf8(data).unwrap()),
            };

            println!("{}", text);
        }
    }
}

输出

[dependencies]
nom = "4.0"

2 个答案:

答案 0 :(得分:6)

您遇到的问题是,除非另有说明,否则Nom旨在始终假设可能会有更多输入。由于您知道此处的输入是完整的,因此需要将解析器的原义输入CompleteByteSlice中(或者如果您使用的是&strCompleteStr)。这些类型是Nom用来表示我们知道没有更多输入的薄包装器。它将使解析器无法完全匹配,将返回Error而不是Incomplete,在这种情况下,将指示解析器使用该最终令牌,而不是要求更多字符。

答案 1 :(得分:1)

为了完整起见,我根据@Zarenor的答案实施了以下更改,解析器现在使用了整个输入。

更改为main.rs

use nom::types::CompleteByteSlice;
use nom::IResult;

named!(separator<CompleteByteSlice, CompleteByteSlice>, is_a!(" ,"));
named!(not_separator<CompleteByteSlice, CompleteByteSlice>, is_not!(" ,"));

fn token<'a>(input: CompleteByteSlice<'a>) -> IResult<CompleteByteSlice<'a>, Token<'a>> {
    alt!(input,
        separator =>     { | s: CompleteByteSlice<'a> | Token::Separator(s.0) } |
        not_separator => { | n: CompleteByteSlice<'a> | Token::Field(n.0)     }
    )
}

named!(sequence<CompleteByteSlice, Vec<Token>>, many1!(token));

pub fn scan(input: &[u8]) -> Vec<Token> {
    let (_, seq) = sequence(CompleteByteSlice(input)).unwrap();
    seq
}