Почему мой номинальный парсер не использует весь ввод, оставляя последний фрагмент не разбранным? - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь разделить строку журнала на пробел и запятую, чтобы создать Vector из Token с Field и Separator, как показано в коде ниже.

Моя проблема в том, что nom, кажется, не поглощает всю строку журнала, она оставляет последнюю часть без анализа - в этом случае 08:33:58).

main.rs

#![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);
        }
    }
}

Cargo.toml

[dependencies]
nom = "4.0"

выход

f[docker]
s[ ]
f[INFO]
s[ ]
f[2019-10-01]
s[ ]
f[08:33:58]
s[,]
f[878]
s[ ]
f[[1]]
s[ ]
f[schedule:run]
s[ ]
f[Running]
s[ ]
f[job]
s[ ]
f[Every]
s[ ]
f[1]
s[ ]
f[hour]
s[ ]
f[do]
s[ ]
f[_precache_systems_streaks()]
s[ ]
f[(last]
s[ ]
f[run:]
s[ ]
f[2018-09-21]
s[ ]
f[07:33:58]
s[, ]
f[next]
s[ ]
f[run:]
s[ ]
f[2018-09-21]
s[ ]

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

Для полноты я внес следующие изменения в соответствии с ответом @ 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
}
0 голосов
/ 09 октября 2018

Проблема, с которой вы сталкиваетесь, заключается в том, что Nom разработан так, чтобы всегда предполагать, что может быть больше ввода, если вы не укажете иначе.Поскольку вы знаете, что ваш ввод здесь завершен, вам нужно передать синтаксическим анализаторам ваш литерал, заключенный в CompleteByteSlice (или, если вы использовали &str, CompleteStr).Эти типы представляют собой тонкие обертки, которые Nom использует, чтобы показать, что мы знаем, что поступающих данных больше нет.Это сделает так, что синтаксический анализатор, который не сможет выполнить полное совпадение, вернет Error вместо Incomplete, и в этом случае будет инструктировать синтаксический анализатор использовать этот окончательный токен, а не запрашивать больше символов.

...