public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] macros: Add abstraction around multiple matches
@ 2022-06-08 12:17 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-06-08 12:17 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:9f73e827aba2d485898e9c2d3d4adcca1b2cf77d

commit 9f73e827aba2d485898e9c2d3d4adcca1b2cf77d
Author: Arthur Cohen <arthur.cohen@embecosm.com>
Date:   Wed Mar 9 17:30:35 2022 +0100

    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>

Diff:
---
 gcc/rust/expand/rust-macro-expand.cc         | 31 ++++-----
 gcc/rust/expand/rust-macro-expand.h          | 95 ++++++++++++++++++++++------
 gcc/rust/expand/rust-macro-substitute-ctx.cc | 19 +++---
 gcc/rust/expand/rust-macro-substitute-ctx.h  |  4 +-
 4 files changed, 99 insertions(+), 50 deletions(-)

diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
index 84a526c0c0c..00316792840 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -3102,7 +3102,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 ();
@@ -3111,9 +3111,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;
@@ -3726,7 +3727,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 ())
     {
@@ -3736,20 +3740,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 ());
 	}
     }
 
@@ -3759,7 +3752,7 @@ MacroExpander::match_repetition (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
diff --git a/gcc/rust/expand/rust-macro-expand.h b/gcc/rust/expand/rust-macro-expand.h
index 88d0e6e8496..dd48cbd5078 100644
--- a/gcc/rust/expand/rust-macro-expand.h
+++ b/gcc/rust/expand/rust-macro-expand.h
@@ -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
@@ -89,18 +135,21 @@ public:
 
   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 ();
@@ -108,19 +157,27 @@ public:
 
     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.
@@ -174,7 +231,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,
diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.cc b/gcc/rust/expand/rust-macro-substitute-ctx.cc
index 61ab626f101..392a9fe42f1 100644
--- a/gcc/rust/expand/rust-macro-substitute-ctx.cc
+++ b/gcc/rust/expand/rust-macro-substitute-ctx.cc
@@ -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++)
 	{
@@ -66,7 +64,7 @@ SubstituteCtx::substitute_repetition (
 		}
 
 	      // FIXME: Refactor, ugly
-	      repeat_amount = it->second[0].match_amount;
+	      repeat_amount = it->second.get_match_amount ();
 	    }
 	}
     }
@@ -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);
diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.h b/gcc/rust/expand/rust-macro-substitute-ctx.h
index ed83926c32c..e89f9d788a1 100644
--- a/gcc/rust/expand/rust-macro-substitute-ctx.h
+++ b/gcc/rust/expand/rust-macro-substitute-ctx.h
@@ -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)
   {}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-06-08 12:17 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-08 12:17 [gcc/devel/rust/master] macros: Add abstraction around multiple matches Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).