2

I wrote a simple parser with spirit, akin to json (but simpler and more specialised). By following the advice in here, I tried to implement error handling by tracking the error position. In particular, my parsing function is as follows

bool parse_properties(std::istream& is, const std::string &filename, PropertyList &pset)
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace classic = boost::spirit::classic;

    typedef std::istreambuf_iterator<char> base_iterator_type;
    base_iterator_type in_begin(is);

    // convert input iterator to forward iterator, usable by spirit parser
    typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;
    forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
    forward_iterator_type fwd_end;

    // wrap forward iterator with position iterator, to record the position
    typedef classic::position_iterator2<forward_iterator_type> pos_iterator_type;
    pos_iterator_type position_begin(fwd_begin, fwd_end, filename);
    pos_iterator_type position_end;

    qi::rule<pos_iterator_type> skipper = ascii::space | 
    '#' >> *(ascii::char_ - qi::eol) >> qi::eol; 

    property_set_grammar<pos_iterator_type, qi::rule<pos_iterator_type> > g;
    bool r = false;
    try {
        r = phrase_parse(position_begin, 
                         position_end, 
             g, skipper, pset);
    }
    catch(const qi::expectation_failure<pos_iterator_type>& e) {
        const classic::file_position_base<std::string>& pos = e.first.get_position();
        std::stringstream msg;
        msg <<
            "parse error at file " << pos.file <<
            " line " << pos.line << " column " << pos.column << std::endl <<
            "'" << e.first.get_currentline() << "'" << std::endl <<
            std::setw(pos.column) << " " << "^- here";
        throw std::runtime_error(msg.str());
    }

    return r;
}

Unfortunately, it does not work. Function phrase_parse always returns false immediately, both for correct and for incorrect files, and never raises any exception.

However, when I modify the above code to use a simple forward_iterator instead of the classic::position_iterator2 it works fine, but of course it does not track the error position. The very strange thing is that the original example in here works fine. So maybe the problem is related to my grammar. Here it follows:

template <typename Iterator, typename Skipper>
struct property_set_grammar : qi::grammar<Iterator, PropertyList(),
                                          Skipper>
{
    qi::rule<Iterator, Property(), Skipper> prop;
    qi::rule<Iterator, std::string(), Skipper> name;
    qi::rule<Iterator, std::string(), Skipper> type;
    qi::rule<Iterator, std::string(), Skipper> value;
    qi::rule<Iterator, std::string(), Skipper> value_simple;
    qi::rule<Iterator, std::string(), Skipper> value_quoted;
    qi::rule<Iterator, PropertyList(), Skipper> plist;

    property_set_grammar() : 
        property_set_grammar::base_type(plist, "Set of Properties") {
        using qi::lit;
        using qi::alpha;
        using qi::alnum;
        using qi::lexeme;
        using qi::char_;

        name = lexeme[alpha >> *alnum];
        type = lexeme[alpha >> *alnum];
        value_simple = lexeme[*(alnum - lit('"'))];
        value_quoted = lit('"') > lexeme[*(char_ - lit('"'))] > lit('"');
        value = (value_quoted | value_simple);

        prop = name >> '=' > value > ';';
        plist = type >> '(' > name > ')' > '{' >> *(prop | plist) > '}' > ';';
    }
};   

I am using g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2 and version 1.50 of the boost libraries.

Is there anything stupid that I am overlooking? If needed, I can provide the complete code (it's just a few files).

knulp
  • 170
  • 9

1 Answers1

2

It seems that boost::spirit::position_iterator is bugged.

You can edit the headers of position_iterator as suggested in the answer here.

Or you can implement your own position_iterator, I did this by basically copy-and-paste the original code of boost::spirit::position_iterator, then remove some unneeded stuff.

Also, when parsing from istream, make sure to set the noskipws manip:

//is >> std::noskipws;

EDIT: Not needed with istreambuf_iterator

Community
  • 1
  • 1
sbabbi
  • 11,070
  • 2
  • 29
  • 57