如何在Boost Spirit中设置最大递归深度

3
使用boost::spirit,如果我有一个递归规则来解析括号。
rule<std::string::iterator, std::string()> term;
term %= string("(") >> *term >> string(")");

如何限制最大递归深度?例如,如果尝试解析一百万个嵌套括号,则会因为超出堆栈大小而导致段错误。以下是一个完整的示例:

#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>

int main(void)
{
    using namespace boost::spirit;
    using namespace boost::spirit::qi;
    const size_t string_size = 1000000;
    std::string str;
    str.resize(string_size);
    for (size_t s=0; s<str.size()/2; ++s)
      {
        str[s]='(';
        str[str.size() - s -1] = ')';
      }

    rule<std::string::iterator, std::string()> term;
    term %= string("(") >> *term >> string(")");
    std::string h;
    parse(str.begin(), str.end(), term, h);
}

我使用以下命令进行编译:

```我使用以下命令进行编译:```

g++ simple.cxx -o simple -std=c++11

如果我将string_size设置为1000而不是1000000,它就能正常工作。

1个回答

2

在使用 qi::local<>phx::ref() 时,要记录深度。

在这种情况下,一个继承的属性可以很自然地扮演 qi::local 的角色:

qi::rule<std::string::const_iterator, std::string(size_t depth)> term;
qi::_r1_type _depth;
term %= 
    qi::eps(_depth < 32) >>
    qi::string("(") >> *term(_depth + 1) >> qi::string(")");

term在深度超过32时将会失败。

完整示例

在Coliru上实时运行

#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;

int main(void) {
    for (size_t n : { 2, 4, 8, 16, 32, 64 }) {
        auto const str = [&n] {
            std::string str;
            str.reserve(n);
            while (n--) { str.insert(str.begin(), '('); str.append(1, ')'); }
            return str;
        }();
        std::cout << "Input length " << str.length() << "\n";

        qi::rule<std::string::const_iterator, std::string(size_t depth)> term;
        qi::_r1_type _depth;
        term %= 
            qi::eps(_depth < 32) >>
            qi::string("(") >> *term(_depth + 1) >> qi::string(")");

        std::string h;

        auto f = str.begin(), l = str.end();
        bool ok = qi::parse(f, l, term(0u), h);
        if (ok)
            std::cout << "Ok: " << h << "\n";
        else
            std::cout << "Fail\n";

        if (f != l)
            std::cout << "Remaining  unparsed: '" << std::string(f, std::min(f + 40, l)) << "'...\n";
    }
}

输出:

Input length 4
Ok: (())
Input length 8
Ok: (((())))
Input length 16
Ok: (((((((())))))))
Input length 32
Ok: (((((((((((((((())))))))))))))))
Input length 64
Ok: (((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))
Input length 128
Fail
Remaining  unparsed: '(((((((((((((((((((((((((((((((((((((((('...

给你个参考,这个适用于boost版本大于1.55的情况。不幸的是,Debian稳定版只提供boost 1.55,所以我还是有些倒霉。 - Damascus Steel
在包含任何 boost 头文件之前尝试使用 #define BOOST_SPIRIT_USE_PHOENIX_V3 - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接