Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
287 views
in Technique[技术] by (71.8m points)

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

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:


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

} 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...