#ifndef BOOST_PARSER_ERROR_HANDLING_HPP #define BOOST_PARSER_ERROR_HANDLING_HPP #include #include #ifdef _MSC_VER #include #endif #include #include #include #include #include #include #include namespace boost { namespace parser { namespace detail { // All the hard line break code points from the Unicode Line Break // Algorithm; see https://unicode.org/reports/tr14. inline constexpr std::array eol_cps = { {0x000a, 0x000b, 0x000c, 0x000d, 0x0085, 0x2028, 0x2029}}; inline constexpr int eol_cp_mask = 0x000a | 0x000b | 0x000c | 0x000d | 0x0085 | 0x2028 | 0x2029; } /** Returns the `line_position` for `it`, counting lines from the beginning of the input `first`. */ template line_position find_line_position(Iter first, Iter it) { bool prev_cr = false; auto retval = line_position{first, 0, 0}; for (Iter pos = first; pos != it; ++pos) { auto const c = *pos; bool const found = (c & detail::eol_cp_mask) == c && std::find(detail::eol_cps.begin(), detail::eol_cps.end(), c) != detail::eol_cps.end(); if (found) { retval.line_start = std::next(pos); retval.column_number = 0; } else { ++retval.column_number; } if (found && (!prev_cr || c != 0x000a)) ++retval.line_number; prev_cr = c == 0x000d; } return retval; } /** Returns the iterator to the end of the line in which `it` is found. */ template Iter find_line_end(Iter it, Sentinel last) { return parser::detail::text::find_if(it, last, [](auto c) { return (c & detail::eol_cp_mask) == c && std::find( detail::eol_cps.begin(), detail::eol_cps.end(), c) != detail::eol_cps.end(); }); } template std::ostream & write_formatted_message( std::ostream & os, std::string_view filename, Iter first, Iter it, Sentinel last, std::string_view message, int64_t preferred_max_line_length, int64_t max_after_caret) { if (!filename.empty()) os << filename << ':'; auto const position = parser::find_line_position(first, it); os << (position.line_number + 1) << ':' << position.column_number << ": " << message << " here"; if (it == last) os << " (end of input)"; os << ":\n"; std::string underlining(std::distance(position.line_start, it), ' '); detail::trace_input(os, position.line_start, it, false, 1u << 31); if (it == last) { os << '\n' << underlining << "^\n"; os.rdbuf()->pubsync(); return os; } underlining += '^'; int64_t const limit = (std::max)( preferred_max_line_length, (int64_t)underlining.size() + max_after_caret); int64_t i = (int64_t)underlining.size(); auto const line_end = parser::find_line_end(std::next(it), last); detail::trace_input(os, it, line_end, false, limit - i); os << '\n' << underlining << '\n'; os.rdbuf()->pubsync(); return os; } #if defined(_MSC_VER) template std::ostream & write_formatted_message( std::ostream & os, std::wstring_view filename, Iter first, Iter it, Sentinel last, std::string_view message, int64_t preferred_max_line_length, int64_t max_after_caret) { auto const r = filename | parser::detail::text::as_utf8; std::string s(r.begin(), r.end()); return parser::write_formatted_message( os, s, first, it, last, message, preferred_max_line_length, max_after_caret); } #endif template std::ostream & write_formatted_expectation_failure_error_message( std::ostream & os, std::string_view filename, Iter first, Sentinel last, parse_error const & e, int64_t preferred_max_line_length, int64_t max_after_caret) { std::string message = "error: Expected "; message += e.what(); return parser::write_formatted_message( os, filename, first, e.iter, last, message, preferred_max_line_length, max_after_caret); } #if defined(_MSC_VER) template std::ostream & write_formatted_expectation_failure_error_message( std::ostream & os, std::wstring_view filename, Iter first, Sentinel last, parse_error const & e, int64_t preferred_max_line_length, int64_t max_after_caret) { auto const r = filename | parser::detail::text::as_utf8; std::string s(r.begin(), r.end()); return parser::write_formatted_expectation_failure_error_message( os, s, first, last, e, preferred_max_line_length, max_after_caret); } #endif /** An error handler that allows users to supply callbacks to handle the reporting of warnings and errors. The reporting of errors and/or warnings can be suppressed by supplying one or both default-constructed callbacks. */ struct callback_error_handler { using callback_type = std::function; callback_error_handler() {} callback_error_handler( callback_type error, callback_type warning = callback_type()) : error_(error), warning_(warning) {} callback_error_handler( std::string_view filename, callback_type error, callback_type warning = callback_type()) : error_(error), warning_(warning), filename_(filename) {} #if defined(_MSC_VER) || defined(BOOST_PARSER_DOXYGEN) /** This overload is Windows-only. */ callback_error_handler( std::wstring_view filename, callback_type error, callback_type warning = callback_type()) : error_(error), warning_(warning) { auto const r = filename | parser::detail::text::as_utf8; filename_.assign(r.begin(), r.end()); } #endif template error_handler_result operator()(Iter first, Sentinel last, parse_error const & e) const { if (error_) { std::stringstream ss; parser::write_formatted_expectation_failure_error_message( ss, filename_, first, last, e); error_(ss.str()); } return error_handler_result::fail; } template void diagnose( diagnostic_kind kind, std::string_view message, Context const & context, Iter it) const { callback_type const & cb = kind == diagnostic_kind::error ? error_ : warning_; if (!cb) return; std::stringstream ss; parser::write_formatted_message( ss, filename_, parser::_begin(context), it, parser::_end(context), message); cb(ss.str()); } template void diagnose( diagnostic_kind kind, std::string_view message, Context const & context) const { diagnose(kind, message, context, parser::_where(context).begin()); } callback_type error_; callback_type warning_; std::string filename_; }; /** An error handler that just re-throws any exception generated by the parse. */ struct rethrow_error_handler { template error_handler_result operator()(Iter first, Sentinel last, parse_error const & e) const { return error_handler_result::rethrow; } template void diagnose( diagnostic_kind kind, std::string_view message, Context const & context, Iter it) const {} template void diagnose( diagnostic_kind kind, std::string_view message, Context const & context) const {} }; #if defined(_MSC_VER) || defined(BOOST_PARSER_DOXYGEN) /** An error handler that prints to the Visual Studio debugger via calls to `OutputDebugString()`. */ struct vs_output_error_handler : stream_error_handler { vs_output_error_handler() : stream_error_handler{"", detail::vs_cout, detail::vs_cout} {} vs_output_error_handler(std::string_view filename) : stream_error_handler{filename, detail::vs_cout, detail::vs_cout} {} vs_output_error_handler(std::wstring_view filename) : stream_error_handler{filename, detail::vs_cout, detail::vs_cout} {} }; #endif // implementations template error_handler_result default_error_handler::operator()( Iter first, Sentinel last, parse_error const & e) const { parser::write_formatted_expectation_failure_error_message( std::cerr, "", first, last, e); return error_handler_result::fail; } template void default_error_handler::diagnose( diagnostic_kind kind, std::string_view message, Context const & context, Iter it) const { parser::write_formatted_message( std::cerr, "", parser::_begin(context), it, parser::_end(context), message); } template void default_error_handler::diagnose( diagnostic_kind kind, std::string_view message, Context const & context) const { diagnose(kind, message, context, parser::_where(context).begin()); } template error_handler_result stream_error_handler::operator()( Iter first, Sentinel last, parse_error const & e) const { std::ostream * os = err_os_; if (!os) os = &std::cerr; parser::write_formatted_expectation_failure_error_message( *os, filename_, first, last, e); return error_handler_result::fail; } template void stream_error_handler::diagnose( diagnostic_kind kind, std::string_view message, Context const & context, Iter it) const { std::ostream * os = kind == diagnostic_kind::error ? err_os_ : warn_os_; if (!os) os = &std::cerr; parser::write_formatted_message( *os, filename_, parser::_begin(context), it, parser::_end(context), message); } template void stream_error_handler::diagnose( diagnostic_kind kind, std::string_view message, Context const & context) const { diagnose(kind, message, context, parser::_where(context).begin()); } }} #endif