Boost Spirit V2: Parse C/C++ enum syntax with auto value generation

In C/C++, the following syntax automatically assign 0 to apple and 1 to orange. How to do this in Spirit?

enum foo {
  apple,
  orange,
};

You can use local variable and argument to make an incrementing counter. To parse it into the structure:

struct EnumMember {
    std::string name;
    int value;
};

struct Enum {
    std::string name;
    std::vector< EnumMember > member;
};

Use the following code:

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>

using namespace std;

struct EnumMember {
    std::string name;
    int value;
};

struct Enum {
    std::string name;
    std::vector< EnumMember > member;
};

BOOST_FUSION_ADAPT_STRUCT(
    Enum,
    (std::string, name)
    (std::vector<EnumMember>, member)
)

BOOST_FUSION_ADAPT_STRUCT(
    EnumMember,
    (std::string, name)
    (int, value)
)

template <typename Iterator>
struct enum_grammar : boost::spirit::qi::grammar<Iterator, Enum(), boost::spirit::qi::space_type>
{
    enum_grammar() : enum_grammar::base_type(start){
        using boost::spirit::qi::labels::_val;
        using boost::spirit::qi::labels::_a;   // local variable
        using boost::spirit::qi::labels::_r1;  // argument variable
        using boost::spirit::qi::alpha;
        using boost::spirit::qi::lexeme;
        using boost::spirit::qi::lit;
        using boost::spirit::qi::_1;
        using boost::spirit::qi::eps;

        using boost::phoenix::at_c;
        using boost::phoenix::push_back;
        
        identifier  = lexeme[+alpha];
        
        enum_member =
            identifier[at_c<0>(_val) = _1] >>

            // set argument to enum value.
            eps[at_c<1>(_val) = _r1] >>
            lit(',');
        
        enum_type   =
            "enum" >>
            identifier[at_c<0>(_val) = _1] >>

            // reset local variable _a = 0.
            eps[_a = 0] >>
            lit('{') >>

            // For each enum member, increment _a.
            *enum_member(_a)[push_back(at_c<1>(_val), _1)][_a += 1] >>
            lit('}');
        
        start = enum_type;
    }
    boost::spirit::qi::rule<Iterator,
                            Enum(),
                            boost::spirit::qi::locals<int>,  // for local varialbe _a
                            boost::spirit::qi::space_type> enum_type;
    
    boost::spirit::qi::rule<Iterator,
                            std::string(),
                            boost::spirit::qi::space_type> identifier;
    
    boost::spirit::qi::rule<Iterator,
                            EnumMember(int),  // take one int argument.
                            boost::spirit::qi::space_type> enum_member;
    boost::spirit::qi::rule<Iterator, Enum(), boost::spirit::qi::space_type> start;
};

int main()
{
    string input =
        "enum foo {"
        "  apple,"
        "  orange,"
        "}"
        ;
    
    std::string::const_iterator iter = input.begin();
    std::string::const_iterator end  = input.end();

    enum_grammar< std::string::const_iterator > g;

    Enum e;
    bool r = phrase_parse(iter, end, g, boost::spirit::qi::space, e);
    if (r && iter == end) {
        std::cout << "Parsing succeededn";
        cout << e.name << endl;
        for(vector< EnumMember >::const_iterator it = e.member.begin(); it != e.member.end(); ++it){
            cout << "   " << it->name << "[" << it->value << "]" << endl;
        }
    }
    return 0;
}

Advertisements

About Moto

Engineer who likes coding
This entry was posted in Tips. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s