将规则分配给包括自身的序列时,Boost.Spirit.Qi崩溃 [英] Boost.Spirit.Qi crashes when assigning rule to a sequence including itself
问题描述
我有以下MWE:
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
namespace spirit = boost::spirit;
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
int main() {
std::string input("1 2");
qi::rule<std::string::iterator, void(), qi::space_type> parser;
qi::rule<std::string::iterator, void(), qi::space_type> parser2;
qi::rule<std::string::iterator, void(), qi::space_type> parser3;
parser = qi::int_[
std::cerr << phoenix::val("First int: ") << qi::_1 << std::endl
];
parser2 = qi::int_[
std::cerr << phoenix::val("Second int: ") << qi::_1 << std::endl
];
try {
// Comment out these two lines, (finished below ...)
parser3 = parser >> parser2;
phrase_parse(input.begin(), input.end(), parser3, qi::space);
// ... then un-comment these lines, and the program will crash (and no
// exception is caught below).
// parser = parser >> parser2;
// phrase_parse(input.begin(), input.end(), parser, qi::space);
}
catch (...) {
std::cerr << "Exception caught." << std::endl;
}
}
如注释行中所述,如果我将第三个qi :: rule分配给另外两个规则的序列,并使用该第三个规则进行解析,则我的程序将按预期运行.但是,如果我为序列中的第一条规则分配了相同的序列,然后使用该第一条规则进行分析,则程序在运行时将崩溃,由于catch (...) { . . . }
块不执行,显然甚至不会引发异常.>
所以我的问题是:是否有关于'qi::rule
'的规则,我应该知道禁止将包含规则的序列分配给该完全相同的规则,还是由于Boost.Spirit中的错误而导致崩溃. ?
意图
根据cv_and_he的评论,澄清一下,我这个小玩具示例背后的目标是弄清楚如何在运行时生成一些动态解析器.具体来说,如何从仅在运行时知道计数的规则序列中生成规则,例如parser = A1 >> A2 >> ... >> AN;
,而在编译时不知道N
,因此我不能只用一个硬编码一个规则固定数量的>>
这样.这类似于在运行时通过将元素追加到末尾(一次添加一个)来构建列表.
我不确定您要实现的目标,但是copy()似乎就是您要追求的目标
parser = parser.copy() >> parser2;
查看 在Coliru上直播
背景
问题是Qi引用了非终结符,因此您得到了PEG语法建议的解析器语义.
除此之外,Proto表达式树(表达式模板)确实通过引用接受了它们的一些参数.
这两个加在一起有可能真正破坏您的生活,尤其是在动态地使用构造解析器时.简而言之,我认为
之外- 使用继承的属性
- 和 qi :: symbols (包括Nabialek技巧)
即时构建规则. Proto x11/Spirit X3可能会对此做得更好.
在此处查看更多背景:
示例代码
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
namespace spirit = boost::spirit;
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
int main() {
std::string input("1 2");
qi::rule<std::string::iterator, void(), qi::space_type> parser;
qi::rule<std::string::iterator, void(), qi::space_type> parser2;
qi::rule<std::string::iterator, void(), qi::space_type> parser3;
parser = qi::int_[
std::cerr << phoenix::val("First int: ") << qi::_1 << std::endl
];
parser2 = qi::int_[
std::cerr << phoenix::val("Second int: ") << qi::_1 << std::endl
];
try {
// Comment out these two lines, (finished below ...)
parser3 = parser >> parser2;
phrase_parse(input.begin(), input.end(), parser3, qi::space);
parser = parser.copy() >> parser2;
phrase_parse(input.begin(), input.end(), parser, qi::space);
}
catch (...) {
std::cerr << "Exception caught." << std::endl;
}
}
I have the following MWE:
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
namespace spirit = boost::spirit;
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
int main() {
std::string input("1 2");
qi::rule<std::string::iterator, void(), qi::space_type> parser;
qi::rule<std::string::iterator, void(), qi::space_type> parser2;
qi::rule<std::string::iterator, void(), qi::space_type> parser3;
parser = qi::int_[
std::cerr << phoenix::val("First int: ") << qi::_1 << std::endl
];
parser2 = qi::int_[
std::cerr << phoenix::val("Second int: ") << qi::_1 << std::endl
];
try {
// Comment out these two lines, (finished below ...)
parser3 = parser >> parser2;
phrase_parse(input.begin(), input.end(), parser3, qi::space);
// ... then un-comment these lines, and the program will crash (and no
// exception is caught below).
// parser = parser >> parser2;
// phrase_parse(input.begin(), input.end(), parser, qi::space);
}
catch (...) {
std::cerr << "Exception caught." << std::endl;
}
}
As noted in the commented lines, if I assign a third qi::rule to a sequence of another two rules, and parse using that third rule, my program works as expected. However, if I assign the same sequence to the first rule in the sequence, then parse using that first rule, the program will crash when I run it, apparently without even throwing an exception since the catch (...) { . . . }
block does not execute.
So my question is: is there some rule about 'qi::rule
's I should know that forbids assigning a sequence that contains a rule to that very same rule, or is this crash due to a bug in Boost.Spirit.Qi?
Intent
To clarify, in light of cv_and_he's comment, my goal behind this little toy example is to figure out how to do some dynamic parser generation at runtime; specifically how to generate a rule from a sequence of rules whose count is only know at runtime, such as parser = A1 >> A2 >> ... >> AN;
, where N
is not known at compile-time, so I can't just hard-code one rule with a fixed number of '>>
' that way. This would be something akin to building a list at run time by appending elements to the end, one at a time.
I'm not sure what you were trying to achieve, but copy() would seem to be what you're after
parser = parser.copy() >> parser2;
See it Live on Coliru
Background
The problem is Qi takes non-terminals by reference, so you get the parser semantics a PEG grammar would suggest.
Besides that, Proto expression trees (expression templates) do take some of their arguments by reference.
These two combined have a potential to really mess up your life, especially when construction parsers dynamically. In short, I'd argue that, outside
- using inherited attributes
- and qi::symbols (including the Nabialek trick)
constructing rules on the fly is not well supported in Spirit V2. Proto x11 / Spirit X3 may change this for the better.
See more background here:
- C++ Boost qi recursive rule construction
- Generating Spirit parser expressions from a variadic list of alternative parser expressions
- Can Boost Spirit Rules be parameterized
Sample code
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
namespace spirit = boost::spirit;
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
int main() {
std::string input("1 2");
qi::rule<std::string::iterator, void(), qi::space_type> parser;
qi::rule<std::string::iterator, void(), qi::space_type> parser2;
qi::rule<std::string::iterator, void(), qi::space_type> parser3;
parser = qi::int_[
std::cerr << phoenix::val("First int: ") << qi::_1 << std::endl
];
parser2 = qi::int_[
std::cerr << phoenix::val("Second int: ") << qi::_1 << std::endl
];
try {
// Comment out these two lines, (finished below ...)
parser3 = parser >> parser2;
phrase_parse(input.begin(), input.end(), parser3, qi::space);
parser = parser.copy() >> parser2;
phrase_parse(input.begin(), input.end(), parser, qi::space);
}
catch (...) {
std::cerr << "Exception caught." << std::endl;
}
}
这篇关于将规则分配给包括自身的序列时,Boost.Spirit.Qi崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!