Search code examples
boostboost-spiritboost-spirit-x3

Expecting sequences and alternations of char_ parsers to synthesize a string


In the following test case, the alternation of one alpha and a sequence bombs with a long error dump basically saying static assertion failed: The parser expects tuple-like attribute type. Intuitively, I expected the entire rule to produce a string but that's not what happens. I either have to change the left-side of the alternation to +alpha (making both sides vectors) or go the path of semantic actions, at least for the lone char in the alternation (append to _val). Or, change the lone left-side char_ to string. Anyways, I can't figure out what's the proper simple way of parsing a string as trivial as this, any hint is appreciated. TIA.

#include <iostream>

#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;

namespace grammar {

using x3::char_;
using x3::alpha;
using x3::xdigit;

const auto x =
    x3::rule< struct x_class, std::string > { "x" } =
    char_('/') > alpha >> *(alpha | (char_('#') > xdigit));

} // namespace grammar

int main () {
    std::string input{ "/Foobar#F" }, attr;
    auto iter = input.begin ();

    if (phrase_parse (iter, input.end (), grammar::x, x3::space, attr)) {
        std::cout << attr << std::endl;
    }

    return 0;
}

Solution

  • I hate this behaviour too. Qi was much more natural in this respect.

    I honestly don't always know how to "fix" it although

    • in this case it seems you can use raw[] - simplifying the grammar as well
    • sometimes it helps to avoid mixing operator> and operator>>

    Here's what I'd do for your grammar:

    Live On Coliru

    #include <iostream>
    #include <boost/spirit/home/x3.hpp>
    namespace x3 = boost::spirit::x3;
    
    namespace grammar {
        const auto x =
            x3::rule<struct x_class, std::string> { "x" } =
            x3::raw [ '/' > x3::alpha >> *(x3::alpha | ('#' > x3::xdigit)) ];
    }
    
    int main () {
        std::string input{ "/Foobar#F" }, attr;
        auto iter = input.begin ();
    
        if (phrase_parse (iter, input.end (), grammar::x, x3::space, attr)) {
            std::cout << attr << std::endl;
        }
    }
    

    Prints

    /Foobar#F