3

I'm trying to write a parser using Boost::Spirit, and I have the parser written and compiling. The problem is, when I try to compile the parsing function, the compiler throws out a bunch of template errors. Here's the Qi grammar:

template<typename Iterator>
struct etf_parser : qi::grammar<Iterator, std::map<std::string, etfnode>(), ascii::space_type> {
    etf_parser() : etf_parser::base_type(start) {
            using qi::int_;
            using qi::lit;
            using qi::double_;
            using qi::bool_;
            using qi::lexeme;
            using ascii::char_;

            quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];

            dataVal %= (quoted_string | double_ | int_ | bool_ | listObj | pairObj | mapObj);

            pairObj %= ('<' >> dataVal >> ',' >> dataVal >> '>');

            listObj %= '{' >> dataVal % ',' >> '}';

            mapKey %= +qi::char_("a-zA-Z_-0-9.");
            mapPair %= mapKey >> lit('=') >> dataVal;
            mapObj %= '(' >> mapPair % ',' >> ')';
            start %= mapPair >> ';';
    }

    qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;

    // Data value parsers
    qi::rule<Iterator, etfnode(), ascii::space_type> dataVal;
    qi::rule<Iterator, std::vector<etfnode>(), ascii::space_type> listObj;
    qi::rule<Iterator, std::pair<etfnode, etfnode>(), ascii::space_type> pairObj;
    qi::rule<Iterator, std::map<std::string, etfnode>(), ascii::space_type> mapObj;

    qi::rule<Iterator, std::pair<std::string, etfnode>(), ascii::space_type> mapPair;
    qi::rule<Iterator, std::string(), ascii::space_type> mapKey;

    qi::rule<Iterator, std::map<std::string, etfnode>(), ascii::space_type> start;
};

And here's the parsing function. When I comment out the qi::parse call, the code compiles fine:

ETFDocument::ETFDocument(std::string content) {
    etf_parser<std::string::const_iterator> parser;
    std::map<std::string, rwnode> results;
    std::string::const_iterator begin = content.begin();
    std::string::const_iterator end = content.end();
    bool result = qi::parse(begin, end, parser, results);
    if(result) printf("Parsing succeeded\n"); else printf("Parsing failed\n");

    m_root = etfnode(results);
}

The compiler spits out the following error when I try to compile:

In file included from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14:0,
             from /usr/include/boost/spirit/home/qi.hpp:20,
             from /usr/include/boost/spirit/include/qi.hpp:16,
             from libmcg/etf.cpp:8:
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp: In instantiation of ‘bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Context = boost::spirit::context<boost::fusion::cons<std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >&, boost::fusion::nil>, boost::spirit::locals<> >; Skipper = boost::spirit::unused_type; Attribute = std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >; Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >; T1 = std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >(); T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’:
/usr/include/boost/spirit/home/qi/reference.hpp:43:71:   required from ‘bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >; Context = boost::spirit::context<boost::fusion::cons<std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >&, boost::fusion::nil>, boost::spirit::locals<> >; Skipper = boost::spirit::unused_type; Attribute = std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>]’
/usr/include/boost/spirit/home/qi/parse.hpp:86:82:   required from ‘bool boost::spirit::qi::parse(Iterator&, Iterator, const Expr&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >; Expr = etf_parser<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> > >; Attr = std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >]’
libmcg/etf.cpp:113:53:   required from here
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:303:17: error: no match for call to ‘(const function_type {aka const boost::function<bool(__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >&, boost::fusion::nil>, boost::fusion::vector0<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >&)>}) (__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>::context_type&, const boost::spirit::unused_type&)’
In file included from /usr/include/boost/function/detail/maybe_include.hpp:33:0,
             from /usr/include/boost/function/detail/function_iterate.hpp:14,
             from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:67,
             from /usr/include/boost/function.hpp:64,
             from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:16,
             from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
             from /usr/include/boost/spirit/home/qi.hpp:20,
             from /usr/include/boost/spirit/include/qi.hpp:16,
             from libmcg/etf.cpp:8:
/usr/include/boost/function/function_template.hpp:1021:7: note: candidate is:
/usr/include/boost/function/function_template.hpp:754:17: note: boost::function4<R, T1, T2, T3, T4>::result_type boost::function4<R, T1, T2, T3, T4>::operator()(T0, T1, T2, T3) const [with R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<std::map<std::basic_string<char>, boost::recursive_wrapper<ETFDocument::etfnode> >&, boost::fusion::nil>, boost::fusion::vector0<> >&; T3 = const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >&; boost::function4<R, T1, T2, T3, T4>::result_type = bool]
/usr/include/boost/function/function_template.hpp:754:17: note:   no known conversion for argument 4 from ‘const boost::spirit::unused_type’ to ‘const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >&’

As far as I can tell, it's looking for a Skipper, but getting boost::spirit::unused_type instead. I'm not sure why this would happen, since I specified a Skipper in my parser definition. I'm using boost v1.49.0 on gcc 4.7.1.

EDIT: Here's the definition for etfnode. There's a typedef at the beginning of the cpp file (containing the other code fragments) that aliases "etfnode" to "rwnode".

enum DataType {
    DT_INT,
    DT_STRING,
    DT_FLOAT,
    DT_BOOL,
    DT_LIST,
    DT_PAIR,
    DT_MAP
};

struct etfnode;
typedef boost::recursive_wrapper<etfnode> rwnode;
typedef boost::variant<
        int,
        std::string,
        double,
        bool,
        std::vector<rwnode>,
        std::pair<rwnode, rwnode>,
        std::map<std::string, rwnode> > etfvalue;

struct etfnode {
    DataType type;
    etfvalue value;

    etfnode(const std::string& s);
    etfnode(const int i);
    etfnode(const double d);
    etfnode(const bool b);
    etfnode(const std::vector<rwnode>& n);
    etfnode(const std::pair<rwnode, rwnode>& p);
    etfnode(const std::map<std::string, rwnode>& p);
    etfnode();
};

And a test string:

foo = 6;
bar = <"bar", 16.5>;
baz = {
    (
        foobar = "foo",
        bar = 12
    ),
    "foobar"
};
nonpolynomial237
  • 2,109
  • 4
  • 27
  • 35
  • Your error seems to be located in one of the rules that have an attribute of `std::map()`. Shouldn't your `start` rule be `start %= mapObj >> ';';`? –  Jul 09 '12 at 07:50
  • Thanks for pointing that out, although it actually should be `start %= mapPair % ';';`. I changed it and the problem still occurs though. – nonpolynomial237 Jul 09 '12 at 07:54
  • could you add the definition of etfnode and a test string to be parsed? –  Jul 09 '12 at 08:00
  • Done. Also, the etfnode constructors are really trivial - basically initializing the value and type members from each one of the variant types. – nonpolynomial237 Jul 09 '12 at 08:11
  • 1
    Why don't you [make it a SSCCE](http://meta.stackexchange.com/questions/22754/sscce-how-to-provide-examples-for-programming-questions/22762#22762) next time? – sehe Jul 09 '12 at 15:46

1 Answers1

5

I think the most important culprit was the fact that you were using qi::parse instead of qi::phrase_parse, while your grammar explicitely uses a skipper.

I also rewrote the defintion of the etfvalue recursive variant. I'm not sure whether your version should have worked, but at least now, you can just use etfnode in all places where you'd expect to. It looks more consistent to me this way.

Here's code that compiles fine for me. It parses the sample input (see main()) with the following output:

Parsing succeeded
Unparsed remaining: ';'

If you actually wanted to accept the trailing ;, fix the main rule to be more like

start   = *(mapPair >> ';'); // or  *(mapPair >> (';'|qi::eoi))

Good luck!

//add streaming operators for etfnode and etfvalue if you want to debug this:
//#define BOOST_SPIRIT_DEBUG
#include <map>
#include <string>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>

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

enum DataType {
    DT_INT,
    DT_STRING,
    DT_FLOAT,
    DT_BOOL,
    DT_LIST,
    DT_PAIR,
    DT_MAP
};

struct etfnode;
typedef boost::variant<
        int,
        std::string,
        double,
        bool, 
        boost::recursive_wrapper<std::vector<etfnode> >,
        boost::recursive_wrapper<std::pair<etfnode, etfnode> >,
        boost::recursive_wrapper<std::map<std::string, etfnode> > 
        > etfvalue;

struct etfnode {
    DataType type;
    etfvalue value;

    etfnode(const std::string& s)                       { value = s; type = DT_STRING; }
    etfnode(const int i)                                { value = i; type = DT_INT; }
    etfnode(const double d)                             { value = d; type = DT_FLOAT; }
    etfnode(const bool b)                               { value = b; type = DT_BOOL; }
    etfnode(const std::vector<etfnode>& n)              { value = n; type = DT_LIST; }
    etfnode(const std::pair<etfnode, etfnode>& p)       { value = p; type = DT_PAIR; }
    etfnode(const std::map<std::string, etfnode>& p)    { value = p; type = DT_MAP; }
    etfnode() { }
};

template<typename Iterator>
struct etf_parser : qi::grammar<Iterator, std::map<std::string, etfnode>(), ascii::space_type> {
    etf_parser() : etf_parser::base_type(start) {
            using qi::int_;
            using qi::lit;
            using qi::double_;
            using qi::bool_;
            using qi::lexeme;
            using ascii::char_;

            quoted_string = lexeme['"' >> +(char_ - '"') >> '"'];

            dataVal = (quoted_string | double_ | int_ | bool_ 
                    | listObj    | pairObj 
                    | mapObj
                    );

            listObj = '{' >> dataVal % ',' >> '}';

            pairObj = lit('<') >> dataVal >> ',' >> dataVal >> '>';

            mapKey  = +qi::char_("a-zA-Z_-0-9.");
            mapPair = mapKey >> lit('=') >> dataVal;
            mapObj  = '(' >> mapPair % ',' >> ')';

            start   = mapPair % ';';

            BOOST_SPIRIT_DEBUG_NODE(quoted_string);
            BOOST_SPIRIT_DEBUG_NODE(dataVal);
            BOOST_SPIRIT_DEBUG_NODE(listObj);
            //BOOST_SPIRIT_DEBUG_NODE(pairObj);
            BOOST_SPIRIT_DEBUG_NODE(mapObj);
            BOOST_SPIRIT_DEBUG_NODE(mapKey);
            BOOST_SPIRIT_DEBUG_NODE(mapPair);
            BOOST_SPIRIT_DEBUG_NODE(start);
    }

    qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;

    // Data value parsers
    qi::rule<Iterator, etfnode(),                           ascii::space_type> dataVal;
    qi::rule<Iterator, std::vector<etfnode>(),              ascii::space_type> listObj;
    qi::rule<Iterator, std::pair<etfnode, etfnode>(),       ascii::space_type> pairObj;
    qi::rule<Iterator, std::map<std::string, etfnode>(),    ascii::space_type> mapObj;

    qi::rule<Iterator, std::pair<std::string, etfnode>(),   ascii::space_type> mapPair;
    qi::rule<Iterator, std::string(),                       ascii::space_type> mapKey;

    qi::rule<Iterator, std::map<std::string, etfnode>(),    ascii::space_type> start;
};

int main()
{
    etf_parser<std::string::const_iterator> parser;
    std::map<std::string, etfnode> results;

    std::string content = "foo = 6;\n"
        "bar = <\"bar\", 16.5>;\n"
        "baz = {\n"
        "       (\n"
        "                foobar = \"foo\",\n"
        "                bar = 12\n"
        "   ),\n"
        "   \"foobar\"\n"
        "};";

    std::string::const_iterator begin = content.begin();
    std::string::const_iterator end = content.end();
    bool result = qi::phrase_parse(begin, end, parser, ascii::space, results);
    if(result) printf("Parsing succeeded\n"); else printf("Parsing failed\n");

    if (begin!=end)
        std::cout << "Unparsed remaining: '" << std::string(begin,end) << "'\n";

    //m_root = etfnode(results);
}
sehe
  • 374,641
  • 47
  • 450
  • 633