1

I have a chunk of Spirit code which correctly parses std::string input = "RED.MAGIC( 1, 2, 3 )[9].GREEN" into a simple std::vector<std::string>, by using std::vector<std::string> as the primary attribute.

I would like to replace the std::vector<std::string> into a struct my_rec which contains a std::vector<std::string>, but continue using the auto generator, if possible.

When I compile with -DUSE_MY_REC, I get a wall of impenetrable compile errors.

sample compile and run

/tmp$ g++ -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox 
Finished.
MATCHED
/tmp$ g++ -DUSE_MY_REC -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox
WALL OF COMPILE ERRORS --------------------------------------------

sandbox.cpp

// #define BOOST_SPIRIT_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <string>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

#ifdef USE_MY_REC
struct my_rec
{
    std::vector<std::string>    m_pieces;
};

BOOST_FUSION_ADAPT_STRUCT(
  my_rec,
  (std::vector<std::string>,  m_pieces)
)

typedef struct my_rec            MY_TYPE;
#else
typedef std::vector<std::string> MY_TYPE;
#endif

template <typename ITERATOR>
struct my_parser : 
    qi::grammar<ITERATOR, MY_TYPE(), ascii::space_type>
{
    my_parser() : 
        my_parser::base_type( start )
    {
        start %= ( color | fun_call ) % '.'
                ;

        color %=
                qi::string( "RED" )
                | qi::string( "GREEN" )
                | qi::string( "BLUE" )
                ;

        fun_call %= 
                qi::string( "MAGIC" )
                >> '('
                >> +qi::char_("0-9") % ','
                >> ')'
                >> '['
                >> +qi::char_("0-9")
                >> ']'
                ;
    }
    qi::rule<ITERATOR, MY_TYPE(),     ascii::space_type> start, fun_call;
    qi::rule<ITERATOR, std::string(), ascii::space_type> color;
};

int
main( int argc, char* argv[] )
{
    namespace qi    = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    MY_TYPE                  v;
    std::string              str = "RED.MAGIC( 1, 2, 3 )[9].GREEN";
    std::vector<std::string> exp = {{ "RED", "MAGIC", "1", "2", "3", "9", "GREEN" }};
    auto                     it  = str.begin(), end = str.end();
    my_parser<decltype(it)>  g;

    if( qi::phrase_parse( it, end, g, ascii::space, v ) && it==end ) 
    {
        std::cout << "Finished." << std::endl;
#ifndef USE_MY_REC
        if ( !std::equal( v.begin(), v.end(), exp.begin() ))
        {
            std::cout << "MISMATCH" << std::endl;
            for( const auto& x : v )
                std::cout << x << std::endl;
        } else {
            std::cout << "MATCHED" << std::endl;
        }
#endif
    } else
        std::cout << "Error." << std::endl;

    return 0;
}
ildjarn
  • 62,044
  • 9
  • 127
  • 211
kfmfe04
  • 14,936
  • 14
  • 74
  • 140
  • 1
    [Spirit unable to assign attribute to single element struct](http://stackoverflow.com/questions/7770791/spirit-unable-to-assign-attribute-to-single-element-struct-or-fusion-sequence). –  Nov 30 '12 at 22:01
  • +1 for an interesting find, but not sure if workable since in my real code, start is deeply embedded in my real parse rule (not in the beginning like in this toy example) - not sure if eps is ok there yet (doesn't eps always force a match?)... – kfmfe04 Dec 01 '12 at 04:40

2 Answers2

2

As shown in the question linked in the comments in order to assign to an adapted struct with a single member you need to use qi::eps in a sequence. Also you need to change the attribute of your intermediate fun_call rule to std::vector<std::string>().

// #define BOOST_SPIRIT_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <string>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;


struct my_rec
{
    std::vector<std::string>    m_pieces;
};

BOOST_FUSION_ADAPT_STRUCT(
  my_rec,
  (std::vector<std::string>,  m_pieces)
)

typedef struct my_rec            MY_TYPE;


template <typename ITERATOR>
struct my_parser : 
    qi::grammar<ITERATOR, MY_TYPE(), ascii::space_type>
{
    my_parser() : 
        my_parser::base_type( start )
    {
        start %= qi::eps >>( color | fun_call ) % '.' //add qi::eps to make the adaptation of the single member struct work
                ;

        color %=
                qi::string( "RED" )
                | qi::string( "GREEN" )
                | qi::string( "BLUE" )
                ;

        fun_call %= 
                qi::string( "MAGIC" )
                >> '('
                >> +qi::char_("0-9") % ','
                >> ')'
                >> '['
                >> +qi::char_("0-9")
                >> ']'
                ;
    }
    qi::rule<ITERATOR, MY_TYPE(),     ascii::space_type> start;
    qi::rule<ITERATOR, std::vector<std::string>(), ascii::space_type> fun_call; //changed this rule's attribute to vector<string>
    qi::rule<ITERATOR, std::string(), ascii::space_type> color;
};

int
main( int argc, char* argv[] )
{
    namespace qi    = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    MY_TYPE                  v;
    std::string              str = "RED.MAGIC( 1, 2, 3 )[9].GREEN";
    std::vector<std::string> exp;
    exp.push_back("RED");
    exp.push_back("MAGIC");
    exp.push_back("1");
    exp.push_back("2");
    exp.push_back("3");
    exp.push_back("9");
    exp.push_back("GREEN");
    auto                     it  = str.begin(), end = str.end();
    my_parser<decltype(it)>  g;

    if( qi::phrase_parse( it, end, g, ascii::space, v ) && it==end ) 
    {
        std::cout << "Finished." << std::endl;

        if ( !std::equal( v.m_pieces.begin(), v.m_pieces.end(), exp.begin() ))
        {
            std::cout << "MISMATCH" << std::endl;
            for( const auto& x : v.m_pieces )
                std::cout << x << std::endl;
        } else {
            std::cout << "MATCHED" << std::endl;
        }

    } else
        std::cout << "Error." << std::endl;

    return 0;
}
0

You may use boost::spirit::qi::as to convert parsed data to a vector and then put it into your structure.

fun_call %= as<std::vector<std::string>>()[...];
BigBoss
  • 6,904
  • 2
  • 23
  • 38