Search code examples
c++boostboost-spirit-qi

Use of optional parser in spirit qi


I'm trying to parse either an additive expression of the form "A+C", or "A" alone. After a few tests I realized that the problem is apparently my use of the optional parser, so to exemplify:

qi::rule<string::iterator, string()> Test;

Test =
(
    qi::string("A")[qi::_val= qi::_1]
    >> -(
            qi::string("B")[qi::_val += qi::_1]
            >> qi::string("C")[qi::_val += qi::_1]
        )
)
;

string s1, s2;
s1 = "AB";
bool a= qi::parse(s1.begin(), s1.end(), Test, s2);

The idea is to parse 'A' or "ABC", but if the s1 value is "AB" without 'C', the value of a is true. I believe that although I put parenthesis after the operator '-' and then I use the ">>" operator, the 'C' part is considered optional, and not the B>>C as a whole. Any ideas?


Solution

  • Container attributes are not backtracked.

    That's a performance choice. You need to explicitly control propagation using e.g. qi::hold:

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    
    namespace qi = boost::spirit::qi;
    
    int main() {
        using It = std::string::const_iterator;
        qi::rule<It, std::string()> Test;
    
        Test =
            (
             qi::char_('A')
             >> -qi::hold [
                    qi::char_('B')
                >> qi::char_('C')
             ]
            )
            ;
    
        for (std::string const input : { "A", "AB", "ABC" })
        {
            std::cout << "-------------------------\nTesting '" << input << "'\n";
            It f = input.begin(), l = input.end();
    
            std::string parsed;
            bool ok = qi::parse(f, l, Test, parsed);
            if (ok)
                std::cout << "Parsed success: " << parsed << "\n";
            else
                std::cout << "Parsed failed\n";
    
            if (f != l)
                std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
        }
    }
    

    Prints:

    -------------------------
    Testing 'A'
    Parsed success: A
    -------------------------
    Testing 'AB'
    Parsed success: A
    Remaining unparsed: 'B'
    -------------------------
    Testing 'ABC'
    Parsed success: ABC
    

    Note I have made a number of simplifications.

    See also: