Skip to content

Commit 1f4e2de

Browse files
Merge #1002
1002: macros: Add abstraction around multiple matches r=CohenArthur a=CohenArthur 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: Arthur Cohen <arthur.cohen@embecosm.com>
2 parents 51c7cf4 + 9f73e82 commit 1f4e2de

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
@@ -3190,7 +3190,7 @@ MacroExpander::expand_decl_macro (Location invoc_locus,
31903190

31913191
// find matching arm
31923192
AST::MacroRule *matched_rule = nullptr;
3193-
std::map<std::string, std::vector<MatchedFragment>> matched_fragments;
3193+
std::map<std::string, MatchedFragmentContainer> matched_fragments;
31943194
for (auto &rule : rules_def.get_rules ())
31953195
{
31963196
sub_stack.push ();
@@ -3199,9 +3199,10 @@ MacroExpander::expand_decl_macro (Location invoc_locus,
31993199

32003200
if (did_match_rule)
32013201
{
3202-
for (auto &kv : matched_fragments)
3203-
rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (),
3204-
kv.second.size ());
3202+
// Debugging
3203+
// for (auto &kv : matched_fragments)
3204+
// rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (),
3205+
// kv.second.get_fragments ().size ());
32053206

32063207
matched_rule = &rule;
32073208
break;
@@ -3825,7 +3826,10 @@ MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
38253826
rust_debug_loc (rep.get_match_locus (), "%s matched %lu times",
38263827
res ? "successfully" : "unsuccessfully", match_amount);
38273828

3828-
// We can now set the amount to each fragment we matched in the substack
3829+
// We have to handle zero fragments differently: They will not have been
3830+
// "matched" but they are still valid and should be inserted as a special
3831+
// case. So we go through the stack map, and for every fragment which doesn't
3832+
// exist, insert a zero-matched fragment.
38293833
auto &stack_map = sub_stack.peek ();
38303834
for (auto &match : rep.get_matches ())
38313835
{
@@ -3835,20 +3839,9 @@ MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
38353839
auto fragment = static_cast<AST::MacroMatchFragment *> (match.get ());
38363840
auto it = stack_map.find (fragment->get_ident ());
38373841

3838-
// If we can't find the fragment, but the result was valid, then
3839-
// it's a zero-matched fragment and we can insert it
38403842
if (it == stack_map.end ())
3841-
{
3842-
sub_stack.insert_fragment (
3843-
MatchedFragment::zero (fragment->get_ident ()));
3844-
}
3845-
else
3846-
{
3847-
// We can just set the repetition amount on the first match
3848-
// FIXME: Make this more ergonomic and similar to what we fetch
3849-
// in `substitute_repetition`
3850-
it->second[0].set_match_amount (match_amount);
3851-
}
3843+
sub_stack.insert_matches (fragment->get_ident (),
3844+
MatchedFragmentContainer::zero ());
38523845
}
38533846
}
38543847

@@ -3925,7 +3918,7 @@ transcribe_expression (Parser<MacroInvocLexer> &parser)
39253918
AST::ASTFragment
39263919
MacroExpander::transcribe_rule (
39273920
AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
3928-
std::map<std::string, std::vector<MatchedFragment>> &matched_fragments,
3921+
std::map<std::string, MatchedFragmentContainer> &matched_fragments,
39293922
bool semicolon, ContextType ctx)
39303923
{
39313924
// 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.
@@ -176,7 +233,7 @@ struct MacroExpander
176233

177234
AST::ASTFragment transcribe_rule (
178235
AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
179-
std::map<std::string, std::vector<MatchedFragment>> &matched_fragments,
236+
std::map<std::string, MatchedFragmentContainer> &matched_fragments,
180237
bool semicolon, ContextType ctx);
181238

182239
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)