C++ 自定义预期失败的完整错误消息(boost::spirit::x3)
C++ 自定义预期失败的完整错误消息(boost::spirit::x3),c++,parsing,boost,boost-spirit,boost-spirit-x3,C++,Parsing,Boost,Boost Spirit,Boost Spirit X3,boost::spirit::x3错误处理实用程序允许用户选择在出现预期故障时向用户显示的内容。然而,消息的行号部分似乎不是这样,这正是我想要修改的。因此,我不想在第1行打印出:等。我想在它所在的位置打印一些其他消息,使用相同的行号信息。有人知道我是如何做到这一点的,或者它一开始是否可以修改 编辑: 下面是直接来自以下方面的代码: struct error\u处理程序 { 模板 x3::错误\u处理程序\u错误的结果( 迭代器&first,迭代器const&last ,异常常量和x,上下文常量和
boost::spirit::x3
错误处理实用程序允许用户选择在出现预期故障时向用户显示的内容。然而,消息的行号部分似乎不是这样,这正是我想要修改的。因此,我不想在第1行打印出:等。
我想在它所在的位置打印一些其他消息,使用相同的行号信息。有人知道我是如何做到这一点的,或者它一开始是否可以修改
编辑:
下面是直接来自以下方面的代码:
struct error\u处理程序
{
模板
x3::错误\u处理程序\u错误的结果(
迭代器&first,迭代器const&last
,异常常量和x,上下文常量和上下文)
{
auto&error_handler=x3::get(上下文).get();
std::string message=“Error!应为:“+x.which()+”此处:”;
错误处理程序(x.where(),消息);
返回x3::error\u handler\u result::fail;
}
};
除了打印信息的
on\u error
功能外,它还打印“In line x:”,其中x是行号。我真的不能那样做,它一点也不适合我的项目。哇。首先,我不知道关于该示例和x3::error\u handler
的所有细节
有关如何在X3中从基本原理提供错误处理/诊断消息的详细信息,请参阅本演练:
传统上(如在Qi中),我们将使用迭代器适配器进行位置跟踪:
- 或
- 甚至是这个的经典版本
位置缓存
似乎可以单独使用(参见示例)
然而,令人遗憾的是,x3::annotate_on_success
将注释任务与错误处理混为一谈,因为它假设位置缓存将始终位于错误处理程序内部。这意味着:
- 错误处理程序比严格要求的更复杂
- 这与
不适合继承这一事实相结合(由于私有成员的原因,在保留一些重载的同时,很难明确地重载x3::error\u handler
)operator()
- 除非您至少有一个像()这样的无操作错误处理程序,否则无法使用成功注释
spirit::line\u pos\u迭代器
查看它
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);
}
};
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?");
}
} ...
#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);
}
⑨还修复了一个导致诊断错误显示在第一行(?)的错误,使用x3::error\u handler
implementationWow。首先,我不知道关于该示例和x3::error\u handler
的所有细节
有关如何在X3中从基本原理提供错误处理/诊断消息的详细信息,请参阅本演练:
传统上(如在Qi中),我们将使用迭代器适配器进行位置跟踪:
- 或
- 甚至是这个的经典版本
乍一看,位置缓存
似乎可以单独使用(参见示例)
然而,令人遗憾的是,x3::annotate_on_success
将注释任务与错误处理混为一谈,因为它假设位置缓存将始终位于错误处理程序内部。这意味着:
- 错误处理程序比严格要求的更复杂
- 这与
x3::error\u handler
不适合继承这一事实相结合(由于私有成员的原因,在保留一些重载的同时,很难明确地重载operator()
)
- 除非您至少有一个像()这样的无操作错误处理程序,否则无法使用成功注释
下面是我对提供自定义错误处理程序实现的看法。我将它从内置版本中简化了一点
一种简化也是一种优化,基于迭代器类型是双向的假设。如果没有,我认为您最好使用上面链接的spirit::line\u pos\u迭代器
查看它
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);
}
};
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?");
}
} ...
#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);
}
⑨还修复了一个导致诊断错误显示在第一行(?)的错误,使用x3::error\u handler
implementation您应该提供一个示例。我认为用标准迭代器无法获得行号信息。如果你告诉我你的意思,我可以告诉你怎么做customize@sehe根据“x3::error\u handler…是一个实用程序类,它提供了铿锵风格的错误报告,为您提供了良好的报告,如:第16行:error!Expecting:…”以及稍后的报告“错误处理程序处理所有细节,如确定行号和实际列位置,以及格式化打印的错误消息。我们所要做的就是提供实际的错误字符串,我们从期望的“失败异常…”中提取它。它看起来像是在错误处理程序::打印文件中硬编码的在x3/支持/实用程序/错误报告中。hpp@sehe我在boost网站上添加了一个片段,请参见编辑。显然,正如Jerry提到的,文本确实是硬编码在文件中的。我想知道是否有什么优雅的方法可以在不手动更改文件的情况下覆盖该文件?我已经在不使用该文件的情况下完成了错误处理,所以是的。但我会先比较注释你真的应该提供一个例子。我不认为你能用标准迭代器得到行号信息。如果你告诉我你的意思,我可以告诉你
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