该 boost::spirit::x3
错误处理工具允许用户选择当预期失败发生时向用户显示的内容,但对于行号部分的消息似乎不是这样,这正是我想修改的。然而,对于消息的行号部分似乎并不是这样,这正是我想修改的。因此,我不希望它打印出 In line 1: etc.
我想用同样的行号信息打印一些其他信息。有谁知道我如何能做到这一点,或者它是否可以修改?
EDIT:
下面是直接的代码 https:/www.boost.orgdoclibs1_68_0libsspiritdocx3htmlspirit_x3tutorialserror_handling.html:
struct error_handler
{
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(
Iterator& first, Iterator const& last
, Exception const& x, Context const& context)
{
auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
std::string message = "Error! Expecting: " + x.which() + " here:";
error_handler(x.where(), message);
return x3::error_handler_result::fail;
}
};
此外,还包括: on_error
函数打印出来的信息,它打印的是 "在x行。",其中x是行号。我真的不能这样,它丝毫不符合我的项目。
哇哦,这下好了。首先,我不知道关于这个例子的所有细节和 x3::error_handler<>
.
关于如何在X3中从基本原则出发提供错误处理诊断信息,请看这篇攻略。Spirit X3,这种错误处理方法有用吗?
传统上(如在Qi中),我们会使用迭代器适配器进行位置跟踪。
乍一看像 position_cache
可以单独使用(见例如 Boost Spirit x3不编译).
然而,事实证明--令人遗憾的是。x3::annotate_on_success
将注释任务与错误处理混为一谈,假设位置缓存将 始终 住在错误处理程序中。这就意味着
x3::error_handler<>
并不适合继承(由于私有成员和难以明确地过载的原因)。 operator()
同时保留一些过载)x3::annotate_on_success
除非你至少有一个无操作的错误处理程序,如(活在科利鲁)
template <typename It> struct dummy_handler_for_annotate_on_success {
x3::position_cache<std::vector<It> > pos_cache;
dummy_handler_for_annotate_on_success(It f, It l) : pos_cache(f,l) {}
template <typename T> void tag(T& ast, It first, It last) {
return pos_cache.annotate(ast, first, last);
}
};
并在上下文中出现,在 x3::error_handler_tag
对于 annotate_on_success
来工作。
在积极方面,这 是否 的好处是不需要两个单独的上下文注入,比如。
auto const parser
= x3::with<x3::position_cache_tag>(std::ref(pos_cache)) [
x3::with<x3::error_handler_tag>(error_handler)
[ parser::employees ]
]
;
所以,这是我对提供一个自定义错误处理程序的实现的看法。我在内置版本¹的基础上对它进行了一些简化。
其中一个简化也是一种优化,建立在假设迭代器类型是 双向. 如果没有的话,我想你最好使用
spirit::line_pos_iterator<>
如上图所示。
template <typename It> class diatnostics_handler {
x3::position_cache<std::vector<It> > _pos_cache;
std::ostream& _os;
public:
diatnostics_handler(It f, It l, std::ostream& os) : _pos_cache(f, l), _os(os) {}
void operator()(x3::position_tagged const& ast, std::string const& error_message) const {
auto where = _pos_cache.position_of(ast);
operator()(where.begin(), where.end(), error_message);
}
void operator()(It err_first, std::string const& error_message) const {
operator()(err_first, boost::none, error_message);
}
void operator()(It err_first, boost::optional<It> err_last, std::string const& error_message) const {
auto first = _pos_cache.first(),
last = _pos_cache.last();
while (err_first != last && std::isspace(*err_first))
++err_first;
_os << "L:"<< line_number(err_first) << " "
<< error_message << std::endl;
It cursor = get_line_start(first, err_first);
print_line(cursor, last);
auto score = [&](It& it, char fill) -> auto& {
auto f = _os.fill();
auto n = std::distance(cursor, it);
cursor = it;
return _os << std::setfill(fill) << std::setw(n) << "" << std::setfill(f);
};
if (err_last.has_value()) {
score(err_first, ' ');
score(*err_last, '~') << " <<-- Here" << std::endl;
} else {
score(err_first, '_') << "^_" << std::endl;
}
}
template <typename AST> void tag(AST& ast, It first, It last) {
return _pos_cache.annotate(ast, first, last);
}
auto const& get_position_cache() const { return _pos_cache; }
private:
static constexpr std::array crlf { '\r', '\n' };
auto get_line_start(It first, It pos) const {
return std::find_first_of( // assumed bidir iterators
std::make_reverse_iterator(pos), std::make_reverse_iterator(first),
crlf.begin(), crlf.end()
).base();
}
auto line_number(It i) const {
return 1 + std::count(_pos_cache.first(), i, '\n');
}
void print_line(It f, It l) const {
std::basic_string s(f, std::find_first_of(f, l, crlf.begin(), crlf.end()));
_os << boost::locale::conv::utf_to_utf<char>(s) << std::endl;
}
};
然后你可以像演示 活在科利鲁
custom::diatnostics_handler<It> diags(iter, end, std::clog);
auto const parser
= x3::with<x3::error_handler_tag>(std::ref(diags))
[ parser::employees ]
;
std::vector<ast::employee> ast;
if (phrase_parse(iter, end, parser >> x3::eoi, x3::space, ast)) {
std::cout << "Parsing succeeded\n";
for (auto const& emp : ast) {
std::cout << "got: " << emp << std::endl;
diags(emp.who.last_name, "note: that's a nice last name");
diags(emp.who, "warning: the whole person could be nice?");
}
} ...
哪些打印。
With custom diagnostics only:
Parsing succeeded
got: (23 (Amanda Stefanski) 1000.99)
L:1 note: that's a nice last name
{ 23, "Amanda", "Stefanski", 1000.99 },
~~~~~~~~~~~ <<-- Here
L:1 warning: the whole person could be nice?
{ 23, "Amanda", "Stefanski", 1000.99 },
~~~~~~~~~~~~~~~~~~~~~ <<-- Here
got: (35 (Angie Chilcote) 2000.99)
L:2 note: that's a nice last name
{ 35, "Angie", "Chilcote", 2000.99 }
~~~~~~~~~~ <<-- Here
L:2 warning: the whole person could be nice?
{ 35, "Angie", "Chilcote", 2000.99 }
~~~~~~~~~~~~~~~~~~~ <<-- Here
----- Now with parse error:
L:3 error: expecting: person
'Amanda', "Stefanski", 1000.99 },
_^_
Parsing failed
通过打破 虚合 之间 annotate_on_success
和 x3::error_handler_tag
语境,你可以把它瘦下来,很多。
template <typename It> struct diagnostics_handler {
It _first, _last;
std::ostream& _os;
void operator()(It err_first, std::string const& error_message) const {
size_t line_no = 1;
auto bol = _first;
for (auto it = bol; it != err_first; ++it)
if (*it == '\n') {
bol = it+1;
line_no += 1;
}
_os << "L:" << line_no
<< ":" << std::distance(bol, err_first)
<< " " << error_message << "\n";
}
};
看吧 活在科利鲁
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <iomanip>
#include <string>
namespace x3 = boost::spirit::x3;
namespace ast {
struct name : std::string, x3::position_tagged {
using std::string::string;
using std::string::operator=;
};
struct person : x3::position_tagged { ast::name first_name, last_name; };
struct employee : x3::position_tagged { int age; person who; double salary; };
using boost::fusion::operator<<;
}
BOOST_FUSION_ADAPT_STRUCT(ast::person, first_name, last_name)
BOOST_FUSION_ADAPT_STRUCT(ast::employee, age, who, salary)
namespace custom {
struct diagnostics_handler_tag;
template <typename It> struct diagnostics_handler {
It _first, _last;
std::ostream& _os;
void operator()(It err_first, std::string const& error_message) const {
size_t line_no = 1;
auto bol = _first;
for (auto it = bol; it != err_first; ++it)
if (*it == '\n') {
bol = it+1;
line_no += 1;
}
_os << "L:"<< line_no
<< ":" << std::distance(bol, err_first)
<< " " << error_message << "\n";
}
};
} // namespace custom
namespace parser {
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
struct error_handler {
template <typename It, typename E, typename Ctx>
x3::error_handler_result on_error(It&, It const&, E const& x, Ctx const& ctx) {
auto& handler = x3::get<custom::diagnostics_handler_tag>(ctx);
handler(x.where(), "error: expecting: " + x.which());
return x3::error_handler_result::fail;
}
};
struct annotate_position {
template <typename T, typename Iterator, typename Context>
inline void on_success(const Iterator &first, const Iterator &last, T &ast, const Context &context)
{
auto &position_cache = x3::get<annotate_position>(context).get();
position_cache.annotate(ast, first, last);
}
};
struct quoted_string_class : annotate_position {};
struct person_class : annotate_position {};
struct employee_class : error_handler, annotate_position {};
x3::rule<quoted_string_class, ast::name> const name = "name";
x3::rule<person_class, ast::person> const person = "person";
x3::rule<employee_class, ast::employee> const employee = "employee";
auto const name_def
= x3::lexeme['"' >> +(x3::char_ - '"') >> '"']
;
auto const person_def
= name > ',' > name
;
auto const employee_def
= '{' > x3::int_ > ',' > person > ',' > x3::double_ > '}'
;
BOOST_SPIRIT_DEFINE(name, person, employee)
auto const employees = employee >> *(',' >> employee);
}
void parse(std::string const& input) {
using It = std::string::const_iterator;
It iter = input.begin(), end = input.end();
x3::position_cache<std::vector<It> > pos_cache(iter, end);
custom::diagnostics_handler<It> diags { iter, end, std::clog };
auto const parser =
x3::with<parser::annotate_position>(std::ref(pos_cache)) [
x3::with<custom::diagnostics_handler_tag>(diags) [
parser::employees
]
];
std::vector<ast::employee> ast;
if (phrase_parse(iter, end, parser >> x3::eoi, x3::space, ast)) {
std::cout << "Parsing succeeded\n";
for (auto const& emp : ast) {
std::cout << "got: " << emp << std::endl;
diags(pos_cache.position_of(emp.who.last_name).begin(), "note: that's a nice last name");
diags(pos_cache.position_of(emp.who).begin(), "warning: the whole person could be nice?");
}
} else {
std::cout << "Parsing failed\n";
ast.clear();
}
}
static std::string const
good_input = R"({ 23, "Amanda", "Stefanski", 1000.99 },
{ 35, "Angie", "Chilcote", 2000.99 }
)",
bad_input = R"(
{ 23,
'Amanda', "Stefanski", 1000.99 },
)";
int main() {
std::cout << "With custom diagnostics only:" << std::endl;
parse(good_input);
std::cout << "\n\n ----- Now with parse error:" << std::endl;
parse(bad_input);
}
打印。
With custom diagnostics only:
Parsing succeeded
got: (23 (Amanda Stefanski) 1000.99)
L:1:16 note: that's a nice last name
L:1:6 warning: the whole person could be nice?
got: (35 (Angie Chilcote) 2000.99)
L:2:23 note: that's a nice last name
L:2:14 warning: the whole person could be nice?
----- Now with parse error:
L:2:13 error: expecting: person
Parsing failed
¹ 还修复了一个错误,该错误导致诊断程序在第一行(?)显示错误。x3::error_handler<>
实施