0

我正在尝试使用 nom5.0 解析以下备用字符串

"A-Za-z0-9"

或者

"A-Z|a-z|0-9"

我尝试了以下但无济于事

pub enum Node {
    Range(Vec<u8>),
}

fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
    map(
        separated_list(
            alt((tag("|"), tag(""))),
            tuple((take(1usize), tag("-"), take(1usize))),
        ),
        |v: Vec<(&[u8], _, &[u8])>| {
            Node::Range(
                v.iter()
                    .map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
                    .flatten()
                    .collect(),
            )
        },
    )(input)
}

版本 2。

fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
        alt((
            map(
                separated_list(tag("|"), tuple((take(1usize), tag("-"), take(1usize)))),
                |v: Vec<(&[u8], _, &[u8])>| {
                    Node::Range(
                        v.iter()
                            .map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
                            .flatten()
                            .collect(),
                    )
                },
            ),
            map(
                many1(tuple((take(1usize), tag("-"), take(1usize)))),
                |v: Vec<(&[u8], _, &[u8])>| {
                    Node::Range(
                        v.iter()
                            .map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
                            .flatten()
                            .collect(),
                    )
                },
            ),
        ))(input)
    }



#[test]
fn parse_compound() {
    println!("{:?}", compound_range(b"A-Za-z0-9"));
    println!("{:?}", compound_range(b"A-Z|a-z|0-9"));
}

我可以得到第一个或第二个来解析,但不能同时解析。有没有办法表达这个?

4

1 回答 1

2

问题是它nom总是采用它看到的第一条路径例如,它不必消耗所有输入)。因此,您理想情况下想要做的是将第一个"a-z"(或其他)之后的路径拆分为两个可能的路径之一:您是否|作为分隔符处理。

这是因为nom它是一个解析器组合库,不像正则表达式那样工作,它可以回溯到需要找到有效的东西。

无论如何,这样的事情应该有效:

fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
    let single_range = |input| map(
        separated_pair(take(1usize), tag("-"), take(1usize)),
        |(l, r): (&[u8], &[u8])| (l[0], r[0])
    )(input);

    map(
        opt(
            map(
                pair(
                    single_range,
                    alt((
                        preceded(tag("|"), separated_nonempty_list(
                            tag("|"),
                            single_range,
                        )),
                        many0(single_range)
                    ))
                ),
                |(first, rest)| Node::Range(
                    std::iter::once(first).chain(rest).flat_map(|(l, r)| l..r).collect()
                )
            ),
        ),
        |o| o.unwrap_or_else(|| Node::Range(Vec::new()))
    )(input)
}

有没有更好的办法?大概。鉴于特定任务,手动实现您正在编写的解析器的那部分实际上可能是有意义的。它是这样工作的吗?大概。(我没有测试过)

还有一点要记住:这可能会消耗太多,如果您期望其他一些适合它之后的模式的东西。

于 2019-08-16T17:03:36.620 回答