Search code examples
c++parsingboost

How to parse two strings using boost::spirit?


I am still trying to wrap my head around Boost::Spirit.
I want to parse two words into a variable. When I can do that, into a struct.

The single word compiles, the Variable doesn't. Why?

#include <boost/spirit/include/qi.hpp>
#include <boost/tuple/tuple.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;

/*
class Syntax : public qi::parser{

};
*/

int main()
{
    //get user input
    std::string input;
    std::getline(std::cin, input);
    auto it = input.begin();

    bool result;
    //define grammar for a single word
    auto word_grammar = +qi::alnum - qi::space;

    std::string singleWord;

    result = qi::parse(
        it, input.end(),
        word_grammar,
        singleWord
    );

    if(!result){
        std::cout << "Failed to parse a word" << '\n';
        return -1;
    }

    std::cout << "\"" << singleWord << "\"" << '\n';

    //Now parse two words into a variable
    std::cout << "Variable:\n";
    typedef boost::tuple<std::string, std::string> Variable;
    Variable variable;
    auto variable_grammar = word_grammar >> word_grammar;

    result = qi::parse(
        it, input.end(),
        variable_grammar,
        variable
    );

    if(!result){
        std::cout << "Failed to parse a variable" << '\n';
        return -1;
    }

    std::cout << "\"" << variable.get<0>() << "\" \"" << variable.get<1>() << "\"" << '\n';

    //now parse a list of variables
    std::cout << "List of Variables:\n";
    std::list<Variable> variables;

    result = qi::parse(
        it, input.end(),
        variable_grammar % +qi::space,
        variable
    );

    if(!result){
        std::cout << "Failed to parse a list of variables" << '\n';
        return -1;
    }

    for(auto var : variables)
        std::cout << "DataType: " << var.get<0>() << ", VariableName: " << var.get<1>() << '\n';

}

In the end I want to parse something like this:

int a
float b
string name

Templates are nice, but when problems occur the error messages are just not human readable (thus no point in posting them here).
I am using the gcc


Solution

  • Sorry to take so long. I've been building a new web server in a hurry and had much to learn.

    Here is what it looks like in X3. I think it is easier to deal with than qi. And then, I've used it a lot more. But then qi is much more mature, richer. That said, x3 is meant to be adaptable, hackable. So you can make it do just about anything you want.

    So, live on coliru

    #include <string>
    #include <iostream>
    #include <vector>
    #include <boost/spirit/home/x3.hpp>
    #include <boost/tuple/tuple.hpp>
    //as pointed out, for the error 'The parser expects tuple-like attribute type'
    #include <boost/fusion/adapted/boost_tuple.hpp>
    
    //our declarations
    using Variable = boost::tuple<std::string, std::string>;
    using Vector = std::vector<Variable>;
    
    namespace parsers {
        using namespace boost::spirit::x3;
    
        auto const word = lexeme[+char_("a-zA-Z")];
        //note, using 'space' as the stock skipper
        auto const tuple = word >> word;
    }
    
    std::ostream& operator << (std::ostream& os, /*const*/ Variable& obj) {
        return os << obj.get<0>() << ' ' << obj.get<1>();
    }
    
    std::ostream& operator << (std::ostream& os, /*const*/ Vector& obj) {
        for (auto& item : obj)
            os << item << " : ";
        return os;
    }
    
    template<typename P, typename A>
    bool test_parse(std::string in, P parser, A& attr) {
        auto begin(in.begin());
        bool r = phrase_parse(begin, in.end(), parser, boost::spirit::x3::space, attr);
        std::cout << "result:\n " << attr << std::endl;
        return r;
    }
    
    int main()
    {
        //not recomended but this is testing stuff
        using namespace boost::spirit::x3;
        using namespace parsers;
    
        std::string input("first second third forth");
    
        //parse one word
        std::string singleWord;
        test_parse(input, word, singleWord);
    
        //parse two words into a variable
        Variable variable;
        test_parse(input, tuple, variable);
    
        //parse two sets of two words
        Vector vector;
        test_parse(input, *tuple, vector);
    }
    

    You may like this form of testing. You can concentrate on testing parsers without a lot of extra code. It makes it easier down the road to keep your basic parsers in their own namespace. Oh yea, x3 compiles much faster than qi!