我正在尝试使用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
?
答案 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"])
);