After reading Custom error on rule level? #657 I thought I'll brave, adapt and combine it with sehe's approach at How do I get which() to work correctly in boost spirit x3 expectation_failure?. Unfortunately, I didn't get it to work - somewhere I've got lost, see coliru.
Even with x3::expect[]
the expectation_failure what()
member returns its own name (expected is 'double', 'int' or even 'char x' in case of use of mandatory
):
'a .e-z' error handler id 'rule_a': expect boost::spirit::x3::expectation_failure
Also, the idea was to 'bind' the helper with_error_handler
(also known as as
) to the mandatory
context/struct
template <typename RuleID, typename AttributeT>
struct mandatory_type {
template <typename Expr>
auto with_error_handler(Expr&& expr, char const* name = typeid(decltype(expr)).name()) const {
using tag = my_error_handler<RuleID>;
return x3::rule<tag, AttributeT>{ name } = x3::as_parser(std::forward<Expr>(expr));
}
template <typename Expr>
auto operator()(Expr&& expr, char const* name = typeid(decltype(expr)).name()) const {
return x3::expect[ with_error_handler<RuleID, AttributeT>(expr, name) ];
}
};
which doesn't compile. I hope the intent can be clear seen from source:
#include <boost/spirit/home/x3.hpp>
#include <boost/core/demangle.hpp>
#include <string_view>
#include <iostream>
namespace x3 = boost::spirit::x3;
template <typename RuleID>
struct my_error_handler
{
template <typename Iterator, typename Exception, typename Context>
static x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& e, Context const&)
{
char const* id_name = typeid(RuleID).name();
std::cerr << "error handler id '" << boost::core::demangle(id_name) << "': "
<< "expect " << e.what() << "\n";
return x3::error_handler_result::accept;
}
};
template <typename RuleID, typename AttributeT>
struct mandatory_type {
template <typename Expr>
auto with_error_handler(Expr&& expr, char const* name = typeid(decltype(expr)).name()) const {
using tag = my_error_handler<RuleID>;
return x3::rule<tag, AttributeT>{ name } = x3::as_parser(std::forward<Expr>(expr));
}
template <typename Expr>
auto operator()(Expr&& expr, char const* name = typeid(decltype(expr)).name()) const {
return x3::expect[ with_error_handler<RuleID, AttributeT>(expr, name) ];
}
};
template <typename RuleID, typename T> static const mandatory_type<RuleID, T> mandatory = {};
struct rule_a : my_error_handler<rule_a> {};
struct rule_b : my_error_handler<rule_b> {};
struct rule_grammar : my_error_handler<rule_grammar> {};
auto const a = x3::rule<rule_a>{ "double" } = x3::expect[x3::double_];
auto const b = x3::rule<rule_b>{ "int" } = x3::expect[x3::int_];
auto const grammar = x3::rule<rule_grammar>{ "grammar" } =
'a' >> a
| 'b' >> b
| x3::expect['x']
//| mandatory<rule_grammar, char>('x', "char x")
;
int main() {
for (std::string_view test: { "a 1.23", "b 123", "a .e-z", "b .0", "x", "d" }) {
std::cerr << "'" << test << "' ";
if (!phrase_parse(begin(test), end(test), grammar, x3::space)) {
std::cerr << "parse error\n";
}
else {
std::cout << "ok\n";
}
}
}