boost/parser/replace.hpp
#ifndef BOOST_PARSER_REPLACE_HPP
#define BOOST_PARSER_REPLACE_HPP
#include <boost/parser/search.hpp>
#if !defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS
namespace boost::parser {
namespace detail {
template<typename T, bool = std::is_pointer_v<remove_cv_ref_t<T>>>
constexpr auto range_value_type =
wrapper<remove_cv_ref_t<range_value_t<T>>>{};
template<typename T>
constexpr auto range_value_type<T, true> = wrapper<
remove_cv_ref_t<std::remove_pointer_t<remove_cv_ref_t<T>>>>{};
template<typename T>
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<T>{}) {
return format::utf8;
} else {
#endif
using value_t = typename decltype(range_value_type<T>)::type;
if constexpr (std::is_same_v<value_t, char>) {
return no_format;
#if defined(__cpp_char8_t)
} else if constexpr (std::is_same_v<value_t, char8_t>) {
return format::utf8;
#endif
} else if constexpr (
std::is_same_v<value_t, char16_t>
#ifdef _MSC_VER
|| std::is_same_v<T, wchar_t>
#endif
) {
return format::utf16;
} else if constexpr (
std::is_same_v<value_t, char32_t>
#ifndef _MSC_VER
|| std::is_same_v<T, wchar_t>
#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<typename T>
constexpr text::format
range_utf_format_v = detail::range_utf_format<remove_cv_ref_t<T>>();
template<typename V1, typename V2>
using concat_reference_t =
std::common_type_t<range_reference_t<V1>, range_reference_t<V2>>;
template<typename V1, typename V2>
using concat_value_t =
std::common_type_t<range_value_t<V1>, range_value_t<V2>>;
template<typename V1, typename V2>
using concat_rvalue_reference_t = std::common_type_t<
range_rvalue_reference_t<V1>,
range_rvalue_reference_t<V2>>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename ReplacementV, typename V>
concept concatable = requires {
typename detail::concat_reference_t<ReplacementV, V>;
typename detail::concat_value_t<ReplacementV, V>;
typename detail::concat_rvalue_reference_t<ReplacementV, V>;
};
// clang-format on
#else
template<typename ReplacementV, typename V>
// clang-format off
using concatable_expr = decltype(
std::declval<concat_reference_t<ReplacementV, V>>(),
std::declval<concat_value_t<ReplacementV, V>>(),
std::declval<concat_rvalue_reference_t<ReplacementV, V>>());
// clang-format on
template<typename ReplacementV, typename V>
constexpr bool concatable =
is_detected_v<concatable_expr, ReplacementV, V>;
#endif
template<
typename V1,
typename V2
#if !BOOST_PARSER_USE_CONCEPTS
,
typename Enable = std::enable_if_t<concatable<V1, V2>>
#endif
>
#if BOOST_PARSER_USE_CONCEPTS
requires concatable<V1, V2>
#endif
struct either_iterator_impl
: detail::stl_interfaces::iterator_interface<
either_iterator_impl<V1, V2>,
std::forward_iterator_tag,
concat_value_t<V1, V2>,
concat_reference_t<V1, V2>>
{
constexpr either_iterator_impl() = default;
constexpr either_iterator_impl(iterator_t<V1> it) : it_(it) {}
template<typename V = V2>
constexpr either_iterator_impl(iterator_t<V> it) : it_(it)
{}
constexpr concat_reference_t<V1, V2> 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<V1, V2>,
std::forward_iterator_tag,
concat_value_t<V1, V2>,
concat_reference_t<V1, V2>>;
using base_type::operator++;
private:
std::variant<iterator_t<V1>, iterator_t<V2>> it_;
};
template<typename V1, typename V2>
using either_iterator = std::conditional_t<
std::is_same_v<iterator_t<V1>, iterator_t<V2>>,
iterator_t<V1>,
either_iterator_impl<V1, V2>>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename ReplacementV, typename V>
concept replacement_for = requires (ReplacementV replacement, V base) {
{ either_iterator<V, ReplacementV>(replacement.begin()) };
{ either_iterator<V, ReplacementV>(replacement.end()) };
{ either_iterator<V, ReplacementV>(base.begin()) };
};
// clang-format on
#else
template<typename ReplacementV, typename V>
using replacement_for_expr = decltype(
either_iterator<V, ReplacementV>(
std::declval<ReplacementV&>().begin()),
either_iterator<V, ReplacementV>(
std::declval<ReplacementV&>().end()),
either_iterator<V, ReplacementV>(std::declval<V&>().begin()));
template<typename ReplacementV, typename V>
constexpr bool replacement_for =
is_detected_v<replacement_for_expr, ReplacementV, 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<ReplacementV, V> &&
(detail::range_utf_format_v<V> ==
detail::range_utf_format_v<ReplacementV>)>
#endif
>
#if BOOST_PARSER_USE_CONCEPTS
requires detail::replacement_for<ReplacementV, V> &&
(detail::range_utf_format_v<V> ==
detail::range_utf_format_v<ReplacementV>)
#endif
struct replace_view
: detail::stl_interfaces::view_interface<replace_view<
V,
ReplacementV,
Parser,
GlobalState,
ErrorHandler,
SkipParser>>
{
constexpr replace_view() = default;
constexpr replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> 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<Parser, GlobalState, ErrorHandler> 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<V>
#endif
{
return base_;
}
constexpr V base() && { return std::move(base_); }
constexpr V replacement() const &
#if BOOST_PARSER_USE_CONCEPTS
requires std::copy_constructible<V>
#endif
{
return replacement_;
}
constexpr V replacement() && { return std::move(replacement_); }
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, ReplacementV>>>>
{
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, ReplacementV>>;
using reference_type = BOOST_PARSER_SUBRANGE<ref_t_iter>;
constexpr iterator() = default;
constexpr iterator(
detail::maybe_const<Const, 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 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<I, S>(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<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, 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_;
ReplacementV replacement_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<SkipParser> 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, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
ReplacementV &&,
trace)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
ReplacementV &&)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
ReplacementV &&,
trace)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
ReplacementV &&)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
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<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
std::declval<ReplacementV>(),
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
// clang-format off
std::ranges::viewable_range<R> &&
std::ranges::viewable_range<ReplacementR> &&
// clang-format on
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
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,
ReplacementR && replacement,
trace trace_mode = trace::off) const
// clang-format on
{
return replace_view(
to_range<R>::call((R &&) r),
parser,
skip,
to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::call((ReplacementR &&)
replacement),
trace_mode);
}
template<
parsable_range R,
std::ranges::range ReplacementR,
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires
// clang-format off
std::ranges::viewable_range<R> &&
std::ranges::viewable_range<ReplacementR> &&
// clang-format on
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
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,
ReplacementR && replacement,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(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<is_parsable_range_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
SkipParser && skip,
ReplacementR && replacement = ReplacementR{},
Trace trace_mode = Trace{}) const
{
if constexpr (
is_parser_iface<remove_cv_ref_t<SkipParser>> &&
is_range<remove_cv_ref_t<ReplacementR>> &&
std::is_same_v<Trace, trace>) {
// (r, parser, skip, replacement, trace) case
return impl(
(R &&) r,
parser,
skip,
(ReplacementR &&) replacement,
trace_mode);
} else if constexpr (
is_range<remove_cv_ref_t<SkipParser>> &&
std::is_same_v<remove_cv_ref_t<ReplacementR>, trace> &&
std::is_same_v<Trace, trace>) {
// (r, parser, replacement, trace) case
return impl(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(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<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
ReplacementR && replacement,
trace trace_mode = trace::off) const
{
return replace_view(
to_range<R>::call((R &&) r),
parser,
skip,
to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::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<detail::replace_impl>
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<boost::parser::replace_view<
V,
ReplacementV,
Parser,
GlobalState,
ErrorHandler,
SkipParser>> = std::ranges::enable_borrowed_range<V> &&
std::ranges::enable_borrowed_range<ReplacementV>;
#endif
#endif
#endif