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

2 Commits

Author SHA1 Message Date
Zach Laine
06fb60c78f Add an intial sketch of a memoization system. 2024-03-17 20:09:39 -05:00
Zach Laine
24288a03a2 Add quoted_string, to automate probably the most commonly-written parser of
all time -- the quoted string.
2024-03-11 02:38:26 -05:00
18 changed files with 2490 additions and 293 deletions

View File

@@ -41,6 +41,7 @@
[import ../example/user_error_handler.cpp]
[import ../test/parser.cpp]
[import ../test/parser_rule.cpp]
[import ../test/parser_quoted_string.cpp]
[import ../include/boost/parser/concepts.hpp]
[import ../include/boost/parser/error_handling_fwd.hpp]
@@ -218,6 +219,8 @@
[def _lower_ [globalref boost::parser::lower `lower`]]
[def _upper_ [globalref boost::parser::upper `upper`]]
[def _quot_str_ [globalref boost::parser::quoted_string `quoted_string`]]
[def _RES_ ['[^RESOLVE]]`()`]
[def _RES_np_ ['[^RESOLVE]]]
[def _ATTR_ ['[^ATTR]]`()`]

View File

@@ -38,6 +38,8 @@ itself be used as a parser; it must be called. In the table below:
* `p`, `p1`, `p2`, ... are parsers.
* `escapes` is a _symbols_t_ object, where `T` is `char` or `char32_t`.
[note The definition of `parsable_range_like` is:
[parsable_range_like_concept]
@@ -326,6 +328,31 @@ the input they match unless otherwise stated in the table below.]
[ _symbols_ is an associative container of key, value pairs. Each key is a _std_str_ and each value has type `T`. In the Unicode parsing path, the strings are considered to be UTF-8 encoded; in the non-Unicode path, no encoding is assumed. _symbols_ Matches the longest prefix `pre` of the input that is equal to one of the keys `k`. If the length `len` of `pre` is zero, and there is no zero-length key, it does not match the input. If `len` is positive, the generated attribute is the value associated with `k`.]
[ `T` ]
[ Unlike the other entries in this table, _symbols_ is a type, not an object. ]]
[[ _quot_str_ ]
[ Matches `'"'`, followed by zero or more characters, followed by `'"'`. ]
[ _std_str_ ]
[ The result does not include the quotes. A quote within the string can be written by escaping it with a backslash. A backslash within the string can be written by writing two consecutive backslashes. Any other use of a backslash will fail the parse. Skipping is disabled while parsing the entire string, as if using _lexeme_. ]]
[[ `_quot_str_(c)` ]
[ Matches `c`, followed by zero or more characters, followed by `c`. ]
[ _std_str_ ]
[ The result does not include the `c` quotes. A `c` within the string can be written by escaping it with a backslash. A backslash within the string can be written by writing two consecutive backslashes. Any other use of a backslash will fail the parse. Skipping is disabled while parsing the entire string, as if using _lexeme_. ]]
[[ `_quot_str_(r)` ]
[ Matches some character `Q` in `r`, followed by zero or more characters, followed by `Q`. ]
[ _std_str_ ]
[ The result does not include the `Q` quotes. A `Q` within the string can be written by escaping it with a backslash. A backslash within the string can be written by writing two consecutive backslashes. Any other use of a backslash will fail the parse. Skipping is disabled while parsing the entire string, as if using _lexeme_. ]]
[[ `_quot_str_(c, symbols)` ]
[ Matches `c`, followed by zero or more characters, followed by `c`. ]
[ _std_str_ ]
[ The result does not include the `c` quotes. A `c` within the string can be written by escaping it with a backslash. A backslash within the string can be written by writing two consecutive backslashes. A backslash followed by a successful match using `symbols` will be interpreted as the corresponding value produced by `symbols`. Any other use of a backslash will fail the parse. Skipping is disabled while parsing the entire string, as if using _lexeme_. ]]
[[ `_quot_str_(r, symbols)` ]
[ Matches some character `Q` in `r`, followed by zero or more characters, followed by `Q`. ]
[ _std_str_ ]
[ The result does not include the `Q` quotes. A `Q` within the string can be written by escaping it with a backslash. A backslash within the string can be written by writing two consecutive backslashes. A backslash followed by a successful match using `symbols` will be interpreted as the corresponding value produced by `symbols`. Any other use of a backslash will fail the parse. Skipping is disabled while parsing the entire string, as if using _lexeme_. ]]
]
[important All the character parsers, like _ch_, _cp_ and _cu_ produce either

View File

@@ -738,6 +738,106 @@ no sense.
[endsect]
[section Alternative Parsers]
Frequently, you need to parse something that might have one of several forms.
`operator|` is overloaded to form alternative parsers. For example:
namespace bp = boost::parser;
auto const parser_1 = bp::int_ | bp::eps;
`parser_1` matches an integer, or if that fails, it matches /epsilon/, the
empty string. This is equivalent to writing:
namespace bp = boost::parser;
auto const parser_2 = -bp::int_;
However, neither `parser_1` nor `parser_2` is equivalent to writing this:
namespace bp = boost::parser;
auto const parser_3 = bp::eps | bp::int_; // Does not do what you think.
The reason is that alternative parsers try each of their subparsers, one at a
time, and stop on the first one that matches. /Epsilon/ matches anything,
since it is zero length and consumes no input. It even matches the end of
input. This means that `parser_3` is equivalent to _e_ by itself.
[note For this reason, writing `_e_ | p` for any parser p is considered a bug.
Debug builds will assert when `_e_ | p` is encountered. ]
[warning This kind of error is very common when _e_ is involved, and also very
easy to detect. However, it is possible to write `P1 >> P2`, where `P1` is a
prefix of `P2`, such as `int_ | int >> int_`, or `repeat(4)[hex_digit] |
repeat(8)[hex_digit]`. This is almost certainly an error, but is impossible
to detect in the general case _emdash_ remember that _rs_ can be separately
compiled, and consider a pair of rules whose associated `_def` parsers are
`int_` and `int_ >> int_`, respectively.]
[endsect]
[section Parsing Quoted Strings]
It is very common to need to parse quoted strings. Quoted strings are
slightly tricky, though, when using a skipper (and you should be using a
skipper 99% of the time). You don't want to allow arbitrary whitespace in the
middle of your strings, and you also don't want to remove all whitespace from
your strings. Both of these things will happen with the typical skipper,
_ws_.
So, here is how most people would write a quoted string parser:
namespace bp = boost::parser;
const auto string = bp::lexeme['"' >> *(bp::char_ - '"') > '"'];
Some things to note:
* the result is a string;
* the quotes are not included in the result;
* there is an expectation point before the close-quote;
* the use of _lexeme_ disables skipping in the parser, and it must be written
around the quotes, not around the `operator*` expression; and
* there's no way to write a quote in the middle of the string.
This is a very common pattern. I have written a quoted string parser like
this dozens of times. The parser above is the quick-and-dirty version. A
more robust version would be able to handle escaped quotes within the string,
and then would immediately also need to support escaped escape characters.
_Parser_ provides _quot_str_ to use in place of this very common pattern. It
supports quote- and escaped-character-escaping, using backslash as the escape
character.
[quoted_string_example_1_2]
As common as this use case is, there are very similar use cases that it does
not cover. So, _quot_str_ has some options. If you call it with a single
character, it returns a _quot_str_ that uses that single character as the
quote-character.
[quoted_string_example_3]
You can also supply a range of characters. One of the characters from the
range must quote the whole string; mismatches are not allowed. Think of how
Python allows you to quote a string with either `'"'` or `'\''`, but the same
character must be used on both sides.
[quoted_string_example_4]
Another common thing to do in a quoted string parser is to recognize escape
sequences. If you have simple escape sequencecs that do not require any real
parsing, like say the simple escape sequences from C++, you can provide a
_symbols_ object as well. The template parameter `T` to _symbols_t_ must be
`char` or `char32_t`. You don't need to include the escaped backslash or the
escaped quote character, since those always work.
[quoted_string_example_5]
[endsect]
[section Parsing In Detail]
Now that you've seen some examples, let's see how parsing works in a bit more
@@ -1052,43 +1152,6 @@ for more information.]
[endsect]
[section Alternative Parsers]
Frequently, you need to parse something that might have one of several forms.
`operator|` is overloaded to form alternative parsers. For example:
namespace bp = boost::parser;
auto const parser_1 = bp::int_ | bp::eps;
`parser_1` matches an integer, or if that fails, it matches /epsilon/, the
empty string. This is equivalent to writing:
namespace bp = boost::parser;
auto const parser_2 = -bp::int_;
However, neither `parser_1` nor `parser_2` is equivalent to writing this:
namespace bp = boost::parser;
auto const parser_3 = bp::eps | bp::int_; // Does not do what you think.
The reason is that alternative parsers try each of their subparsers, one at a
time, and stop on the first one that matches. /Epsilon/ matches anything,
since it is zero length and consumes no input. It even matches the end of
input. This means that `parser_3` is equivalent to _e_ by itself.
[note For this reason, writing `_e_ | p` for any parser p is considered a bug.
Debug builds will assert when `_e_ | p` is encountered. ]
[warning This kind of error is very common when _e_ is involved, and also very
easy to detect. However, it is possible to write `P1 >> P2`, where `P1` is a
prefix of `P2`, such as `int_ | int >> int_`, or `repeat(4)[hex_digit] |
repeat(8)[hex_digit]`. This is almost certainly an error, but is impossible
to detect in the general case _emdash_ remember that _rs_ can be separately
compiled, and consider a pair of rules whose associated `_def` parsers are
`int_` and `int_ >> int_`, respectively.]
[endsect]
[section The Parsers And Their Uses]
_Parser_ comes with all the parsers most parsing tasks will ever need. Each

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

View File

@@ -278,6 +278,13 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename Quotes, typename Escapes>
void print_parser(
Context const & context,
quoted_string_parser<Quotes, Escapes> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, bool NewlinesOnly, bool NoNewlines>
void print_parser(
Context const & context,

View File

@@ -482,8 +482,7 @@ namespace boost { namespace parser { namespace detail {
template<
typename Context,
typename ResolvedExpected,
bool Integral = std::is_integral<ResolvedExpected>{},
int SizeofExpected = sizeof(ResolvedExpected)>
bool Integral = std::is_integral<ResolvedExpected>{}>
struct print_expected_char_impl
{
static void call(
@@ -495,13 +494,17 @@ namespace boost { namespace parser { namespace detail {
}
};
template<typename Context, typename Expected>
struct print_expected_char_impl<Context, Expected, true, 4>
template<typename Context>
struct print_expected_char_impl<Context, char32_t, true>
{
static void
call(Context const & context, std::ostream & os, Expected expected)
call(Context const & context, std::ostream & os, char32_t expected)
{
std::array<char32_t, 1> cps = {{(char32_t)expected}};
if (expected == '\'') {
os << "'\\''";
return;
}
std::array<char32_t, 1> cps = {{expected}};
auto const r = cps | text::as_utf8;
os << "'";
for (auto c : r) {
@@ -689,6 +692,27 @@ namespace boost { namespace parser { namespace detail {
os << "\"";
}
template<typename Context, typename Quotes, typename Escapes>
void print_parser(
Context const & context,
quoted_string_parser<Quotes, Escapes> const & parser,
std::ostream & os,
int components)
{
os << "quoted_string(";
if constexpr (is_nope_v<Quotes>) {
detail::print_expected_char_impl<Context, char32_t>::call(
context, os, parser.ch_);
} else {
os << '"';
for (auto c : parser.chs_ | text::as_utf8) {
detail::print_char(os, c);
}
os << '"';
}
os << ')';
}
template<typename Context, bool NewlinesOnly, bool NoNewlines>
void print_parser(
Context const & context,

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;
@@ -342,12 +345,17 @@ namespace boost { namespace parser {
character being matched. */
struct digit_parser;
/** Maches a particular string, delimited by an iterator sentinel pair;
/** Matches a particular string, delimited by an iterator sentinel pair;
produces no attribute. */
template<typename StrIter, typename StrSentinel>
struct string_parser;
/** Maches an end-of-line (`NewlinesOnly == true`), whitespace
/** Matches a string delimited by quotation marks; produces a
`std::string` attribute. */
template<typename Quotes = detail::nope, typename Escapes = detail::nope>
struct quoted_string_parser;
/** Matches an end-of-line (`NewlinesOnly == true`), whitespace
(`NewlinesOnly == false`), or (`NoNewlines == true`) blank (whitespace
but not newline) code point, based on the Unicode definitions of each
(also matches the two code points `"\r\n"`). Produces no
@@ -355,7 +363,7 @@ namespace boost { namespace parser {
template<bool NewlinesOnly, bool NoNewlines>
struct ws_parser;
/** Maches the strings "true" and "false", producing an attribute of
/** Matches the strings "true" and "false", producing an attribute of
`true` or `false`, respectively, and fails on any other input. */
struct bool_parser;
@@ -412,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)
@@ -62,6 +63,7 @@ add_test_executable(parser_rule_with_params)
add_test_executable(parser_action)
add_test_executable(parser_action_with_params)
add_test_executable(parser_symbol_table)
add_test_executable(parser_quoted_string)
add_test_executable(tracing)
add_test_executable(parse_empty)
add_test_executable(tuple_aggregate)

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);
}
}

View File

@@ -0,0 +1,279 @@
/**
* 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/parser.hpp>
#include <boost/parser/transcode_view.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
bp::symbols<char> const cu_escapes = {{"t", '\t'}, {"r", '\r'}, {"n", '\n'}};
bp::symbols<char32_t> const cp_escapes = {
{"t", '\t'}, {"r", '\r'}, {"n", '\n'}};
TEST(quoted_string, basic)
{
constexpr auto parser = bp::quoted_string;
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
auto result = bp::parse(R"("foo")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"("foo\\")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"("\"foo\"")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "\"foo\"");
}
}
TEST(quoted_string, different_char)
{
constexpr auto parser = bp::quoted_string('\'');
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
auto result = bp::parse(R"('foo')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"('foo\\')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"('\'foo\'')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "'foo'");
}
}
TEST(quoted_string, different_char_with_escapes)
{
{
auto parser = bp::quoted_string('\'', cu_escapes);
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
auto result = bp::parse(R"('foo\t')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\t");
}
{
auto result = bp::parse(R"('foo\x')", parser, bp::ws);
EXPECT_FALSE(result);
}
}
{
auto parser = bp::quoted_string('\'', cp_escapes);
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
auto result = bp::parse(R"('\tfoo')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "\tfoo");
}
{
auto result = bp::parse(R"('f\xoo')", parser, bp::ws);
EXPECT_FALSE(result);
}
}
}
TEST(quoted_string, char_set)
{
constexpr auto parser = bp::quoted_string("'\"");
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
EXPECT_FALSE(bp::parse(R"('foo")", parser, bp::ws));
EXPECT_FALSE(bp::parse(R"("foo')", parser, bp::ws));
}
{
auto result = bp::parse(R"('foo')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"("foo")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"('foo\\')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"("foo\\")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"('\'foo\'')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "'foo'");
}
{
auto result = bp::parse(R"("\"foo\"")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "\"foo\"");
}
{
// Can't escape arbitrary characters, only backslash and the quote
// character.
EXPECT_FALSE(bp::parse(R"("\'foo")", parser, bp::ws));
}
}
TEST(quoted_string, char_set_with_escapes)
{
{
auto parser = bp::quoted_string("'\"", cu_escapes);
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
EXPECT_FALSE(bp::parse(R"('foo")", parser, bp::ws));
EXPECT_FALSE(bp::parse(R"("foo')", parser, bp::ws));
}
{
auto result = bp::parse(R"('foo\t')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\t");
}
{
auto result = bp::parse(R"("\tfoo")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "\tfoo");
}
{
auto result = bp::parse(R"('foo\x')", parser, bp::ws);
EXPECT_FALSE(result);
}
}
{
auto parser = bp::quoted_string("'\"", cp_escapes);
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
EXPECT_FALSE(bp::parse(R"('foo")", parser, bp::ws));
EXPECT_FALSE(bp::parse(R"("foo')", parser, bp::ws));
}
{
auto result = bp::parse(R"('foo\t')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\t");
}
{
auto result = bp::parse(R"("\tfoo")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "\tfoo");
}
{
auto result = bp::parse(R"('foo\x')", parser, bp::ws);
EXPECT_FALSE(result);
}
}
}
TEST(quoted_string, doc_examples)
{
//[ quoted_string_example_1_2
namespace bp = boost::parser;
auto result1 = bp::parse("\"some text\"", bp::quoted_string, bp::ws);
assert(result1);
std::cout << *result1 << "\n"; // Prints: some text
auto result2 =
bp::parse("\"some \\\"text\\\"\"", bp::quoted_string, bp::ws);
assert(result2);
std::cout << *result2 << "\n"; // Prints: some "text"
//]
//[ quoted_string_example_3
auto result3 = bp::parse("!some text!", bp::quoted_string('!'), bp::ws);
assert(result3);
std::cout << *result3 << "\n"; // Prints: some text
//]
//[ quoted_string_example_4
auto result4 = bp::parse("'some text'", bp::quoted_string("'\""), bp::ws);
assert(result4);
std::cout << *result4 << "\n"; // Prints: some text
//]
//[ quoted_string_example_5
// the c++ simple escapes
bp::symbols<char> const escapes = {
{"'", '\''},
{"?", '\?'},
{"a", '\a'},
{"b", '\b'},
{"f", '\f'},
{"n", '\n'},
{"r", '\r'},
{"t", '\t'},
{"v", '\v'}};
auto result5 =
bp::parse("\"some text\r\"", bp::quoted_string('"', escapes), bp::ws);
assert(result5);
std::cout << *result5 << "\n"; // Prints (with a CRLF newline): some text
//]
}

View File

@@ -312,6 +312,15 @@ int main()
PARSE(string("h"));
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| quoted_string() |\n"
<< "----------------------------------------\n";
PARSE(quoted_string);
PARSE(quoted_string('\''));
PARSE(quoted_string("'\""));
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| eol |\n"