Skip to content

Commit 1e64b6e

Browse files
committed
Add insert/erase/clear operations on symbol tables for the current parse and
subsequent parses both. Add better tests for the added API and the previous subset of the operations already present. Fix errors revealed by the tests. Fixes #183.
1 parent 41fb399 commit 1e64b6e

File tree

3 files changed

+369
-17
lines changed

3 files changed

+369
-17
lines changed

include/boost/parser/parser.hpp

Lines changed: 190 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,25 +1609,80 @@ namespace boost { namespace parser {
16091609
V base_;
16101610
};
16111611

1612+
enum class symbol_table_op { insert, erase, clear };
1613+
1614+
template<typename T>
1615+
struct symbol_table_operation
1616+
{
1617+
std::string key_;
1618+
std::optional<T> value_;
1619+
symbol_table_op kind_;
1620+
};
1621+
1622+
template<typename T>
1623+
void apply_symbol_table_operations(
1624+
std::vector<std::pair<std::string, T>> & initial_elements,
1625+
std::vector<symbol_table_operation<T>> & pending_operations)
1626+
{
1627+
auto lower_bound = [&initial_elements](std::string const & str) {
1628+
return std::lower_bound(
1629+
initial_elements.begin(),
1630+
initial_elements.end(),
1631+
str,
1632+
[&str](auto const & a, auto b) {
1633+
return a.first < b;
1634+
});
1635+
};
1636+
1637+
for (auto & op : pending_operations) {
1638+
if (op.kind_ == symbol_table_op::insert) {
1639+
auto it = lower_bound(op.key_);
1640+
if (it == initial_elements.end() ||
1641+
it->first != op.key_) {
1642+
initial_elements.insert(
1643+
it,
1644+
std::pair<std::string, T>(
1645+
std::move(op.key_), std::move(*op.value_)));
1646+
} else {
1647+
it->second = std::move(*op.value_);
1648+
}
1649+
} else if (op.kind_ == symbol_table_op::erase) {
1650+
auto it = lower_bound(op.key_);
1651+
if (it != initial_elements.end() && it->first == op.key_)
1652+
initial_elements.erase(it);
1653+
} else {
1654+
initial_elements.clear();
1655+
}
1656+
}
1657+
1658+
pending_operations.clear();
1659+
}
1660+
16121661
template<typename Context, typename T>
16131662
auto get_trie(
1614-
Context const & context, symbol_parser<T> const & symbol_parser)
1663+
Context const & context, symbol_parser<T> const & sym_parser)
16151664
{
16161665
using trie_t = text::trie_map<std::vector<char32_t>, T>;
16171666
using result_type = std::pair<trie_t &, bool>;
16181667
symbol_table_tries_t & symbol_table_tries =
16191668
*context.symbol_table_tries_;
16201669

1621-
auto & [any, has_case_folded] =
1622-
symbol_table_tries[(void *)&symbol_parser];
1670+
auto & [any, has_case_folded, pending_ops] =
1671+
symbol_table_tries[(void *)&sym_parser.ref()];
16231672

16241673
bool const needs_case_folded = context.no_case_depth_;
16251674

16261675
if (!any.has_value()) {
16271676
any = trie_t{};
16281677
has_case_folded = false;
16291678
trie_t & trie = *std::any_cast<trie_t>(&any);
1630-
for (auto const & e : symbol_parser.initial_elements()) {
1679+
if (pending_ops) {
1680+
detail::apply_symbol_table_operations(
1681+
sym_parser.initial_elements(),
1682+
sym_parser.pending_operations());
1683+
pending_ops = false;
1684+
}
1685+
for (auto const & e : sym_parser.initial_elements()) {
16311686
trie.insert(e.first | text::as_utf32, e.second);
16321687
if (needs_case_folded) {
16331688
trie.insert(
@@ -2153,6 +2208,67 @@ namespace boost { namespace parser {
21532208
Iter & it_;
21542209
};
21552210

2211+
template<typename Parser>
2212+
using has_parser_data_member_expr =
2213+
decltype(std::declval<Parser>().parser_);
2214+
template<typename Parser>
2215+
constexpr bool has_parser_data_member_v =
2216+
is_detected_v<has_parser_data_member_expr, Parser>;
2217+
2218+
template<typename Parser>
2219+
using has_parsers_data_member_expr =
2220+
decltype(std::declval<Parser>().parsers_);
2221+
template<typename Parser>
2222+
constexpr bool has_parsers_data_member_v =
2223+
is_detected_v<has_parsers_data_member_expr, Parser>;
2224+
2225+
template<typename Context, typename Parser>
2226+
void visit_symbol_table_parsers_impl(
2227+
Context & context, Parser const & p);
2228+
2229+
template<typename Context, typename Parser>
2230+
void visit_symbol_table_parser(Context & context, Parser const & p)
2231+
{
2232+
if constexpr (has_parser_data_member_v<Parser>) {
2233+
detail::visit_symbol_table_parsers_impl(context, p.parser_);
2234+
}
2235+
}
2236+
template<typename Context, typename T>
2237+
void visit_symbol_table_parser(
2238+
Context & context, symbol_parser<T> const & p)
2239+
{
2240+
auto & [_, has_case_folded, pending_ops] =
2241+
(*context.symbol_table_tries_)[(void *)&p.ref()];
2242+
has_case_folded = false;
2243+
pending_ops = !p.pending_operations().empty();
2244+
}
2245+
2246+
template<typename Context, typename Parser>
2247+
void visit_symbol_table_parsers_impl(
2248+
Context & context, Parser const & p)
2249+
{
2250+
if constexpr (has_parsers_data_member_v<Parser>) {
2251+
auto visit = [&context](auto & parser) {
2252+
detail::visit_symbol_table_parsers_impl(context, parser);
2253+
};
2254+
detail::hl::for_each(p.parsers_, visit);
2255+
} else {
2256+
detail::visit_symbol_table_parser(context, p);
2257+
}
2258+
}
2259+
2260+
template<
2261+
typename Context,
2262+
typename Parser,
2263+
typename GlobalState,
2264+
typename ErrorHandler>
2265+
void visit_symbol_table_parsers(
2266+
Context & context,
2267+
parser_interface<Parser, GlobalState, ErrorHandler> const & p)
2268+
{
2269+
detail::visit_symbol_table_parsers_impl(context, p.parser_);
2270+
}
2271+
21562272
template<
21572273
bool Debug,
21582274
typename Iter,
@@ -2179,6 +2295,7 @@ namespace boost { namespace parser {
21792295
error_handler,
21802296
parser.globals_,
21812297
symbol_table_tries);
2298+
detail::visit_symbol_table_parsers(context, parser);
21822299
auto const flags =
21832300
Debug ? detail::enable_trace(detail::flags::gen_attrs)
21842301
: detail::flags::gen_attrs;
@@ -2227,6 +2344,7 @@ namespace boost { namespace parser {
22272344
error_handler,
22282345
parser.globals_,
22292346
symbol_table_tries);
2347+
detail::visit_symbol_table_parsers(context, parser);
22302348
auto const flags =
22312349
Debug ? detail::enable_trace(detail::flags::gen_attrs)
22322350
: detail::flags::gen_attrs;
@@ -2281,6 +2399,7 @@ namespace boost { namespace parser {
22812399
callbacks,
22822400
parser.globals_,
22832401
symbol_table_tries);
2402+
detail::visit_symbol_table_parsers(context, parser);
22842403
auto const flags =
22852404
Debug ? detail::enable_trace(detail::flags::gen_attrs)
22862405
: detail::flags::gen_attrs;
@@ -2332,6 +2451,7 @@ namespace boost { namespace parser {
23322451
error_handler,
23332452
parser.globals_,
23342453
symbol_table_tries);
2454+
detail::visit_symbol_table_parsers(context, parser);
23352455
auto const flags =
23362456
Debug ? detail::enable_trace(detail::default_flags())
23372457
: detail::default_flags();
@@ -2377,6 +2497,7 @@ namespace boost { namespace parser {
23772497
error_handler,
23782498
parser.globals_,
23792499
symbol_table_tries);
2500+
detail::visit_symbol_table_parsers(context, parser);
23802501
auto const flags =
23812502
Debug ? detail::enable_trace(detail::default_flags())
23822503
: detail::default_flags();
@@ -2430,6 +2551,7 @@ namespace boost { namespace parser {
24302551
callbacks,
24312552
parser.globals_,
24322553
symbol_table_tries);
2554+
detail::visit_symbol_table_parsers(context, parser);
24332555
auto const flags =
24342556
Debug ? detail::enable_trace(detail::default_flags())
24352557
: detail::default_flags();
@@ -4976,6 +5098,15 @@ namespace boost { namespace parser {
49765098
}
49775099
}
49785100

5101+
/** Erases the entry whose UTF-8 match string is `str` from the copy
5102+
of the symbol table inside the parse context `context`. */
5103+
template<typename Context>
5104+
void clear(Context const & context) const
5105+
{
5106+
auto [trie, _] = detail::get_trie(context, ref());
5107+
trie.clear();
5108+
}
5109+
49795110
template<
49805111
typename Iter,
49815112
typename Sentinel,
@@ -5025,7 +5156,9 @@ namespace boost { namespace parser {
50255156
}
50265157
}
50275158

5028-
std::vector<std::pair<std::string_view, T>> initial_elements_;
5159+
mutable std::vector<std::pair<std::string, T>> initial_elements_;
5160+
mutable std::vector<detail::symbol_table_operation<T>>
5161+
pending_operations_;
50295162
symbol_parser const * copied_from_;
50305163

50315164
symbol_parser const & ref() const noexcept
@@ -5034,11 +5167,16 @@ namespace boost { namespace parser {
50345167
return *copied_from_;
50355168
return *this;
50365169
}
5037-
std::vector<std::pair<std::string_view, T>> const &
5170+
std::vector<std::pair<std::string, T>> &
50385171
initial_elements() const noexcept
50395172
{
50405173
return ref().initial_elements_;
50415174
}
5175+
std::vector<detail::symbol_table_operation<T>> &
5176+
pending_operations() const noexcept
5177+
{
5178+
return ref().pending_operations_;
5179+
}
50425180

50435181
std::string_view diagnostic_text_;
50445182
};
@@ -5598,30 +5736,59 @@ namespace boost { namespace parser {
55985736
{}
55995737
symbols(std::initializer_list<std::pair<std::string_view, T>> il)
56005738
{
5601-
this->parser_.initial_elements_ = il;
5739+
this->parser_.initial_elements_.resize(il.size());
5740+
std::copy(il.begin(), il.end(),
5741+
this->parser_.initial_elements_.begin());
56025742
}
56035743
symbols(
56045744
char const * diagnostic_text,
56055745
std::initializer_list<std::pair<std::string_view, T>> il) :
56065746
parser_interface<symbol_parser<T>>(
56075747
symbol_parser<T>(diagnostic_text))
56085748
{
5609-
this->parser_.initial_elements_ = il;
5749+
this->parser_.initial_elements_.resize(il.size());
5750+
std::copy(il.begin(), il.end(),
5751+
this->parser_.initial_elements_.begin());
56105752
}
56115753

56125754
using parser_interface<symbol_parser<T>>::operator();
56135755

5614-
/** Adds an entry consisting of a UTF-8 string `str` to match, and an
5615-
associated attribute `x`, to `*this`. The entry is added for use
5616-
in all subsequent top-level parses. Subsequent lookups during the
5617-
current top-level parse will not match `str`. */
5756+
/** Inserts an entry consisting of a UTF-8 string `str` to match, and
5757+
an associated attribute `x`, to `*this`. The entry is added for
5758+
use in all subsequent top-level parses. Subsequent lookups during
5759+
the current top-level parse will not necessarily match `str`. */
56185760
symbols & insert_for_next_parse(std::string_view str, T x)
56195761
{
5620-
this->parser_.initial_elements_.push_back(
5621-
std::pair<std::string_view, T>(str, std::move(x)));
5762+
this->parser_.pending_operations().push_back(
5763+
detail::symbol_table_operation<T>{
5764+
std::string(str),
5765+
std::move(x),
5766+
detail::symbol_table_op::insert});
56225767
return *this;
56235768
}
56245769

5770+
/** Erases the entry whose UTF-8 match string is `str`, from `*this`.
5771+
The entry will no longer be available for use in all subsequent
5772+
top-level parses. `str` will not be removed from the symbols
5773+
matched in the current top-level parse. */
5774+
void erase_for_next_parse(std::string_view str)
5775+
{
5776+
this->parser_.pending_operations().push_back(
5777+
detail::symbol_table_operation<T>{
5778+
std::string(str),
5779+
std::nullopt,
5780+
detail::symbol_table_op::erase});
5781+
}
5782+
5783+
/** Erases all the entries from the copy of the symbol table inside
5784+
the parse context `context`. */
5785+
void clear_for_next_parse()
5786+
{
5787+
this->parser_.pending_operations().push_back(
5788+
detail::symbol_table_operation<T>{
5789+
{}, std::nullopt, detail::symbol_table_op::clear});
5790+
}
5791+
56255792
/** Equivalent to `insert_for_next_parse(str, std::move(x))`. */
56265793
symbols & operator()(std::string_view str, T x)
56275794
{
@@ -5655,7 +5822,15 @@ namespace boost { namespace parser {
56555822
{
56565823
this->parser_.erase(context, str);
56575824
}
5658-
};
5825+
5826+
/** Erases all the entries from the copy of the symbol table inside
5827+
the parse context `context`. */
5828+
template<typename Context>
5829+
void clear(Context const & context) const
5830+
{
5831+
this->parser_.clear(context);
5832+
}
5833+
};
56595834

56605835
#ifndef BOOST_PARSER_DOXYGEN
56615836

include/boost/parser/parser_fwd.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,15 @@ namespace boost { namespace parser {
8080
in_apply_parser = 1 << 3
8181
};
8282

83+
struct symbol_table_trie_element
84+
{
85+
std::any trie_;
86+
bool has_case_folded_;
87+
bool pending_operations_;
88+
};
89+
8390
using symbol_table_tries_t =
84-
std::map<void *, std::pair<std::any, bool>, std::less<void *>>;
91+
std::map<void *, symbol_table_trie_element, std::less<void *>>;
8592

8693
template<
8794
bool DoTrace,

0 commit comments

Comments
 (0)