Search code examples
c++boost-spirit

Boost Spirit how to pass a local reference as attribute


How can I pass _a as reference to a sub rule which has an attribute of the same type:

rule(_a)

does not work.

The code looks like:

qi::rule<Iterator, Mdlx(), qi::locals<std::string, long32>, Skipper> mdl; // parent rule
qi::rule<Iterator, Model(long32&), Skipper> model; // sub rule

...

mdl =
        -version[at_c<0>(_val) = _1]
        // wrong code! pass _b because it has the type long32
        //>> -model(_a)[at_c<1>(_val) = _1] // pass local as attribute
        >> -model(_b)[at_c<1>(_val) = _1] // pass local as attribute
        >> -sequences[at_c<2>(_val) = _1]
        >> -global_sequences[at_c<3>(_val) = _1]
        >> -textures[at_c<4>(_val) = _1]
        >> -materials[at_c<5>(_val) = _1]
        >> repeat(_a)[
            geoset_animation
        ]
    ;

model =
        lit("Model")
        >> string_literal[at_c<0>(_val) = _1]
        >> lit('{')
            >> -(
                lit("NumGeosetAnims")
                >> integer_literal[_r1 = _1] // assign the value to the passed attribute
                >> lit(',')
            )
            >> lit("BlendTime") >> integer_literal[at_c<1>(_val) = _1]
            >> lit(',')
            >> bounds[at_c<2>(_val) = _1]
        >> lit('}')
    ;

As you can see I want to pass a local as reference to the sub rule which should set the long32 value. The value is then used in the parent rule as local variable _a.

edit: I've found the error in my code. I was passing _a but the first local has type std::string. I had to pass _b instead!


Solution

  • After the clarification of the question, I surmise that you are looking for a feature more like Spirit's Inherited Attributes. Most significantly, these are always passed at invocation, so they need not be default-constructible and can be reference types.

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <iostream>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    namespace qi = boost::spirit::qi;
    
    template <typename It>
    struct my_parser : qi::grammar<It> {
        my_parser() : my_parser::base_type(start) {
            using namespace qi;
    
            start                            = my_int_rule;
            my_int_rule                      = my_rule_with_inherited_attribute(_val);
            my_rule_with_inherited_attribute = int_ [ _r1 = _1 ]; // copied into the reference passed
    
            BOOST_SPIRIT_DEBUG_NODES((start)(my_rule_with_inherited_attribute)(my_int_rule))
        }
    
      private:
        qi::rule<It> start;
        qi::rule<It, int() > my_int_rule;
        qi::rule<It, void(int&) > my_rule_with_inherited_attribute;
    };
    
    int main()
    {
        using It = std::string::const_iterator;
        my_parser<It> p;
    
        std::string const input = "123";
    
        bool ok = qi::parse(input.begin(), input.end(), p);
    
        std::cout << "Parse " << (ok? "success":"failed") << "\n";
    }
    

    Prints

    <start>
      <try>123</try>
      <my_int_rule>
        <try>123</try>
        <my_rule_with_inherited_attribute>
          <try>123</try>
          <success></success>
          <attributes>[, 123]</attributes>
        </my_rule_with_inherited_attribute>
        <success></success>
        <attributes>[123]</attributes>
      </my_int_rule>
      <success></success>
      <attributes>[]</attributes>
    </start>
    Parse success