This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

patch to add #pushdef, #popdef to cccp,cpplib


This small patch adds a pair of preprocessor line directives.
They implement a very simple macro stack control mechanism.
This allows crude but effective lexical scoping for macro names
in a way that I believe is in harmony with the flavor of cpp.

It has to potential to alleviate a common class of macro name
collision problems.  My personal motivation was to get nice
composition properties from a generic macro-based name prefixing
mechanism I use for some cpp-based generic collections.  And I'm
sure there are many other mechanisms I haven't thought of.  It
seems unlikely that I am the first or the last person to want
this particular extension to ANSI preprocessor directives.
Hopefully there isn't a lot of antagonism toward this sort of
extension.

So here is the patch.  It's really only semantically half the size
that it looks because the code is replicated in cpplib and cccp.
It hasn't been exhaustively tested, but the code worked in basic
tests like:

	source text			expanded text
	x				x
	#pushdef x 1
	x				1
	#pushdef x 2
	x				2
	#pushdef x 3
	x				3
	#popdef x
	x				2
	#popdef x
	x				1
	#popdef x
	x				x

It also seems to work fine on some of the corner cases like
"#popdef notamacro" and "#popdef @#$A%".

Let me know what you think.

Chuck <cblake@lcs.mit.edu>

diff -ruwBP egcs-19980707/gcc/cccp.c egcs-19980707.cblake/gcc/cccp.c
--- egcs-19980707/gcc/cccp.c	Sun Jun 28 02:09:45 1998
+++ egcs-19980707.cblake/gcc/cccp.c	Mon Jul 13 10:52:56 1998
@@ -666,6 +666,7 @@
 struct hashnode {
   struct hashnode *next;	/* double links for easy deletion */
   struct hashnode *prev;
+  struct hashnode *stack;
   struct hashnode **bucket_hdr;	/* also, a back pointer to this node's hash
 				   chain is kept, in case the node is the head
 				   of the chain and gets deleted.  */
@@ -816,6 +817,8 @@
 static int do_undef DO_PROTO;
 static int do_warning DO_PROTO;
 static int do_xifdef DO_PROTO;
+static int do_pushdef DO_PROTO;
+static int do_popdef DO_PROTO;
 
 /* Here is the actual list of #-directives, most-often-used first.  */
 
@@ -841,6 +844,10 @@
   {  5, do_ident, "ident", T_IDENT},
   {  6, do_assert, "assert", T_ASSERT},
   {  8, do_unassert, "unassert", T_UNASSERT},
+#ifndef NO_PUSHPOP
+  {  7, do_pushdef, "pushdef", T_DEFINE},
+  {  6, do_popdef, "popdef", T_UNDEF},
+#endif
   {  -1, 0, "", T_UNUSED},
 };
 
@@ -5811,6 +5818,63 @@
   return 1;
 }
 
+/* Process a #pushdef command.	This non-ANSI extension allows a crude but
+   effective form of lexically scoping for macro names.  This routine works
+   mostly like #define.  The checking of whether or not to replace the
+   definition is cut out and if it is not a new macro, the whole HASHNODE
+   is pushed onto a stack implemented by a new link field in HASHNODE's. */
+static int
+do_pushdef (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op;
+     struct directive *keyword;
+{
+  int hashcode;
+  MACRODEF mdef;
+  HASHNODE *hp, *old;
+
+  mdef = create_definition (buf, limit, op);
+  if (!mdef.defn)
+    return 1;
+
+  hashcode = hashf (mdef.symnam, mdef.symlen, HASHSIZE);
+
+  if ((hp = lookup (mdef.symnam, mdef.symlen, hashcode)) != NULL)
+    {
+      /* Pushing non-macro symbols is not ok. */
+      if (hp->type != T_PCSTRING && hp->type != T_MACRO && hp->type != T_CONST)
+      {
+	if (debug_output && op)
+	  pass_thru_directive (buf, limit, op, keyword);
+
+	pedwarn ("`%.*s' redefined", mdef.symlen, mdef.symnam);
+	if (hp->type == T_MACRO)
+	  pedwarn_with_file_and_line (hp->value.defn->file,
+				      hp->value.defn->file_len,
+				      hp->value.defn->line,
+				      "this is the location of the previous definition");
+      }
+      /* Copy current definition onto top of stack.  We take care to preserve
+	 the address of the macro as it may be pointed to from elsewhere. */
+      old = (HASHNODE *) xmalloc (sizeof *hp + hp->length + 1);
+      *old = *hp;		/* copy data & links (old->stack=hp->stack) */
+      hp->stack = old;		/* link to top of stack */
+      hp->type = T_MACRO;	/* reset type & defn fields */
+      hp->value.defn = mdef.defn;
+    }
+  else
+    {
+      /* If we are passing through #define and #undef directives, do
+	 that for this new definition now.  */
+      if (debug_output && op)
+	pass_thru_directive (buf, limit, op, keyword);
+      install (mdef.symnam, mdef.symlen, T_MACRO,
+	       (char *) mdef.defn, hashcode);
+    }
+  return 0;
+}
+
+
 /* Check a purported macro name SYMNAME, and yield its length.
    USAGE is the kind of name this is intended for.  */
 
@@ -6811,6 +6875,68 @@
   return 0;
 }
 
+/*
+ * Pop the definition of a symbol from the definition stack.  This is
+ * equivalent to undef if the stack has only one entry.  Unlike undef
+ * this prints a warning/error if popdef is invoked on an undefined
+ * macro name.	Also unlike undef, "#popdef N M K A B" will pop
+ * definitions for all the listed macros.
+ */
+
+/*
+   Ok.	I'm not sure why do_undef() has a while() loop. do_define()
+   seems to pretty much assume macro names are unique, so we will too.
+   Also, delete macro is pretty bad about leaking memory already, so
+   we punt on deallocation cleanliness for now.
+*/
+static int
+do_popdef (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op;
+     struct directive *keyword;
+{
+  int sym_length = 0;
+  HASHNODE *hp;
+  U_CHAR *orig_buf = buf;
+
+  while (1) {
+      int tmp = errors;
+      buf += sym_length;
+      SKIP_WHITE_SPACE (buf);
+      if (buf == limit)
+	      break;
+      /* if this fails we need to break out of the loop. */
+      sym_length = check_macro_name (buf, "macro");
+      if (errors > tmp)
+	      break;
+      if ((hp = lookup (buf, sym_length, -1))) {
+	  /* TODO: If we are generating additional info for debugging
+		   (with -g) then we need to pass through #popdef's as
+		   the appropriate #undef + #define combination. */
+	if (hp->type != T_MACRO)
+	  warning ("popdef-ing `%s'", hp->name);
+	if (hp->stack) {			/* not bottom of stack -- pop. */
+	  HASHNODE  *prev0 = hp->prev,
+		    *next0 = hp->next,
+		   **bukt0 = hp->bucket_hdr;
+	  *hp = *hp->stack;
+	  hp->prev = prev0;
+	  hp->next = next0;
+	  hp->bucket_hdr = bukt0;
+	  if (debug_output && op)
+	    pass_thru_directive (orig_buf, limit, op, keyword);
+	} else
+	  delete_macro (hp);
+      } else {
+	char fmt[64];
+	sprintf(fmt, "popdef of undefined macro `%%%d.%ds'",
+		sym_length, sym_length);
+	error(fmt, buf);
+      }
+  }
+  return 0;
+}
+
 /* Report an error detected by the program we are processing.
    Use the text of the line in the error message.
    (We use error because it prints the filename & line#.)  */
@@ -9361,6 +9487,7 @@
   hp->prev = NULL;
   if (hp->next != NULL)
     hp->next->prev = hp;
+  hp->stack = NULL;
   hp->type = type;
   hp->length = len;
   hp->value.cpval = value;
diff -ruwBP egcs-19980707/gcc/cpphash.c egcs-19980707.cblake/gcc/cpphash.c
--- egcs-19980707/gcc/cpphash.c	Tue May 19 04:42:09 1998
+++ egcs-19980707.cblake/gcc/cpphash.c	Mon Jul 13 01:34:49 1998
@@ -176,6 +176,7 @@
   hp->prev = NULL;
   if (hp->next != NULL)
     hp->next->prev = hp;
+  hp->stack = NULL;
   hp->type = type;
   hp->length = len;
   if (hp->type == T_CONST)
diff -ruwBP egcs-19980707/gcc/cpphash.h egcs-19980707.cblake/gcc/cpphash.h
--- egcs-19980707/gcc/cpphash.h	Tue May 19 04:42:11 1998
+++ egcs-19980707.cblake/gcc/cpphash.h	Mon Jul 13 01:06:55 1998
@@ -12,6 +12,7 @@
 struct hashnode {
   struct hashnode *next;	/* double links for easy deletion */
   struct hashnode *prev;
+  struct hashnode *stack;	/* stack structure for #pushdef/#popdef */
   struct hashnode **bucket_hdr;	/* also, a back pointer to this node's hash
 				   chain is kept, in case the node is the head
 				   of the chain and gets deleted. */
diff -ruwBP egcs-19980707/gcc/cpplib.c egcs-19980707.cblake/gcc/cpplib.c
--- egcs-19980707/gcc/cpplib.c	Mon Jul  6 17:14:13 1998
+++ egcs-19980707.cblake/gcc/cpplib.c	Mon Jul 13 10:55:27 1998
@@ -374,6 +374,8 @@
 static int do_assert PARAMS ((cpp_reader *, struct directive *, U_CHAR *, U_CHAR *));
 static int do_unassert PARAMS ((cpp_reader *, struct directive *, U_CHAR *, U_CHAR *));
 static int do_warning PARAMS ((cpp_reader *, struct directive *, U_CHAR *, U_CHAR *));
+static int do_pushdef PARAMS ((cpp_reader *, struct directive *, U_CHAR *, U_CHAR *));
+static int do_popdef PARAMS ((cpp_reader *, struct directive *, U_CHAR *, U_CHAR *));
 
 #define IS_INCLUDE_DIRECTIVE_TYPE(t) \
 ((int) T_INCLUDE <= (int) (t) && (int) (t) <= (int) T_IMPORT)
@@ -403,6 +405,10 @@
 #endif
   {  6, do_assert, "assert", T_ASSERT, 1},
   {  8, do_unassert, "unassert", T_UNASSERT, 1},
+#ifndef NO_PUSHPOP
+  {  7, do_pushdef, "pushdef", T_DEFINE},
+  {  6, do_popdef, "popdef", T_UNDEF},
+#endif
   {  -1, 0, "", T_UNUSED},
 };
 
@@ -1790,7 +1796,7 @@
 	}
       /* Replace the old definition.  */
       hp->type = T_MACRO;
-      hp->value.defn = mdef.defn;
+      hp->value.defn = mdef.defn;	/* memory leak -- release old defn */
     }
   else
     {
@@ -1809,6 +1815,69 @@
   return 1;
 }
 
+/* Process a #pushdef command.	This non-ANSI extension allows a crude but
+   effective form of lexically scoping for macro names.  This routine works
+   mostly like #define.  The checking of whether or not to replace the
+   definition is cut out and if it is not a new macro, the whole HASHNODE
+   is pushed onto a stack implemented by a new link field in HASHNODE's. */
+
+static int
+do_pushdef(pfile, keyword, buf, limit)
+     cpp_reader *pfile;
+     struct directive *keyword;
+     U_CHAR *buf, *limit;
+{
+  int hashcode;
+  MACRODEF mdef;
+  HASHNODE *hp, *old;
+
+  mdef = create_definition (buf, limit, pfile, keyword == NULL);
+  if (!mdef.defn)
+    return 1;
+
+  hashcode = hashf (mdef.symnam, mdef.symlen, HASHSIZE);
+
+  if ((hp = cpp_lookup (pfile, mdef.symnam, mdef.symlen, hashcode)) != NULL)
+    {
+      /* Pushing non-macro symbols is not ok. */
+      if (hp->type != T_PCSTRING && hp->type != T_MACRO && hp->type != T_CONST)
+      {
+	  U_CHAR *msg;		/* what pain...  */
+
+	  /* If we are passing through #define and #undef directives, do
+	     that for this re-definition now.  */
+	  if (CPP_OPTIONS (pfile)->debug_output && keyword)
+	    pass_thru_directive (buf, limit, pfile, keyword);
+
+	  msg = (U_CHAR *) alloca (mdef.symlen + 22);
+	  *msg = '`';
+	  bcopy (mdef.symnam, msg + 1, mdef.symlen);
+	  strcpy ((char *) (msg + mdef.symlen + 1), "' redefined");
+	  cpp_pedwarn (pfile, msg);
+	  if (hp->type == T_MACRO)
+	    cpp_pedwarn_with_file_and_line (pfile, hp->value.defn->file, hp->value.defn->line,
+				      "this is the location of the previous definition");
+	}
+      /* Copy current definition onto top of stack.  We take care to preserve
+	 the address of the macro as it may be pointed to from elsewhere. */
+      old = (HASHNODE *) xmalloc (sizeof *hp + hp->length + 1);
+      *old = *hp;		/* copy data & links (old->stack=hp->stack) */
+      hp->stack = old;		/* link to top of stack */
+      hp->type = T_MACRO;	/* reset type & defn fields */
+      hp->value.defn = mdef.defn;
+    }
+  else
+    {
+      /* If we are passing through #define and #undef directives, do
+	 that for this new definition now.  */
+      if (CPP_OPTIONS (pfile)->debug_output && keyword)
+	pass_thru_directive (buf, limit, pfile, keyword);
+      install (mdef.symnam, mdef.symlen, T_MACRO, 0,
+	       (char *) mdef.defn, hashcode);
+    }
+  return 0;
+}
+
 /* This structure represents one parsed argument in a macro call.
    `raw' points to the argument text as written (`raw_length' is its length).
    `expanded' points to the argument's macro-expansion
@@ -3850,6 +3919,68 @@
 }
 
 /*
+ * Pop the definition of a symbol from the definition stack.  This is
+ * equivalent to undef if the stack has only one entry.  Unlike undef
+ * this prints a warning/error if popdef is invoked on an undefined
+ * macro name.	Also unlike undef, "#popdef N M K A B" will pop
+ * definitions for all the listed macros.
+ */
+
+/*
+   Ok.	I'm not sure why do_undef() has a while() loop. do_define()
+   seems to pretty much assume macro names are unique, so we will too.
+   Also, delete macro is pretty bad about leaking memory already, so
+   we punt on deallocation cleanliness for now.
+*/
+static int
+do_popdef (pfile, keyword, buf, limit)
+     cpp_reader *pfile;
+     struct directive *keyword;
+     U_CHAR *buf, *limit;
+{
+  int sym_length;
+  HASHNODE *hp;
+  U_CHAR *orig_buf = buf;
+
+  while (1) {
+      int tmp = pfile->errors;
+      buf += sym_length;
+      SKIP_WHITE_SPACE (buf);
+      if (buf == limit)
+	      break;
+      /* if this fails we need to break out of the loop. */
+      sym_length = check_macro_name (pfile, buf, "macro");
+      if (pfile->errors > tmp)
+	      break;
+      if ((hp = cpp_lookup (pfile, buf, sym_length, -1))) {
+	  /* TODO: If we are generating additional info for debugging
+		   (with -g) then we need to pass through #popdef's as
+		   the appropriate #undef + #define combination. */
+	if (hp->type != T_MACRO)
+	  cpp_warning (pfile, "popdef-ing `%s'", hp->name);
+	if (hp->stack) {			/* not bottom of stack -- pop. */
+	  HASHNODE  *prev0 = hp->prev,
+		    *next0 = hp->next,
+		   **bukt0 = hp->bucket_hdr;
+	  *hp = *hp->stack;
+	  hp->prev = prev0;
+	  hp->next = next0;
+	  hp->bucket_hdr = bukt0;
+	  if (CPP_OPTIONS (pfile)->debug_output && keyword)
+	    pass_thru_directive (orig_buf, limit, pfile, keyword);
+	} else
+	  delete_macro (hp);
+      } else {
+	char fmt[64];
+	sprintf(fmt, "popdef of undefined macro `%%%d.%ds'",
+		sym_length, sym_length);
+	cpp_error(pfile, fmt, buf);
+      }
+  }
+  return 0;
+}
+
+/*
  * Report an error detected by the program we are processing.
  * Use the text of the line in the error message.
  * (We use error because it prints the filename & line#.)
Binary files egcs-19980707/gcc/cppmain and egcs-19980707.cblake/gcc/cppmain differ


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]