Boost.Spirit.Qi crashes when assigning rule to a sequence including itself

橙三吉。 提交于 2019-12-18 08:55:31

问题


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.


回答1:


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;
    }

} 


来源:https://stackoverflow.com/questions/20158897/boost-spirit-qi-crashes-when-assigning-rule-to-a-sequence-including-itself

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!