在boost :: spirit :: qi中,可以在运行时动态修改规则定义 [英] in boost::spirit::qi, is it possible to dynamically modify rule definition in runtime
问题描述
我写了一些语法与boost :: spirit :: qi :: rule来解析互联网包。语法如下:
qi :: rule< Iterator>开始,请求,响应,状态,查询;
start =(request | response | status | query)>> lit(\r\\\
);
以提高性能,用户可能想在运行时跳过一些规则。忽略响应,状态,查询并且只尝试匹配请求,因此规则将更改为:
start =(request)>> lit(\r\\\
);
是否可以这样做?例如,是否有像disable()这样的函数只是禁用规则response,status和query?
此外,如果
性能非常重要,您甚至无法再进行3或4个额外的字符比较- (通常,Spirit的自动属性处理并不总是最佳)或
- 考虑静态优化扫描(因此Boost Xpressive或Spirit Lex)
- 可能是做错了事(如果你有一个次优的语法,你可以有很多回溯杀死性能 - 很像退化正则表达式( https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-ReReoS ))
也就是说,这里有一些选项:
1。使用可变规则通过 qi :: lazy
#include< boost / spirit / include / qi.hpp>
命名空间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,
//
允许;
static qi :: rule< It> const start = qi :: lazy(phx :: ref(allowed))>> qi :: lit(\r\\\
);
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;打破;
case 1:allowed = request |响应|查询打破;
case 2:allowed = request |响应|状态|查询打破;
}
std :: cout<< status:< test(status \r\\\
)< \t
<< response:< test(response\r\\\
)<< \t
<< request:< test(request\r\\\
)< \\\
;
}
}
像Mike提到的,这也用于Nabialek
这样打印,例如:
status:0 response:1 request:1
status:0 response:1 request:1
status:0 response :0请求:1
状态:0响应:1请求:1
状态:1响应:1请求:1
状态:0响应:1请求:1
状态: 0响应:1请求:1
状态:0响应:0请求:1
状态:0响应:0请求:1
状态:0响应:1请求:1
2。使用继承的属性与 qi :: lazy
与上面很相似,你可以传递'subrules'属性。我不知道我会推荐这个,因为我在过去的例子中看到未定义的行为,例如
- 分解Spirit规则的常见部分(阅读评论! )
- 还 C ++ Boost qi递归规则构造如何
qi :: lazy
本身不支持继承属性
3。只需使用单独的规则
这是最自然的,在我看来:
std :: function< bool(string)>测试;
switch(rand()%3)
{
case 0:test = [&](std :: string const& input){return qi :: parse ,end(input),request); };打破;
case 1:test = [&](std :: string const& input){return qi :: parse(begin(input),end(input),request | response | };打破;
case 2:test = [&](std :: string const& input){return qi :: parse(begin(input),end(input),request | response | status | query); };打破;
}
查看完整示例: http://coliru.stacked-crooked.com/a/603f093add6b9799
I wrote some grammar with boost::spirit::qi::rule to parse the internet packet. the grammar is something like:
qi::rule<Iterator> start, request, response, status, query ;
start = (request | response | status | query) >> lit("\r\n");
to improve the performance, user maybe want to skip some rules in the runtime, e.g. ignore "response","status","query" and only try to match request, so the rule will change to:
start = (request ) >> lit("\r\n");
is it possible to do that? e.g, is there a function like "disable()" to just disable the rule "response", "status" and "query"?
The most natural approach would be to just use a different parser for your more constrained occasions.
Also, if performance is so important that you can't even spare 3 or 4 extra character comparisons
- you should probably do a handwritten parser (in general, Spirit's automatic attribute handling isn't always optimal) or
- consider statically optimized scanning (so Boost Xpressive or Spirit Lex)
- you might perhaps be doing something wrong (if you have a suboptimal grammar you can have much backtracking kill the performance - much like degenerate Regular Expressions (https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS))
That said, here are some options:
1. Use a variable rule through 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";
}
}
Like Mike mentions, this is also used in the Nabialek trick, although qi::lazy
is the essential ingredient here.
This prints, e.g.:
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. Use inherited attributes with qi::lazy
Pretty similar to the above, you could pass 'subrules' as inherited attributes. I'm not sure I'd recommend this, as I've seen Undefined Behaviour crop up in past examples, see e.g.
- Factoring out common parts of Spirit rules (read the comments!)
- also C++ Boost qi recursive rule construction on how
qi::lazy
itself doesn't support inherited attributes
3. Just use separate rules
This is most natural, in my opinion:
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;
}
See full sample: http://coliru.stacked-crooked.com/a/603f093add6b9799
这篇关于在boost :: spirit :: qi中,可以在运行时动态修改规则定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!