danteisalive
danteisalive

Reputation: 31

Parsing into a struct using boost spirit

It's my first time using boos spirit and i want to parse inputs like this to a struct :

fanout_n #(2, 0, 0) FANOUT_2 (c7552_wire_2, {c7552_wire_2_0, c7552_wire_2_1});

or

fanout_n #(2, 0, 0) FANOUT_2 ({wire1,wire2} , {c7552_wire_2_0, c7552_wire_2_1});

my struct is like this:

struct GateStruct
{
    int numberOfInputs;
    std::string gateName;
    std::vector<std::string> wireNames_first;
    std::vector<std::string> wireNames_second;
};

for example after parsing, gate has to contain these values :

struct GateStruct
{
    int numberOfInputs = 2;
    std::string gateName = FANOUT_2;
    std::vector<std::string> wireNames_first = {wire1 , wire2};
    std::vector<std::string> wireNames_second = {c7552_wire_2_0, c7552_wire_2_1};
};gate

my grammar is this:

template <typename Iterator>
struct gate_parser : qi::grammar<Iterator, GateStruct(), ascii::space_type>
{
    gate_parser() : gate_parser::base_type(start)
    {
        using qi::int_;
        using qi::lit;
        using qi::double_;
        using qi::lexeme;
        using ascii::char_;
        using qi::_1;
        //using phoenix::ref;

        wirenameString %= lexeme[+(char_)];
          numberString = lit("(") >> int_ >> *(lit(",") >> lit("0")) >> lit(")");
        wireList    = -(lit("{")) >> wirenameString >> *(lit(",") >> wirenameString) >> -(lit("}")); //will parse this: {wire1 , wire2 , ...,wiren}

        start %= lit("fanout_n")
                 >> lit("#")
                 >> numberString
                 >> wirenameString
                 >> lit("(")
                 >> wireList
                 >> lit(",")
                 >> wireList
                 >> lit(");")
                 ;

    }


    qi::rule<Iterator , int() , ascii::space_type > numberString;
    qi::rule<Iterator , std::string(), ascii::space_type > wirenameString;
    qi::rule<Iterator , std::vector<std::string >() , ascii::space_type> wireList;
    qi::rule<Iterator , GateStruct(), ascii::space_type > start;
};

and use this code to parse inputs into struct:

int main(){

using boost::spirit::ascii::space;
typedef std::string::const_iterator iterator_type;
typedef client::gate_parser<iterator_type> gate_parser;

gate_parser g;
std::string str;
while (getline(std::cin, str))
{
    if (str.empty() || str[0] == 'q' || str[0] == 'Q')
        break;

    client::GateStruct gate;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();
    bool r = phrase_parse(iter, end, g, space, gate);

    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing succeeded\n";
        std::cout << "\n-------------------------\n";
    }
    else
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing failed\n";
        std::cout << "Remaining unparsed: '" << std::string(iter,end) << "'\n";
        std::cout << "-------------------------\n";
    }
}

std::cout << "Bye... :-) \n\n";
return 0;

}

it compiles without any error or warning but unfortunately it doesn't parse my inputs at all.

Upvotes: 0

Views: 1099

Answers (1)

sehe
sehe

Reputation: 392911

+qi::char_ consumes all input because everything matches.

Fixing the grammar: Live On Coliru

Notes:

  1. Enable BOOST_SPIRIT_DEBUG
  2. Print the unparsed part of the input
  3. Limit the characters that can make up a wirenameString so it doesn't "eat" all input:

    wirenameString = +char_("a-zA-Z_0-9"); // lexeme implicit because no skipper
    
  4. write the wireList so either expects balanced {...}, or none.

    wireList    = ('{' >> wirenameString % ',' >> '}')
                | wirenameString;
    

    Note the use of the list parser operator (%)

  5. Now the start rule becomes

    start %= qi::lexeme[ "fanout_n" ]
             >> '#' >> numberString
             >> wirenameString >> '(' >> wireList >> ',' >> wireList >> ");"
           ;
    

    All of this works because none of whitespace, (){}, are valid wirename string characters.

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

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

    struct GateStruct {
        int numberOfInputs;
        std::string gateName;
        std::vector<std::string> wireNames_first;
        std::vector<std::string> wireNames_second;
    };
}

BOOST_FUSION_ADAPT_STRUCT(client::GateStruct ,
                          (int ,numberOfInputs)
                          (std::string, gateName)
                          (std::vector<std::string>, wireNames_first)
                          (std::vector<std::string>, wireNames_second)
                         )

namespace client 
{
    template <typename Iterator>
    struct gate_parser : qi::grammar<Iterator, GateStruct(), ascii::space_type> {
        gate_parser() : gate_parser::base_type(start) {
            using qi::int_;
            using qi::lit;
            using ascii::char_;

            wirenameString = +char_("a-zA-Z_0-9"); // lexeme implicit because no skipper
            numberString   = "(" >> int_ >> *(',' >> lit('0')) >> ')';
            wireList       = ('{' >> wirenameString % ',' >> '}')
                        | wirenameString;

            start %= qi::lexeme[ "fanout_n" ]
                     >> '#' >> numberString
                     >> wirenameString >> '(' >> wireList >> ',' >> wireList >> ");"
                   ;

            BOOST_SPIRIT_DEBUG_NODES( (start)(numberString)(wirenameString)(wireList) )
        }
      private:

        qi::rule<Iterator , int(),                      ascii::space_type>  numberString;
        qi::rule<Iterator , std::string()               /* lexeme */>       wirenameString;
        qi::rule<Iterator , std::vector<std::string>(), ascii::space_type>  wireList;
        qi::rule<Iterator , GateStruct(),               ascii::space_type>  start;
    };

}

int main() 
{
    using boost::spirit::ascii::space;
    typedef std::string::const_iterator iterator_type;
    typedef client::gate_parser<iterator_type> gate_parser;

    gate_parser g;
    std::string str;
    while(getline(std::cin, str)) {
        if(str.empty() || str[0] == 'q' || str[0] == 'Q') {
            break;
        }

        client::GateStruct gate;
        std::string::const_iterator iter = str.begin();
        std::string::const_iterator end = str.end();
        bool r = phrase_parse(iter, end, g, space, gate);

        if(r) {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << "\n-------------------------\n";
        } else {
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
        }
        if (iter != end)
            std::cout << "Remaining unparsed: '" << std::string(iter,end) << "'\n";
    }

    std::cout << "Bye...\n";
}

Upvotes: 2

Related Questions