在boost :: spirit :: qi中,是否可以在运行时动态修改规则定义

时间:2013-11-07 14:48:38

标签: c++ boost boost-spirit boost-spirit-qi

我用boost :: spirit :: qi :: rule写了一些语法来解析互联网数据包。语法是这样的:

qi::rule<Iterator> start, request, response, status, query ;
start = (request | response | status | query) >> lit("\r\n");

为了提高性能,用户可能希望跳过运行时中的一些规则,例如忽略“响应”,“状态”,“查询”并且只尝试匹配请求,因此规则将更改为:

start = (request ) >> lit("\r\n"); 

可以这样做吗?例如,是否有像“禁用()”这样的功能来禁用规则“响应”,“状态”和“查询”?

2 个答案:

答案 0 :(得分:6)

最自然的方法是在更多受约束的场合使用不同的解析器。

此外,如果表现如此重要,您甚至无法进行3或4次额外角色比较

  • 你应该做一个手写的解析器(一般来说,Spirit的自动属性处理并不总是最优的)或
  • 考虑静态优化扫描(如Boost Xpressive或Spirit Lex)
  • 你可能做错了(如果你有一个次优的语法,你可以有很多回溯杀死性能 - 就像退化的正则表达式(https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS))

那就是说,这里有一些选择:

1。通过qi::lazy

使用变量规则
#include <boost/spirit/include/qi.hpp>

namespace qi    = boost::spirit::qi;
namespace phx   = boost::phoenix;

int main()
{
    typedef std::string::const_iterator It;
    qi::rule<It> 
        request  = "request",
        response = "response",
        status   = "status",
        query    = "query",
        // 
        allowed;

    static qi::rule<It> const start = qi::lazy(phx::ref(allowed)) >> qi::lit("\r\n");
    static const auto test = [](std::string const& input) { return qi::parse(begin(input), end(input), start); };

    for (int i=0; i<10; ++i)
    {
        switch(rand()%3)
        {
            case 0: allowed = request; break;
            case 1: allowed = request | response | query; break;
            case 2: allowed = request | response | status | query; break;
        }

        std::cout << "status: "    << test("status\r\n")   << "\t"
                  << "response: "  << test("response\r\n") << "\t"
                  << "request:  "  << test("request\r\n")  << "\n";
    }
}

就像迈克提到的那样,这也用在Nabialek技巧中,虽然qi::lazy是这里的基本要素。

打印,例如:

status: 0   response: 1 request:  1
status: 0   response: 1 request:  1
status: 0   response: 0 request:  1
status: 0   response: 1 request:  1
status: 1   response: 1 request:  1
status: 0   response: 1 request:  1
status: 0   response: 1 request:  1
status: 0   response: 0 request:  1
status: 0   response: 0 request:  1
status: 0   response: 1 request:  1

2。将继承的属性与qi::lazy

一起使用

与上面的内容非常类似,您可以将'subrules'作为继承属性传递。我不确定我会推荐这个,因为我在过去的例子中看到了未定义的行为,见例如。

3。只需使用单独的规则

在我看来,这是最自然的:

std::function<bool(string)> test;
switch(rand()%3)
{
    case 0: test = [&](std::string const& input) { return qi::parse(begin(input), end(input), request); }; break;
    case 1: test = [&](std::string const& input) { return qi::parse(begin(input), end(input), request | response | query); }; break;
    case 2: test = [&](std::string const& input) { return qi::parse(begin(input), end(input), request | response | status | query); }; break;
}

查看完整示例:http://coliru.stacked-crooked.com/a/603f093add6b9799

答案 1 :(得分:1)

是的,可以使用qi::symbols解析器。可以在运行时更改使用的符号,因此您可以更改行为。要将此解析器用于完整规则,有一个小技巧,称为nabialek技巧http://boost-spirit.com/home/articles/qi-example/nabialek-trick/

基本上它显示了如何在符号解析器中挂钩完整的规则。