自定义预期失败的完整错误消息(boost::spirit::x3)
Customizing the full error message for expectation failures (boost::spirit::x3)
boost::spirit::x3
错误处理实用程序允许用户选择在发生预期失败时向用户显示的内容。但是,消息的行号部分似乎并非如此,这正是我想修改的内容。因此,与其打印出In line 1: etc.
,不如在具有相同行号信息的位置打印其他一些消息。有谁知道我该怎么做,或者它是否一开始是可以修改的?
编辑:
以下是直接来自 https://www.boost.org/doc/libs/1_68_0/libs/spirit/doc/x3/html/spirit_x3/tutorials/error_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
函数外,它还打印"In line x:",其中x是行号。我真的不能拥有它,它根本不适合我的项目。
哇。首先,我不知道有关该示例的所有详细信息,并且x3::error_handler<>
.
有关如何从基本原则在 X3 中提供错误处理/诊断消息的良好细分,请参阅此演练:Spirit X3,这种错误处理方法有用吗?
传统上(如在 Qi 中(,我们将使用迭代器适配器进行位置跟踪:
- 在提升精神语法或跨平台方式中获取当前行,以获取找到给定选项的 INI 文件的行号
- 甚至是这个的经典版本 如何将迭代器传递给灵气中的函数
乍一看,position_cache
似乎可以单独使用(参见例如。提升精神 x3 未编译(。
然而,事实证明 - 可悲的是 -x3::annotate_on_success
将注释任务与错误处理混为一谈,假设位置缓存将始终存在于错误处理程序中。这立即意味着:
-
错误处理程序比严格要求的更复杂
-
这与
x3::error_handler<>
不太适合继承的事实(由于私有成员和在保留一些重载的同时明确重载operator()
很棘手( -
x3::annotate_on_success
根本不可用,除非您至少有一个无操作错误处理程序,例如 (Live On Coliru(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); } };
并将其呈现在
annotate_on_success
工作x3::error_handler_tag
的上下文中。 -
从积极的方面来说,这确实有不需要两个单独的上下文注入的好处,例如:
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 diagnostics_handler {
x3::position_cache<std::vector<It> > _pos_cache;
std::ostream& _os;
public:
diagnostics_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;
}
};
然后你可以像Live On Coliru一样演示
custom::diagnostics_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 succeededn";
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 succeededn";
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 failedn";
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 << "nn ----- 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<>
实现
- Boost Spirit,获取迭代器内部语义动作
- boost::spirit::karma 替代生成器,带有 boost::variant 由字符串和字符串别名组成
- boost::spirit::x3 中的通用解析器生成器
- Boost.Spirit将表达转换为AST
- 使用 Boost.Spirit 解析具有混合数据类型的 OBJ 文件?
- 如何处理Boost Spirit X3导致Visual Studio 2019 "static initialization order fiasco"?
- Boost Spirit X3:将(一些)空格解析为枚举
- Boost Spirit x3 条件(三元)运算符解析器
- 你如何从 Boost Spirit X3 词法解析器中获取字符串?
- 将 Boost.Spirit.X3 解析器拆分为多个 TU
- boost::spirit::x3 中的简单字符串解析器不起作用
- boost::spirit指针属性是用nullptr初始化的吗?
- Boost.Spirit Alternative Parser parallelization
- 如何在 boost::spirit::qi 中将某些语义操作排除在 AST 之外
- 自定义预期失败的完整错误消息(boost::spirit::x3)
- boost::spirit--试图编译大多数简单代码的编译器错误
- Boost.Spirit.Qi 语法,用于 Boost.Fusion 自适应结构中的默认值
- 使用 Boost.Spirit 解析嵌套列表
- (如何)我可以在不安装完整的提升库的情况下使用 boost::spirit X3 吗?
- n-ary布尔语法从中缀到前缀的Boost::Spirit转换?