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?
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
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
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:
#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;
}
}