C++ boost::带参数的精神惰性解析器?
我在这方面找不到任何东西,除了不祥的暗示,这可能是完全不可能的,但我不想简单地相信这一点,因为在这种情况下,懒惰的解析器似乎毫无用处。我想做的是在解析时根据前面的一些非终结符的结果选择一个解析器。基本上可以归结为:C++ boost::带参数的精神惰性解析器?,c++,parsing,boost-spirit,C++,Parsing,Boost Spirit,我在这方面找不到任何东西,除了不祥的暗示,这可能是完全不可能的,但我不想简单地相信这一点,因为在这种情况下,懒惰的解析器似乎毫无用处。我想做的是在解析时根据前面的一些非终结符的结果选择一个解析器。基本上可以归结为: static rule<Constant *(Scope &)> &get_constant_parser(Typename type); rule<Constant *(Scope &, Typename)> constant {
static rule<Constant *(Scope &)> &get_constant_parser(Typename type);
rule<Constant *(Scope &, Typename)> constant {
lazy(phoenix::bind(&get_constant_parser, _r2))(_r1)
};
那么,我如何为一个懒惰的解析器提供参数呢?如果确实不可能,那么有人知道为什么吗
对不起,这不是一个适当的MWE,现在我希望有人做过它之前,只是知道答案。如果您想积极调查并需要MWE,请告知我;-) 所以很抱歉,我认为这确实是不可能的[*] 然而,一切都没有失去。如果您可以使用
qi::locals
而不是传递“参数”(继承的属性),那么您就可以了
- 例如,参见此处使用qi::locals的地方
非惰性(符号*)
来避开对“惰性(arg)
”的需求。这一想法是由我的直觉引起的,即您正在尝试进行名称空间/域相关的查找。见例
- -这有很多不相关的复杂性,但很好地展示了如何匹配不同域上的函数名和变量
[*]
- 规则参数化是通过
的qi::Rule
实现的,它创建了operator()
解析器的实例qi::parameterized\u nonterminal
- 惰性解析器的计算是这样执行的:
将qi::Lazy
包装成phoenix::actor
,然后(通过元编译器)将其转换成proto::terminal
/qi::Lazy\u解析器
qi::Lazy\u指令
所以,在您的示例中,Phoenix actor被转换为一个Proto终端,然后call操作符创建了一个Spirit元编译器无法理解的Proto表达式
我猜它应该是惰性的(phoenix::bind(&get_constant_parser,_r2)(_r1)),因为您需要在实际规则中调用
- 试图在不了解凤凰和精灵是如何交流的情况下进行这种巫术是极其困难的。让我们试着深入探讨一下:
操作符()
,但phoenix不允许您这样调用操作符()
应该工作的是:lazy(phoenix::bind(phoenix::bind(&get\u constant\u parser,\u r2),\u 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";
}
#包括
#包括
#包括
#包括
名称空间qi=boost::spirit::qi;
名称空间phx=boost::phoenix;
int main()
{
使用通过的规则\u t=qi::rule;
qi::规则懒散调用
=qi::lazy(phx::bind)(qi::labels::r2,//绑定是一种延迟调用`operator()`的方法
qi::labels::_r1));//与之等价的非惰性变量是`_r2(_r1)`
INTV;
char const*s=nullptr;
已通过规则inout=qi::attr(qi::labels::_r1);
if(qi::parse(s,s,lazyinvoke(phx::val(123),phx::cref(inout)),v))
很有趣,我有点认为沿着这些思路一定是可能的,但我无法理解所需的语法脑筋。不幸的是,它对我来说仍然崩溃,因为在某个地方(我认为在内部绑定中
)我的作用域&
参数被转换为常量作用域
,这失败了,因为这些东西是不可复制的。我接受这一点,因为这是一个有效的概念证明,非常准确地回答了我的问题。不过,它附带了一些警告,一个重要的警告是,如果你在代码中真的做了类似的事情,每一个代码审阅者将与WTF一样。同样重要的是,一旦你开始考虑这些恶作剧,也许是时候重新考虑你的设计了。关于这一点,请参阅@sehe的答案。太棒了!我能够通过将范围&
转换为范围*
来实现内部绑定,从而使其可复制。这是一个黑客,bu这是一种可以在许多地方将复杂性从O(c^5n)降低到O(c^n)的黑客技术。很有可能这是一个凤凰虫(它还有其他的const
ness问题)。如果你有时间做一个MCVE,我建议你打开一个bug报告。而且,我很高兴旧的误解已经被揭穿。好吧,qi::locals
肯定是不够的,因为信息需要传递。我的关键字本质上是一个类型名,想想例如bool
和number
,并解析任意的e当然,这些类型的Xpression我必须使用完全不同的非终结符。我还没有真正研究过它,也许符号表可以为我做到这一点。一个问题可能是符号(即类型名称)可能没有被逐字解析,但从以前解析的对象的属性读取。例如,想象一下将布尔赋值给布尔变量引用。
#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";
}