Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in boost::spirit::qi, is it possible to dynamically modify rule definition in runtime

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"?

like image 255
Rui Zhou Avatar asked Mar 22 '23 18:03

Rui Zhou


2 Answers

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

like image 173
sehe Avatar answered Apr 27 '23 02:04

sehe


Yes this is possible by using the qi::symbols parser. One can change the used symbols at runtime, so you could alter the behaviour. To use this parser for complete rules there is a little trick, called the nabialek trick http://boost-spirit.com/home/articles/qi-example/nabialek-trick/.

Basically it shows how to hook complete rules in the symbols parser.

like image 26
Mike M Avatar answered Apr 27 '23 04:04

Mike M