views:

21

answers:

1

I just started to dig into Boost::Spirit, latest version by now -- V2.4. The essense of my problem is following:

I would like to parse strings like "1a2" or "3b4". So the rule I use is:

  (double_ >> lit('b') >> double_)
| (double_ >> lit('a') >> double_);

The attribute of the rule must be "vector <double>". And I'm reading it into the container.

The complete code:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <cstring>

int main(int argc, char * argv[])
{
    using namespace std;
    using namespace boost::spirit;
    using namespace boost::spirit::qi;
    using boost::phoenix::arg_names::arg1;

    char const * first = "1a2";
    char const * last  = first + std::strlen(first);
    vector<double> h;

    rule<char const *, vector<double>()> or_test;
    or_test %=    (double_ >> lit('b') >> double_) 
            | (double_ >> lit('a') >> double_);

    if (parse(first, last, or_test,h)) {
           cout << "parse success: "; 
           for_each(h.begin(), h.end(), (cout << arg1 << " "));
           cout << "end\n";
    } else cout << "parse error\n" << endl;
    return 0;
 }

I'm compiling it with g++ 4.4.3. And it returns "1 1 2". While I expect "1 2".

As far as I understand this happens because parser:

  • goes to the first alternative
  • reads a double_ and stores it in the container
  • then stops at "a", while expecting lit("b")
  • goes to the second alternative
  • reads two more doubles

My question is -- Is this a correct behavior, and if yes -- why?

+1  A: 

That's expected behavior. During backtracking Spirit does not 'unmake' changes to attributes. Therefore, you should use the hold[] directive explicitly forcing the parser to hold on to a copy of the attribute (allowing to roll back any attribute change):

or_test =    
        hold[double_ >> lit('b') >> double_)]
    |   (double_ >> lit('a') >> double_)
    ; 

This directive needs to be applied to all alternatives modifying the attribute, except the last one.

hkaiser
Thank you for your answer. Sorry for stupid question -- but I really carefully read the documentation and found nothing about such a thing. Can I ask a follow up -- is there a better way to do the same/similar thing?
Kostya
That's the best you can do, and I don't see a reason to do it differently. Since `hold[]` is explicit you have full control over the induced overhead, allowing it to be minimized.
hkaiser