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

boost::spirit lazy parser with arguments?

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

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 returns 适合给定类型名称的解析器,但是 该解析器需要类型 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,请告诉我 ;-)

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

然而,并非一无所有。如果您可以使用 qi::locals 而不是传递 "arguments"( 继承属性 ),那么您应该没问题。

  • 参见例如Parse quoted strings with boost::spirit 这里使用了 qi::locals

根据您的实际 目标,您可以通过调用 non_lazy(symbols*) 来避开对“lazy(arg)”的需求。这个想法是由我的直觉提示的,您正在尝试进行 namespace/domain 依赖查找。参见例如

  • - 这有很多不相关的复杂性,但很好地展示了如何匹配函数名称与不同域中的变量。

[*]

在不了解凤凰和精灵的实际交流方式的情况下尝试施展这种魔法是极其困难的。让我们尝试深入研究它:

  1. 规则参数化通过 qi::ruleoperator() 发生,它创建了 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))


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

#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