Search code examples
c++boostboost-spirit

Boost Spirit : can't manage to have the alternative operator working as I expect


I can not manage to have the spirit alternative parser working (or let'say do what I expect).

Here is my MCVE

#include <string>
#include <iostream>

#include <boost/variant.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/spirit/include/qi.hpp>

using namespace boost::spirit;
namespace charset = boost::spirit::qi::standard_wide;

using Expr = boost::variant<std::string, int >;

int main(int argc, char ** argv)
{
    std::string intToParse = "{int:  45}";
    std::string stringToParse = "{string:  \"foo\"}";

    using It = std::string::const_iterator;
    using Sk = qi::space_type;
    qi::rule<It, std::string(), Sk> parseString;
    qi::rule<It, int(), Sk> parseInt;
    qi::rule<It, std::string(),Sk> string_;
    
    qi::rule<It,Expr(),Sk > orParse;


    string_ =
            qi::lit('"')
            > *(charset::char_ - '"')
            > '"';
    
    parseString = qi::omit[  qi::string("string")] >  qi::lit(':') > string_;

    parseInt = qi::omit[ qi::string("int")] > qi::lit(':') > qi::uint_ ;

    orParse =  qi::omit[qi::lit('{')] >  parseString | parseInt >  '}';
    
    orParse.name_ =  "alternative parser";
    parseString.name_ = "string parser";
    parseInt.name_= "int parser";

    qi::debug(orParse);
    qi::debug(parseString);
    qi::debug(parseInt);

    Expr res2;
    qi::phrase_parse(stringToParse.cbegin(), stringToParse.cend(), orParse, qi::space, res2);
    std::cout << res2<< std::endl;


    Expr res;
    qi::phrase_parse(intToParse.cbegin(), intToParse.cend(), orParse, qi::space, res);
    std::cout << res << std::endl;
  
    return 0;
}

When the first alternative fails, parsing stop, second rule is not attempted ! What did I get wrong ?

Here is the output showing my issue , for the stringToParse , first alternative fails but then I expect int parser to be exercised which is not the case !

<alternative parser>
  <try>{string:  "foo"}</try>
  <string parser>
    <try>string:  "foo"}</try>
    <success>}</success>
    <attributes>[[f, o, o]]</attributes>
  </string parser>
  <success>}</success>
  <attributes>[[f, o, o]]</attributes>
</alternative parser>
foo
<alternative parser>
  <try>{int:  45}</try>
  <string parser>
    <try>int:  45}</try>
    <fail/>
  </string parser>
  <fail/>
</alternative parser>
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::spirit::qi::expectation_failure<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > >'
  what():  boost::spirit::qi::expectation_failure
Aborted (core dumped)

Solution

  • operator > is higher in C++ Operator Precedence table than operator | hence parenthesis are needed:

    orParse =  qi::omit[qi::lit('{')] >  (parseString | parseInt) >  '}';
    //                              here ^             and here ^
    

    Result:

    <alternative parser>
      <try>{string:  "foo"}</try>
      <string parser>
        <try>string:  "foo"}</try>
        <success>}</success>
        <attributes>[[f, o, o]]</attributes>
      </string parser>
      <success></success>
      <attributes>[[f, o, o]]</attributes>
    </alternative parser>
    foo
    <alternative parser>
      <try>{int:  45}</try>
      <string parser>
        <try>int:  45}</try>
        <fail/>
      </string parser>
      <int parser>
        <try>int:  45}</try>
        <success>}</success>
        <attributes>[45]</attributes>
      </int parser>
      <success></success>
      <attributes>[45]</attributes>
    </alternative parser>
    45