如何使用精神解析器获取错误位置 [英] How to get error position using the spirit parser
问题描述
我写了一个简单的解析器与精神,类似于json(但更简单和更专业)。按照此处,我试图通过跟踪错误位置实现错误处理。特别是,我的解析函数如下
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);
//将输入迭代器转换为向前迭代器,由精神解析器使用
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;
//用位置迭代器包装前向迭代器,记录位置
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<<
文件中解析错误< 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;
}
不幸的是,它不工作。函数 phrase_parse
始终立即返回false,无论是正确的文件还是不正确的文件,并且从不引发任何异常。
但是,当我修改上面的代码使用一个简单的 forward_iterator
而不是 classic :: position_iterator2
它工作正常,但当然它不跟踪错误位置。非常奇怪的是,这里工作正常。所以也许这个问题与我的语法有关。下面是:
模板< typename Iterator,typename Skipper>
struct property_set_grammar:qi :: grammar< Iterator,PropertyList(),
Skipper>
{
qi :: rule< Iterator,Property(),Skipper>支柱;
qi :: rule< Iterator,std :: string(),Skipper>名称;
qi :: rule< Iterator,std :: string(),Skipper>类型;
qi :: rule< Iterator,std :: string(),Skipper>值;
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;
使用qi :: alpha;
使用qi :: alnum;
使用qi :: lexeme;
using qi :: char_;
name = lexeme [>> * alnum];
type = lexeme [alpha>> * alnum];
value_simple = lexeme [*(alnum - lit(''))];
value_quoted = lit('')> lexeme [*(char_-lit(''))]> lit('');
value =(value_quoted | value_simple);
prop = name>> '='>值> ';';
plist = type>> '('> name>')'> '{'>> *(prop | plist)> '}'> ';';
}
};
我使用g ++(Ubuntu / Linaro 4.7.2-2ubuntu1)4.7.2和1.50版提升库。
有什么愚蠢的,我忽略吗?如果需要,我可以提供完整的代码(只是一些文件)。
:position_iterator有bug。
您可以按照 position_iterator .com / questions / 14005273 / strange-error-with-boostspiritposition-iterator2>这里。
或者你可以实现自己的position_iterator通过基本上复制和粘贴原始代码 boost :: spirit :: position_iterator
,然后删除一些不需要的东西。
此外,从 istream
解析时,请务必设置 noskipws
manip:
//是>> std :: noskipws;
EDIT: istreambuf_iterator不需要
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).
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
这篇关于如何使用精神解析器获取错误位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!