cpplib: Implement _Pragma operator

Neil Booth NeilB@earthling.net
Mon Oct 30 12:31:00 GMT 2000


This patch implements _Pragma as introduced in the C99 standard.  A
few minor changes to existing infrastructure were necessary.

Firstly, the destringized string does not pass through stages 1 and 2
of processing; i.e. trigraphs, UCNs and escaped newlines are not
processed.  We should not do this for preprocessed input either, but
we have been doing.  I added a new flag "from_stage3" to indicate not
to do this, and we use this for _Pragma, command line directives, and
preprocessed buffers.

Also, cpp_push_buffer would check that we weren't mid-macro expansion.
This is no longer a valid check since _Pragma can be part of a macro
expansion, so I moved the check to stack_include_file.

run_directive now emits an error if the buffer isn't exhausted (e.g.
multi-line command line -D, or _Pragma).

The patch looks bigger than it is owing to re-indentation.  Will
commit when an x86-linux bootstrap and test run completes.

Documentation, website and testsuite updates coming soon.

Neil.

	* cppfiles.c (stack_include_file): Check for stacked contexts
	here.
	* cpphash.h (_cpp_do__Pragma): New prototype.
	* cppinit.c (cpp_reader_init): Add _Pragma keyword to hash table.

	* cpplex.c (skip_escaped_newlines): Only process trigraphs and
	escaped newlines if !(buffer->from_stage3).
	(_cpp_lex_token): Warn about missing newlines iff
	!buffer->from_stage3.

	* cpplib.c (get__Pragma_string, destringize,
	_cpp_do__Pragma): New functions.
	(run_directive): Set output_line for _Pragma to avoid line
	markers in output.  Set from_stage3 and prevent macro expansion
	for _Pragma and command-line options.  Check buffer exhaustion.
	(cpp_push_buffer): Don't check for stacked macro contexts, as
	this is perfectly legitimate for _Pragma.  Move the check to
	stack_include_file instead. Set from_stage3 iff buffer is
	preprocessed input.

	* cpplib.h (struct cpp_buffer): Make warned_cplusplus_comments
	unsigned. New boolean from_stage3.
	(struct spec_nodes): Add n__Pragma.

	* cppmacro.c (enter_macro_context): Flip sense of return value.
	(_cpp_get_token): Handle _Pragma operator.

Index: cppfiles.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cppfiles.c,v
retrieving revision 1.83
diff -u -p -r1.83 cppfiles.c
--- cppfiles.c	2000/10/28 17:59:04	1.83
+++ cppfiles.c	2000/10/30 20:26:15
@@ -206,6 +206,9 @@ stack_include_file (pfile, inc)
 {
   cpp_buffer *fp;
 
+  if (pfile->context->prev)
+    cpp_ice (pfile, "attempt to push file buffer with contexts stacked");
+
   if (DO_NOT_REREAD (inc))
     return 0;
 
Index: cpphash.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cpphash.h,v
retrieving revision 1.77
diff -u -p -r1.77 cpphash.h
--- cpphash.h	2000/10/28 17:59:05	1.77
+++ cpphash.h	2000/10/30 20:26:15
@@ -203,6 +203,7 @@ extern void _cpp_unlock_pool		PARAMS ((c
 extern int _cpp_test_assertion PARAMS ((cpp_reader *, int *));
 extern int _cpp_handle_directive PARAMS ((cpp_reader *, int));
 extern void _cpp_define_builtin	PARAMS ((cpp_reader *, const char *));
+extern void _cpp_do__Pragma	PARAMS ((cpp_reader *));
 extern void _cpp_init_stacks	PARAMS ((cpp_reader *));
 extern void _cpp_cleanup_stacks	PARAMS ((cpp_reader *));
 extern void _cpp_init_internal_pragmas PARAMS ((cpp_reader *));
Index: cppinit.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cppinit.c,v
retrieving revision 1.112
diff -u -p -r1.112 cppinit.c
--- cppinit.c	2000/10/28 17:59:05	1.112
+++ cppinit.c	2000/10/30 20:26:24
@@ -487,6 +487,7 @@ cpp_reader_init (pfile)
   s = &pfile->spec_nodes;
   s->n_L                = cpp_lookup (pfile, DSC("L"));
   s->n_defined		= cpp_lookup (pfile, DSC("defined"));
+  s->n__Pragma		= cpp_lookup (pfile, DSC("_Pragma"));
   s->n__STRICT_ANSI__   = cpp_lookup (pfile, DSC("__STRICT_ANSI__"));
   s->n__CHAR_UNSIGNED__ = cpp_lookup (pfile, DSC("__CHAR_UNSIGNED__"));
   s->n__VA_ARGS__       = cpp_lookup (pfile, DSC("__VA_ARGS__"));
Index: cpplex.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cpplex.c,v
retrieving revision 1.108
diff -u -p -r1.108 cpplex.c
--- cpplex.c	2000/10/29 09:56:00	1.108
+++ cpplex.c	2000/10/30 20:26:25
@@ -181,71 +181,77 @@ trigraph_ok (pfile, from_char)
 
 /* Skips any escaped newlines introduced by NEXT, which is either a
    '?' or a '\\'.  Returns the next character, which will also have
-   been placed in buffer->read_ahead.  */
+   been placed in buffer->read_ahead.  This routine performs
+   preprocessing stages 1 and 2 of the ISO C standard.  */
 static cppchar_t
 skip_escaped_newlines (buffer, next)
      cpp_buffer *buffer;
      cppchar_t next;
 {
-  cppchar_t next1;
-  const unsigned char *saved_cur;
-  int space;
-
-  do
+  /* Only do this if we apply stages 1 and 2.  */
+  if (!buffer->from_stage3)
     {
-      if (buffer->cur == buffer->rlimit)
-	break;
-      
-      SAVE_STATE ();
-      if (next == '?')
+      cppchar_t next1;
+      const unsigned char *saved_cur;
+      int space;
+
+      do
 	{
-	  next1 = *buffer->cur++;
-	  if (next1 != '?' || buffer->cur == buffer->rlimit)
+	  if (buffer->cur == buffer->rlimit)
+	    break;
+      
+	  SAVE_STATE ();
+	  if (next == '?')
 	    {
-	      RESTORE_STATE ();
-	      break;
+	      next1 = *buffer->cur++;
+	      if (next1 != '?' || buffer->cur == buffer->rlimit)
+		{
+		  RESTORE_STATE ();
+		  break;
+		}
+
+	      next1 = *buffer->cur++;
+	      if (!_cpp_trigraph_map[next1]
+		  || !trigraph_ok (buffer->pfile, next1))
+		{
+		  RESTORE_STATE ();
+		  break;
+		}
+
+	      /* We have a full trigraph here.  */
+	      next = _cpp_trigraph_map[next1];
+	      if (next != '\\' || buffer->cur == buffer->rlimit)
+		break;
+	      SAVE_STATE ();
+	    }
+
+	  /* We have a backslash, and room for at least one more character.  */
+	  space = 0;
+	  do
+	    {
+	      next1 = *buffer->cur++;
+	      if (!is_nvspace (next1))
+		break;
+	      space = 1;
 	    }
+	  while (buffer->cur < buffer->rlimit);
 
-	  next1 = *buffer->cur++;
-	  if (!_cpp_trigraph_map[next1] || !trigraph_ok (buffer->pfile, next1))
+	  if (!is_vspace (next1))
 	    {
 	      RESTORE_STATE ();
 	      break;
 	    }
 
-	  /* We have a full trigraph here.  */
-	  next = _cpp_trigraph_map[next1];
-	  if (next != '\\' || buffer->cur == buffer->rlimit)
-	    break;
-	  SAVE_STATE ();
+	  if (space)
+	    cpp_warning (buffer->pfile,
+			 "backslash and newline separated by space");
+
+	  next = handle_newline (buffer, next1);
+	  if (next == EOF)
+	    cpp_pedwarn (buffer->pfile, "backslash-newline at end of file");
 	}
-
-      /* We have a backslash, and room for at least one more character.  */
-      space = 0;
-      do
-	{
-	  next1 = *buffer->cur++;
-	  if (!is_nvspace (next1))
-	    break;
-	  space = 1;
-	}
-      while (buffer->cur < buffer->rlimit);
-
-      if (!is_vspace (next1))
-	{
-	  RESTORE_STATE ();
-	  break;
-	}
-
-      if (space)
-	cpp_warning (buffer->pfile,
-		     "backslash and newline separated by space");
-
-      next = handle_newline (buffer, next1);
-      if (next == EOF)
-	cpp_pedwarn (buffer->pfile, "backslash-newline at end of file");
+      while (next == '\\' || next == '?');
     }
-  while (next == '\\' || next == '?');
 
   buffer->read_ahead = next;
   return next;
@@ -863,8 +869,8 @@ _cpp_lex_token (pfile, result)
     {
     case EOF:
       /* Non-empty files should end in a newline.  Ignore for command
-	 line - we get e.g. -A options with no trailing \n.  */
-      if (pfile->lexer_pos.col != 0 && pfile->done_initializing)
+	 line and _Pragma buffers.  */
+      if (pfile->lexer_pos.col != 0 && !buffer->from_stage3)
 	cpp_pedwarn (pfile, "no newline at end of file");
       pfile->state.skip_newlines = 1;
       result->type = CPP_EOF;
Index: cpplib.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cpplib.c,v
retrieving revision 1.210
diff -u -p -r1.210 cpplib.c
--- cpplib.c	2000/10/29 22:45:13	1.210
+++ cpplib.c	2000/10/30 20:26:30
@@ -99,6 +99,9 @@ static void do_pragma_once	PARAMS ((cpp_
 static void do_pragma_poison	PARAMS ((cpp_reader *));
 static void do_pragma_system_header	PARAMS ((cpp_reader *));
 static void do_pragma_dependency	PARAMS ((cpp_reader *));
+static int get__Pragma_string	PARAMS ((cpp_reader *, cpp_token *));
+static unsigned char *destringize	PARAMS ((const cpp_string *,
+						 unsigned int *));
 static int parse_answer PARAMS ((cpp_reader *, struct answer **, int));
 static cpp_hashnode *parse_assertion PARAMS ((cpp_reader *, struct answer **,
 					      int));
@@ -345,7 +348,7 @@ run_directive (pfile, dir_no, buf, count
      size_t count;
      const char *name;
 {
-  if (cpp_push_buffer (pfile, (const U_CHAR *)buf, count) != NULL)
+  if (cpp_push_buffer (pfile, (const U_CHAR *) buf, count) != NULL)
     {
       const struct directive *dir = &dtable[dir_no];
 
@@ -353,15 +356,27 @@ run_directive (pfile, dir_no, buf, count
 	CPP_BUFFER (pfile)->nominal_fname = name;
       else
 	CPP_BUFFER (pfile)->nominal_fname = _("<command line>");
-      CPP_BUFFER (pfile)->lineno = (unsigned int)-1;
 
+      /* A kludge to avoid line markers for _Pragma.  */
+      if (dir_no == T_PRAGMA)
+	pfile->lexer_pos.output_line = CPP_BUFFER (pfile)->prev->lineno;
+
+      /* For _Pragma, the text is passed through preprocessing stage 3
+	 only, i.e. no trigraphs, no escaped newline removal, and no
+	 macro expansion.  Do the same for command-line directives.  */
+      pfile->buffer->from_stage3 = 1;
       pfile->state.in_directive = 1;
       pfile->directive = dir;
+      pfile->state.prevent_expansion++;
       (void) (*dir->handler) (pfile);
+      pfile->state.prevent_expansion--;
       pfile->directive = 0;
       pfile->state.in_directive = 0;
 
       skip_rest_of_line (pfile);
+      if (pfile->buffer->cur != pfile->buffer->rlimit)
+	cpp_error (pfile, "extra text after end of #%s directive",
+		   dtable[dir_no].name);
       cpp_pop_buffer (pfile);
     }
 }
@@ -1069,6 +1084,68 @@ do_pragma_dependency (pfile)
     }
 }
 
+/* Check syntax is "(string-literal)".  Returns 0 on success.  */
+static int
+get__Pragma_string (pfile, string)
+     cpp_reader *pfile;
+     cpp_token *string;
+{
+  cpp_token paren;
+
+  cpp_get_token (pfile, &paren);
+  if (paren.type != CPP_OPEN_PAREN)
+    return 1;
+
+  cpp_get_token (pfile, string);
+  if (string->type != CPP_STRING && string->type != CPP_WSTRING)
+    return 1;
+
+  cpp_get_token (pfile, &paren);
+  return paren.type != CPP_CLOSE_PAREN;
+}
+
+/* Returns a malloced buffer containing a destringized cpp_string by
+   removing the first \ of \" and \\ sequences.  */
+static unsigned char *
+destringize (in, len)
+     const cpp_string *in;
+     unsigned int *len;
+{
+  const unsigned char *src, *limit;
+  unsigned char *dest, *result;
+
+  dest = result = (unsigned char *) xmalloc (in->len);
+  for (src = in->text, limit = src + in->len; src < limit;)
+    {
+      /* We know there is a character following the backslash.  */
+      if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
+	src++;
+      *dest++ = *src++;
+    }
+
+  *len = dest - result;
+  return result;
+}
+
+void
+_cpp_do__Pragma (pfile)
+     cpp_reader *pfile;
+{
+  cpp_token string;
+  unsigned char *buffer;
+  unsigned int len;
+
+  if (get__Pragma_string (pfile, &string))
+    {
+      cpp_error (pfile, "_Pragma takes a parenthesized string literal");
+      return;
+    }
+
+  buffer = destringize (&string.val.str, &len);
+  run_directive (pfile, T_PRAGMA, (char *) buffer, len, _("<_Pragma>"));
+  free ((PTR) buffer);
+}
+
 /* Just ignore #sccs, on systems where we define it at all.  */
 #ifdef SCCS_DIRECTIVE
 static void
@@ -1626,12 +1703,6 @@ cpp_push_buffer (pfile, buffer, length)
       return NULL;
     }
 
-  if (pfile->context->prev)
-    {
-      cpp_ice (pfile, "buffer pushed with contexts stacked");
-      skip_rest_of_line (pfile);
-    }
-
   new = xobnew (pfile->buffer_ob, cpp_buffer);
   /* Clears, amongst other things, if_stack and mi_cmacro.  */
   memset (new, 0, sizeof (cpp_buffer));
@@ -1641,6 +1712,8 @@ cpp_push_buffer (pfile, buffer, length)
   new->rlimit = buffer + length;
   new->prev = buf;
   new->pfile = pfile;
+  /* Preprocessed files don't do trigraph and escaped newline processing.  */
+  new->from_stage3 = CPP_OPTION (pfile, preprocessed);
   /* No read ahead or extra char initially.  */
   new->read_ahead = EOF;
   new->extra_char = EOF;
Index: cpplib.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cpplib.h,v
retrieving revision 1.128
diff -u -p -r1.128 cpplib.h
--- cpplib.h	2000/10/29 07:07:20	1.128
+++ cpplib.h	2000/10/30 20:26:34
@@ -287,7 +287,12 @@ struct cpp_buffer
      The warning happens only for C89 extended mode with -pedantic on,
      or for -Wtraditional, and only once per file (otherwise it would
      be far too noisy).  */
-  char warned_cplusplus_comments;
+  unsigned char warned_cplusplus_comments;
+
+  /* True if we don't process trigraphs and escaped newlines.  True
+     for preprocessed input, command line directives, and _Pragma
+     buffers.  */
+  unsigned char from_stage3;
 };
 
 /* Maximum nesting of cpp_buffers.  We use a static limit, partly for
@@ -509,6 +514,7 @@ struct spec_nodes
 {
   cpp_hashnode *n_L;			/* L"str" */
   cpp_hashnode *n_defined;		/* defined operator */
+  cpp_hashnode *n__Pragma;		/* _Pragma operator */
   cpp_hashnode *n__STRICT_ANSI__;	/* STDC_0_IN_SYSTEM_HEADERS */
   cpp_hashnode *n__CHAR_UNSIGNED__;	/* plain char is unsigned */
   cpp_hashnode *n__VA_ARGS__;		/* C99 vararg macros */
Index: cppmacro.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cppmacro.c,v
retrieving revision 1.15
diff -u -p -r1.15 cppmacro.c
--- cppmacro.c	2000/10/30 08:41:23	1.15
+++ cppmacro.c	2000/10/30 20:26:41
@@ -689,7 +689,8 @@ funlike_invocation_p (pfile, node, list)
 
 /* Push the context of a macro onto the context stack.  TOKEN is the
    macro name.  If we can successfully start expanding the macro,
-   TOKEN is replaced with the first token of the expansion.  */
+   TOKEN is replaced with the first token of the expansion, and we
+   return non-zero.  */
 static int
 enter_macro_context (pfile, token)
      cpp_reader *pfile;
@@ -704,7 +705,7 @@ enter_macro_context (pfile, token)
   if (macro->disabled)
     {
       token->flags |= NO_EXPAND;
-      return 1;
+      return 0;
     }
 
   /* Save the position of the outermost macro invocation.  */
@@ -718,7 +719,7 @@ enter_macro_context (pfile, token)
     {
       if (!pfile->context->prev)
 	unlock_pools (pfile);
-      return 1;
+      return 0;
     }
 
   /* Now push its context.  */
@@ -740,7 +741,7 @@ enter_macro_context (pfile, token)
   /* Disable the macro within its expansion.  */
   macro->disabled = 1;
 
-  return 0;
+  return 1;
 }
 
 /* Move to the next context.  Create one if there is none.  */
@@ -922,6 +923,7 @@ _cpp_get_token (pfile, token)
      cpp_reader *pfile;
      cpp_token *token;
 {
+ next_token:
   for (;;)
     {
       cpp_context *context = pfile->context;
@@ -959,22 +961,33 @@ _cpp_get_token (pfile, token)
       if (token->flags & PASTE_LEFT)
 	paste_all_tokens (pfile, token);
 
-      if (token->type != CPP_NAME
-	  || token->val.node->type != NT_MACRO
-	  || pfile->state.prevent_expansion
-	  || token->flags & NO_EXPAND)
+      if (token->type != CPP_NAME)
 	break;
 
-      /* Macros, built-in or not, invalidate controlling macros.  */
+      /* Identifiers in general invalidate controlling macros.
+	 (Directive names are lexed from _cpp_handle_directive).  */
       pfile->mi_state = MI_FAILED;
 
-      if (token->val.node->flags & NODE_BUILTIN)
+      /* Handle macros and the _Pragma operator.  */
+      if (token->val.node->type == NT_MACRO
+	  && !pfile->state.prevent_expansion
+	  && !(token->flags & NO_EXPAND))
 	{
-	  builtin_macro (pfile, token);
-	  break;
+	  if (token->val.node->flags & NODE_BUILTIN)
+	    {
+	      builtin_macro (pfile, token);
+	      break;
+	    }
+
+	  if (enter_macro_context (pfile, token))
+	    continue;
 	}
-      else if (enter_macro_context (pfile, token))
+
+      if (token->val.node != pfile->spec_nodes.n__Pragma)
 	break;
+
+      _cpp_do__Pragma (pfile);
+      goto next_token;
     }
 }
 


More information about the Gcc-patches mailing list