Skip to content

Commit

Permalink
Merge #1002
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bors[bot] and CohenArthur authored Mar 11, 2022
2 parents 51c7cf4 + 9f73e82 commit 1f4e2de
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 50 deletions.
31 changes: 12 additions & 19 deletions gcc/rust/expand/rust-macro-expand.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3190,7 +3190,7 @@ MacroExpander::expand_decl_macro (Location invoc_locus,

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

if (did_match_rule)
{
for (auto &kv : matched_fragments)
rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (),
kv.second.size ());
// Debugging
// for (auto &kv : matched_fragments)
// rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (),
// kv.second.get_fragments ().size ());

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

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

// If we can't find the fragment, but the result was valid, then
// it's a zero-matched fragment and we can insert it
if (it == stack_map.end ())
{
sub_stack.insert_fragment (
MatchedFragment::zero (fragment->get_ident ()));
}
else
{
// We can just set the repetition amount on the first match
// FIXME: Make this more ergonomic and similar to what we fetch
// in `substitute_repetition`
it->second[0].set_match_amount (match_amount);
}
sub_stack.insert_matches (fragment->get_ident (),
MatchedFragmentContainer::zero ());
}
}

Expand Down Expand Up @@ -3925,7 +3918,7 @@ transcribe_expression (Parser<MacroInvocLexer> &parser)
AST::ASTFragment
MacroExpander::transcribe_rule (
AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
std::map<std::string, std::vector<MatchedFragment>> &matched_fragments,
std::map<std::string, MatchedFragmentContainer> &matched_fragments,
bool semicolon, ContextType ctx)
{
// we can manipulate the token tree to substitute the dollar identifiers so
Expand Down
95 changes: 76 additions & 19 deletions gcc/rust/expand/rust-macro-expand.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,32 +54,78 @@ struct MatchedFragment
std::string fragment_ident;
size_t token_offset_begin;
size_t token_offset_end;
size_t match_amount;

MatchedFragment (std::string identifier, size_t token_offset_begin,
size_t token_offset_end, size_t match_amount = 1)
size_t token_offset_end)
: fragment_ident (identifier), token_offset_begin (token_offset_begin),
token_offset_end (token_offset_end), match_amount (match_amount)
token_offset_end (token_offset_end)
{}

/**
* Empty constructor for uninitialized fragments
*/
MatchedFragment () : MatchedFragment ("", 0, 0) {}

std::string as_string () const
{
return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
+ std::to_string (token_offset_end);
}
};

class MatchedFragmentContainer
{
public:
MatchedFragmentContainer (std::vector<MatchedFragment> fragments)
: fragments (fragments)
{}

/**
* Create a valid fragment matched zero times. This is useful for repetitions
* which allow the absence of a fragment, such as * and ?
*/
static MatchedFragment zero (std::string identifier)
static MatchedFragmentContainer zero ()
{
// We don't need offsets since there is "no match"
return MatchedFragment (identifier, 0, 0, 0);
return MatchedFragmentContainer ({});
}

std::string as_string () const
/**
* Create a valid fragment matched one time
*/
static MatchedFragmentContainer one (MatchedFragment fragment)
{
return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
+ std::to_string (token_offset_end) + " (matched "
+ std::to_string (match_amount) + " times)";
return MatchedFragmentContainer ({fragment});
}

void set_match_amount (size_t new_amount) { match_amount = new_amount; }
/**
* Add a matched fragment to the container
*/
void add_fragment (MatchedFragment fragment)
{
fragments.emplace_back (fragment);
}

size_t get_match_amount () const { return fragments.size (); }
const std::vector<MatchedFragment> &get_fragments () const
{
return fragments;
}
// const std::string &get_fragment_name () const { return fragment_name; }

bool is_single_fragment () const { return get_match_amount () == 1; }
const MatchedFragment get_single_fragment () const
{
rust_assert (get_match_amount () == 1);

return fragments[0];
}

private:
/**
* Fragments matched `match_amount` times. This can be an empty vector
* in case having zero matches is allowed (i.e ? or * operators)
*/
std::vector<MatchedFragment> fragments;
};

class SubstitutionScope
Expand All @@ -89,38 +135,49 @@ class SubstitutionScope

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

std::map<std::string, std::vector<MatchedFragment>> pop ()
std::map<std::string, MatchedFragmentContainer> pop ()
{
auto top = stack.back ();
stack.pop_back ();
return top;
}

std::map<std::string, std::vector<MatchedFragment>> &peek ()
std::map<std::string, MatchedFragmentContainer> &peek ()
{
return stack.back ();
}

/**
* Insert a new matched fragment into the current substitution map
*/
void insert_fragment (MatchedFragment fragment)
{
auto &current_map = stack.back ();
auto it = current_map.find (fragment.fragment_ident);

if (it == current_map.end ())
{
auto new_frags = std::vector<MatchedFragment> ();
new_frags.emplace_back (fragment);
current_map.insert ({fragment.fragment_ident, new_frags});
current_map.insert (
{fragment.fragment_ident, MatchedFragmentContainer::one (fragment)});
}
else
{
auto &frags = it->second;
frags.emplace_back (fragment);
frags.add_fragment (fragment);
}
}

void insert_matches (std::string key, MatchedFragmentContainer matches)
{
auto &current_map = stack.back ();
auto it = current_map.find (key);
rust_assert (it == current_map.end ());

current_map.insert ({key, matches});
}

private:
std::vector<std::map<std::string, std::vector<MatchedFragment>>> stack;
std::vector<std::map<std::string, MatchedFragmentContainer>> stack;
};

// Object used to store shared data (between functions) for macro expansion.
Expand Down Expand Up @@ -176,7 +233,7 @@ struct MacroExpander

AST::ASTFragment transcribe_rule (
AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
std::map<std::string, std::vector<MatchedFragment>> &matched_fragments,
std::map<std::string, MatchedFragmentContainer> &matched_fragments,
bool semicolon, ContextType ctx);

bool match_fragment (Parser<MacroInvocLexer> &parser,
Expand Down
19 changes: 9 additions & 10 deletions gcc/rust/expand/rust-macro-substitute-ctx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
}
else
{
// Replace
// We only care about the vector when expanding repetitions.
// Just access the first element of the vector.
// FIXME: Clean this up so it makes more sense
auto &frag = it->second[0];
auto &frag = it->second.get_single_fragment ();
for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end;
offs++)
{
Expand Down Expand Up @@ -66,7 +64,7 @@ SubstituteCtx::substitute_repetition (
}

// FIXME: Refactor, ugly
repeat_amount = it->second[0].match_amount;
repeat_amount = it->second.get_match_amount ();
}
}
}
Expand Down Expand Up @@ -98,19 +96,20 @@ SubstituteCtx::substitute_repetition (

for (size_t i = 0; i < repeat_amount; i++)
{
std::map<std::string, std::vector<MatchedFragment>> sub_map;
std::map<std::string, MatchedFragmentContainer> sub_map;
for (auto &kv_match : fragments)
{
std::vector<MatchedFragment> sub_vec;
MatchedFragment sub_fragment;

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

sub_map.insert ({kv_match.first, sub_vec});
sub_map.insert (
{kv_match.first, MatchedFragmentContainer::one (sub_fragment)});
}

auto substitute_context = SubstituteCtx (input, new_macro, sub_map);
Expand Down
4 changes: 2 additions & 2 deletions gcc/rust/expand/rust-macro-substitute-ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ class SubstituteCtx
{
std::vector<std::unique_ptr<AST::Token>> &input;
std::vector<std::unique_ptr<AST::Token>> &macro;
std::map<std::string, std::vector<MatchedFragment>> &fragments;
std::map<std::string, MatchedFragmentContainer> &fragments;

public:
SubstituteCtx (std::vector<std::unique_ptr<AST::Token>> &input,
std::vector<std::unique_ptr<AST::Token>> &macro,
std::map<std::string, std::vector<MatchedFragment>> &fragments)
std::map<std::string, MatchedFragmentContainer> &fragments)
: input (input), macro (macro), fragments (fragments)
{}

Expand Down

0 comments on commit 1f4e2de

Please sign in to comment.