1

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)
sandwood
  • 2,038
  • 20
  • 38

2 Answers2

1

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
doqtor
  • 8,414
  • 2
  • 20
  • 36
1

So, the precedence issue has been pointed out. Note that you could have caught that by using the compiler's warnings:

test.cpp|25 col 23| warning: suggest parentheses around comparison in operand of ‘|’ []8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wparentheses-Wparentheses]8;;]

There are a number of other issues. Let me start with the problematic one:

Use Lexemes Where Required

Either don't use a skipper or wrap in qi::lexeme[] where the skiupper must not act. Specifically, your string_ rule should probably not be skipping whitespace.

See also Boost spirit skipper issues

Other issues

  • There's a slight inconsistency in the charset usage. This may or may not be a big deal. I usually go with the default (qiL::char_, lit, string) when it's not.

  • Why manually juggle the internals of debugging rules?

     BOOST_SPIRIT_DEBUG_NODES((orParse)(parseString)(parseInt))
    
  • Why omit attributes on directives that don't expose attributes?

  • Why use qi::string when you want to omit the attribute anyways? Use lit instead

  • Remember to handle error conditions and partial parses

Simplified and Fixed Code

Live On Coliru

#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>

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

using Expr = boost::variant<std::string, int >;
using It = std::string::const_iterator;
using Sk = charset::space_type;

int main()
{
    // lexeme!
    qi::rule<It, std::string()> string_ 
        = '"' > *~charset::char_('"') > '"';

    // with skipper
    Sk skip;
    qi::rule<It, Expr(),        Sk> orParse;
    qi::rule<It, std::string(), Sk> parseString;
    qi::rule<It, int(),         Sk> parseInt;

    parseString = qi::lit("string") > ':' > string_;
    parseInt    = qi::lit("int")    > ':' > qi::uint_;
    orParse     = '{' > (parseString | parseInt) > '}';
    
    BOOST_SPIRIT_DEBUG_NODES((orParse)(parseString)(parseInt))

    for (std::string const input: { "{int:  45}", "{string:  \"foo\"}" }) {
        Expr res;
        It f = input.begin(), l = input.end();

        if (phrase_parse(f, l, orParse, skip, res)) {
            std::cout << "Parsed: " << res << std::endl;
        } else {
            std::cout << "Failed" << std::endl;
        }
        if (f != l) {
            std::cout << "Remaining: " << std::string(f,l) << "\n";
        }
    }
}

Prints

Parsed: 45
Parsed: foo

And if debug enabled:

<orParse>
  <try>{int:  45}</try>
  <parseString>
    <try>int:  45}</try>
    <fail/>
  </parseString>
  <parseInt>
    <try>int:  45}</try>
    <success>}</success>
    <attributes>[45]</attributes>
  </parseInt>
  <success></success>
  <attributes>[45]</attributes>
</orParse>
<orParse>
  <try>{string:  "foo"}</try>
  <parseString>
    <try>string:  "foo"}</try>
    <success>}</success>
    <attributes>[[f, o, o]]</attributes>
  </parseString>
  <success></success>
  <attributes>[[f, o, o]]</attributes>
</orParse>
sehe
  • 374,641
  • 47
  • 450
  • 633