File indexing completed on 2025-09-17 08:40:31
0001 #ifndef BOOST_PARSER_ERROR_HANDLING_HPP
0002 #define BOOST_PARSER_ERROR_HANDLING_HPP
0003
0004 #include <boost/parser/error_handling_fwd.hpp>
0005 #include <boost/parser/detail/printing.hpp>
0006 #ifdef _MSC_VER
0007 #include <boost/parser/detail/vs_output_stream.hpp>
0008 #endif
0009
0010 #include <boost/parser/detail/text/algorithm.hpp>
0011 #include <boost/parser/detail/text/transcode_iterator.hpp>
0012
0013 #include <algorithm>
0014 #include <array>
0015 #include <functional>
0016 #include <iostream>
0017 #include <sstream>
0018
0019
0020 namespace boost { namespace parser {
0021
0022 namespace detail {
0023
0024
0025
0026 inline constexpr std::array<int, 7> eol_cps = {
0027 {0x000a, 0x000b, 0x000c, 0x000d, 0x0085, 0x2028, 0x2029}};
0028
0029 inline constexpr int eol_cp_mask =
0030 0x000a | 0x000b | 0x000c | 0x000d | 0x0085 | 0x2028 | 0x2029;
0031 }
0032
0033
0034
0035 template<typename Iter>
0036 line_position<Iter> find_line_position(Iter first, Iter it)
0037 {
0038 bool prev_cr = false;
0039 auto retval = line_position<Iter>{first, 0, 0};
0040 for (Iter pos = first; pos != it; ++pos) {
0041 auto const c = *pos;
0042 bool const found =
0043 (c & detail::eol_cp_mask) == c &&
0044 std::find(detail::eol_cps.begin(), detail::eol_cps.end(), c) !=
0045 detail::eol_cps.end();
0046 if (found) {
0047 retval.line_start = std::next(pos);
0048 retval.column_number = 0;
0049 } else {
0050 ++retval.column_number;
0051 }
0052 if (found && (!prev_cr || c != 0x000a))
0053 ++retval.line_number;
0054 prev_cr = c == 0x000d;
0055 }
0056 return retval;
0057 }
0058
0059
0060
0061 template<typename Iter, typename Sentinel>
0062 Iter find_line_end(Iter it, Sentinel last)
0063 {
0064 return parser::detail::text::find_if(it, last, [](auto c) {
0065 return (c & detail::eol_cp_mask) == c &&
0066 std::find(
0067 detail::eol_cps.begin(), detail::eol_cps.end(), c) !=
0068 detail::eol_cps.end();
0069 });
0070 }
0071
0072 template<typename Iter, typename Sentinel>
0073 std::ostream & write_formatted_message(
0074 std::ostream & os,
0075 std::string_view filename,
0076 Iter first,
0077 Iter it,
0078 Sentinel last,
0079 std::string_view message,
0080 int64_t preferred_max_line_length,
0081 int64_t max_after_caret)
0082 {
0083 if (!filename.empty())
0084 os << filename << ':';
0085 auto const position = parser::find_line_position(first, it);
0086 os << (position.line_number + 1) << ':' << position.column_number
0087 << ": " << message << " here";
0088 if (it == last)
0089 os << " (end of input)";
0090 os << ":\n";
0091
0092 std::string underlining(std::distance(position.line_start, it), ' ');
0093 detail::trace_input(os, position.line_start, it, false, 1u << 31);
0094 if (it == last) {
0095 os << '\n' << underlining << "^\n";
0096 os.rdbuf()->pubsync();
0097 return os;
0098 }
0099
0100 underlining += '^';
0101
0102 int64_t const limit = (std::max)(
0103 preferred_max_line_length,
0104 (int64_t)underlining.size() + max_after_caret);
0105
0106 int64_t i = (int64_t)underlining.size();
0107 auto const line_end = parser::find_line_end(std::next(it), last);
0108 detail::trace_input(os, it, line_end, false, limit - i);
0109
0110 os << '\n' << underlining << '\n';
0111 os.rdbuf()->pubsync();
0112
0113 return os;
0114 }
0115
0116 #if defined(_MSC_VER)
0117 template<typename Iter, typename Sentinel>
0118 std::ostream & write_formatted_message(
0119 std::ostream & os,
0120 std::wstring_view filename,
0121 Iter first,
0122 Iter it,
0123 Sentinel last,
0124 std::string_view message,
0125 int64_t preferred_max_line_length,
0126 int64_t max_after_caret)
0127 {
0128 auto const r = filename | parser::detail::text::as_utf8;
0129 std::string s(r.begin(), r.end());
0130 return parser::write_formatted_message(
0131 os,
0132 s,
0133 first,
0134 it,
0135 last,
0136 message,
0137 preferred_max_line_length,
0138 max_after_caret);
0139 }
0140 #endif
0141
0142 template<typename Iter, typename Sentinel>
0143 std::ostream & write_formatted_expectation_failure_error_message(
0144 std::ostream & os,
0145 std::string_view filename,
0146 Iter first,
0147 Sentinel last,
0148 parse_error<Iter> const & e,
0149 int64_t preferred_max_line_length,
0150 int64_t max_after_caret)
0151 {
0152 std::string message = "error: Expected ";
0153 message += e.what();
0154 return parser::write_formatted_message(
0155 os,
0156 filename,
0157 first,
0158 e.iter,
0159 last,
0160 message,
0161 preferred_max_line_length,
0162 max_after_caret);
0163 }
0164
0165 #if defined(_MSC_VER)
0166 template<typename Iter, typename Sentinel>
0167 std::ostream & write_formatted_expectation_failure_error_message(
0168 std::ostream & os,
0169 std::wstring_view filename,
0170 Iter first,
0171 Sentinel last,
0172 parse_error<Iter> const & e,
0173 int64_t preferred_max_line_length,
0174 int64_t max_after_caret)
0175 {
0176 auto const r = filename | parser::detail::text::as_utf8;
0177 std::string s(r.begin(), r.end());
0178 return parser::write_formatted_expectation_failure_error_message(
0179 os, s, first, last, e, preferred_max_line_length, max_after_caret);
0180 }
0181 #endif
0182
0183
0184
0185
0186
0187 struct callback_error_handler
0188 {
0189 using callback_type = std::function<void(std::string const &)>;
0190
0191 callback_error_handler() {}
0192 callback_error_handler(
0193 callback_type error, callback_type warning = callback_type()) :
0194 error_(error), warning_(warning)
0195 {}
0196 callback_error_handler(
0197 std::string_view filename,
0198 callback_type error,
0199 callback_type warning = callback_type()) :
0200 error_(error), warning_(warning), filename_(filename)
0201 {}
0202 #if defined(_MSC_VER) || defined(BOOST_PARSER_DOXYGEN)
0203
0204 callback_error_handler(
0205 std::wstring_view filename,
0206 callback_type error,
0207 callback_type warning = callback_type()) :
0208 error_(error), warning_(warning)
0209 {
0210 auto const r = filename | parser::detail::text::as_utf8;
0211 filename_.assign(r.begin(), r.end());
0212 }
0213 #endif
0214 template<typename Iter, typename Sentinel>
0215 error_handler_result
0216 operator()(Iter first, Sentinel last, parse_error<Iter> const & e) const
0217 {
0218 if (error_) {
0219 std::stringstream ss;
0220 parser::write_formatted_expectation_failure_error_message(
0221 ss, filename_, first, last, e);
0222 error_(ss.str());
0223 }
0224 return error_handler_result::fail;
0225 }
0226
0227 template<typename Context, typename Iter>
0228 void diagnose(
0229 diagnostic_kind kind,
0230 std::string_view message,
0231 Context const & context,
0232 Iter it) const
0233 {
0234 callback_type const & cb =
0235 kind == diagnostic_kind::error ? error_ : warning_;
0236 if (!cb)
0237 return;
0238 std::stringstream ss;
0239 parser::write_formatted_message(
0240 ss,
0241 filename_,
0242 parser::_begin(context),
0243 it,
0244 parser::_end(context),
0245 message);
0246 cb(ss.str());
0247 }
0248
0249 template<typename Context>
0250 void diagnose(
0251 diagnostic_kind kind,
0252 std::string_view message,
0253 Context const & context) const
0254 {
0255 diagnose(kind, message, context, parser::_where(context).begin());
0256 }
0257
0258 callback_type error_;
0259 callback_type warning_;
0260 std::string filename_;
0261 };
0262
0263
0264
0265 struct rethrow_error_handler
0266 {
0267 template<typename Iter, typename Sentinel>
0268 error_handler_result
0269 operator()(Iter first, Sentinel last, parse_error<Iter> const & e) const
0270 {
0271 return error_handler_result::rethrow;
0272 }
0273
0274 template<typename Context, typename Iter>
0275 void diagnose(
0276 diagnostic_kind kind,
0277 std::string_view message,
0278 Context const & context,
0279 Iter it) const
0280 {}
0281
0282 template<typename Context>
0283 void diagnose(
0284 diagnostic_kind kind,
0285 std::string_view message,
0286 Context const & context) const
0287 {}
0288 };
0289
0290 #if defined(_MSC_VER) || defined(BOOST_PARSER_DOXYGEN)
0291
0292
0293 struct vs_output_error_handler : stream_error_handler
0294 {
0295 vs_output_error_handler() :
0296 stream_error_handler{"", detail::vs_cout, detail::vs_cout}
0297 {}
0298
0299 vs_output_error_handler(std::string_view filename) :
0300 stream_error_handler{filename, detail::vs_cout, detail::vs_cout}
0301 {}
0302
0303 vs_output_error_handler(std::wstring_view filename) :
0304 stream_error_handler{filename, detail::vs_cout, detail::vs_cout}
0305 {}
0306 };
0307 #endif
0308
0309
0310
0311
0312 template<typename Iter, typename Sentinel>
0313 error_handler_result default_error_handler::operator()(
0314 Iter first, Sentinel last, parse_error<Iter> const & e) const
0315 {
0316 parser::write_formatted_expectation_failure_error_message(
0317 std::cerr, "", first, last, e);
0318 return error_handler_result::fail;
0319 }
0320
0321 template<typename Context, typename Iter>
0322 void default_error_handler::diagnose(
0323 diagnostic_kind kind,
0324 std::string_view message,
0325 Context const & context,
0326 Iter it) const
0327 {
0328 parser::write_formatted_message(
0329 std::cerr,
0330 "",
0331 parser::_begin(context),
0332 it,
0333 parser::_end(context),
0334 message);
0335 }
0336
0337 template<typename Context>
0338 void default_error_handler::diagnose(
0339 diagnostic_kind kind,
0340 std::string_view message,
0341 Context const & context) const
0342 {
0343 diagnose(kind, message, context, parser::_where(context).begin());
0344 }
0345
0346 template<typename Iter, typename Sentinel>
0347 error_handler_result stream_error_handler::operator()(
0348 Iter first, Sentinel last, parse_error<Iter> const & e) const
0349 {
0350 std::ostream * os = err_os_;
0351 if (!os)
0352 os = &std::cerr;
0353 parser::write_formatted_expectation_failure_error_message(
0354 *os, filename_, first, last, e);
0355 return error_handler_result::fail;
0356 }
0357
0358 template<typename Context, typename Iter>
0359 void stream_error_handler::diagnose(
0360 diagnostic_kind kind,
0361 std::string_view message,
0362 Context const & context,
0363 Iter it) const
0364 {
0365 std::ostream * os = kind == diagnostic_kind::error ? err_os_ : warn_os_;
0366 if (!os)
0367 os = &std::cerr;
0368 parser::write_formatted_message(
0369 *os,
0370 filename_,
0371 parser::_begin(context),
0372 it,
0373 parser::_end(context),
0374 message);
0375 }
0376
0377 template<typename Context>
0378 void stream_error_handler::diagnose(
0379 diagnostic_kind kind,
0380 std::string_view message,
0381 Context const & context) const
0382 {
0383 diagnose(kind, message, context, parser::_where(context).begin());
0384 }
0385
0386 }}
0387
0388 #endif