error_handling.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. #ifndef BOOST_PARSER_ERROR_HANDLING_HPP
  2. #define BOOST_PARSER_ERROR_HANDLING_HPP
  3. #include <boost/parser/error_handling_fwd.hpp>
  4. #include <boost/parser/detail/printing.hpp>
  5. #ifdef _MSC_VER
  6. #include <boost/parser/detail/vs_output_stream.hpp>
  7. #endif
  8. #include <boost/parser/detail/text/algorithm.hpp>
  9. #include <boost/parser/detail/text/transcode_iterator.hpp>
  10. #include <algorithm>
  11. #include <array>
  12. #include <functional>
  13. #include <iostream>
  14. #include <sstream>
  15. namespace boost { namespace parser {
  16. namespace detail {
  17. // All the hard line break code points from the Unicode Line Break
  18. // Algorithm; see https://unicode.org/reports/tr14.
  19. inline constexpr std::array<int, 7> eol_cps = {
  20. {0x000a, 0x000b, 0x000c, 0x000d, 0x0085, 0x2028, 0x2029}};
  21. inline constexpr int eol_cp_mask =
  22. 0x000a | 0x000b | 0x000c | 0x000d | 0x0085 | 0x2028 | 0x2029;
  23. }
  24. /** Returns the `line_position` for `it`, counting lines from the
  25. beginning of the input `first`. */
  26. template<typename Iter>
  27. line_position<Iter> find_line_position(Iter first, Iter it)
  28. {
  29. bool prev_cr = false;
  30. auto retval = line_position<Iter>{first, 0, 0};
  31. for (Iter pos = first; pos != it; ++pos) {
  32. auto const c = *pos;
  33. bool const found =
  34. (c & detail::eol_cp_mask) == c &&
  35. std::find(detail::eol_cps.begin(), detail::eol_cps.end(), c) !=
  36. detail::eol_cps.end();
  37. if (found) {
  38. retval.line_start = std::next(pos);
  39. retval.column_number = 0;
  40. } else {
  41. ++retval.column_number;
  42. }
  43. if (found && (!prev_cr || c != 0x000a))
  44. ++retval.line_number;
  45. prev_cr = c == 0x000d;
  46. }
  47. return retval;
  48. }
  49. /** Returns the iterator to the end of the line in which `it` is
  50. found. */
  51. template<typename Iter, typename Sentinel>
  52. Iter find_line_end(Iter it, Sentinel last)
  53. {
  54. return parser::detail::text::find_if(it, last, [](auto c) {
  55. return (c & detail::eol_cp_mask) == c &&
  56. std::find(
  57. detail::eol_cps.begin(), detail::eol_cps.end(), c) !=
  58. detail::eol_cps.end();
  59. });
  60. }
  61. template<typename Iter, typename Sentinel>
  62. std::ostream & write_formatted_message(
  63. std::ostream & os,
  64. std::string_view filename,
  65. Iter first,
  66. Iter it,
  67. Sentinel last,
  68. std::string_view message,
  69. int64_t preferred_max_line_length,
  70. int64_t max_after_caret)
  71. {
  72. if (!filename.empty())
  73. os << filename << ':';
  74. auto const position = parser::find_line_position(first, it);
  75. os << (position.line_number + 1) << ':' << position.column_number
  76. << ": " << message << " here";
  77. if (it == last)
  78. os << " (end of input)";
  79. os << ":\n";
  80. std::string underlining(std::distance(position.line_start, it), ' ');
  81. detail::trace_input(os, position.line_start, it, false, 1u << 31);
  82. if (it == last) {
  83. os << '\n' << underlining << "^\n";
  84. os.rdbuf()->pubsync();
  85. return os;
  86. }
  87. underlining += '^';
  88. int64_t const limit = (std::max)(
  89. preferred_max_line_length,
  90. (int64_t)underlining.size() + max_after_caret);
  91. int64_t i = (int64_t)underlining.size();
  92. auto const line_end = parser::find_line_end(std::next(it), last);
  93. detail::trace_input(os, it, line_end, false, limit - i);
  94. os << '\n' << underlining << '\n';
  95. os.rdbuf()->pubsync();
  96. return os;
  97. }
  98. #if defined(_MSC_VER)
  99. template<typename Iter, typename Sentinel>
  100. std::ostream & write_formatted_message(
  101. std::ostream & os,
  102. std::wstring_view filename,
  103. Iter first,
  104. Iter it,
  105. Sentinel last,
  106. std::string_view message,
  107. int64_t preferred_max_line_length,
  108. int64_t max_after_caret)
  109. {
  110. auto const r = filename | parser::detail::text::as_utf8;
  111. std::string s(r.begin(), r.end());
  112. return parser::write_formatted_message(
  113. os,
  114. s,
  115. first,
  116. it,
  117. last,
  118. message,
  119. preferred_max_line_length,
  120. max_after_caret);
  121. }
  122. #endif
  123. template<typename Iter, typename Sentinel>
  124. std::ostream & write_formatted_expectation_failure_error_message(
  125. std::ostream & os,
  126. std::string_view filename,
  127. Iter first,
  128. Sentinel last,
  129. parse_error<Iter> const & e,
  130. int64_t preferred_max_line_length,
  131. int64_t max_after_caret)
  132. {
  133. std::string message = "error: Expected ";
  134. message += e.what();
  135. return parser::write_formatted_message(
  136. os,
  137. filename,
  138. first,
  139. e.iter,
  140. last,
  141. message,
  142. preferred_max_line_length,
  143. max_after_caret);
  144. }
  145. #if defined(_MSC_VER)
  146. template<typename Iter, typename Sentinel>
  147. std::ostream & write_formatted_expectation_failure_error_message(
  148. std::ostream & os,
  149. std::wstring_view filename,
  150. Iter first,
  151. Sentinel last,
  152. parse_error<Iter> const & e,
  153. int64_t preferred_max_line_length,
  154. int64_t max_after_caret)
  155. {
  156. auto const r = filename | parser::detail::text::as_utf8;
  157. std::string s(r.begin(), r.end());
  158. return parser::write_formatted_expectation_failure_error_message(
  159. os, s, first, last, e, preferred_max_line_length, max_after_caret);
  160. }
  161. #endif
  162. /** An error handler that allows users to supply callbacks to handle the
  163. reporting of warnings and errors. The reporting of errors and/or
  164. warnings can be suppressed by supplying one or both
  165. default-constructed callbacks. */
  166. struct callback_error_handler
  167. {
  168. using callback_type = std::function<void(std::string const &)>;
  169. callback_error_handler() {}
  170. callback_error_handler(
  171. callback_type error, callback_type warning = callback_type()) :
  172. error_(error), warning_(warning)
  173. {}
  174. callback_error_handler(
  175. std::string_view filename,
  176. callback_type error,
  177. callback_type warning = callback_type()) :
  178. error_(error), warning_(warning), filename_(filename)
  179. {}
  180. #if defined(_MSC_VER) || defined(BOOST_PARSER_DOXYGEN)
  181. /** This overload is Windows-only. */
  182. callback_error_handler(
  183. std::wstring_view filename,
  184. callback_type error,
  185. callback_type warning = callback_type()) :
  186. error_(error), warning_(warning)
  187. {
  188. auto const r = filename | parser::detail::text::as_utf8;
  189. filename_.assign(r.begin(), r.end());
  190. }
  191. #endif
  192. template<typename Iter, typename Sentinel>
  193. error_handler_result
  194. operator()(Iter first, Sentinel last, parse_error<Iter> const & e) const
  195. {
  196. if (error_) {
  197. std::stringstream ss;
  198. parser::write_formatted_expectation_failure_error_message(
  199. ss, filename_, first, last, e);
  200. error_(ss.str());
  201. }
  202. return error_handler_result::fail;
  203. }
  204. template<typename Context, typename Iter>
  205. void diagnose(
  206. diagnostic_kind kind,
  207. std::string_view message,
  208. Context const & context,
  209. Iter it) const
  210. {
  211. callback_type const & cb =
  212. kind == diagnostic_kind::error ? error_ : warning_;
  213. if (!cb)
  214. return;
  215. std::stringstream ss;
  216. parser::write_formatted_message(
  217. ss,
  218. filename_,
  219. parser::_begin(context),
  220. it,
  221. parser::_end(context),
  222. message);
  223. cb(ss.str());
  224. }
  225. template<typename Context>
  226. void diagnose(
  227. diagnostic_kind kind,
  228. std::string_view message,
  229. Context const & context) const
  230. {
  231. diagnose(kind, message, context, parser::_where(context).begin());
  232. }
  233. callback_type error_;
  234. callback_type warning_;
  235. std::string filename_;
  236. };
  237. /** An error handler that just re-throws any exception generated by the
  238. parse. */
  239. struct rethrow_error_handler
  240. {
  241. template<typename Iter, typename Sentinel>
  242. error_handler_result
  243. operator()(Iter first, Sentinel last, parse_error<Iter> const & e) const
  244. {
  245. return error_handler_result::rethrow;
  246. }
  247. template<typename Context, typename Iter>
  248. void diagnose(
  249. diagnostic_kind kind,
  250. std::string_view message,
  251. Context const & context,
  252. Iter it) const
  253. {}
  254. template<typename Context>
  255. void diagnose(
  256. diagnostic_kind kind,
  257. std::string_view message,
  258. Context const & context) const
  259. {}
  260. };
  261. #if defined(_MSC_VER) || defined(BOOST_PARSER_DOXYGEN)
  262. /** An error handler that prints to the Visual Studio debugger via calls
  263. to `OutputDebugString()`. */
  264. struct vs_output_error_handler : stream_error_handler
  265. {
  266. vs_output_error_handler() :
  267. stream_error_handler{"", detail::vs_cout, detail::vs_cout}
  268. {}
  269. vs_output_error_handler(std::string_view filename) :
  270. stream_error_handler{filename, detail::vs_cout, detail::vs_cout}
  271. {}
  272. vs_output_error_handler(std::wstring_view filename) :
  273. stream_error_handler{filename, detail::vs_cout, detail::vs_cout}
  274. {}
  275. };
  276. #endif
  277. // implementations
  278. template<typename Iter, typename Sentinel>
  279. error_handler_result default_error_handler::operator()(
  280. Iter first, Sentinel last, parse_error<Iter> const & e) const
  281. {
  282. parser::write_formatted_expectation_failure_error_message(
  283. std::cerr, "", first, last, e);
  284. return error_handler_result::fail;
  285. }
  286. template<typename Context, typename Iter>
  287. void default_error_handler::diagnose(
  288. diagnostic_kind kind,
  289. std::string_view message,
  290. Context const & context,
  291. Iter it) const
  292. {
  293. parser::write_formatted_message(
  294. std::cerr,
  295. "",
  296. parser::_begin(context),
  297. it,
  298. parser::_end(context),
  299. message);
  300. }
  301. template<typename Context>
  302. void default_error_handler::diagnose(
  303. diagnostic_kind kind,
  304. std::string_view message,
  305. Context const & context) const
  306. {
  307. diagnose(kind, message, context, parser::_where(context).begin());
  308. }
  309. template<typename Iter, typename Sentinel>
  310. error_handler_result stream_error_handler::operator()(
  311. Iter first, Sentinel last, parse_error<Iter> const & e) const
  312. {
  313. std::ostream * os = err_os_;
  314. if (!os)
  315. os = &std::cerr;
  316. parser::write_formatted_expectation_failure_error_message(
  317. *os, filename_, first, last, e);
  318. return error_handler_result::fail;
  319. }
  320. template<typename Context, typename Iter>
  321. void stream_error_handler::diagnose(
  322. diagnostic_kind kind,
  323. std::string_view message,
  324. Context const & context,
  325. Iter it) const
  326. {
  327. std::ostream * os = kind == diagnostic_kind::error ? err_os_ : warn_os_;
  328. if (!os)
  329. os = &std::cerr;
  330. parser::write_formatted_message(
  331. *os,
  332. filename_,
  333. parser::_begin(context),
  334. it,
  335. parser::_end(context),
  336. message);
  337. }
  338. template<typename Context>
  339. void stream_error_handler::diagnose(
  340. diagnostic_kind kind,
  341. std::string_view message,
  342. Context const & context) const
  343. {
  344. diagnose(kind, message, context, parser::_where(context).begin());
  345. }
  346. }}
  347. #endif