Boost.SSpirit.Qi在将规则分配给包括自身在内的序列时崩溃

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

本文关键字:崩溃 Qi SSpirit 规则 Boost 分配 包括自      更新时间:2023-10-16

我有以下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::规则分配给另外两个规则的序列,并使用第三个规则进行解析,我的程序将按预期工作。然而,如果我将相同的序列分配给序列中的第一条规则,然后使用第一条规则进行解析,那么当我运行程序时,程序将崩溃,显然甚至没有抛出异常,因为catch (...) { . . . }块没有执行。

因此,我的问题是:关于"qi::rule"的我应该知道的规则是否禁止将包含规则的序列分配给同一规则,或者这次崩溃是由于Boost.SSpirit.Qi中的错误造成的?

意向

为了澄清,根据cv_and_he的评论,我在这个小玩具示例背后的目标是弄清楚如何在运行时生成一些动态解析器;特别是如何从一系列规则中生成规则,这些规则的计数只在运行时知道,例如parser = A1 >> A2 >> ... >> AN;,其中N在编译时是未知的,所以我不能用这种方式硬编码一个固定数量为">>"的规则。这类似于在运行时通过一次一个地在末尾添加元素来构建列表。

我不确定你想要实现什么,但copy()似乎是你追求的目标

    parser = parser.copy() >> parser2;

查看Coliru直播


背景

问题是Qi通过引用获取非终端,因此您可以获得PEG语法所建议的语法分析器语义。

除此之外,Proto表达式树(表达式模板)确实引用了它们的一些参数。

这两者结合在一起可能会真正扰乱你的生活,尤其是当构建解析器动态时。简而言之,我认为,在之外

  • 使用继承的属性
  • qi::符号(包括Nabialek技巧)

在Spirit V2中,动态构建规则并没有得到很好的支持。Proto x11/Spirit X3可能会让这一点变得更好。

点击此处查看更多背景:

  • C++Boost qi递归规则构造
  • 从替代语法分析器表达式的可变列表生成Spirit语法分析器表达式
  • Boost Spirit规则可以参数化吗

示例代码

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