Skip to content

Commit 9f73e82

Browse files
macros: Add abstraction around multiple matches
Adds an extra layer of abstraction around keeping multiple matches for the same fragment. This avoids ugly code fetching the first match in order to get the amounf of matches given by the user, while still allowing zero-matches to exist. Co-authored-by: philberty <philip.herron@embecosm.com>
1 parent 39c0425 commit 9f73e82

File tree

4 files changed

+99
-50
lines changed

4 files changed

+99
-50
lines changed

gcc/rust/expand/rust-macro-expand.cc

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3102,7 +3102,7 @@ MacroExpander::expand_decl_macro (Location invoc_locus,
31023102

31033103
// find matching arm
31043104
AST::MacroRule *matched_rule = nullptr;
3105-
std::map<std::string, std::vector<MatchedFragment>> matched_fragments;
3105+
std::map<std::string, MatchedFragmentContainer> matched_fragments;
31063106
for (auto &rule : rules_def.get_rules ())
31073107
{
31083108
sub_stack.push ();
@@ -3111,9 +3111,10 @@ MacroExpander::expand_decl_macro (Location invoc_locus,
31113111

31123112
if (did_match_rule)
31133113
{
3114-
for (auto &kv : matched_fragments)
3115-
rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (),
3116-
kv.second.size ());
3114+
// Debugging
3115+
// for (auto &kv : matched_fragments)
3116+
// rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (),
3117+
// kv.second.get_fragments ().size ());
31173118

31183119
matched_rule = &rule;
31193120
break;
@@ -3726,7 +3727,10 @@ MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
37263727
rust_debug_loc (rep.get_match_locus (), "%s matched %lu times",
37273728
res ? "successfully" : "unsuccessfully", match_amount);
37283729

3729-
// We can now set the amount to each fragment we matched in the substack
3730+
// We have to handle zero fragments differently: They will not have been
3731+
// "matched" but they are still valid and should be inserted as a special
3732+
// case. So we go through the stack map, and for every fragment which doesn't
3733+
// exist, insert a zero-matched fragment.
37303734
auto &stack_map = sub_stack.peek ();
37313735
for (auto &match : rep.get_matches ())
37323736
{
@@ -3736,20 +3740,9 @@ MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
37363740
auto fragment = static_cast<AST::MacroMatchFragment *> (match.get ());
37373741
auto it = stack_map.find (fragment->get_ident ());
37383742

3739-
// If we can't find the fragment, but the result was valid, then
3740-
// it's a zero-matched fragment and we can insert it
37413743
if (it == stack_map.end ())
3742-
{
3743-
sub_stack.insert_fragment (
3744-
MatchedFragment::zero (fragment->get_ident ()));
3745-
}
3746-
else
3747-
{
3748-
// We can just set the repetition amount on the first match
3749-
// FIXME: Make this more ergonomic and similar to what we fetch
3750-
// in `substitute_repetition`
3751-
it->second[0].set_match_amount (match_amount);
3752-
}
3744+
sub_stack.insert_matches (fragment->get_ident (),
3745+
MatchedFragmentContainer::zero ());
37533746
}
37543747
}
37553748

@@ -3759,7 +3752,7 @@ MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
37593752
AST::ASTFragment
37603753
MacroExpander::transcribe_rule (
37613754
AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
3762-
std::map<std::string, std::vector<MatchedFragment>> &matched_fragments,
3755+
std::map<std::string, MatchedFragmentContainer> &matched_fragments,
37633756
bool semicolon, ContextType ctx)
37643757
{
37653758
// we can manipulate the token tree to substitute the dollar identifiers so

gcc/rust/expand/rust-macro-expand.h

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,32 +54,78 @@ struct MatchedFragment
5454
std::string fragment_ident;
5555
size_t token_offset_begin;
5656
size_t token_offset_end;
57-
size_t match_amount;
5857

5958
MatchedFragment (std::string identifier, size_t token_offset_begin,
60-
size_t token_offset_end, size_t match_amount = 1)
59+
size_t token_offset_end)
6160
: fragment_ident (identifier), token_offset_begin (token_offset_begin),
62-
token_offset_end (token_offset_end), match_amount (match_amount)
61+
token_offset_end (token_offset_end)
62+
{}
63+
64+
/**
65+
* Empty constructor for uninitialized fragments
66+
*/
67+
MatchedFragment () : MatchedFragment ("", 0, 0) {}
68+
69+
std::string as_string () const
70+
{
71+
return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
72+
+ std::to_string (token_offset_end);
73+
}
74+
};
75+
76+
class MatchedFragmentContainer
77+
{
78+
public:
79+
MatchedFragmentContainer (std::vector<MatchedFragment> fragments)
80+
: fragments (fragments)
6381
{}
6482

6583
/**
6684
* Create a valid fragment matched zero times. This is useful for repetitions
6785
* which allow the absence of a fragment, such as * and ?
6886
*/
69-
static MatchedFragment zero (std::string identifier)
87+
static MatchedFragmentContainer zero ()
7088
{
71-
// We don't need offsets since there is "no match"
72-
return MatchedFragment (identifier, 0, 0, 0);
89+
return MatchedFragmentContainer ({});
7390
}
7491

75-
std::string as_string () const
92+
/**
93+
* Create a valid fragment matched one time
94+
*/
95+
static MatchedFragmentContainer one (MatchedFragment fragment)
7696
{
77-
return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
78-
+ std::to_string (token_offset_end) + " (matched "
79-
+ std::to_string (match_amount) + " times)";
97+
return MatchedFragmentContainer ({fragment});
8098
}
8199

82-
void set_match_amount (size_t new_amount) { match_amount = new_amount; }
100+
/**
101+
* Add a matched fragment to the container
102+
*/
103+
void add_fragment (MatchedFragment fragment)
104+
{
105+
fragments.emplace_back (fragment);
106+
}
107+
108+
size_t get_match_amount () const { return fragments.size (); }
109+
const std::vector<MatchedFragment> &get_fragments () const
110+
{
111+
return fragments;
112+
}
113+
// const std::string &get_fragment_name () const { return fragment_name; }
114+
115+
bool is_single_fragment () const { return get_match_amount () == 1; }
116+
const MatchedFragment get_single_fragment () const
117+
{
118+
rust_assert (get_match_amount () == 1);
119+
120+
return fragments[0];
121+
}
122+
123+
private:
124+
/**
125+
* Fragments matched `match_amount` times. This can be an empty vector
126+
* in case having zero matches is allowed (i.e ? or * operators)
127+
*/
128+
std::vector<MatchedFragment> fragments;
83129
};
84130

85131
class SubstitutionScope
@@ -89,38 +135,49 @@ class SubstitutionScope
89135

90136
void push () { stack.push_back ({}); }
91137

92-
std::map<std::string, std::vector<MatchedFragment>> pop ()
138+
std::map<std::string, MatchedFragmentContainer> pop ()
93139
{
94140
auto top = stack.back ();
95141
stack.pop_back ();
96142
return top;
97143
}
98144

99-
std::map<std::string, std::vector<MatchedFragment>> &peek ()
145+
std::map<std::string, MatchedFragmentContainer> &peek ()
100146
{
101147
return stack.back ();
102148
}
103149

150+
/**
151+
* Insert a new matched fragment into the current substitution map
152+
*/
104153
void insert_fragment (MatchedFragment fragment)
105154
{
106155
auto &current_map = stack.back ();
107156
auto it = current_map.find (fragment.fragment_ident);
108157

109158
if (it == current_map.end ())
110159
{
111-
auto new_frags = std::vector<MatchedFragment> ();
112-
new_frags.emplace_back (fragment);
113-
current_map.insert ({fragment.fragment_ident, new_frags});
160+
current_map.insert (
161+
{fragment.fragment_ident, MatchedFragmentContainer::one (fragment)});
114162
}
115163
else
116164
{
117165
auto &frags = it->second;
118-
frags.emplace_back (fragment);
166+
frags.add_fragment (fragment);
119167
}
120168
}
121169

170+
void insert_matches (std::string key, MatchedFragmentContainer matches)
171+
{
172+
auto &current_map = stack.back ();
173+
auto it = current_map.find (key);
174+
rust_assert (it == current_map.end ());
175+
176+
current_map.insert ({key, matches});
177+
}
178+
122179
private:
123-
std::vector<std::map<std::string, std::vector<MatchedFragment>>> stack;
180+
std::vector<std::map<std::string, MatchedFragmentContainer>> stack;
124181
};
125182

126183
// Object used to store shared data (between functions) for macro expansion.
@@ -174,7 +231,7 @@ struct MacroExpander
174231

175232
AST::ASTFragment transcribe_rule (
176233
AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
177-
std::map<std::string, std::vector<MatchedFragment>> &matched_fragments,
234+
std::map<std::string, MatchedFragmentContainer> &matched_fragments,
178235
bool semicolon, ContextType ctx);
179236

180237
bool match_fragment (Parser<MacroInvocLexer> &parser,

gcc/rust/expand/rust-macro-substitute-ctx.cc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
1616
}
1717
else
1818
{
19-
// Replace
2019
// We only care about the vector when expanding repetitions.
2120
// Just access the first element of the vector.
22-
// FIXME: Clean this up so it makes more sense
23-
auto &frag = it->second[0];
21+
auto &frag = it->second.get_single_fragment ();
2422
for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end;
2523
offs++)
2624
{
@@ -66,7 +64,7 @@ SubstituteCtx::substitute_repetition (
6664
}
6765

6866
// FIXME: Refactor, ugly
69-
repeat_amount = it->second[0].match_amount;
67+
repeat_amount = it->second.get_match_amount ();
7068
}
7169
}
7270
}
@@ -98,19 +96,20 @@ SubstituteCtx::substitute_repetition (
9896

9997
for (size_t i = 0; i < repeat_amount; i++)
10098
{
101-
std::map<std::string, std::vector<MatchedFragment>> sub_map;
99+
std::map<std::string, MatchedFragmentContainer> sub_map;
102100
for (auto &kv_match : fragments)
103101
{
104-
std::vector<MatchedFragment> sub_vec;
102+
MatchedFragment sub_fragment;
105103

106104
// FIXME: Hack: If a fragment is not repeated, how does it fit in the
107105
// submap? Do we really want to expand it? Is this normal behavior?
108-
if (kv_match.second.size () == 1)
109-
sub_vec.emplace_back (kv_match.second[0]);
106+
if (kv_match.second.is_single_fragment ())
107+
sub_fragment = kv_match.second.get_single_fragment ();
110108
else
111-
sub_vec.emplace_back (kv_match.second[i]);
109+
sub_fragment = kv_match.second.get_fragments ()[i];
112110

113-
sub_map.insert ({kv_match.first, sub_vec});
111+
sub_map.insert (
112+
{kv_match.first, MatchedFragmentContainer::one (sub_fragment)});
114113
}
115114

116115
auto substitute_context = SubstituteCtx (input, new_macro, sub_map);

gcc/rust/expand/rust-macro-substitute-ctx.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ class SubstituteCtx
2424
{
2525
std::vector<std::unique_ptr<AST::Token>> &input;
2626
std::vector<std::unique_ptr<AST::Token>> &macro;
27-
std::map<std::string, std::vector<MatchedFragment>> &fragments;
27+
std::map<std::string, MatchedFragmentContainer> &fragments;
2828

2929
public:
3030
SubstituteCtx (std::vector<std::unique_ptr<AST::Token>> &input,
3131
std::vector<std::unique_ptr<AST::Token>> &macro,
32-
std::map<std::string, std::vector<MatchedFragment>> &fragments)
32+
std::map<std::string, MatchedFragmentContainer> &fragments)
3333
: input (input), macro (macro), fragments (fragments)
3434
{}
3535

0 commit comments

Comments
 (0)