expectation.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*=============================================================================
  2. Copyright (c) 2017 wanghan02
  3. Copyright (c) 2024 Nana Sakisaka
  4. Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. =============================================================================*/
  7. #if !defined(BOOST_SPIRIT_X3_SUPPORT_EXPECTATION_HPP)
  8. #define BOOST_SPIRIT_X3_SUPPORT_EXPECTATION_HPP
  9. #if !defined(BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE)
  10. # define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 1
  11. #endif
  12. #include <boost/config.hpp> // for BOOST_SYMBOL_VISIBLE, BOOST_ATTRIBUTE_NODISCARD
  13. #include <boost/core/ignore_unused.hpp>
  14. #include <boost/spirit/home/x3/support/unused.hpp>
  15. #include <boost/spirit/home/x3/support/context.hpp>
  16. // We utilize `x3::traits::build_optional<...>` for customization point
  17. // instead of directly wrapping `expectation_failure` with `boost::optional`.
  18. // This would make it possible for the user to eliminate the usages of
  19. // `boost::optional<T>`, and use `std::optional<T>` everywhere.
  20. //
  21. // Note that we are intentionally including this header regardless of
  22. // the value of BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE, since the
  23. // helper types defined in non-throwing version might still be required
  24. // when the users benchmark their application just by switching the
  25. // macro while keeping their implementation unmodified.
  26. //
  27. // This will make it possible for the users to unconditionally
  28. // inject `x3::expectation_failure_optional<Iterator>` into their parser,
  29. // safely assuming that the value is no-op in throwing mode.
  30. #include <boost/spirit/home/x3/support/traits/optional_traits.hpp>
  31. // This is required for partial specialization of relevant helpers.
  32. // TODO: Add a macro to discard all #includes of <boost/optional.hpp>.
  33. // (this is TODO because it requires changes in `optional_traits.hpp`.)
  34. #include <boost/optional.hpp>
  35. #include <optional>
  36. #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE // throwing mode
  37. # define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_API BOOST_SYMBOL_VISIBLE
  38. # define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_BASE : std::runtime_error
  39. # define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS throwing
  40. # include <boost/throw_exception.hpp>
  41. # include <stdexcept>
  42. #else // non-throwing mode
  43. # define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_API
  44. # define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_BASE
  45. # define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS non_throwing
  46. #endif
  47. #include <string>
  48. #include <type_traits>
  49. namespace boost { namespace spirit { namespace x3
  50. {
  51. struct expectation_failure_tag;
  52. inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
  53. {
  54. template <typename Iterator>
  55. struct BOOST_SPIRIT_X3_EXPECTATION_FAILURE_API
  56. expectation_failure BOOST_SPIRIT_X3_EXPECTATION_FAILURE_BASE
  57. {
  58. public:
  59. expectation_failure(Iterator where, std::string const& which)
  60. #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
  61. : std::runtime_error("boost::spirit::x3::expectation_failure"),
  62. #else
  63. :
  64. #endif
  65. where_(where), which_(which)
  66. {}
  67. BOOST_ATTRIBUTE_NODISCARD
  68. constexpr Iterator const& where() const noexcept { return where_; }
  69. BOOST_ATTRIBUTE_NODISCARD
  70. constexpr std::string const& which() const noexcept { return which_; }
  71. private:
  72. Iterator where_;
  73. std::string which_;
  74. };
  75. template <typename Context>
  76. using expectation_failure_t = std::remove_cv_t<std::remove_reference_t<
  77. decltype(x3::get<expectation_failure_tag>(std::declval<Context>()))>>;
  78. template <typename Iterator>
  79. using expectation_failure_optional =
  80. typename traits::build_optional<expectation_failure<Iterator>>::type;
  81. // x3::where(x), x3::which(x)
  82. // Convenient accessors for absorbing the variation of
  83. // optional/reference_wrapper interfaces.
  84. // beware ADL - we should avoid overgeneralization here.
  85. namespace expectation_failure_helpers
  86. {
  87. // bare type
  88. template <typename Iterator>
  89. BOOST_ATTRIBUTE_NODISCARD
  90. constexpr decltype(auto) where(expectation_failure<Iterator> const& failure) noexcept { return failure.where(); }
  91. template <typename Iterator>
  92. BOOST_ATTRIBUTE_NODISCARD
  93. constexpr decltype(auto) which(expectation_failure<Iterator> const& failure) noexcept { return failure.which(); }
  94. // std::optional
  95. template <typename Iterator>
  96. BOOST_ATTRIBUTE_NODISCARD
  97. constexpr decltype(auto) where(std::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->where(); }
  98. template <typename Iterator>
  99. BOOST_ATTRIBUTE_NODISCARD
  100. constexpr decltype(auto) which(std::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->which(); }
  101. // boost::optional
  102. template <typename Iterator>
  103. BOOST_ATTRIBUTE_NODISCARD
  104. constexpr decltype(auto) where(boost::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->where(); }
  105. template <typename Iterator>
  106. BOOST_ATTRIBUTE_NODISCARD
  107. constexpr decltype(auto) which(boost::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->which(); }
  108. // std::optional + std::reference_wrapper
  109. template <typename Iterator>
  110. BOOST_ATTRIBUTE_NODISCARD
  111. constexpr decltype(auto) where(std::reference_wrapper<std::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->where(); }
  112. template <typename Iterator>
  113. BOOST_ATTRIBUTE_NODISCARD
  114. constexpr decltype(auto) which(std::reference_wrapper<std::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->which(); }
  115. // boost::optional + std::reference_wrapper
  116. template <typename Iterator>
  117. BOOST_ATTRIBUTE_NODISCARD
  118. constexpr decltype(auto) where(std::reference_wrapper<boost::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->where(); }
  119. template <typename Iterator>
  120. BOOST_ATTRIBUTE_NODISCARD
  121. constexpr decltype(auto) which(std::reference_wrapper<boost::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->which(); }
  122. } // expectation_failure_helpers
  123. using expectation_failure_helpers::where;
  124. using expectation_failure_helpers::which;
  125. } // inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
  126. #if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
  127. namespace detail {
  128. inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
  129. {
  130. inline constexpr bool has_expectation_failure_impl(unused_type const&) noexcept = delete;
  131. inline constexpr bool has_expectation_failure_impl(bool& failure) noexcept {
  132. return failure;
  133. }
  134. template <typename Iterator>
  135. constexpr bool has_expectation_failure_impl(std::optional<expectation_failure<Iterator>> const& failure) noexcept
  136. {
  137. return failure.has_value();
  138. }
  139. template <typename Iterator>
  140. constexpr bool has_expectation_failure_impl(boost::optional<expectation_failure<Iterator>> const& failure) noexcept
  141. {
  142. return failure.has_value();
  143. }
  144. template <typename T>
  145. constexpr bool has_expectation_failure_impl(std::reference_wrapper<T> const& ref) noexcept
  146. {
  147. return has_expectation_failure_impl(ref.get());
  148. }
  149. template <typename Iterator, typename T>
  150. constexpr void set_expectation_failure_impl(bool& failure, T&& value)
  151. {
  152. failure = std::forward<T>(value);
  153. }
  154. template <typename Iterator, typename T>
  155. constexpr void set_expectation_failure_impl(std::optional<expectation_failure<Iterator>>& failure, T&& value)
  156. {
  157. failure = std::forward<T>(value);
  158. }
  159. template <typename Iterator, typename T>
  160. constexpr void set_expectation_failure_impl(boost::optional<expectation_failure<Iterator>>& failure, T&& value)
  161. {
  162. failure = std::forward<T>(value);
  163. }
  164. template <typename AnyExpectationFailure, typename T>
  165. constexpr void set_expectation_failure_impl(std::reference_wrapper<AnyExpectationFailure>& failure, T&& value)
  166. {
  167. set_expectation_failure_impl(failure.get(), std::forward<T>(value));
  168. }
  169. template <typename Iterator>
  170. constexpr void clear_expectation_failure_impl(unused_type const&) noexcept = delete;
  171. template <typename Iterator>
  172. constexpr void clear_expectation_failure_impl(bool& failure) noexcept
  173. {
  174. failure = false;
  175. }
  176. template <typename Iterator>
  177. constexpr void clear_expectation_failure_impl(std::optional<expectation_failure<Iterator>>& failure) noexcept
  178. {
  179. failure.reset();
  180. }
  181. template <typename Iterator>
  182. constexpr void clear_expectation_failure_impl(boost::optional<expectation_failure<Iterator>>& failure) noexcept
  183. {
  184. failure.reset();
  185. }
  186. template <typename T>
  187. constexpr void clear_expectation_failure_impl(std::reference_wrapper<T>& ref) noexcept
  188. {
  189. return clear_expectation_failure_impl(ref.get());
  190. }
  191. } // inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
  192. } // detail
  193. #endif
  194. inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
  195. {
  196. template <typename Context>
  197. BOOST_ATTRIBUTE_NODISCARD
  198. constexpr bool has_expectation_failure(Context const& context) noexcept {
  199. #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
  200. boost::ignore_unused(context);
  201. return false;
  202. #else
  203. using T = expectation_failure_t<Context>;
  204. static_assert(
  205. !std::is_same_v<unused_type, T>,
  206. "Context type was not specified for x3::expectation_failure_tag. "
  207. "You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
  208. "Note that you must also bind the context to your skipper."
  209. );
  210. return detail::has_expectation_failure_impl(
  211. x3::get<expectation_failure_tag>(context));
  212. #endif
  213. }
  214. //
  215. // Creation of a brand new expectation_failure instance.
  216. // This is the primary overload.
  217. //
  218. template <typename Iterator, typename Subject, typename Context>
  219. constexpr void set_expectation_failure(
  220. Iterator const& where,
  221. Subject const& subject,
  222. Context const& context
  223. ) {
  224. #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
  225. boost::ignore_unused(where, subject, context);
  226. #else
  227. using T = expectation_failure_t<Context>;
  228. static_assert(
  229. !std::is_same_v<unused_type, T>,
  230. "Context type was not specified for x3::expectation_failure_tag. "
  231. "You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
  232. "Note that you must also bind the context to your skipper."
  233. );
  234. if constexpr (std::is_same_v<T, bool>)
  235. {
  236. boost::ignore_unused(where, subject);
  237. detail::set_expectation_failure_impl(
  238. x3::get<expectation_failure_tag>(context),
  239. true);
  240. }
  241. else
  242. {
  243. detail::set_expectation_failure_impl(
  244. x3::get<expectation_failure_tag>(context),
  245. expectation_failure<Iterator>(where, what(subject)));
  246. }
  247. #endif
  248. }
  249. //
  250. // Copy-assignment of existing expectation_failure instance.
  251. //
  252. // When you're in the situation where this functionality is
  253. // *really* needed, it essentially means that you have
  254. // multiple valid exceptions at the same time.
  255. //
  256. // There are only two decent situations that I can think of:
  257. //
  258. // (a) When you are writing a custom parser procedure with very specific characteristics:
  259. // 1. You're forking a context.
  260. // 2. Your parser class has delegated some process to child parser(s).
  261. // 3. The child parser(s) have raised an exceptation_failure.
  262. // 4. You need to propagate the failure back to the parent context.
  263. //
  264. // (b) When you truly need a nested exception.
  265. // That is, you're trying to preserve a nested exception structure
  266. // raised by nested directive: e.g. `x3::expect[x3::expect[p]]`.
  267. // Note that all builtin primitives just save the first error,
  268. // so this structure does not exist in core (as of now).
  269. //
  270. template <typename AnyExpectationFailure, typename Context>
  271. constexpr void set_expectation_failure(
  272. AnyExpectationFailure const& existing_failure,
  273. Context const& context
  274. ) {
  275. #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
  276. boost::ignore_unused(existing_failure, context);
  277. #else
  278. using T = expectation_failure_t<Context>;
  279. static_assert(
  280. !std::is_same_v<T, unused_type>,
  281. "Context type was not specified for x3::expectation_failure_tag. "
  282. "You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
  283. "Note that you must also bind the context to your skipper."
  284. );
  285. static_assert(
  286. std::is_assignable_v<T, AnyExpectationFailure const&>,
  287. "previous/current expectation failure types should be compatible"
  288. );
  289. detail::set_expectation_failure_impl(
  290. x3::get<expectation_failure_tag>(context), existing_failure);
  291. #endif
  292. }
  293. #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
  294. template <typename Context>
  295. constexpr decltype(auto) get_expectation_failure(Context const&) = delete;
  296. #else
  297. template <typename Context>
  298. BOOST_ATTRIBUTE_NODISCARD
  299. constexpr decltype(auto) get_expectation_failure(Context const& context)
  300. {
  301. using T = expectation_failure_t<Context>;
  302. static_assert(
  303. !std::is_same_v<T, unused_type>,
  304. "Context type was not specified for x3::expectation_failure_tag. "
  305. "You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
  306. "Note that you must also bind the context to your skipper."
  307. );
  308. return x3::get<expectation_failure_tag>(context);
  309. }
  310. #endif
  311. template <typename Context>
  312. constexpr void clear_expectation_failure(Context const& context) noexcept
  313. {
  314. #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
  315. boost::ignore_unused(context);
  316. #else
  317. using T = expectation_failure_t<Context>;
  318. static_assert(
  319. !std::is_same_v<T, unused_type>,
  320. "Context type was not specified for x3::expectation_failure_tag. "
  321. "You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
  322. "Note that you must also bind the context to your skipper."
  323. );
  324. detail::clear_expectation_failure_impl(
  325. x3::get<expectation_failure_tag>(context));
  326. #endif
  327. }
  328. } // inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
  329. }}}
  330. #endif