Search code examples
c++boost-spiritboost-spirit-qi

Spirit semantic actions after eliminating Left Recursion


After following the sample in the spirit-classic FAQ to eliminate Left Recursion, I am unable to figure out the right placeholders for Phoenix semantic actions. My non-working grammar is shown below:

template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, expr(), Skipper>
{
    parser() : parser::base_type(expression)
    {
        using namespace qi;

        expression =
                term                        [_val = _1]
                >> *( (char_('+') >> term)  [_val = phx::construct<binop<op_add>>(_1, _2)]
                    | (char_('-') >> term)  [_val = phx::construct<binop<op_sub>>(_1, _2)]
                    ) ;
        term =
            factor                          [_val = _1]
                >> *( (char_('*') >> factor)    [_val = phx::construct<binop<op_mul>>(_1, _2)]
                    | (char_('/') >> factor)    [_val = phx::construct<binop<op_div>>(_1, _2)]
                   );

        factor =
                uint_                           [_val = _1]
            | var_                          [_val = _1]
            | ('(' >> expression >> ')')    [_val = _1]
            | (char_('-') > factor)         [_val = phx::construct<unop<op_uminus>>(_1)]
            | (char_('+') > factor)         [_val = _1]
            ;


        var_ = qi::lexeme[ +alpha ];

        BOOST_SPIRIT_DEBUG_NODE(expression);
        BOOST_SPIRIT_DEBUG_NODE(term);
        BOOST_SPIRIT_DEBUG_NODE(factor);
        BOOST_SPIRIT_DEBUG_NODE(var_);
    }
  private:
    qi::rule<It, var() , Skipper> var_;
    qi::rule<It, expr(), Skipper> expression, term, factor;
};

Any help on the proper way to handle attributes would be greatly appreciated.

Thanks.


Solution

  • I suppose you don't actually want to

    >> *( (char_('+') >> term)  [_val = phx::construct<binop<op_add>>(_1, _2)]
        | (char_('-') >> term)  [_val = phx::construct<binop<op_sub>>(_1, _2)]
    

    Pass '+' as the first constructor parameter, because the type binop<op_add> already reflects the kind of operator. So, you would likely want to have the left-hand operand as the first argument.

    This is the argument that you now parse here:

    term                        [_val = _1]
    

    This should probably clue you in: you just assigned it to... _val! So, there's your solution:

    >> *( (char_('+') >> term)  [_val = phx::construct<binop<op_add>>(_val, _2)]
        | (char_('-') >> term)  [_val = phx::construct<binop<op_sub>>(_val, _2)]
    

    However, since the exposed attribute from char_(...) isn't used, you can replace it:

    >> *( (lit('+') >> term)  [_val = phx::construct<binop<op_add>>(_val, _1)]
        | (lit('-') >> term)  [_val = phx::construct<binop<op_sub>>(_val, _1
    

    )]