Boost.Spirit.Qi:在分析时动态创建“差异”解析器

时间:2013-06-08 15:13:35

标签: c++ boost boost-spirit boost-spirit-qi boost-phoenix

“差异”解析器可以由二进制-(减号)运算符创建:

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> - 但我找不到如何使用它。

1 个答案:

答案 0 :(得分:2)

在我看来,你并不是在寻找一种充满活力的差异&#34;表达这么多,而是一个动态的变量替代(a | b | c ...)&#34;表达式:

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

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

expr - orCombine(alternatives)

!orCombine(alternatives) >> expr

现在,完成这项工作有许多粗略的边缘,我将首先解释。幸运的是,有一种更简单的方法,使用qi::symbols,我将在此之后展示。

棘手的东西

如果你愿意,你可以生成&#34;按需使用替代解析器表达式,具有相当多的魔法。我在这个答案中展示了如何做到这一点:

但是

  1. 它充满了陷阱(因为proto表达不适合复制) 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)
                );
    

    看看你如何需要替代解析器的真正动态组合,我不知道如何在不增加复杂性和微妙错误机会的情况下根据您的需求进行调整( 相信我,我已经尝试 )。

  3. 所以,我推荐一个尝试过的&amp;真正的做法是&#34;滥用&#34;现有的&#34;动态&#34;解析器:

    使用qi::symbols

    简化

    这个想法从着名的&#34; Nabialek Trick&#34;中丢失了。它使用qi :: symbols,因此具有出色的运行时性能特征 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\n";
            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 我相信这个问题即将在即将推出的新版Spirit中删除(目前被称为&#34; Spirit X3&#34;实验版)

    2 它使用Tries来查找匹配项