#ifndef BOOST_PARSER_REPLACE_HPP #define BOOST_PARSER_REPLACE_HPP #include #if !defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS namespace boost::parser { namespace detail { template>> constexpr auto range_value_type = wrapper>>{}; template constexpr auto range_value_type = wrapper< remove_cv_ref_t>>>{}; template constexpr text::format range_utf_format() { #if !BOOST_PARSER_USE_CONCEPTS // Special case: the metafunctions above will not detect char8_t // in C++17 mode, since it does not exit yet! So, we need to // detect utf8_view in particular, and know that its use implies // format::utf8. if constexpr (is_utf8_view{}) { return format::utf8; } else { #endif using value_t = typename decltype(range_value_type)::type; if constexpr (std::is_same_v) { return no_format; #if defined(__cpp_char8_t) } else if constexpr (std::is_same_v) { return format::utf8; #endif } else if constexpr ( std::is_same_v #ifdef _MSC_VER || std::is_same_v #endif ) { return format::utf16; } else if constexpr ( std::is_same_v #ifndef _MSC_VER || std::is_same_v #endif ) { return format::utf32; } else { static_assert( sizeof(T) && false, "Looks like you're trying to pass a range to replace " "or transform_replace that has a non-character type " "for its value type. This is not supported."); } #if !BOOST_PARSER_USE_CONCEPTS } #endif } template constexpr text::format range_utf_format_v = detail::range_utf_format>(); template using concat_reference_t = std::common_type_t, range_reference_t>; template using concat_value_t = std::common_type_t, range_value_t>; template using concat_rvalue_reference_t = std::common_type_t< range_rvalue_reference_t, range_rvalue_reference_t>; #if BOOST_PARSER_USE_CONCEPTS template concept concatable = requires { typename detail::concat_reference_t; typename detail::concat_value_t; typename detail::concat_rvalue_reference_t; }; #else template using concatable_expr = decltype(std::declval>(), std::declval>(), std::declval>()); template constexpr bool concatable = is_detected_v; #endif template< typename V1, typename V2 #if !BOOST_PARSER_USE_CONCEPTS , typename Enable = std::enable_if_t> #endif > #if BOOST_PARSER_USE_CONCEPTS requires concatable #endif struct either_iterator_impl : detail::stl_interfaces::iterator_interface< either_iterator_impl, std::forward_iterator_tag, concat_value_t, concat_reference_t> { constexpr either_iterator_impl() = default; constexpr either_iterator_impl(iterator_t it) : it_(it) {} template constexpr either_iterator_impl(iterator_t it) : it_(it) {} constexpr concat_reference_t operator*() const { if (it_.index() == 0) { return *std::get<0>(it_); } else { return *std::get<1>(it_); } } constexpr either_iterator_impl & operator++() { if (it_.index() == 0) ++std::get<0>(it_); else ++std::get<1>(it_); return *this; } friend constexpr bool operator==(either_iterator_impl lhs, either_iterator_impl rhs) { if (lhs.it_.index() != rhs.it_.index()) return false; if (lhs.it_.index() == 0) return std::get<0>(lhs.it_) == std::get<0>(rhs.it_); else return std::get<1>(lhs.it_) == std::get<1>(rhs.it_); } using base_type = detail::stl_interfaces::iterator_interface< either_iterator_impl, std::forward_iterator_tag, concat_value_t, concat_reference_t>; using base_type::operator++; private: std::variant, iterator_t> it_; }; template using either_iterator = std::conditional_t< std::is_same_v, iterator_t>, iterator_t, either_iterator_impl>; #if BOOST_PARSER_USE_CONCEPTS template concept replacement_for = requires(ReplacementV replacement, V base) { { either_iterator(replacement.begin()) }; { either_iterator(replacement.end()) }; { either_iterator(base.begin()) }; }; #else template using replacement_for_expr = decltype( either_iterator( std::declval().begin()), either_iterator( std::declval().end()), either_iterator(std::declval().begin())); template constexpr bool replacement_for = is_detected_v; #endif } /** 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 the given replacement for a match, `replacement`. In addition to the template parameter constraints, `V` and `ReplacementV` must be ranges of `char`, or must have the same UTF format, and `V` and `ReplacementV` 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::ranges::viewable_range ReplacementV, #else typename V, typename ReplacementV, #endif typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser #if !BOOST_PARSER_USE_CONCEPTS , typename Enable = std::enable_if_t< detail::replacement_for && (detail::range_utf_format_v == detail::range_utf_format_v)> #endif > #if BOOST_PARSER_USE_CONCEPTS requires detail::replacement_for && (detail::range_utf_format_v == detail::range_utf_format_v) #endif struct replace_view : detail::stl_interfaces::view_interface> { constexpr replace_view() = default; constexpr replace_view( V base, parser_interface const & parser, parser_interface const & skip, ReplacementV replacement, trace trace_mode = trace::off) : base_(std::move(base)), replacement_(std::move(replacement)), parser_(parser), skip_(skip), trace_mode_(trace_mode) {} constexpr replace_view( V base, parser_interface const & parser, ReplacementV replacement, trace trace_mode = trace::off) : base_(std::move(base)), replacement_(std::move(replacement)), 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 V replacement() const & #if BOOST_PARSER_USE_CONCEPTS requires std::copy_constructible #endif { return replacement_; } constexpr V replacement() && { return std::move(replacement_); } 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 const new_match = parser::search( r_, parent_->parser_, parent_->skip_, parent_->trace_mode_); 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_->replacement_.begin()), ref_t_iter(parent_->replacement_.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_; ReplacementV replacement_; parser_interface parser_; parser_interface skip_; trace trace_mode_; }; // deduction guides template< typename V, typename ReplacementV, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> replace_view( V &&, parser_interface, parser_interface, ReplacementV &&, trace) -> replace_view< detail::text::detail::all_t, detail::text::detail::all_t, Parser, GlobalState, ErrorHandler, SkipParser>; template< typename V, typename ReplacementV, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> replace_view( V &&, parser_interface, parser_interface, ReplacementV &&) -> replace_view< detail::text::detail::all_t, detail::text::detail::all_t, Parser, GlobalState, ErrorHandler, SkipParser>; template< typename V, typename ReplacementV, typename Parser, typename GlobalState, typename ErrorHandler> replace_view( V &&, parser_interface, ReplacementV &&, trace) -> replace_view< detail::text::detail::all_t, detail::text::detail::all_t, Parser, GlobalState, ErrorHandler, parser_interface>>; template< typename V, typename ReplacementV, typename Parser, typename GlobalState, typename ErrorHandler> replace_view( V &&, parser_interface, ReplacementV &&) -> replace_view< detail::text::detail::all_t, detail::text::detail::all_t, Parser, GlobalState, ErrorHandler, parser_interface>>; namespace detail { template< typename V, typename ReplacementV, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> using replace_view_expr = decltype(replace_view< V, ReplacementV, Parser, GlobalState, ErrorHandler, SkipParser>( std::declval(), std::declval< parser_interface const &>(), std::declval const &>(), std::declval(), trace::on)); template< typename V, typename ReplacementV, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> constexpr bool can_replace_view = is_detected_v< replace_view_expr, V, ReplacementV, Parser, GlobalState, ErrorHandler, SkipParser>; struct replace_impl { #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, std::ranges::range ReplacementR, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> requires std::ranges::viewable_range && std::ranges::viewable_range && can_replace_view< to_range_t, decltype(to_range< ReplacementR, true, detail::range_utf_format_v>:: call(std::declval())), Parser, GlobalState, ErrorHandler, SkipParser> [[nodiscard]] constexpr auto operator()( R && r, parser_interface const & parser, parser_interface const & skip, ReplacementR && replacement, trace trace_mode = trace::off) const { return replace_view( to_range::call((R &&)r), parser, skip, to_range< ReplacementR, true, detail::range_utf_format_v>::call((ReplacementR &&) replacement), trace_mode); } template< parsable_range R, std::ranges::range ReplacementR, typename Parser, typename GlobalState, typename ErrorHandler> requires std::ranges::viewable_range && std::ranges::viewable_range && can_replace_view< to_range_t, decltype(to_range< ReplacementR, true, detail::range_utf_format_v>:: call(std::declval())), Parser, GlobalState, ErrorHandler, parser_interface>> [[nodiscard]] constexpr auto operator()( R && r, parser_interface const & parser, ReplacementR && replacement, trace trace_mode = trace::off) const { return (*this)( (R &&)r, parser, parser_interface>{}, (ReplacementR &&)replacement, trace_mode); } #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename ReplacementR = trace, typename Trace = trace, typename Enable = std::enable_if_t>> [[nodiscard]] constexpr auto operator()( R && r, parser_interface const & parser, SkipParser && skip, ReplacementR && replacement = ReplacementR{}, Trace trace_mode = Trace{}) const { if constexpr ( is_parser_iface> && is_range> && std::is_same_v) { // (r, parser, skip, replacement, trace) case return impl( (R &&) r, parser, skip, (ReplacementR &&) replacement, trace_mode); } else if constexpr ( is_range> && std::is_same_v, trace> && std::is_same_v) { // (r, parser, replacement, trace) case return impl( (R &&) r, parser, parser_interface>{}, (SkipParser &&) skip, replacement); } else { static_assert( sizeof(R) == 1 && false, "Only the signatures replace(R, parser, skip, " "replcement trace = trace::off) and replace(R, parser, " "replacement, trace = trace::off) are supported."); } } private: template< typename R, typename ReplacementR, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> [[nodiscard]] constexpr auto impl( R && r, parser_interface const & parser, parser_interface const & skip, ReplacementR && replacement, trace trace_mode = trace::off) const { return replace_view( to_range::call((R &&) r), parser, skip, to_range< ReplacementR, true, detail::range_utf_format_v>::call((ReplacementR &&) replacement), 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 replace = detail::replace_impl{}; } #if BOOST_PARSER_USE_CONCEPTS template< typename V, typename ReplacementV, 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; #endif #endif #endif