【问题标题】:Boost.Spirit.Qi: dynamically create "difference" parser at parse timeBoost.Spirit.Qi:在解析时动态创建“差异”解析器
【发布时间】:2013-06-08 15:13:35
【问题描述】:

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

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&lt;Left, Right&gt;——但我不知道如何使用它。

【问题讨论】:

  • 在元编程中,我更喜欢写decltype(qi::char_ - (expr1 | expr2 | expr3))。无需知道实际类型。然而,我想出了一个优雅的解决方案,我认为这与此无关。 :)
  • 你是对的,这是元编程中的聪明方法。我什至在这种情况下尝试了decltype(qi::char_ - std::declval&lt;qi::lit&gt;() - std::declval&lt;qi::lit&gt;())...但它无法创建动态解析器​​,因为第一个减法和后者分别生成了不同的类型。

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


【解决方案1】:

在我看来,您并不是在寻找动态的“差异”表达式,而是动态的“可变参数替代 (a|b|c...)”表达式:

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

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

expr - orCombine(alternatives)

!orCombine(alternatives) >> expr

现在,完成这项工作有许多不完善之处,我将首先解释这一点。幸运的是,有一种更简单的方法,使用 qi::symbols,我将在此之后立即演示。

棘手的事情

如果您愿意,您可以根据需要“生成”替代解析器表达式,并使用相当多的技巧。我在这个答案中展示了如何做到这一点:

但是

  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 Trick”。它使用 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(目前被称为“Spirit X3”的实验版)中消除

2 它使用Tries 来查找匹配项

【讨论】:

  • 非常感谢。你的答案是……太棒了。你拯救了我的一天。
  • “神奇”的方式在语义上看起来是正确的,但我能感觉到它有很多的东西。 qi::symbols 看起来更聪明。但我不知道qi::symbolsbool 作为结果类型具有类似于...eps(bool) 的效果。我猜它单独使用时会变成布尔解析器,但从 char_ 集中减去时会变成否定解析器。
  • @saki7 符号查找的“值类型”无关紧要:只有在 trie 中找到条目这一事实才重要(我将代码更改为使用 qi::unused_type 而不是 bool)。
  • 哦...现在我明白了。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2015-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多