Nom 5:多次使用另一个解析器创建组合器

时间:2019-07-02 05:50:12

标签: parsing rust parser-combinators nom

假设我想创建一个组合器,该组合器多次使用另一个解析器,例如,解析由两种引号分隔的字符串:

fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
where
    F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
    map(
        alt((
            tuple((tag("'"), f, tag("'"))),
            tuple((tag("\""), f, tag("\"")))
        )),
        |(_, res, _)| res,
    )
}

按预期,此解析器无法编译,并显示“使用移动值”错误:

149 |     fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
    |                   -                                 - move occurs because `f` has type `F`, which does not implement the `Copy` trait
    |                   |
    |                   consider adding a `Copy` constraint to this type argument
...
155 |                 tuple((tag("'"), f, tag("'"))),
    |                                  - value moved here
156 |                 tuple((tag("\""), f, tag("\"")))
    |                                   ^ value used here after move

但是,我不能仅将CopyClone添加到F边界:特别是Nom的内置函数返回的很多解析器,既没有实现{ {1}}或Clone。我也不能使用Copy作为&f的参数,因为那样将是借位检查错误(tuple是一个临时的局部值,因此不可能返回a解析器构造而成。)

我看到的唯一方法是实际上通过在嵌套的f语句序列中展开来直接在函数中直接实现alt逻辑,但这似乎不是最佳选择。或者也许我缺少一些简单的东西,而实际上有可能仅使用组合器来完成我想做的事情?

我非常确定,如上所述,有一种更好的方式专门编写match组合器,如果有人显示它会很好,但是我的问题是更笼统的-如何编写组合器哪个重用同一个解析器?

1 个答案:

答案 0 :(得分:3)

最简单的方法是对返回的闭包进行显式显示:

fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
where
    F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
    move |i| {
        map(
            alt((
                tuple((tag("'"), &f, tag("'"))),
                tuple((tag("\""), &f, tag("\"")))
            )),
            |(_, res, _)| res,
        )(i)
    }
}

现在,由于有了move关键字,f值被移到了闭包中。然后,在返回的闭包内部,我直接调用复杂的解析器组合器,并且从闭包中返回除输出/错误以外的任何内容,这意味着我可以自由使用对f的引用。