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

Vic*_*aré 5 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)
};
Run Code Online (Sandbox Code Playgroud)

因此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);
             ^~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

那么如何向惰性解析器提供参数呢?如果确实不可能,那么有人知道为什么吗?

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

Nik*_*zev 2

在不了解凤凰和灵魂实际上如何沟通的情况下尝试进行这种巫术是极其困难的。让我们尝试深入研究一下:

  1. 规则参数化是通过创建解析器实例operator()来进行的。qi::ruleqi::parameterized_nonterminal
  2. 惰性解析器评估以这种方式执行:qi::lazy包装phoenix::actorproto::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)).


很久以前,我尝试过像你一样的事情,但也失败了。我还用谷歌搜索了那些说这是不可能的主题,并在那时停止了。但你的问题引起了我的兴趣,经过短暂的尝试和错误(即挠头并挖掘精神来源),我得出了这个概念证明:

#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";
}
Run Code Online (Sandbox Code Playgroud)

https://wandbox.org/permlink/m40DpeMikKRYyvH0