Search code examples
c++boostboost-spiritboost-spirit-qiboost-spirit-x3

spirit x3 rule cannot synthesize attribute of type boost::iterator_range in a sequence parser


In a simple parser test Live On Coliru,

std::string str("x123x");
boost::iterator_range<boost::range_iterator<decltype(str)>::type> attr;
if( x3::parse( boost::begin(str), boost::end(str), x3::lit('x') >> x3::raw[+x3::digit] >> x3::lit('x'), attr ) ) {
    std::cout<<"Match! attr = "<<attr<<std::endl;
} else {
    std::cout<<"Not match!"<<std::endl;
}

The parser

x3::lit('x') >> x3::raw[+x3::digit] >> x3::lit('x')

is supposed to synthesize an attribute of type boost::iterator_range<Iterator>. But it cannot compile. If we remove either of the two x3::lit('x'), it compiles. The same code compiles with qi though Live on Coliru.


Solution

  • Interesting. Actually it does compile:

    Live On Coliru

    #include <iostream>
    #include <boost/spirit/home/x3.hpp>
    
    namespace x3 = boost::spirit::x3;
    
    int main() {
        std::string const str("x123x");
        boost::iterator_range<std::string::const_iterator> attr;
        if(x3::parse(boost::begin(str), boost::end(str), x3::raw[+x3::digit], attr)) {
            std::cout<<"Match! attr = "<<attr<<std::endl;
        } else {
            std::cout<<"Not match!"<<std::endl;
        }
    }
    

    The thing that makes it break is the surrounding context:

    // simple (ok):
    x3::parse(boost::begin(str), boost::end(str), x3::raw[+x3::digit], attr);
    // ok:
    parse(boost::begin(str), boost::end(str), x3::eps >> x3::raw[+x3::digit], attr);
    parse(boost::begin(str), boost::end(str), x3::raw[+x3::digit] >> x3::eps, attr);
    // breaks:
    parse(boost::begin(str), boost::end(str), x3::eps >> x3::raw[+x3::digit] >> x3::eps, attr);
    

    My guess is somehow the meta-programming erronously treats iterator_range as a Fusion sequence in such a case. Of course I think that's a bug.

    You should report this upstream.

    Sadly I haven't found a "homeopathic" workaround.