Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

This is the documentation for a snapshot of the develop branch, built from commit 2daa88693b.

boost/parser/transform_replace.hpp

#ifndef BOOST_PARSER_TRANSFORM_REPLACE_HPP
#define BOOST_PARSER_TRANSFORM_REPLACE_HPP

#include <boost/parser/replace.hpp>

#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) &&                       \
    (!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)


namespace boost::parser {

    namespace detail {

        template<typename F>
        constexpr bool tidy_func = std::is_trivially_copyable_v<F> &&
                                   sizeof(F) <= sizeof(void *) * 2;

        template<typename I, typename S, typename Parser>
        using attr_type = decltype(std::declval<Parser const &>().call(
            std::declval<I &>(),
            std::declval<S>(),
            std::declval<
                parse_context<false, false, I, S, default_error_handler>>(),
            ws,
            detail::default_flags(),
            std::declval<bool &>()));
        template<typename R, typename Parser>
        using range_attr_t = attr_type<iterator_t<R>, sentinel_t<R>, Parser>;

#if BOOST_PARSER_USE_CONCEPTS
        // clang-format off
        template<typename F, typename V, typename Parser>
        concept transform_replacement_for =
            std::regular_invocable<F &, range_attr_t<V, Parser>> &&
            detail::replacement_for<
                std::invoke_result_t<F &, range_attr_t<V, Parser>>, V> &&
            (detail::range_utf_format_v<V> ==
             detail::range_utf_format_v<
                 std::invoke_result_t<F &, range_attr_t<V, Parser>>>);
        // clang-format on
#else
        template<typename F, typename V, typename Parser>
        using transform_replacement_for_expr = decltype(std::declval<F &>()(
            std::declval<range_attr_t<V, Parser>>()));
        template<
            typename F,
            typename V,
            typename Parser,
            bool = is_detected_v<transform_replacement_for_expr, F, V, Parser>>
        constexpr bool transform_replacement_for = false;
        template<typename F, typename V, typename Parser>
        constexpr bool transform_replacement_for<F, V, Parser, true> =
            replacement_for<transform_replacement_for_expr<F, V, Parser>, V> &&
            (detail::range_utf_format_v<V> ==
             detail::range_utf_format_v<
                 transform_replacement_for_expr<F, V, Parser>>);
#endif

        template<
            typename R,
            typename Result,
            text::format OtherFormat = range_utf_format_v<remove_cv_ref_t<R>>,
            text::format Format = range_utf_format_v<remove_cv_ref_t<Result>>>
        struct utf_wrap
        {
            template<typename R_ = R>
            static auto call(R_ && r)
            {
                return (R_ &&) r | as_utf<OtherFormat>;
            }
        };
        template<typename R, typename Result, text::format Format>
        struct utf_wrap<R, Result, Format, Format>
        {
            template<typename R_ = R>
            static R_ && call(R_ && r)
            {
                return (R_ &&) r;
            }
        };
        template<typename R, typename Result>
        struct utf_wrap<R, Result, no_format, no_format>
        {
            template<typename R_ = R>
            static R_ && call(R_ && r)
            {
                return (R_ &&) r;
            }
        };
        template<typename R, typename Result, text::format Format>
        struct utf_wrap<R, Result, no_format, Format>
        {
            // 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<typename R, typename Result, text::format Format>
        struct utf_wrap<R, Result, Format, no_format>
        {
            // 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<typename T>
        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<typename R, typename F, typename Attr>
        struct utf_rvalue_shim
        {
            using result_type = std::invoke_result_t<F &, Attr>;
            using maybe_wrapped_result_type =
                decltype(utf_wrap<R, result_type>::call(
                    std::declval<result_type>()));
            static constexpr bool final_type_is_reference =
                std::is_lvalue_reference_v<maybe_wrapped_result_type>;
            using final_type = std::conditional_t<
                final_type_is_reference,
                regular_ref_wrapper<
                    std::remove_reference_t<maybe_wrapped_result_type>>,
                remove_cv_ref_t<maybe_wrapped_result_type>>;

            template<typename F_ = F>
            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<B>>
            decltype(auto) operator()(Attr && attr) const
            {
                result_ = final_type(
                    utf_wrap<R, result_type>::call((*f_)((Attr &&) attr)));
                return result_->get();
            }
            template<
                bool B = final_type_is_reference,
                typename Enable = std::enable_if_t<B>>
            decltype(auto) operator()(Attr && attr)
            {
                result_ = final_type(
                    utf_wrap<R, result_type>::call((*f_)((Attr &&) attr)));
                return result_->get();
            }
            template<
                bool B = final_type_is_reference,
                typename Enable = std::enable_if_t<!B>>
            final_type & operator()(Attr && attr) const
            {
                result_ = utf_wrap<R, result_type>::call((*f_)((Attr &&) attr));
                return *result_;
            }
            template<
                bool B = final_type_is_reference,
                typename Enable = std::enable_if_t<!B>>
            final_type & operator()(Attr && attr)
            {
                result_ = utf_wrap<R, result_type>::call((*f_)((Attr &&) attr));
                return *result_;
            }

            template<
                bool B = final_type_is_reference,
                typename Enable = std::enable_if_t<B>>
            decltype(auto) get() const
            {
                return result_->get();
            }
            template<
                bool B = final_type_is_reference,
                typename Enable = std::enable_if_t<B>>
            decltype(auto) get()
            {
                return result_->get();
            }
            template<
                bool B = final_type_is_reference,
                typename Enable = std::enable_if_t<!B>>
            final_type & get() const
            {
                return *result_;
            }
            template<
                bool B = final_type_is_reference,
                typename Enable = std::enable_if_t<!B>>
            final_type & get()
            {
                return *result_;
            }

            std::optional<F> f_;
            mutable std::optional<final_type> result_;
        };

        template<
            typename R,
            typename Parser,
            typename GlobalState,
            typename ErrorHandler,
            typename SkipParser>
        auto attr_search_impl(
            R && r,
            parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
            parser_interface<SkipParser> 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<parse_result_outer, bool>,
                "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<decltype(**std::declval<parse_result_outer>())>;

            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<SkipParser, eps_parser<phony>>) {
                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<Parser, GlobalState, ErrorHandler> const & parser,
            parser_interface<SkipParser> const & skip,
            trace trace_mode)
        {
            using value_type = range_value_t<decltype(r)>;
            if constexpr (std::is_same_v<value_type, char>) {
                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<decltype(attr)>>(
                    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<detail::transform_replacement_for<F, V, Parser>>
#endif
        >
#if BOOST_PARSER_USE_CONCEPTS
    requires detail::transform_replacement_for<F, V, Parser>
#endif
    struct transform_replace_view
        : detail::stl_interfaces::view_interface<transform_replace_view<
              V,
              F,
              Parser,
              GlobalState,
              ErrorHandler,
              SkipParser>>
    {
    private:
        using attr_t = detail::range_attr_t<V, Parser>;
        using replacement_range = std::invoke_result_t<F &, attr_t>;

    public:
        constexpr transform_replace_view() = default;
        constexpr transform_replace_view(
            V base,
            parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
            parser_interface<SkipParser> 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<Parser, GlobalState, ErrorHandler> 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<V>
#endif
        {
            return base_;
        }
        constexpr V base() && { return std::move(base_); }

        constexpr F const & f() const { return *f_.f_; }

        constexpr auto begin() { return iterator<false>{this}; }
        constexpr auto end() { return sentinel<false>{}; }

        constexpr auto begin() const
#if BOOST_PARSER_USE_CONCEPTS
            requires std::ranges::range<const V>
#endif
        {
            return iterator<true>{this};
        }
        constexpr auto end() const
#if BOOST_PARSER_USE_CONCEPTS
            requires std::ranges::range<const V>
#endif
        {
            return sentinel<true>{};
        }

        template<bool Const>
        struct sentinel
        {};

        template<bool Const>
        struct iterator
            : detail::stl_interfaces::proxy_iterator_interface<
                  iterator<Const>,
                  std::forward_iterator_tag,
                  BOOST_PARSER_SUBRANGE<detail::either_iterator<
                      detail::maybe_const<Const, V>,
                      detail::maybe_const<Const, replacement_range>>>>
        {
            using I = detail::iterator_t<detail::maybe_const<Const, V>>;
            using S = detail::sentinel_t<detail::maybe_const<Const, V>>;

            using ref_t_iter = detail::either_iterator<
                detail::maybe_const<Const, V>,
                detail::maybe_const<Const, replacement_range>>;
            using reference_type = BOOST_PARSER_SUBRANGE<ref_t_iter>;

            constexpr iterator() = default;
            constexpr iterator(
                detail::maybe_const<Const, transform_replace_view> * 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<I, S>(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<I, S>(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<Const>)
            {
                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<Const, transform_replace_view> * parent_ = {};
            BOOST_PARSER_SUBRANGE<I, S> r_;
            BOOST_PARSER_SUBRANGE<I> curr_;
            I next_it_ = {};
            bool in_match_ = {};
        };

        template<bool Const>
        friend struct iterator;

    private:
        V base_;
        F f_;
        parser_interface<Parser, GlobalState, ErrorHandler> parser_;
        parser_interface<SkipParser> 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, GlobalState, ErrorHandler>,
        parser_interface<SkipParser>,
        F &&,
        trace)
        -> transform_replace_view<
            detail::text::detail::all_t<V>,
            detail::remove_cv_ref_t<F>,
            Parser,
            GlobalState,
            ErrorHandler,
            SkipParser>;

    template<
        typename V,
        typename F,
        typename Parser,
        typename GlobalState,
        typename ErrorHandler,
        typename SkipParser>
    transform_replace_view(
        V &&,
        parser_interface<Parser, GlobalState, ErrorHandler>,
        parser_interface<SkipParser>,
        F &&)
        -> transform_replace_view<
            detail::text::detail::all_t<V>,
            detail::remove_cv_ref_t<F>,
            Parser,
            GlobalState,
            ErrorHandler,
            SkipParser>;

    template<
        typename V,
        typename F,
        typename Parser,
        typename GlobalState,
        typename ErrorHandler>
    transform_replace_view(
        V &&, parser_interface<Parser, GlobalState, ErrorHandler>, F &&, trace)
        -> transform_replace_view<
            detail::text::detail::all_t<V>,
            detail::remove_cv_ref_t<F>,
            Parser,
            GlobalState,
            ErrorHandler,
            parser_interface<eps_parser<detail::phony>>>;

    template<
        typename V,
        typename F,
        typename Parser,
        typename GlobalState,
        typename ErrorHandler>
    transform_replace_view(
        V &&, parser_interface<Parser, GlobalState, ErrorHandler>, F &&)
        -> transform_replace_view<
            detail::text::detail::all_t<V>,
            detail::remove_cv_ref_t<F>,
            Parser,
            GlobalState,
            ErrorHandler,
            parser_interface<eps_parser<detail::phony>>>;

    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<V>(),
            std::declval<
                parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
            std::declval<parser_interface<SkipParser> const &>(),
            std::declval<F>(),
            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
                // clang-format off
                std::ranges::viewable_range<R> &&
                std::regular_invocable<
                    F &,
                    range_attr_t<to_range_t<R>, Parser>> &&
                // clang-format on
                can_transform_replace_view<
                    to_range_t<R>,
                    utf_rvalue_shim<
                        to_range_t<R>,
                        std::remove_cvref_t<F>,
                        range_attr_t<to_range_t<R>, Parser>>,
                    Parser,
                    GlobalState,
                    ErrorHandler,
                    SkipParser>
                // clang-format off
            [[nodiscard]] constexpr auto operator()(
                R && r,
                parser_interface<Parser, GlobalState, ErrorHandler> const &
                    parser,
                parser_interface<SkipParser> const & skip,
                F && f,
                trace trace_mode = trace::off) const
            // clang-format on
            {
                return transform_replace_view(
                    to_range<R>::call((R &&) r),
                    parser,
                    skip,
                    utf_rvalue_shim<
                        to_range_t<R>,
                        std::remove_cvref_t<F>,
                        range_attr_t<to_range_t<R>, Parser>>((F &&) f),
                    trace_mode);
            }

            template<
                parsable_range R,
                std::move_constructible F,
                typename Parser,
                typename GlobalState,
                typename ErrorHandler>
            requires
                // clang-format off
                std::ranges::viewable_range<R> &&
                std::regular_invocable<
                    F &,
                    range_attr_t<to_range_t<R>, Parser>> &&
                // clang-format on
                can_transform_replace_view<
                    to_range_t<R>,
                    utf_rvalue_shim<
                        to_range_t<R>,
                        std::remove_cvref_t<F>,
                        range_attr_t<to_range_t<R>, Parser>>,
                    Parser,
                    GlobalState,
                    ErrorHandler,
                    parser_interface<eps_parser<detail::phony>>>
                // clang-format off
            [[nodiscard]] constexpr auto operator()(
                R && r,
                parser_interface<Parser, GlobalState, ErrorHandler> const &
                    parser,
                F && f,
                trace trace_mode = trace::off) const
            // clang-format on
            {
                return (*this)(
                    (R &&) r,
                    parser,
                    parser_interface<eps_parser<detail::phony>>{},
                    (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<is_parsable_range_v<R>>>
            [[nodiscard]] constexpr auto operator()(
                R && r,
                parser_interface<Parser, GlobalState, ErrorHandler> const &
                    parser,
                SkipParser && skip,
                F && f = F{},
                Trace trace_mode = Trace{}) const
            {
                if constexpr (
                    is_parser_iface<remove_cv_ref_t<SkipParser>> &&
                    std::is_invocable_v<
                        F &,
                        range_attr_t<to_range_t<R>, Parser>> &&
                    std::is_same_v<Trace, trace>) {
                    // (r, parser, skip, f, trace) case
                    return impl(
                        to_range<R>::call((R &&) r),
                        parser,
                        skip,
                        (F &&) f,
                        trace_mode);
                } else if constexpr (
                    std::is_invocable_v<
                        SkipParser &,
                        range_attr_t<to_range_t<R>, Parser>> &&
                    std::is_same_v<remove_cv_ref_t<F>, trace> &&
                    std::is_same_v<Trace, trace>) {
                    // (r, parser, f, trace) case
                    return impl(
                        to_range<R>::call((R &&) r),
                        parser,
                        parser_interface<eps_parser<detail::phony>>{},
                        (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<Parser, GlobalState, ErrorHandler> const &
                    parser,
                parser_interface<SkipParser> 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<F>,
                        range_attr_t<R, Parser>>((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<boost::parser::transform_replace_view<
        V,
        F,
        Parser,
        GlobalState,
        ErrorHandler,
        SkipParser>> = std::ranges::enable_borrowed_range<V> &&
                       (std::ranges::enable_borrowed_range<F> ||
                        boost::parser::detail::tidy_func<F>);
#endif

#endif

#endif