2

I'm using spirit parser for a quite long time but now I have an issue I don't really understand. I want to parse something like a,b->c,d or a,b->d into a struct. The following code does this right if the input is a,b->c,d (the left part of the rule). But if input is a,b->d (the alternativ part), then the produces aa,bb,,d. So it seems that the alternative parser does not clear the already parsed parts.

struct Test
{
  std::string a;
  std::string b;
  std::string c;
  std::string d;
};

BOOST_FUSION_ADAPT_STRUCT(Test,
(std::string, a)
(std::string, b)
(std::string, c)
(std::string, d))
using namespace boost::spirit::qi;
using std::string;
using std::pair;
rule<const char *, Test()> r = (+alnum >> ',' >> +alnum >> "->" >> +alnum >> ',' >> +alnum) | (+alnum >> ',' >> +alnum >> "->" >> attr(string()) >> +alnum);
Test result;
//const char* s = "a,b->c,d"; //produces a Result with a,b,c,d
const char* s = "a,b->d"; // procudes a Result with aa,bb,,d
parse(s, s + strlen(s), r, result);
orhtej2
  • 2,133
  • 3
  • 16
  • 26
SRoeber
  • 61
  • 1
  • No rollback on backtracking is a frequent question, try to use a search engine next time, there is an issue on bugtracker with synopsis, and workarounds https://github.com/boostorg/spirit/issues/378 – Nikita Kniazev Dec 10 '19 at 11:09

1 Answers1

0

Made it into a self-contained repro:

Live On Wandbox

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <iomanip>
#include <iostream>

namespace qi = boost::spirit::qi;

struct Test { std::string a, b, c, d; };
BOOST_FUSION_ADAPT_STRUCT(Test, a,b,c,d)

int main() {
    qi::rule<const char *, Test()> static const r 
        = (+qi::alnum >> ',' >> +qi::alnum >> "->" >> +qi::alnum >> ',' >> +qi::alnum)
        | (+qi::alnum >> ',' >> +qi::alnum >> "->" >> qi::attr(std::string()) >> +qi::alnum);

    for (auto input : {
        "a,b->c,d",
        "a,b->d"
    }) {
        Test result;
        qi::parse(input, input + strlen(input), r, result);
        for (auto const& part: { result.a, result.b, result.c, result.d })
            std::cout << " " << std::quoted(part);
        std::cout << "\n";
    }
}

Printing

 "a" "b" "c" "d"
 "aa" "bb" "" "d"

So, your problem is similar to:

The common solution is qi::hold[] which has some overhead, but works generally:

qi::rule<const char *, Test()> static const r 
    = qi::hold [+qi::alnum >> ',' >> +qi::alnum >> "->" >> +qi::alnum >> ',' >> +qi::alnum]
    | (+qi::alnum >> ',' >> +qi::alnum >> "->" >> qi::attr(std::string()) >> +qi::alnum);

Which prints Live On Wandbox

 "a" "b" "c" "d"
 "a" "b" "" "d"
sehe
  • 374,641
  • 47
  • 450
  • 633