Boost.Spirit.qi:在解析时动态创建差异解析器(&Quot;Difference&Quot;)

"Difference"解析器可以由二元-(减号)运算符创建:

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_, {"}}", "]]"}));

此处,{..., ..., ...}部分实际上是stl容器,但这不是重点;我可以处理该部分。

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


解决方案

在我看来,您寻找的不是一个动态的"差异"表达式,而是一个动态的"可变备选(a|b|c.)"表达式:

expr - a - b - c相当于expr - (a|b|c)

然后您可以使用以下任一选项轻松实现差异:

expr - orCombine(alternatives)

!orCombine(alternatives) >> expr
现在,完成此操作有许多粗糙的边缘,我将首先解释这一点。幸运的是,还有一种更简单的方法,使用qi::symbols,我将在那之后立即演示。

棘手的问题

如果您愿意,您可以按需"生成"替代解析器表达式,只需相当多的技巧。我在下面的回答中演示了如何做到这一点:

  • Generating Spirit parser expressions from a variadic list of alternative parser expressions

但是

  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)
                );
    

    看到您需要替代解析器的真正动态组合,我不明白如何才能在不增加复杂性和出现细微错误的机会的情况下适应您的需求(相信我,我已经尝试过)。

因此,相反,我推荐一种经过尝试的方法&;true,它"滥用"现有的"动态"解析器:

使用qi::symbols

简化 这一观点借用了著名的"纳比亚莱克把戏"。它使用了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;
    }
};

此操作的完整工作示例为here: 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 success
";
        std::cout << "data: " << data << "
";
    }
    else std::cerr << "parse failed: '" << std::string(f,l) << "'
";

    if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'
";
}

1我相信即将发布的新版本的SPIRIT(目前的实验版称为"SPIRIT X3")将删除此问题

2它使用Tries查找匹配项

相关文章