How to get error position using the spirit parser

佐手、 提交于 2019-12-24 03:39:07

问题


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).


回答1:


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



来源:https://stackoverflow.com/questions/14255045/how-to-get-error-position-using-the-spirit-parser

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!