boost :: spirit带有参数的惰性解析器?

时间:2019-05-20 12:26:08

标签: c++ parsing boost-spirit

除了不祥的暗示它可能是完全不可能的,我找不到任何东西,但是我不想简单地相信它,因为在这种情况下,懒惰的解析器似乎没用。我想做的是根据先前某些非终端的结果在解析时选择一个解析器。本质上可以归结为:

static rule<Constant *(Scope &)> &get_constant_parser(Typename type);

rule<Constant *(Scope &, Typename)> constant {
    lazy(phoenix::bind(&get_constant_parser, _r2))(_r1)
};

因此get_constant_parser返回一个适合给定类型名称的解析器,但是该解析器需要一个Scope &类型的参数。因此,直观地讲,我将其记录下来,并将参数添加到惰性解析器中。但是,这给了我一个无效的表达式:

/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:177:13: error: static assertion failed: error_invalid_expression
             BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
             ^~~~~~~~~~~~~~~~~~~~~~~~~

那么我该如何为懒惰的解析器提供参数呢?如果确实不可能,那么有谁知道为什么?

对不起,这不是适当的MWE,现在我希望有人以前做过,并且知道答案。如果您要积极调查并需要MWE,请告诉我;-)

2 个答案:

答案 0 :(得分:2)

在不了解Phoenix和Spirit实际交流方式的情况下尝试进行此类魔术非常困难。让我们尝试深入研究:

  1. 通过创建operator()解析器实例的qi::rule中的qi::parameterized_nonterminal进行规则参数化。
  2. 通过以下方式执行惰性分析器评估:qi::lazyphoenix::actor包装到proto::terminal中,随后将其(由元编译器转换成qi::lazy_parser / qi::lazy_directive

因此,在您的示例中,Phoenix actor被转换为Proto终端,然后调用运算符创建了Spirit元编译器无法理解的Proto表达式。

我的猜测是应该为lazy(phoenix::bind(&get_constant_parser, _r2)(_r1)),因为您需要在实际规则上调用该operator(),但是Phoenix不允许您这样调用operator()

应该起作用的是:lazy(phoenix::bind(phoenix::bind(&get_constant_parser, _r2), _r1))


很久以前,我尝试过类似您正在执行的操作,但也失败了。我还用谷歌搜索那些说不可能的话题,然后就停止了。但是您的问题引起了我的兴趣,经过短暂的尝试和错误(例如,挠头并挖掘Spirit的来源),我得到了这一概念证明:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_argument.hpp>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

int main()
{
    using passed_rule_t = qi::rule<char const*, int(int)>;
    qi::rule<char const*, int(int, passed_rule_t const&)> lazyinvoke
        = qi::lazy(phx::bind(qi::labels::_r2,   // binding is a way to call `operator()` lazily
                             qi::labels::_r1)); // non-lazy equivalent of this is `_r2(_r1)`
    int v;
    char const* s = nullptr;
    passed_rule_t inout = qi::attr(qi::labels::_r1);
    if (qi::parse(s, s, lazyinvoke(phx::val(123), phx::cref(inout)), v))
        std::cout << "OK: " << v << "\n";
    else
        std::cout << "Failed\n";
}

https://wandbox.org/permlink/m40DpeMikKRYyvH0

答案 1 :(得分:1)

所以我很遗憾地说我认为这确实是不可能的[*]

但是,一切并没有丢失。如果您可以使用qi::locals而不是传递“参数”(继承的属性),则应该没事。

根据您的实际目标,尽管您可以通过调用lazy(arg)来减少对“ non_lazy(symbols*)”的需求。这种想法是由我的直觉提示的,您正在尝试进行与名称空间/域相关的查找。参见例如


[*]