#ifndef BOOST_PARSER_TRANSFORM_REPLACE_HPP #define BOOST_PARSER_TRANSFORM_REPLACE_HPP #include #if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \ (!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__ || \ !BOOST_PARSER_USE_CONCEPTS) namespace boost::parser { namespace detail { template constexpr bool tidy_func = std::is_trivially_copyable_v && sizeof(F) <= sizeof(void *) * 2; template using attr_type = decltype(std::declval().call( std::declval(), std::declval(), std::declval< parse_context>(), ws, detail::default_flags(), std::declval())); template using range_attr_t = attr_type, sentinel_t, Parser>; #if BOOST_PARSER_USE_CONCEPTS template concept transform_replacement_for = std::regular_invocable> && detail::replacement_for< std::invoke_result_t>, V> && (detail::range_utf_format_v == detail::range_utf_format_v< std::invoke_result_t>>); #else template using transform_replacement_for_expr = decltype(std::declval()( std::declval>())); template< typename F, typename V, typename Parser, bool = is_detected_v> constexpr bool transform_replacement_for = false; template constexpr bool transform_replacement_for = replacement_for, V> && (detail::range_utf_format_v == detail::range_utf_format_v< transform_replacement_for_expr>); #endif template< typename R, typename Result, text::format OtherFormat = range_utf_format_v>, text::format Format = range_utf_format_v>> struct utf_wrap { template static auto call(R_ && r) { return (R_ &&) r | as_utf; } }; template struct utf_wrap { template static R_ && call(R_ && r) { return (R_ &&) r; } }; template struct utf_wrap { template static R_ && call(R_ && r) { return (R_ &&) r; } }; template struct utf_wrap { // Looks like you tried to use transform_replace() to replace // subranges of chars with subranges of some UTF-N (for N=8, 16, // or 32). Transcoding from char (unkown encoding) is not // supported. Check the return type of your transform function. }; template struct utf_wrap { // Looks like you tried to use transform_replace() to replace // subranges of some UTF-N (for N=8, 16, or 32) with subranges of // chars. Transcoding to char (unkown encoding) is not supported. // Check the return type of your transform function. }; template struct regular_ref_wrapper { regular_ref_wrapper() = default; regular_ref_wrapper(T & ref) : ptr_(&ref) {} T & get() const { return *ptr_; } T * ptr_; }; // This type catches results of calling F, to accommodate when F // returns an rvalue or a type that needs to be transcoded to a // different UTF. template struct utf_rvalue_shim { using result_type = std::invoke_result_t; using maybe_wrapped_result_type = decltype(utf_wrap::call( std::declval())); static constexpr bool final_type_is_reference = std::is_lvalue_reference_v; using final_type = std::conditional_t< final_type_is_reference, regular_ref_wrapper< std::remove_reference_t>, remove_cv_ref_t>; template utf_rvalue_shim(F_ && f) : f_((F_ &&) f) {} // These two only have return values for testing and metaprogramming // purposes. template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> decltype(auto) operator()(Attr && attr) const { result_ = final_type( utf_wrap::call((*f_)((Attr &&) attr))); return result_->get(); } template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> decltype(auto) operator()(Attr && attr) { result_ = final_type( utf_wrap::call((*f_)((Attr &&) attr))); return result_->get(); } template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> final_type & operator()(Attr && attr) const { result_ = utf_wrap::call((*f_)((Attr &&) attr)); return *result_; } template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> final_type & operator()(Attr && attr) { result_ = utf_wrap::call((*f_)((Attr &&) attr)); return *result_; } template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> decltype(auto) get() const { return result_->get(); } template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> decltype(auto) get() { return result_->get(); } template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> final_type & get() const { return *result_; } template< bool B = final_type_is_reference, typename Enable = std::enable_if_t> final_type & get() { return *result_; } std::optional f_; mutable std::optional result_; }; template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> auto attr_search_impl( R && r, parser_interface const & parser, parser_interface const & skip, trace trace_mode) { auto first = text::detail::begin(r); auto const last = text::detail::end(r); auto match_first = first; auto match_last = first; auto before = [&match_first](auto & ctx) { match_first = _where(ctx).begin(); }; auto after = [&match_last](auto & ctx) { match_last = _where(ctx).begin(); }; auto const search_parser = omit[*(char_ - parser)] >> -lexeme[eps[before] >> parser::skip[parser] >> eps[after]]; using parse_result_outer = decltype(parser::prefix_parse( first, last, search_parser, trace_mode)); static_assert( !std::is_same_v, "If you're seeing this error, you passed a parser to " "transform_replace() that has no attribute. Please fix."); using parse_result = remove_cv_ref_t())>; using return_tuple = tuple< decltype(BOOST_PARSER_SUBRANGE(first, first)), parse_result>; if (first == last) { return return_tuple( BOOST_PARSER_SUBRANGE(first, first), parse_result{}); } if constexpr (std::is_same_v>) { auto result = parser::prefix_parse( first, last, search_parser, trace_mode); if (*result) { return return_tuple( BOOST_PARSER_SUBRANGE(match_first, match_last), std::move(**result)); } } else { auto result = parser::prefix_parse( first, last, search_parser, skip, trace_mode); if (*result) { return return_tuple( BOOST_PARSER_SUBRANGE(match_first, match_last), std::move(**result)); } } return return_tuple( BOOST_PARSER_SUBRANGE(first, first), parse_result{}); } template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> auto attr_search_repack_shim( R && r, parser_interface const & parser, parser_interface const & skip, trace trace_mode) { using value_type = range_value_t; if constexpr (std::is_same_v) { return detail::attr_search_impl( (R &&) r, parser, skip, trace_mode); } else { auto r_unpacked = detail::text::unpack_iterator_and_sentinel( text::detail::begin(r), text::detail::end(r)); auto result = detail::attr_search_impl( r | as_utf32, parser, skip, trace_mode); auto subrng = parser::get(result, llong<0>{}); auto & attr = parser::get(result, llong<1>{}); return tuple< decltype(BOOST_PARSER_SUBRANGE( r_unpacked.repack(subrng.begin().base()), r_unpacked.repack(subrng.end().base()))), remove_cv_ref_t>( BOOST_PARSER_SUBRANGE( r_unpacked.repack(subrng.begin().base()), r_unpacked.repack(subrng.end().base())), std::move(attr)); } } } /** Produces a range of subranges of a given range `base`. Each subrange is either a subrange of `base` that does not match the given parser `parser`, or is `f(*boost::parser::parse(match, parser))`, where `f` is the given invocable and `match` is the matching subrange. In addition to the template parameter constraints, `F` must be invocable with the attribute type of `Parser`; `V` and the range type produced by `F`, "`Rf`" must be ranges of `char`, or must have the same UTF format; and `V` and `Rf` must meet the same compatibility requirements as described in `std::ranges::join_view`. */ template< #if BOOST_PARSER_USE_CONCEPTS std::ranges::viewable_range V, std::move_constructible F, #else typename V, typename F, #endif typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser #if !BOOST_PARSER_USE_CONCEPTS , typename Enable = std::enable_if_t> #endif > #if BOOST_PARSER_USE_CONCEPTS requires detail::transform_replacement_for #endif struct transform_replace_view : detail::stl_interfaces::view_interface> { private: using attr_t = detail::range_attr_t; using replacement_range = std::invoke_result_t; public: constexpr transform_replace_view() = default; constexpr transform_replace_view( V base, parser_interface const & parser, parser_interface const & skip, F f, trace trace_mode = trace::off) : base_(std::move(base)), f_(std::move(f)), parser_(parser), skip_(skip), trace_mode_(trace_mode) {} constexpr transform_replace_view( V base, parser_interface const & parser, F f, trace trace_mode = trace::off) : base_(std::move(base)), f_(std::move(f)), parser_(parser), skip_(), trace_mode_(trace_mode) {} constexpr V base() const & #if BOOST_PARSER_USE_CONCEPTS requires std::copy_constructible #endif { return base_; } constexpr V base() && { return std::move(base_); } constexpr F const & f() const { return *f_.f_; } constexpr auto begin() { return iterator{this}; } constexpr auto end() { return sentinel{}; } constexpr auto begin() const #if BOOST_PARSER_USE_CONCEPTS requires std::ranges::range #endif { return iterator{this}; } constexpr auto end() const #if BOOST_PARSER_USE_CONCEPTS requires std::ranges::range #endif { return sentinel{}; } template struct sentinel {}; template struct iterator : detail::stl_interfaces::proxy_iterator_interface< iterator, std::forward_iterator_tag, BOOST_PARSER_SUBRANGE, detail::maybe_const>>> { using I = detail::iterator_t>; using S = detail::sentinel_t>; using ref_t_iter = detail::either_iterator< detail::maybe_const, detail::maybe_const>; using reference_type = BOOST_PARSER_SUBRANGE; constexpr iterator() = default; constexpr iterator( detail::maybe_const * parent) : parent_(parent), r_(parent_->base_.begin(), parent_->base_.end()), curr_(r_.begin(), r_.begin()), next_it_(r_.begin()), in_match_(true) { ++*this; } constexpr iterator & operator++() { if (in_match_) { r_ = BOOST_PARSER_SUBRANGE(next_it_, r_.end()); auto new_match_and_attr = detail::attr_search_repack_shim( r_, parent_->parser_, parent_->skip_, parent_->trace_mode_); auto const new_match = parser::get(new_match_and_attr, llong<0>{}); parent_->f_( parser::get(std::move(new_match_and_attr), llong<1>{})); if (new_match.begin() == curr_.end()) { curr_ = new_match; } else { curr_ = BOOST_PARSER_SUBRANGE(next_it_, new_match.begin()); in_match_ = false; } next_it_ = new_match.end(); } else { if (!curr_.empty()) { curr_ = BOOST_PARSER_SUBRANGE(curr_.end(), next_it_); in_match_ = true; } if (curr_.empty()) r_ = BOOST_PARSER_SUBRANGE(next_it_, r_.end()); } return *this; } constexpr reference_type operator*() const { if (in_match_) { return reference_type( ref_t_iter(parent_->f_.get().begin()), ref_t_iter(parent_->f_.get().end())); } else { return reference_type( ref_t_iter(curr_.begin()), ref_t_iter(curr_.end())); } } friend constexpr bool operator==(iterator lhs, iterator rhs) { return lhs.r_.begin() == rhs.r_.begin(); } friend constexpr bool operator==(iterator it, sentinel) { return it.r_.begin() == it.r_.end(); } using base_type = detail::stl_interfaces::proxy_iterator_interface< iterator, std::forward_iterator_tag, reference_type>; using base_type::operator++; private: detail::maybe_const * parent_ = {}; BOOST_PARSER_SUBRANGE r_; BOOST_PARSER_SUBRANGE curr_; I next_it_ = {}; bool in_match_ = {}; }; template friend struct iterator; private: V base_; F f_; parser_interface parser_; parser_interface skip_; trace trace_mode_; }; // deduction guides template< typename V, typename F, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> transform_replace_view( V &&, parser_interface, parser_interface, F &&, trace) -> transform_replace_view< detail::text::detail::all_t, detail::remove_cv_ref_t, Parser, GlobalState, ErrorHandler, SkipParser>; template< typename V, typename F, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> transform_replace_view( V &&, parser_interface, parser_interface, F &&) -> transform_replace_view< detail::text::detail::all_t, detail::remove_cv_ref_t, Parser, GlobalState, ErrorHandler, SkipParser>; template< typename V, typename F, typename Parser, typename GlobalState, typename ErrorHandler> transform_replace_view( V &&, parser_interface, F &&, trace) -> transform_replace_view< detail::text::detail::all_t, detail::remove_cv_ref_t, Parser, GlobalState, ErrorHandler, parser_interface>>; template< typename V, typename F, typename Parser, typename GlobalState, typename ErrorHandler> transform_replace_view( V &&, parser_interface, F &&) -> transform_replace_view< detail::text::detail::all_t, detail::remove_cv_ref_t, Parser, GlobalState, ErrorHandler, parser_interface>>; namespace detail { template< typename V, typename F, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> using transform_replace_view_expr = decltype(transform_replace_view< V, F, Parser, GlobalState, ErrorHandler, SkipParser>( std::declval(), std::declval< parser_interface const &>(), std::declval const &>(), std::declval(), trace::on)); template< typename V, typename F, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> constexpr bool can_transform_replace_view = is_detected_v< transform_replace_view_expr, V, F, Parser, GlobalState, ErrorHandler, SkipParser>; struct transform_replace_impl { #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, std::move_constructible F, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> requires std::ranges::viewable_range && std::regular_invocable< F &, range_attr_t, Parser>> && can_transform_replace_view< to_range_t, utf_rvalue_shim< to_range_t, std::remove_cvref_t, range_attr_t, Parser>>, Parser, GlobalState, ErrorHandler, SkipParser> [[nodiscard]] constexpr auto operator()( R && r, parser_interface const & parser, parser_interface const & skip, F && f, trace trace_mode = trace::off) const { return transform_replace_view( to_range::call((R &&)r), parser, skip, utf_rvalue_shim< to_range_t, std::remove_cvref_t, range_attr_t, Parser>>((F &&)f), trace_mode); } template< parsable_range R, std::move_constructible F, typename Parser, typename GlobalState, typename ErrorHandler> requires std::ranges::viewable_range && std::regular_invocable< F &, range_attr_t, Parser>> && can_transform_replace_view< to_range_t, utf_rvalue_shim< to_range_t, std::remove_cvref_t, range_attr_t, Parser>>, Parser, GlobalState, ErrorHandler, parser_interface>> [[nodiscard]] constexpr auto operator()( R && r, parser_interface const & parser, F && f, trace trace_mode = trace::off) const { return (*this)( (R &&)r, parser, parser_interface>{}, (F &&)f, trace_mode); } #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename F = trace, typename Trace = trace, typename Enable = std::enable_if_t>> [[nodiscard]] constexpr auto operator()( R && r, parser_interface const & parser, SkipParser && skip, F && f = F{}, Trace trace_mode = Trace{}) const { if constexpr ( is_parser_iface> && std::is_invocable_v< F &, range_attr_t, Parser>> && std::is_same_v) { // (r, parser, skip, f, trace) case return impl( to_range::call((R &&) r), parser, skip, (F &&) f, trace_mode); } else if constexpr ( std::is_invocable_v< SkipParser &, range_attr_t, Parser>> && std::is_same_v, trace> && std::is_same_v) { // (r, parser, f, trace) case return impl( to_range::call((R &&) r), parser, parser_interface>{}, (SkipParser &&) skip, f); } else { static_assert( sizeof(R) == 1 && false, "Only the signatures replace(R, parser, skip, " "replcement trace = trace::off) and replace(R, parser, " "f, trace = trace::off) are supported."); } } private: template< typename R, typename F, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> [[nodiscard]] constexpr auto impl( R && r, parser_interface const & parser, parser_interface const & skip, F && f, trace trace_mode = trace::off) const { return transform_replace_view( (R &&) r, parser, skip, utf_rvalue_shim< R, remove_cv_ref_t, range_attr_t>((F &&) f), trace_mode); } #endif }; } /** A range adaptor object ([range.adaptor.object]). Given subexpressions `E` and `P`, `Q`, `R`, and 'S', each of the expressions `replace(E, P)`, `replace(E, P, Q)`. `replace(E, P, Q, R)`, and `replace(E, P, Q, R, S)` are expression-equivalent to `replace_view(E, P)`, `replace_view(E, P, Q)`, `replace_view(E, P, Q, R)`, `replace_view(E, P, Q, R, S)`, respectively. */ inline constexpr detail::stl_interfaces::adaptor< detail::transform_replace_impl> transform_replace = detail::transform_replace_impl{}; } #if BOOST_PARSER_USE_CONCEPTS template< typename V, typename F, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> constexpr bool std::ranges::enable_borrowed_range> = std::ranges::enable_borrowed_range && (std::ranges::enable_borrowed_range || boost::parser::detail::tidy_func); #endif #endif #endif