结合使用 nom 的 `alt` 和 `map` 函数来修改对象,具体取决于哪个解析器成功

问题描述 投票:0回答:1

我有一个结构体,其中包含单词列表和数字列表

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 个问题混合在一起让我感到困惑。

dictionary rust nom alt-attribute
1个回答
0
投票

我认为通常将可变值传递给解析器有点奇怪 - 通常我会取出解析后的数据,然后迭代它以插入到

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] }
© www.soinside.com 2019 - 2024. All rights reserved.