自定义期望失败的完整错误信息(boost::spirit::x3)

问题描述 投票:1回答:1

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是行号。我真的不能这样,它丝毫不符合我的项目。

c++ parsing boost boost-spirit boost-spirit-x3
1个回答
0
投票

哇哦,这下好了。首先,我不知道关于这个例子的所有细节和 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_successx3::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<> 实施

© www.soinside.com 2019 - 2024. All rights reserved.