| /*============================================================================= |
| Copyright (c) 2014 Joel de Guzman |
| |
| Distributed under the Boost Software License, Version 1.0. (See accompanying |
| file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| ==============================================================================*/ |
| #if !defined(BOOST_SPIRIT_X3_ERROR_REPORTING_MAY_19_2014_00405PM) |
| #define BOOST_SPIRIT_X3_ERROR_REPORTING_MAY_19_2014_00405PM |
| |
| #include <boost/spirit/home/x3/support/ast/position_tagged.hpp> |
| #include <boost/spirit/home/x3/support/utility/utf8.hpp> |
| #include <ostream> |
| |
| // Clang-style error handling utilities |
| |
| namespace boost { namespace spirit { namespace x3 |
| { |
| // tag used to get our error handler from the context |
| struct error_handler_tag; |
| |
| template <typename Iterator> |
| class error_handler |
| { |
| public: |
| |
| typedef Iterator iterator_type; |
| |
| error_handler( |
| Iterator first, Iterator last, std::ostream& err_out |
| , std::string file = "", int tabs = 4) |
| : err_out(err_out) |
| , file(file) |
| , tabs(tabs) |
| , pos_cache(first, last) {} |
| |
| typedef void result_type; |
| |
| void operator()(Iterator err_pos, std::string const& error_message) const; |
| void operator()(Iterator err_first, Iterator err_last, std::string const& error_message) const; |
| void operator()(position_tagged pos, std::string const& message) const |
| { |
| auto where = pos_cache.position_of(pos); |
| (*this)(where.begin(), where.end(), message); |
| } |
| |
| template <typename AST> |
| void tag(AST& ast, Iterator first, Iterator last) |
| { |
| return pos_cache.annotate(ast, first, last); |
| } |
| |
| boost::iterator_range<Iterator> position_of(position_tagged pos) const |
| { |
| return pos_cache.position_of(pos); |
| } |
| |
| position_cache<std::vector<Iterator>> const& get_position_cache() const |
| { |
| return pos_cache; |
| } |
| |
| private: |
| |
| void print_file_line(std::size_t line) const; |
| void print_line(Iterator line_start, Iterator last) const; |
| void print_indicator(Iterator& line_start, Iterator last, char ind) const; |
| void skip_whitespace(Iterator& err_pos, Iterator last) const; |
| void skip_non_whitespace(Iterator& err_pos, Iterator last) const; |
| Iterator get_line_start(Iterator first, Iterator pos) const; |
| std::size_t position(Iterator i) const; |
| |
| std::ostream& err_out; |
| std::string file; |
| int tabs; |
| position_cache<std::vector<Iterator>> pos_cache; |
| }; |
| |
| template <typename Iterator> |
| void error_handler<Iterator>::print_file_line(std::size_t line) const |
| { |
| if (file != "") |
| { |
| err_out << "In file " << file << ", "; |
| } |
| else |
| { |
| err_out << "In "; |
| } |
| |
| err_out << "line " << line << ':' << std::endl; |
| } |
| |
| template <typename Iterator> |
| void error_handler<Iterator>::print_line(Iterator start, Iterator last) const |
| { |
| auto end = start; |
| while (end != last) |
| { |
| auto c = *end; |
| if (c == '\r' || c == '\n') |
| break; |
| else |
| ++end; |
| } |
| typedef typename std::iterator_traits<Iterator>::value_type char_type; |
| std::basic_string<char_type> line{start, end}; |
| err_out << x3::to_utf8(line) << std::endl; |
| } |
| |
| template <typename Iterator> |
| void error_handler<Iterator>::print_indicator(Iterator& start, Iterator last, char ind) const |
| { |
| for (; start != last; ++start) |
| { |
| auto c = *start; |
| if (c == '\r' || c == '\n') |
| break; |
| else if (c == '\t') |
| for (int i = 0; i < tabs; ++i) |
| err_out << ind; |
| else |
| err_out << ind; |
| } |
| } |
| |
| template <typename Iterator> |
| void error_handler<Iterator>::skip_whitespace(Iterator& err_pos, Iterator last) const |
| { |
| // make sure err_pos does not point to white space |
| while (err_pos != last) |
| { |
| char c = *err_pos; |
| if (std::isspace(c)) |
| ++err_pos; |
| else |
| break; |
| } |
| } |
| |
| template <typename Iterator> |
| void error_handler<Iterator>::skip_non_whitespace(Iterator& err_pos, Iterator last) const |
| { |
| // make sure err_pos does not point to white space |
| while (err_pos != last) |
| { |
| char c = *err_pos; |
| if (std::isspace(c)) |
| break; |
| else |
| ++err_pos; |
| } |
| } |
| |
| template <class Iterator> |
| inline Iterator error_handler<Iterator>::get_line_start(Iterator first, Iterator pos) const |
| { |
| Iterator latest = first; |
| for (Iterator i = first; i != pos; ++i) |
| if (*i == '\r' || *i == '\n') |
| latest = i; |
| return latest; |
| } |
| |
| template <typename Iterator> |
| std::size_t error_handler<Iterator>::position(Iterator i) const |
| { |
| std::size_t line { 1 }; |
| typename std::iterator_traits<Iterator>::value_type prev { 0 }; |
| |
| for (Iterator pos = pos_cache.first(); pos != i; ++pos) { |
| auto c = *pos; |
| switch (c) { |
| case '\n': |
| if (prev != '\r') ++line; |
| break; |
| case '\r': |
| ++line; |
| break; |
| default: |
| break; |
| } |
| prev = c; |
| } |
| |
| return line; |
| } |
| |
| template <typename Iterator> |
| void error_handler<Iterator>::operator()( |
| Iterator err_pos, std::string const& error_message) const |
| { |
| Iterator first = pos_cache.first(); |
| Iterator last = pos_cache.last(); |
| |
| // make sure err_pos does not point to white space |
| skip_whitespace(err_pos, last); |
| |
| print_file_line(position(err_pos)); |
| err_out << error_message << std::endl; |
| |
| Iterator start = get_line_start(first, err_pos); |
| if (start != first) |
| ++start; |
| print_line(start, last); |
| print_indicator(start, err_pos, '_'); |
| err_out << "^_" << std::endl; |
| } |
| |
| template <typename Iterator> |
| void error_handler<Iterator>::operator()( |
| Iterator err_first, Iterator err_last, std::string const& error_message) const |
| { |
| Iterator first = pos_cache.first(); |
| Iterator last = pos_cache.last(); |
| |
| // make sure err_pos does not point to white space |
| skip_whitespace(err_first, last); |
| |
| print_file_line(position(err_first)); |
| err_out << error_message << std::endl; |
| |
| Iterator start = get_line_start(first, err_first); |
| if (start != first) |
| ++start; |
| print_line(start, last); |
| print_indicator(start, err_first, ' '); |
| print_indicator(start, err_last, '~'); |
| err_out << " <<-- Here" << std::endl; |
| } |
| |
| }}} |
| |
| #endif |