如何将任何一个字符串向量与nom匹配?

时间:2017-06-08 19:38:41

标签: parsing rust nom

我正在尝试使用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!内使用现有的解析器,例如alternative_wrapper

3 个答案:

答案 0 :(得分:2)

从错误开始,第一个错误是由于named!只接受一个参数,即输入字符串。 named!会为您声明一个函数,在这种情况下会使用签名fn(&[u8]) -> IResult<&[u8],&[u8]>。对于任何其他参数没有任何魔力,因此尝试将months向量作为第二个参数传递将不起作用。有一个名为named!的{​​{1}}变体可用于声明具有更多参数的函数,而不仅仅是应该对其进行排序的输入。

第二个错误类似但反转。您仅使用输入调用named_args!,而alternative_wrapper没有向量。 call!宏实际上可以传递参数,但您必须明确地进行传递,即call!

有了解决错误的原因,您就会询问如何创建解析器。好吧,实际上,call!(myparser, monts) 已经通过签名一个名词解析器,但由于你没有通过一个nom宏声明它,所以没有任何魔法输入传递发生,这就是为什么当你尝试过时alternative_wrapper在函数体中不起作用。

为了在您自己声明的函数中使用其他组合器,您必须手动将输入传递给最外层的宏。在这种情况下,它只有tag!,但如果您要使用tag!,然后使用其中的多个宏,则只需要将输入传递给{{ 1}}。我将提供一个工作版本,其中包含一些额外的调整:

do_parse!

您可以在rust playground

中查看

答案 1 :(得分: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!构建一个tag!表达式,但是从文档中我不会立即明白你是怎么做的。

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

根据您要完成的具体内容,可能还有其他一些方法可以完成您正在尝试完成的任务。例如,您可以解析任何单词,然后根据您的某个选项对其进行验证。

答案 2 :(得分: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"])
);