1

我正在尝试使用 nom 创建一个解析器,它将解析一些可能是许多选项之一的文本。当值在编译时已知时, Nom 具有alt!,但我的值不会。

这是我尝试创建自己的解析器Vec<String>来匹配的,我遇到了几个问题。

#[macro_use]
extern crate nom;

use nom::IResult;

fn alternative_wrapper<'a>(input: &'a [u8], alternatives: Vec<String>) -> IResult<&'a [u8], &'a [u8]> {
    for alternative in alternatives {
        // tag!("alternative");
        println!("{}", alternative);
    }
    return IResult::Done(input, "test".as_bytes());
}

#[test]
fn test_date() {
    let input = "May";
    named!(alternative, call!(alternative_wrapper));
    let months = vec!(
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
        ).iter().map(|s| s.to_string()).collect();
    println!("{:?}", alternative("May".as_bytes(), months));
}

我知道我的alternative_wrapper函数实际上并没有做任何有用的事情,但这不是问题。这就是 Rust 抱怨这个片段的原因:

error[E0061]: this function takes 1 parameter but 2 parameters were supplied
  --> src/parser.rs:32:34
   |
17 |     named!(alternative, call!(alternative_wrapper));
   |     ------------------------------------------------ defined here
...
32 |     println!("{:?}", alternative("May".as_bytes(), months));
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 parameter
   |
   = note: this error originates in a macro outside of the current crate

error[E0061]: this function takes 2 parameters but 1 parameter was supplied
  --> src/parser.rs:17:5
   |
6  | / fn alternative_wrapper<'a>(input: &'a [u8], alternatives: Vec<String>) -> IResult<&'a [u8], &'a
[u8]> {
7  | |     for alternative in alternatives {
8  | |         // tag!("alternative");
9  | |         println!("{}", alternative);
10 | |     }
11 | |     return IResult::Done(input, "test".as_bytes());
12 | | }
   | |_- defined here
...
17 |       named!(alternative, call!(alternative_wrapper));
   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 parameters
   |
   = note: this error originates in a macro outside of the current crate

如何从我的函数中创建解析器?以及如何使用现有的解析器,例如tag!from inside alternative_wrapper

4

3 回答 3

3

从错误开始,第一个错误是由于named!只采用了一个参数,即输入字符串。named!将为您声明一个函数,在这种情况下使用签名fn(&[u8]) -> IResult<&[u8],&[u8]>. 任何其他参数都没有魔法,所以试图将你的months向量作为第二个参数传递是行不通的。有一种named!调用的变体named_args!可用于声明具有更多参数的函数,而不仅仅是应该对其进行排序的输入。

第二个错误类似但相反。您alternative_wrapper通过 仅使用输入而没有向量来调用call!call!宏实际上可以传递参数,但你必须明确地这样做,即call!(myparser, monts).

理清了错误的原因后,您正在询问如何创建解析器。好吧,实际上,alternative_wrapper已经是一个通过签名的 nom 解析器,但是由于您没有通过 nom 宏声明它,因此不会发生任何神奇的输入传递,这就是为什么tag!当您尝试时在函数体中不起作用的原因。

为了在您自己声明的函数中使用其他组合器,您必须手动将输入传递给最外层的宏。在这种情况下tag!do_parse!只有do_parse!. 我将在这里提供一个带有一些额外调整的工作版本:

#[macro_use]
extern crate nom;

use std::str;
use nom::IResult;

fn alternative<'a>(input: &'a [u8], alternatives: &Vec<String>) -> IResult<&'a [u8], &'a [u8]> {
    for alternative in alternatives {
        match tag!(input, alternative.as_bytes()) {
            done@IResult::Done(..) => return done,
            _ => () // continue
        }
    }
    IResult::Error(nom::ErrorKind::Tag) // nothing found.
}

fn main() {
    let months: Vec<String> = vec![
        "January", "February", "March", "April", "May", "June", "July",
        "August", "September", "October", "November", "December"
    ].into_iter().map(String::from).collect();

    fn print_res(r: IResult<&[u8],&[u8]>) {
        println!("{:?}", r);
        println!("{:?}\n", str::from_utf8(r.unwrap().1).unwrap());
    }
    print_res(alternative(b"May", &months));
    print_res(alternative(b"August", &months));
    print_res(alternative(b"NoGood", &months));
}

你可以在铁锈游乐场看看。

于 2017-09-02T07:55:43.323 回答
1

我对 nom 不是很熟悉,并且还在学习 Rust,但我过去使用过解析器组合器。

抛开注意事项不谈,看起来named!宏生成了一个只接受一个参数的函数,即要解析的字符串。

为了满足 nom 的期望,我想我会将写作alternative_wrapper视为一个返回函数的函数。测试最终将如下所示:

#[test]
fn test_date() {
    let months = vec!(
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
        ).iter().map(|s| s.to_string()).collect();
    let parser = generate_alternative_parser(months);
    named!(alternative, call!(parser));
    println!("{:?}", alternative("May".as_bytes()));
}

看起来您需要alt!从 s 构造一个表达式tag!,但是从文档中我并没有立即明白您将如何做到这一点。

您的选项列表最终来自哪里?

根据您要完成的具体工作,可能还有其他一些方法可以完成您要完成的工作。例如,您可能能够解析任何单词,然后根据您的选项之一对其进行验证。

于 2017-06-09T20:48:27.083 回答
0

使用 Nom 4,这是一个完全通用的输入,无论您的解析器操作什么,它都可以工作:

/// Dynamic version of `alt` that takes a slice of strings
fn alternative<T>(input: T, alternatives: &[&'static str]) -> IResult<T, T>
where
    T: InputTake,
    T: Compare<&'static str>,
    T: InputLength,
    T: AtEof,
    T: Clone,
{
    let mut last_err = None;
    for alternative in alternatives {
        let inp = input.clone();
        match tag!(inp, &**alternative) {
            done @ Ok(..) => return done,
            err @ Err(..) => last_err = Some(err), // continue
        }
    }
    last_err.unwrap()
}

/// Usage
named!(test<Span, Span>,
    call!(alternative, &["a", "b", "c"])
);
于 2019-01-20T07:49:48.187 回答