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

spirit x3: locally defined rule definition must have an attribute attached?


Simple x3 code cannot compile because nothing is attached to the 2nd ruleTest or the whole parser. Even if we put x3::omit[ruleTest] around the second ruleTest it still cannot compile.

void Test(std::string const& str) {
    auto const ruleTest = x3::rule<struct _, std::string>{} =
        *(x3::char_ - ' ')[([](auto& ctx){x3::_val(ctx)+='x';})];
    x3::parse(boost::begin(str), boost::end(str), 
        ruleTest[([](auto& ctx){std::cout<<x3::_attr(ctx)<<std::endl;})] >>
        ' ' >>
        ruleTest
    );
} 

Only when we attach a lambda or an attribute to x3::parse or define ruleTest globally with BOOST_SPIRIT_DEFINE could solve the problem.

void Test(std::string const& str) {
    auto const ruleTest = x3::rule<struct _, std::string>{} =
        *(x3::char_ - ' ')[([](auto& ctx){x3::_val(ctx)+='x';})];
    std::string attr;
    x3::parse(boost::begin(str), boost::end(str), 
        ruleTest[([](auto& ctx){std::cout<<x3::_attr(ctx)<<std::endl;})] >>
        ' ' >>
        ruleTest, attr);
}

Solution

  • The crux of the error appears to be

    test.cpp|9 col 59| error: no match for ‘operator+=’ (operand types are ‘boost::spirit::x3::unused_type’ and ‘char’)
    

    This is because the compiler sees the actual type of the bound attribute (none) is x3::unused_type and therefore the semantic action doesn't compile.

    I'm not even sure how you would want it work since you cannot update characters to 'x' in an attribute that doesn't exist.

    Here's a proposed "fix":

    struct { 
        void operator()(std::string& s, char c) const { s += c; }
        void operator()(...)                    const { }
    } ll;
    
    auto const ruleTest 
        = x3::rule<struct _, std::string>{} 
        = *(x3::char_ - ' ') [([ll](auto& ctx){ ll(x3::_val(ctx), 'x');})]
        ;
    

    See it Live On Coliru

    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    
    void Test(std::string const& str) {
        namespace x3 = boost::spirit::x3;
    
        struct { 
            void operator()(std::string& s, char c) const { s += c; }
            void operator()(...)                    const { }
        } ll;
    
        auto const ruleTest 
            = x3::rule<struct _, std::string>{} 
            = *(x3::char_ - ' ') [([ll](auto& ctx){ ll(x3::_val(ctx), 'x');})]
            ;
    
        //std::string attr;
        auto f = begin(str), l = end(str);
        bool ok = x3::parse(f, l, 
                ruleTest [([](auto& ctx){std::cout<<x3::_attr(ctx)<<std::endl;})] 
                >> ' ' >> ruleTest);
    
        if (ok)     std::cout << "Parse success\n";
        else        std::cout << "Parse failed\n";
        if (f != l) std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
    
    int main() {
        Test("abc def");
    }