我有一个结构体,其中包含单词列表和数字列表
struct NumbersAndWords {
pub words: Vec<String>,
// ideally numbers would be Vec<i32>
pub numbers: Vec<String>,
}
impl NumbersAndWords {
pub fn insert_number(&mut self, num: &str) {
self.numbers.push(String::from(num));
}
pub fn insert_word(&mut self, word: &str) {
self.words.push(String::from(word));
}
}
现在我想解析一段包含单词和数字的文本,例如
"monkey 10 apple tree 20"
。我想使用 alt
组合器来解析使用 alpha1
的单词或使用 digit1
的数字。根据使用的解析器,我想将解析结果添加到 NumbersAndWords
的相应字段中。现在这两个字段都是字符串的向量 - 理想情况下 numbers
是 Vec<i32>
,但这对我来说太困难了,因为
我尝试编写的解析器函数看起来像这样(但现在可以工作,因为出现了两个借用。不幸的是,我无法解决这个问题:
fn parser<'a>(i: &'a str, numbers_and_words: &mut NumbersAndWords) -> IResult<&'a str, ??? > {
alt((
map(alpha1, |word| numbers_and_words.insert_word(word)),
map(digit1, |num| numbers_and_words.insert_number(num)),
))(i)
有办法解决这个问题吗?甚至是 nom 更喜欢的方式?使用枚举并以某种方式匹配
alt(...)
的值会更好吗?
我不确定同时使用 alt 和地图是否是一个好主意。有没有或多或少“优雅”的方法来解决这个问题?理想情况下,也将
num
解析为 i32,并且 numbers
字段是 Vec。 (但是这个 alt(...) 解析器的返回类型会非常复杂。
我认为可能有 2 甚至 3 个问题混合在一起让我感到困惑。
我认为通常将可变值传递给解析器有点奇怪 - 通常我会取出解析后的数据,然后迭代它以插入到
numbers_and_words
中。可变输入是你不能使用 alt
的原因;如果您返回解析的数据并使用它单独改变 numbers_and_words
,事情可能会成功。但无论如何,这适用于解析。
use nom::{
character::complete::{alpha1, digit1, space1},
multi::separated_list0,
IResult,
};
fn parse_token<'a>(i: &'a str, numbers_and_words: &mut NumbersAndWords) -> IResult<&'a str, ()> {
if let Ok((remainder, digits)) = digit1::<_, nom::error::Error<&'a str>>(i) {
numbers_and_words.insert_number(digits.parse().unwrap());
Ok((remainder, ()))
} else if let Ok((remainder, word)) = alpha1::<_, nom::error::Error<&'a str>>(i) {
numbers_and_words.insert_word(word);
Ok((remainder, ()))
} else {
Err(nom::Err::Error(nom::error::Error {
input: i,
code: nom::error::ErrorKind::Fail,
}))
}
}
fn parse_all<'a>(i: &'a str, numbers_and_words: &mut NumbersAndWords) -> IResult<&'a str, Vec<()>> {
separated_list0(space1, |i| parse_token(i, numbers_and_words))(i)
}
fn main() {
let mut numbers_and_words = NumbersAndWords {
words: Vec::new(),
numbers: Vec::new(),
};
let input = "monkey 10 apple tree 20";
parse_all(input, &mut numbers_and_words).unwrap();
println!("{numbers_and_words:?}");
}
NumbersAndWords { words: ["monkey", "apple", "tree"], numbers: [10, 20] }