2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-19 16:32:13 +00:00

1 Commits

Author SHA1 Message Date
Zach Laine
06fb60c78f Add an intial sketch of a memoization system. 2024-03-17 20:09:39 -05:00
11 changed files with 1708 additions and 244 deletions

View File

@@ -51,14 +51,16 @@ namespace boost { namespace parser {
typename S,
typename ErrorHandler,
typename GlobalState>
using minimal_parse_context = decltype(detail::make_context<false, false>(
std::declval<I>(),
std::declval<S>(),
std::declval<bool &>(),
std::declval<int &>(),
std::declval<ErrorHandler const &>(),
std::declval<detail::nope &>(),
std::declval<detail::symbol_table_tries_t &>()));
using minimal_parse_context =
decltype(detail::make_context<false, false>(
std::declval<I>(),
std::declval<S>(),
std::declval<bool &>(),
std::declval<int &>(),
std::declval<ErrorHandler const &>(),
std::declval<detail::nope &>(),
std::declval<detail::symbol_table_tries_t &>(),
std::declval<detail::nope &>()));
template<typename T, typename I, typename S, typename GlobalState>
concept error_handler =

View File

@@ -0,0 +1,44 @@
#ifndef BOOST_PARSER_DETAIL_COUNTED_ITERATOR_HPP
#define BOOST_PARSER_DETAIL_COUNTED_ITERATOR_HPP
#include <boost/parser/detail/stl_interfaces/iterator_interface.hpp>
namespace boost::parser::detail {
template<typename I, typename S>
struct counted_iterator
: stl_interfaces::iterator_interface<
counted_iterator<I, S>,
std::forward_iterator_tag,
std::remove_cv_t<
std::remove_reference_t<decltype(*std::declval<I>())>>,
decltype(*std::declval<I>())>
{
constexpr counted_iterator() = default;
constexpr explicit counted_iterator(I & it) : it_(it) {}
constexpr size_t count() const { return count_; }
constexpr bool operator==(S last) const { return it_ == last; }
constexpr I base() const { return it_; }
constexpr counted_iterator & operator++()
{
++it_;
++count_;
return this;
}
private:
friend stl_interfaces::access;
constexpr I & base_reference() { return it_; }
constexpr I base_reference() const { return it_; }
I it_ = I();
size_t count_ = 0;
};
//
}
#endif

View File

@@ -0,0 +1,458 @@
#ifndef BOOST_PARSER_DETAIL_MEMOS_HPP
#define BOOST_PARSER_DETAIL_MEMOS_HPP
#if __has_include(<boost/unordered/unordered_flat_map.hpp>)
#include <boost/unordered/unordered_flat_map.hpp>
#define BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP 1
#define BOOST_PARSER_MEMO_NS boost_unordered_flat_map
#else
#define BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP 0
#define BOOST_PARSER_MEMO_NS std_containers
#endif
#include <algorithm>
#include <type_traits>
#include <typeinfo>
#include <unordered_map>
#include <variant> // monostate
#include <vector>
namespace boost::parser::detail {
inline constexpr unsigned int next_pot(unsigned int i)
{
--i;
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
return ++i;
}
template<int Size, int Align>
struct trivial_type
{};
template<typename T>
using trivial_type_for = trivial_type<sizeof(T), alignof(T)>;
template<typename T, int N>
struct list_node
{
list_node() = default;
list_node(list_node * n) : next(n) {}
~list_node()
{
for (int i = 0; i < size; ++i) {
void * pos = buf + i * sizeof(T);
T * obj = static_cast<T *>(pos);
obj->~T();
}
}
void * push() { return buf + size++ * sizeof(T); }
void * get(int n) { return buf + n * sizeof(T); }
alignas(T[N]) std::byte buf[sizeof(T[N])];
list_node * next = nullptr;
int size = 0;
};
template<int Size, int Align, int N>
struct list_node<trivial_type<Size, Align>, N>
{
list_node() = default;
list_node(list_node * n) : next(n) {}
~list_node() {}
void * push() { return buf + size++ * Size; }
void * get(int n) { return buf + n * Size; }
alignas(Align) std::byte buf[Size * N];
list_node * next = nullptr;
int size = 0;
};
template<typename T, int N>
struct linked_list
{
using list_node_type = list_node<T, N>;
linked_list() = default;
linked_list(linked_list const &) = delete;
linked_list & operator=(linked_list const &) = delete;
~linked_list()
{
while (head_) {
auto * node = head_;
head_ = head_->next;
delete node;
}
}
// The bool indicates whether placement new is required.
std::pair<void *, bool> push()
{
if (next_.node) {
if (next_.idx == next_.node->size) {
next_.node = next_.node->next;
next_.idx = 0;
}
if (next_.node && next_.idx < next_.node->size)
return {next_.node->get(next_.idx++), false};
}
if (!head_)
head_ = new list_node_type();
else if (head_->size == N)
head_ = new list_node_type(head_);
return {head_->push(), true};
}
void reclaim() { next_ = {head_, 0}; }
private:
struct position
{
list_node_type * node = nullptr;
int idx = 0;
};
list_node_type * head_ = nullptr;
position next_{};
};
// http://jonkagstrom.com/mx3/mx3_rev2.html
inline size_t hash_mix(size_t x)
{
size_t const m = 0xe9846af9b1a615d;
x ^= x >> 32;
x *= m;
x ^= x >> 32;
x *= m;
x ^= x >> 28;
return x;
}
inline size_t hash_combine(size_t seed) { return seed; }
template<typename T, typename... Ts>
size_t hash_combine(size_t seed, T const & x, Ts const &... xs)
{
auto next_seed =
detail::hash_mix(seed + 0x9e3779b9 + std::hash<T>{}(x));
return detail::hash_combine(next_seed, xs...);
}
struct memo_items_base
{
memo_items_base(size_t id_token)
#if !BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP
:
id_token_(id_token)
#endif
{}
virtual ~memo_items_base() = 0;
virtual std::pair<void *, bool> new_item() = 0;
virtual void reclaim() = 0;
#if !BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP
size_t const id_token_ = 0;
#endif
};
inline memo_items_base::~memo_items_base() {}
inline constexpr size_t max_tidy_bytes = 16; // TODO: Tune.
template<typename T>
constexpr bool tidy = std::is_trivially_copyable_v<T> &&
std::is_trivially_destructible_v<T> &&
sizeof(T) < max_tidy_bytes;
template<typename T>
constexpr bool fat = 256 < sizeof(T);
template<typename T, bool Fat = fat<T>>
struct memo_items_impl : memo_items_base
{
memo_items_impl() : memo_items_base(typeid(T).hash_code()) {}
virtual ~memo_items_impl() = default;
virtual std::pair<void *, bool> new_item() { return list_.push(); }
virtual void reclaim() { list_.reclaim(); }
private:
linked_list<T, 16> list_; // TODO: Try with other values of N too.
};
template<int Size, int Align, bool Fat>
struct memo_items_impl<trivial_type<Size, Align>, Fat> : memo_items_base
{
memo_items_impl() :
memo_items_base(typeid(trivial_type<Size, Align>).hash_code())
{}
virtual ~memo_items_impl() = default;
virtual std::pair<void *, bool> new_item() { return list_.push(); }
virtual void reclaim() { list_.reclaim(); }
private:
linked_list<trivial_type<Size, Align>, 64> list_;
};
template<typename T>
struct memo_items_impl<T, true> : memo_items_base
{
memo_items_impl() : memo_items_base(typeid(T).hash_code()) {}
virtual ~memo_items_impl() = default;
virtual std::pair<void *, bool> new_item() { return list_.push(); }
virtual void reclaim() { list_.reclaim(); }
private:
linked_list<T, 1> list_;
};
struct identity
{
template<typename T>
constexpr decltype(auto) operator()(T && x) const
{
return (T &&) x;
}
};
inline namespace BOOST_PARSER_MEMO_NS {
template<
typename Key,
typename OtherDatum = std::monostate,
typename Proj = identity>
struct memos
{
memos() = default;
explicit memos(Proj proj) : proj_(std::move(proj)) {}
~memos()
{
#if BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP
for (auto [key, value] : memo_items_) {
delete value;
}
#else
for (auto value : memo_items_) {
delete value;
}
#endif
}
enum kind { success, failure };
template<typename A>
struct monostate_ptr_like
{
monostate_ptr_like() : valid(false) {}
explicit monostate_ptr_like(bool v) : valid(v) {}
explicit operator bool() const { return valid; }
bool operator==(nullptr_t) const { return !valid; }
A & operator*() { return value; }
A const & operator*() const { return value; }
A value = A();
bool valid = false;
static_assert(std::is_empty_v<A>);
};
template<typename A, bool Empty = std::is_empty_v<A>>
struct ref
{
ref() = default;
ref(A * v, OtherDatum * od) : valid(true), value(v), datum(od)
{}
explicit operator bool() const { return valid; }
kind get_kind() const
{
return value == nullptr ? failure : success;
}
bool valid = false;
A * value = nullptr;
OtherDatum * datum = 0;
};
template<typename A>
struct ref<A, true>
{
ref() = default;
ref(A *, OtherDatum * od) : valid(true), value(true), datum(od)
{}
explicit operator bool() const { return valid; }
kind get_kind() const
{
return value == nullptr ? failure : success;
}
bool valid = false;
monostate_ptr_like<A> value;
OtherDatum * datum = 0;
};
template<typename A, bool Empty = std::is_empty_v<A>>
struct const_ref
{
const_ref() = default;
const_ref(A const * v, OtherDatum const * od) :
valid(true), value(v), datum(od)
{}
explicit operator bool() const { return valid; }
kind get_kind() const
{
return value == nullptr ? failure : success;
}
bool valid = false;
A const * value = nullptr;
OtherDatum const * datum = 0;
};
template<typename A>
struct const_ref<A, true>
{
const_ref() = default;
const_ref(A const *, OtherDatum const * od) :
valid(true), value(true), datum(od)
{}
explicit operator bool() const { return valid; }
kind get_kind() const
{
return value == nullptr ? failure : success;
}
bool valid = false;
monostate_ptr_like<A> value;
OtherDatum const * datum = 0;
};
template<typename P, typename A, typename... Params>
ref<A> insert(kind k, Key key, Params const &... params)
{
if constexpr (!(std::is_constructible_v<std::hash<Params>> &&
...)) {
return {};
} else {
auto const hash = detail::hash_combine(
proj_(key),
typeid(P).hash_code(),
typeid(A).hash_code(),
std::hash<Params>{}(params)...);
entry & obj_and_other = table_[hash];
if (!obj_and_other.obj && k == success) {
auto const id =
tidy<A> ? typeid(trivial_type_for<A>).hash_code()
: typeid(A).hash_code();
if (std::is_empty_v<A>) {
obj_and_other = {nullptr, OtherDatum()};
} else {
#if BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP
memo_items_base *& items = memo_items_[id];
if (!items) {
if (tidy<A>) {
items = new memo_items_impl<
trivial_type_for<A>>;
} else {
items = new memo_items_impl<A>;
}
}
auto [pos, needs_construction] = items->new_item();
A * obj = needs_construction
? new (pos) A()
: static_cast<A *>(pos);
obj_and_other = {obj, OtherDatum()};
#else
auto it = std::lower_bound(
memo_items_.begin(),
memo_items_.end(),
id,
[](memo_items_base * base, size_t id) {
return base->id_token_ < id;
});
if (it == memo_items_.end() ||
(*it)->id_token_ != id) {
if (tidy<A>) {
it = memo_items_.insert(
it,
new memo_items_impl<
trivial_type_for<A>>);
} else {
it = memo_items_.insert(
it, new memo_items_impl<A>);
}
}
auto [pos, needs_construction] = (*it)->new_item();
A * obj = needs_construction
? new (pos) A()
: static_cast<A *>(pos);
obj_and_other = {obj, OtherDatum()};
#endif
}
}
return {
static_cast<A *>(obj_and_other.obj),
&obj_and_other.datum};
}
}
template<typename P, typename A, typename... Params>
const_ref<A> find(Key key, Params const &... params) const
{
if constexpr (!(std::is_constructible_v<std::hash<Params>> &&
...)) {
return {};
} else {
auto const hash = detail::hash_combine(
proj_(key),
typeid(P).hash_code(),
typeid(A).hash_code(),
std::hash<Params>{}(params)...);
auto it = table_.find(hash);
if (it == table_.end()) {
return {};
} else {
return {
static_cast<A const *>(it->second.obj),
&it->second.datum};
}
}
}
void reclaim()
{
#if BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP
for (auto [key, items] : memo_items_) {
items->reclaim();
}
#else
for (auto items : memo_items_) {
items->reclaim();
}
#endif
table_.clear();
}
// For testing.
size_t item_stores() const { return memo_items_.size(); }
size_t items() const { return table_.size(); }
private:
struct entry
{
void * obj;
[[maybe_unused]] OtherDatum datum;
};
#if BOOST_PARSER_HAVE_BOOST_UNORDERED_FLAT_MAP
boost::unordered_flat_map<size_t, memo_items_base *> memo_items_;
boost::unordered_flat_map<size_t, entry> table_;
#else
std::vector<memo_items_base *> memo_items_;
std::unordered_map<size_t, entry> table_;
#endif
Proj proj_;
};
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@
#include <boost/parser/config.hpp>
#include <boost/parser/error_handling_fwd.hpp>
#include <boost/parser/detail/memos.hpp>
#include <any>
#include <cstdint>
@@ -79,7 +80,8 @@ namespace boost { namespace parser {
bool UseCallbacks,
typename Iter,
typename Sentinel,
typename ErrorHandler>
typename ErrorHandler,
typename Memos>
inline auto make_context(
Iter first,
Sentinel last,
@@ -87,7 +89,8 @@ namespace boost { namespace parser {
int & indent,
ErrorHandler const & error_handler,
nope &,
symbol_table_tries_t & symbol_table_tries) noexcept;
symbol_table_tries_t & symbol_table_tries,
Memos & memos) noexcept;
struct skip_skipper;
@@ -417,7 +420,8 @@ namespace boost { namespace parser {
template<
typename Parser,
typename GlobalState = detail::nope,
typename ErrorHandler = default_error_handler>
typename ErrorHandler = default_error_handler,
bool Memoize = false>
struct parser_interface;
using no_attribute = detail::nope;

View File

@@ -210,6 +210,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser
#if !BOOST_PARSER_USE_CONCEPTS
,
@@ -231,12 +232,14 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>>
{
constexpr replace_view() = default;
constexpr replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
ReplacementV replacement,
trace trace_mode = trace::off) :
@@ -248,7 +251,8 @@ namespace boost::parser {
{}
constexpr replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
ReplacementV replacement,
trace trace_mode = trace::off) :
base_(std::move(base)),
@@ -395,7 +399,7 @@ namespace boost::parser {
private:
V base_;
ReplacementV replacement_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
@@ -407,10 +411,11 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>,
ReplacementV &&,
trace)
@@ -420,6 +425,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
@@ -428,10 +434,11 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>,
ReplacementV &&)
-> replace_view<
@@ -440,6 +447,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
@@ -447,10 +455,11 @@ namespace boost::parser {
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
ReplacementV &&,
trace)
-> replace_view<
@@ -459,6 +468,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
template<
@@ -466,10 +476,11 @@ namespace boost::parser {
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
ReplacementV &&)
-> replace_view<
detail::text::detail::all_t<V>,
@@ -477,6 +488,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
@@ -486,6 +498,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename Memoize,
typename SkipParser>
using replace_view_expr = decltype(replace_view<
V,
@@ -493,10 +506,14 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize::value,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize::value> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
std::declval<ReplacementV>(),
trace::on));
@@ -507,6 +524,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool can_replace_view = is_detected_v<
replace_view_expr,
@@ -515,6 +533,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
std::bool_constant<Memoize>,
SkipParser>;
struct replace_impl
@@ -527,6 +546,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
requires
// clang-format off
@@ -545,11 +565,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
ReplacementR && replacement,
@@ -573,7 +594,8 @@ namespace boost::parser {
range_like ReplacementR,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
requires
// clang-format off
(std::is_pointer_v<std::remove_cvref_t<R>> ||
@@ -591,11 +613,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
ReplacementR && replacement,
trace trace_mode = trace::off) const
@@ -616,14 +639,18 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser,
typename ReplacementR = trace,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
SkipParser && skip,
ReplacementR && replacement = ReplacementR{},
Trace trace_mode = Trace{}) const
@@ -666,11 +693,15 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
parser_interface<SkipParser> const & skip,
ReplacementR && replacement,
trace trace_mode = trace::off) const
@@ -709,6 +740,7 @@ template<
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool std::ranges::enable_borrowed_range<boost::parser::replace_view<
V,
@@ -716,6 +748,7 @@ constexpr bool std::ranges::enable_borrowed_range<boost::parser::replace_view<
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>> = std::ranges::enable_borrowed_range<V> &&
std::ranges::enable_borrowed_range<ReplacementV>;
#endif

View File

@@ -93,10 +93,12 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
auto search_impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
@@ -127,6 +129,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::borrowed_subrange_t<R>
@@ -135,7 +138,8 @@ namespace boost::parser {
#endif
search_repack_shim(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
@@ -172,6 +176,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser
#if !BOOST_PARSER_USE_CONCEPTS
,
@@ -180,7 +185,8 @@ namespace boost::parser {
>
auto search(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off)
{
@@ -210,11 +216,13 @@ namespace boost::parser {
detail::is_parsable_iter_v<I> &&
detail::is_equality_comparable_with_v<I, S>>
#endif
>
,
bool Memoize>
auto search(
I first,
S last,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off)
{
@@ -234,7 +242,8 @@ namespace boost::parser {
#endif
typename Parser,
typename GlobalState,
typename ErrorHandler
typename ErrorHandler,
bool Memoize
#if !BOOST_PARSER_USE_CONCEPTS
,
typename Enable = std::enable_if_t<detail::is_parsable_range_like_v<R>>
@@ -242,7 +251,8 @@ namespace boost::parser {
>
auto search(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
trace trace_mode = trace::off)
{
return parser::search(
@@ -273,11 +283,13 @@ namespace boost::parser {
detail::is_parsable_iter_v<I> &&
detail::is_equality_comparable_with_v<I, S>>
#endif
>
,
bool Memoize>
auto search(
I first,
S last,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
trace trace_mode = trace::off)
{
return parser::search(
@@ -299,15 +311,22 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
struct search_all_view
: detail::stl_interfaces::view_interface<
search_all_view<V, Parser, GlobalState, ErrorHandler, SkipParser>>
: detail::stl_interfaces::view_interface<search_all_view<
V,
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>>
{
constexpr search_all_view() = default;
constexpr search_all_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) :
base_(std::move(base)),
@@ -317,7 +336,8 @@ namespace boost::parser {
{}
constexpr search_all_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
trace trace_mode = trace::off) :
base_(std::move(base)),
parser_(parser),
@@ -421,7 +441,7 @@ namespace boost::parser {
private:
V base_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
@@ -432,10 +452,11 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
search_all_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>,
trace)
-> search_all_view<
@@ -443,6 +464,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
@@ -450,43 +472,52 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
search_all_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>)
-> search_all_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
search_all_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, trace)
V &&,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
trace)
-> search_all_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
search_all_view(V &&, parser_interface<Parser, GlobalState, ErrorHandler>)
typename ErrorHandler,
bool Memoize>
search_all_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler, Memoize>)
-> search_all_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
@@ -495,16 +526,21 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename Memoize,
typename SkipParser>
using search_all_view_expr = decltype(search_all_view<
V,
Parser,
GlobalState,
ErrorHandler,
Memoize::value,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize::value> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
trace::on));
@@ -513,6 +549,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool can_search_all_view = is_detected_v<
search_all_view_expr,
@@ -520,6 +557,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
std::bool_constant<Memoize>,
SkipParser>;
struct search_all_impl
@@ -530,7 +568,7 @@ namespace boost::parser {
parsable_range_like R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename ErrorHandler, bool Memoize,
typename SkipParser>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
@@ -540,11 +578,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
@@ -558,7 +597,7 @@ namespace boost::parser {
parsable_range_like R,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler, bool Memoize>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
@@ -567,11 +606,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
trace trace_mode = trace::off) const
// clang-format on
@@ -590,14 +630,18 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser =
parser_interface<eps_parser<detail::phony>>,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
SkipParser const & skip = SkipParser{},
Trace trace_mode = Trace{}) const
{
@@ -631,11 +675,15 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
{
@@ -663,11 +711,16 @@ template<
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool std::ranges::enable_borrowed_range<
boost::parser::
search_all_view<V, Parser, GlobalState, ErrorHandler, SkipParser>> =
std::ranges::enable_borrowed_range<V>;
constexpr bool
std::ranges::enable_borrowed_range<boost::parser::search_all_view<
V,
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>> = std::ranges::enable_borrowed_range<V>;
#endif
#endif

View File

@@ -18,15 +18,21 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
struct split_view
: detail::stl_interfaces::view_interface<
split_view<V, Parser, GlobalState, ErrorHandler, SkipParser>>
struct split_view : detail::stl_interfaces::view_interface<split_view<
V,
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>>
{
constexpr split_view() = default;
constexpr split_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) :
base_(std::move(base)),
@@ -36,7 +42,8 @@ namespace boost::parser {
{}
constexpr split_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
trace trace_mode = trace::off) :
base_(std::move(base)),
parser_(parser),
@@ -147,7 +154,7 @@ namespace boost::parser {
private:
V base_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
@@ -158,10 +165,11 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
split_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>,
trace)
-> split_view<
@@ -169,6 +177,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
@@ -176,43 +185,52 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
split_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
split_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, trace)
V &&,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
trace)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
split_view(V &&, parser_interface<Parser, GlobalState, ErrorHandler>)
typename ErrorHandler,
bool Memoize>
split_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler, Memoize>)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
@@ -221,16 +239,21 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename Memoize,
typename SkipParser>
using split_view_expr = decltype(split_view<
V,
Parser,
GlobalState,
ErrorHandler,
SkipParser>(
V,
Parser,
GlobalState,
ErrorHandler,
Memoize::value,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize::value> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
trace::on));
@@ -239,6 +262,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool can_split_view = is_detected_v<
split_view_expr,
@@ -246,6 +270,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
std::bool_constant<Memoize>,
SkipParser>;
struct split_impl
@@ -257,6 +282,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
@@ -266,11 +292,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
@@ -284,7 +311,8 @@ namespace boost::parser {
parsable_range_like R,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
@@ -293,11 +321,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
trace trace_mode = trace::off) const
// clang-format on
@@ -316,14 +345,18 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser =
parser_interface<eps_parser<detail::phony>>,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
SkipParser const & skip = SkipParser{},
Trace trace_mode = Trace{}) const
{
@@ -357,11 +390,15 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
{
@@ -389,10 +426,11 @@ template<
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool std::ranges::enable_borrowed_range<
boost::parser::
split_view<V, Parser, GlobalState, ErrorHandler, SkipParser>> =
split_view<V, Parser, GlobalState, ErrorHandler, Memoize, SkipParser>> =
std::ranges::enable_borrowed_range<V>;
#endif

View File

@@ -212,10 +212,12 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
auto attr_search_impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
@@ -282,10 +284,12 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
auto attr_search_repack_shim(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
@@ -334,6 +338,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser
#if !BOOST_PARSER_USE_CONCEPTS
,
@@ -351,6 +356,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>>
{
private:
@@ -361,7 +367,8 @@ namespace boost::parser {
constexpr transform_replace_view() = default;
constexpr transform_replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
F f,
trace trace_mode = trace::off) :
@@ -373,7 +380,8 @@ namespace boost::parser {
{}
constexpr transform_replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
F f,
trace trace_mode = trace::off) :
base_(std::move(base)),
@@ -518,7 +526,7 @@ namespace boost::parser {
private:
V base_;
F f_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
@@ -530,10 +538,11 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
transform_replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>,
F &&,
trace)
@@ -543,6 +552,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
@@ -551,10 +561,11 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
transform_replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
parser_interface<SkipParser>,
F &&)
-> transform_replace_view<
@@ -563,6 +574,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>;
template<
@@ -570,15 +582,20 @@ namespace boost::parser {
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
transform_replace_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, F &&, trace)
V &&,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
F &&,
trace)
-> transform_replace_view<
detail::text::detail::all_t<V>,
detail::remove_cv_ref_t<F>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
template<
@@ -586,15 +603,19 @@ namespace boost::parser {
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
transform_replace_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, F &&)
V &&,
parser_interface<Parser, GlobalState, ErrorHandler, Memoize>,
F &&)
-> transform_replace_view<
detail::text::detail::all_t<V>,
detail::remove_cv_ref_t<F>,
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
@@ -604,6 +625,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename Memoize,
typename SkipParser>
using transform_replace_view_expr = decltype(transform_replace_view<
V,
@@ -611,10 +633,14 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize::value,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize::value> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
std::declval<F>(),
trace::on));
@@ -625,6 +651,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool can_transform_replace_view = is_detected_v<
transform_replace_view_expr,
@@ -633,6 +660,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
std::bool_constant<Memoize>,
SkipParser>;
struct transform_replace_impl
@@ -645,6 +673,7 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
requires
// clang-format off
@@ -663,11 +692,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
parser_interface<SkipParser> const & skip,
F && f,
@@ -690,7 +720,8 @@ namespace boost::parser {
std::move_constructible F,
typename Parser,
typename GlobalState,
typename ErrorHandler>
typename ErrorHandler,
bool Memoize>
requires
// clang-format off
(std::is_pointer_v<std::remove_cvref_t<R>> ||
@@ -708,11 +739,12 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
Memoize,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser_interface<Parser, GlobalState, ErrorHandler, Memoize> const &
parser,
F && f,
trace trace_mode = trace::off) const
@@ -733,14 +765,18 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser,
typename F = trace,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
SkipParser && skip,
F && f = F{},
Trace trace_mode = Trace{}) const
@@ -787,11 +823,15 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<
Parser,
GlobalState,
ErrorHandler,
Memoize> const & parser,
parser_interface<SkipParser> const & skip,
F && f,
trace trace_mode = trace::off) const
@@ -830,6 +870,7 @@ template<
typename Parser,
typename GlobalState,
typename ErrorHandler,
bool Memoize,
typename SkipParser>
constexpr bool
std::ranges::enable_borrowed_range<boost::parser::transform_replace_view<
@@ -838,6 +879,7 @@ constexpr bool
Parser,
GlobalState,
ErrorHandler,
Memoize,
SkipParser>> = std::ranges::enable_borrowed_range<V> &&
(std::ranges::enable_borrowed_range<F> ||
boost::parser::detail::tidy_func<F>);

View File

@@ -46,6 +46,7 @@ macro(add_test_executable name)
add_test(NAME ${name} COMMAND ${name} --gtest_catch_exceptions=1)
endmacro()
add_test_executable(memos)
add_test_executable(all_t)
add_test_executable(search)
add_test_executable(split)

452
test/memos.cpp Normal file
View File

@@ -0,0 +1,452 @@
/**
* Copyright (C) 2024 T. Zachary Laine
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/detail/memos.hpp>
#include <variant> // for monostate
#include <gtest/gtest.h>
using namespace boost::parser;
TEST(memos, linked_list_1)
{
{
detail::linked_list<std::string, 1> list;
}
{
detail::linked_list<std::string, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
}
{
detail::linked_list<int, 1> list;
}
{
detail::linked_list<int, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
}
// reclaim, no reuse
{
detail::linked_list<std::string, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
list.reclaim();
}
{
detail::linked_list<int, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
list.reclaim();
}
// reclaim, partial reuse
{
detail::linked_list<std::string, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
list.reclaim();
for (int i = 0; i < 9; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
char const expected[] = {char('a' + (16 - 1) - i), 0};
EXPECT_EQ(*str, expected);
*str = "reused";
}
}
{
detail::linked_list<int, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
list.reclaim();
for (int i = 0; i < 9; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
EXPECT_EQ(*j, (16 - 1) - i);
*j = -1;
}
}
// reclaim, full reuse
{
detail::linked_list<std::string, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
list.reclaim();
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
char const expected[] = {char('a' + (16 - 1) - i), 0};
EXPECT_EQ(*str, expected);
*str = "reused";
}
}
{
detail::linked_list<int, 1> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
list.reclaim();
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
EXPECT_EQ(*j, (16 - 1) - i);
*j = -1;
}
}
}
TEST(memos, linked_list_8)
{
{
detail::linked_list<std::string, 8> list;
}
{
detail::linked_list<std::string, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
}
{
detail::linked_list<int, 8> list;
}
{
detail::linked_list<int, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
}
// reclaim, no reuse
{
detail::linked_list<std::string, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
list.reclaim();
}
{
detail::linked_list<int, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
list.reclaim();
}
// reclaim, partial reuse
{
detail::linked_list<std::string, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
list.reclaim();
for (int i = 0; i < 9; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
char c = i < 8 ? char('a' + i + 8) : char('a' + i - 8);
char const expected[] = {c, 0};
EXPECT_EQ(*str, expected);
*str = "reused";
}
}
{
detail::linked_list<int, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
list.reclaim();
for (int i = 0; i < 9; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
if (i < 8)
EXPECT_EQ(*j, i + 8);
else
EXPECT_EQ(*j, i - 8);
*j = -1;
}
}
// reclaim, full reuse
{
detail::linked_list<std::string, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
*str = 'a' + i;
}
list.reclaim();
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
std::string * str = needs_new ? new (pos) std::string()
: static_cast<std::string *>(pos);
char c = i < 8 ? char('a' + i + 8) : char('a' + i - 8);
char const expected[] = {c, 0};
EXPECT_EQ(*str, expected);
*str = "reused";
}
}
{
detail::linked_list<int, 8> list;
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
*j = i;
}
list.reclaim();
for (int i = 0; i < 16; ++i) {
auto [pos, needs_new] = list.push();
int * j = needs_new ? new (pos) int() : static_cast<int *>(pos);
if (i < 8)
EXPECT_EQ(*j, i + 8);
else
EXPECT_EQ(*j, i - 8);
*j = -1;
}
}
}
struct one
{};
struct two
{};
TEST(memos, basic)
{
constexpr auto failure = detail::memos<size_t, size_t>::failure;
constexpr auto success = detail::memos<size_t, size_t>::success;
{
detail::memos<size_t, size_t> memos;
}
{
detail::memos<size_t, size_t> memos;
detail::memos<size_t, size_t>::const_ref<int> ref = memos.find<one, int>(13);
EXPECT_FALSE(ref);
EXPECT_EQ(ref.valid, false);
EXPECT_EQ(ref.value, nullptr);
EXPECT_EQ(ref.datum, nullptr);
}
{
detail::memos<size_t, size_t> memos;
detail::memos<size_t, size_t>::ref<int> ref =
memos.insert<one, int>(failure, 13);
EXPECT_TRUE(ref);
EXPECT_EQ(ref.get_kind(), failure);
EXPECT_EQ(ref.valid, true);
EXPECT_EQ(ref.value, nullptr);
EXPECT_NE(ref.datum, nullptr);
EXPECT_EQ(*ref.datum, 0);
EXPECT_FALSE((memos.find<one, int>(12)));
EXPECT_FALSE((memos.find<one, int>(13, 42)));
EXPECT_FALSE((memos.find<two, int>(13)));
EXPECT_FALSE((memos.find<one, double>(13)));
detail::memos<size_t, size_t>::const_ref<int> cref = memos.find<one, int>(13);
EXPECT_TRUE(cref);
EXPECT_EQ(cref.get_kind(), failure);
EXPECT_EQ(cref.valid, true);
EXPECT_EQ(cref.value, nullptr);
EXPECT_NE(cref.datum, nullptr);
EXPECT_EQ(*cref.datum, 0);
}
{
detail::memos<size_t, size_t> memos;
detail::memos<size_t, size_t>::ref<int> ref =
memos.insert<one, int>(success, 13);
EXPECT_TRUE(ref);
EXPECT_EQ(ref.get_kind(), success);
EXPECT_EQ(ref.valid, true);
EXPECT_NE(ref.value, nullptr);
EXPECT_NE(ref.datum, nullptr);
EXPECT_EQ(*ref.datum, 0);
EXPECT_FALSE((memos.find<one, int>(12)));
EXPECT_FALSE((memos.find<one, int>(13, 42)));
EXPECT_FALSE((memos.find<two, int>(13)));
EXPECT_FALSE((memos.find<one, double>(13)));
detail::memos<size_t, size_t>::const_ref<int> cref = memos.find<one, int>(13);
EXPECT_TRUE(cref);
EXPECT_EQ(cref.get_kind(), success);
EXPECT_EQ(cref.valid, true);
EXPECT_NE(cref.value, nullptr);
EXPECT_NE(cref.datum, nullptr);
EXPECT_EQ(*cref.datum, 0);
}
// types sharing the same trivial_type<size, align>
{
detail::memos<size_t, size_t> memos;
detail::memos<size_t, size_t>::ref<int> ref =
memos.insert<one, int>(success, 13);
EXPECT_TRUE(ref);
EXPECT_EQ(ref.get_kind(), success);
EXPECT_EQ(ref.valid, true);
EXPECT_NE(ref.value, nullptr);
EXPECT_NE(ref.datum, nullptr);
EXPECT_EQ(*ref.datum, 0);
*ref.value = 42;
EXPECT_FALSE((memos.find<one, int>(12)));
EXPECT_FALSE((memos.find<one, int>(13, 42)));
EXPECT_FALSE((memos.find<two, int>(13)));
EXPECT_FALSE((memos.find<one, float>(13)));
detail::memos<size_t, size_t>::const_ref<int> cref = memos.find<one, int>(13);
EXPECT_TRUE(cref);
EXPECT_EQ(cref.get_kind(), success);
EXPECT_EQ(cref.valid, true);
EXPECT_NE(cref.value, nullptr);
EXPECT_EQ(*cref.value, 42);
EXPECT_NE(cref.datum, nullptr);
EXPECT_EQ(*cref.datum, 0);
detail::memos<size_t, size_t>::ref<float> ref2 =
memos.insert<one, float>(success, 18);
EXPECT_TRUE(ref2);
EXPECT_EQ(ref2.get_kind(), success);
EXPECT_EQ(ref2.valid, true);
EXPECT_NE(ref2.value, nullptr);
EXPECT_NE(ref2.datum, nullptr);
EXPECT_EQ(*ref2.datum, 0);
*ref2.value = 13.0f;
EXPECT_FALSE((memos.find<one, float>(17)));
EXPECT_FALSE((memos.find<one, float>(18, 42)));
EXPECT_FALSE((memos.find<two, float>(18)));
EXPECT_FALSE((memos.find<one, char32_t>(18)));
detail::memos<size_t, size_t>::const_ref<float> cref2 = memos.find<one, float>(18);
EXPECT_TRUE(cref2);
EXPECT_EQ(cref2.get_kind(), success);
EXPECT_EQ(cref2.valid, true);
EXPECT_NE(cref2.value, nullptr);
EXPECT_EQ(*cref2.value, 13.0f);
EXPECT_NE(cref2.datum, nullptr);
EXPECT_EQ(*cref2.datum, 0);
detail::memos<size_t, size_t>::ref<char32_t> ref3 =
memos.insert<two, char32_t>(success, 21);
EXPECT_TRUE(ref3);
EXPECT_EQ(ref3.get_kind(), success);
EXPECT_EQ(ref3.valid, true);
EXPECT_TRUE(ref3.value);
EXPECT_NE(ref3.datum, nullptr);
EXPECT_EQ(*ref3.datum, 0);
*ref3.value = U'c';
EXPECT_FALSE((memos.find<two, char32_t>(20)));
EXPECT_FALSE((memos.find<two, char32_t>(21, 42)));
EXPECT_FALSE((memos.find<one, char32_t>(21)));
EXPECT_FALSE((memos.find<two, int>(21)));
detail::memos<size_t, size_t>::const_ref<char32_t> cref3 =
memos.find<two, char32_t>(21);
EXPECT_TRUE(cref3);
EXPECT_EQ(cref3.get_kind(), success);
EXPECT_EQ(cref3.valid, true);
EXPECT_TRUE(cref3.value);
EXPECT_EQ((unsigned int)*cref3.value, (unsigned int)U'c');
EXPECT_NE(cref3.datum, nullptr);
EXPECT_EQ(*cref3.datum, 0);
// three items
EXPECT_EQ(memos.items(), 3u);
// all three items use the same liked list for storage
EXPECT_EQ(memos.item_stores(), 1u);
}
// empty types
{
detail::memos<size_t, size_t> memos;
detail::memos<size_t, size_t>::ref<std::monostate> ref =
memos.insert<one, std::monostate>(success, 13);
EXPECT_TRUE(ref);
EXPECT_EQ(ref.get_kind(), success);
EXPECT_EQ(ref.valid, true);
EXPECT_EQ(*ref.value, std::monostate{});
*ref.value = std::monostate{};
EXPECT_NE(ref.datum, nullptr);
EXPECT_EQ(*ref.datum, 0);
EXPECT_FALSE((memos.find<one, std::monostate>(12)));
EXPECT_FALSE((memos.find<one, std::monostate>(13, 42)));
EXPECT_FALSE((memos.find<two, std::monostate>(13)));
EXPECT_FALSE((memos.find<one, double>(13)));
detail::memos<size_t, size_t>::const_ref<std::monostate> cref =
memos.find<one, std::monostate>(13);
EXPECT_TRUE(cref);
EXPECT_EQ(cref.get_kind(), success);
EXPECT_EQ(cref.valid, true);
EXPECT_EQ(*cref.value, std::monostate{});
EXPECT_NE(cref.datum, nullptr);
EXPECT_EQ(*cref.datum, 0);
}
}