Boost.Spirit.Qi:在解析时动态创建"difference"解析器

Boost.Spirit.Qi: dynamically create "difference" parser at parse time

本文关键字:创建 Spirit difference 动态 Qi Boost      更新时间:2023-10-16

可以通过二进制-(减号)运算符创建一个"差异"解析器:

rule = qi::char_ - qi::lit("}}")

甚至复合差异:

rule = qi::char_ - qi::lit("}}") - qi::lit("]]")

但是我如何在解析时生成差异解析器的整个结果呢?
我猜它可能是像下面这样的形式:

phoenix::function<difference_parser_impl> difference_parser;
rule = qi::lazy(difference_parser(qi::char_, {"}}", "]]"}));

这里,{..., ..., ...}部分实际上是一个静态容器,但这不是重点;我可以处理那部分。

我已经找到了模板qi::difference<Left, Right> -但我不知道如何使用它

在我看来,你不是在寻找一个动态的"差异"表达式,而是一个动态的"可变的替代(a|b|c…)"表达式:

expr - a - b - c等价于expr - (a|b|c)

你可以很容易地实现差异使用:

expr - orCombine(alternatives)

!orCombine(alternatives) >> expr

现在,完成这项工作有很多不明确的地方,我将首先解释。幸运的是,有一种更简单的方法,使用qi::symbols,我将在后面演示。

棘手的东西

如果您愿意,您可以使用一些魔法,按需"生成"替代解析器表达式。我在下面的回答中展示了如何做到这一点:

  • 从可选解析器表达式的可变列表生成Spirit解析器表达式

,

  1. 它充满了陷阱(因为原型表达式不适合复制)1
  2. 它方便地使用变量以避免中间存储(注意deepcopy_以防止未定义行为):

    template<typename ...Expr>
    void parse_one_of(Expr& ...expressions)
    {
        auto parser = boost::fusion::fold(
                    boost::tie(expressions...),
                    qi::eps(false),
                    deepcopy_(arg2 | arg1)
                );
    

    看到您如何需要真正的动态组合的替代解析器,我不知道如何适应您的需求,而不增加复杂性和微妙错误的机会(相信我,我已经尝试过)。

所以,我推荐使用&"滥用"现有"动态"解析器的真实方法:

简化使用qi::symbols

这个想法几乎借用了著名的"Nabialek把戏"。它使用qi::符号,因此具有优秀的运行时性能特征2

废话不多说,下面是如何使用它的一个示例,从字符串字面值的向量开始:

template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, std::string(), Skipper>
{
    parser() : parser::base_type(start)
    {
        static const std::vector<std::string> not_accepted { "}}", "]]" };
        using namespace qi;
        exclude = exclusions(not_accepted);
        start = *(char_ - exclude);
        BOOST_SPIRIT_DEBUG_NODE(start);
    }
  private:
    qi::rule<It, std::string(), Skipper> start;
    typedef qi::symbols<char, qi::unused_type> Exclude;
    Exclude exclude;
    template<typename Elements>
    Exclude exclusions(Elements const& elements) {
        Exclude result;
        for(auto& el : elements)
            result.add(el);
        return result;
    }
};

一个完整的工作示例在这里:http://coliru.stacked-crooked.com/view?id=ddbb2549674bfed90e3c8df33b048574-7616891f9fd25da6391c2728423de797,它打印

parse success
data: 123
trailing unparsed: ']] 4'

以后参考:

#include <boost/spirit/include/qi.hpp>
namespace qi    = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, std::string(), Skipper>
{
    parser() : parser::base_type(start)
    {
        static const std::vector<std::string> not_accepted { "}}", "]]" };
        using namespace qi;
        exclude = exclusions(not_accepted);
        start = *(char_ - exclude);
        BOOST_SPIRIT_DEBUG_NODE(start);
    }
  private:
    qi::rule<It, std::string(), Skipper> start;
    typedef qi::symbols<char, qi::unused_type> Exclude;
    Exclude exclude;
    template<typename Elements>
    Exclude exclusions(Elements const& elements) {
        Exclude result;
        for(auto& el : elements)
            result.add(el);
        return result;
    }
};
int main()
{
    const std::string input = "1 2 3]] 4";
    typedef std::string::const_iterator It;
    It f(begin(input)), l(end(input));
    parser<It> p;
    std::string data;
    bool ok = qi::phrase_parse(f,l,p,qi::space,data);
    if (ok)   
    {
        std::cout << "parse successn";
        std::cout << "data: " << data << "n";
    }
    else std::cerr << "parse failed: '" << std::string(f,l) << "'n";
    if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'n";
}

1我相信这个问题将在即将到来的精灵新版本(目前被称为"精灵X3"的实验版本)中被删除

2使用Tries查找匹配