cpphash.h (_cpp_push_text_context): Update.
authorNeil Booth <neil@daikokuya.demon.co.uk>
Sun, 9 Jun 2002 20:04:17 +0000 (20:04 +0000)
committerNeil Booth <neil@gcc.gnu.org>
Sun, 9 Jun 2002 20:04:17 +0000 (20:04 +0000)
* cpphash.h (_cpp_push_text_context): Update.
(_cpp_arguments_ok): New.
* cppmacro.c (_cpp_arguments_ok): New, split out from...
(collect_args): ...here.
(_cpp_push_text_context): Change inputs.
* cpptrad.c (struct fun_macro, maybe_start_funlike, save_argument,
replace_args_and_push): New.
(lex_identifier, _cpp_lex_identifier_trad, scan_parameters):
Don't use IS macros directly.
(scan_out_logical_line): Handle function-like macro argument
collection.
(push_replacement_text): Update.
(replacement_length): Remove.
(_cpp_create_trad_definition): Don't skip whitespace before
checking for '('.

From-SVN: r54412

gcc/ChangeLog
gcc/cpphash.h
gcc/cppmacro.c
gcc/cpptrad.c

index 79b875c127d3604a30b8874d1d5179bfb8609960..4888b7988e08c33f0125b910e2d664a00e7d61cd 100644 (file)
@@ -1,3 +1,21 @@
+2002-06-09  Neil Booth  <neil@daikokuya.demon.co.uk>
+
+       * cpphash.h (_cpp_push_text_context): Update.
+       (_cpp_arguments_ok): New.
+       * cppmacro.c (_cpp_arguments_ok): New, split out from...
+       (collect_args): ...here.
+       (_cpp_push_text_context): Change inputs.
+       * cpptrad.c (struct fun_macro, maybe_start_funlike, save_argument,
+       replace_args_and_push): New.
+       (lex_identifier, _cpp_lex_identifier_trad, scan_parameters):
+       Don't use IS macros directly.
+       (scan_out_logical_line): Handle function-like macro argument
+       collection.
+       (push_replacement_text): Update.
+       (replacement_length): Remove.
+       (_cpp_create_trad_definition): Don't skip whitespace before
+       checking for '('.
+
 2002-06-09  Marek Michalkiewicz  <marekm@amelek.gda.pl>
 
        * config/avr/avr.c (avr_mcu_types): Update for new devices.
index 9a4915daf7235c61889c9c88e788700b30982157..6baf211958d6a3c16107d0ee7d961676aed54963 100644 (file)
@@ -438,9 +438,12 @@ extern void _cpp_free_definition   PARAMS ((cpp_hashnode *));
 extern bool _cpp_create_definition     PARAMS ((cpp_reader *, cpp_hashnode *));
 extern void _cpp_pop_context           PARAMS ((cpp_reader *));
 extern void _cpp_push_text_context     PARAMS ((cpp_reader *, cpp_hashnode *,
-                                                const uchar *, const uchar*));
+                                                const uchar *, size_t));
 extern bool _cpp_save_parameter                PARAMS ((cpp_reader *, cpp_macro *,
                                                 cpp_hashnode *));
+extern bool _cpp_arguments_ok          PARAMS ((cpp_reader *, cpp_macro *,
+                                                const cpp_hashnode *,
+                                                unsigned int));
 
 /* In cpphash.c */
 extern void _cpp_init_hashtable                PARAMS ((cpp_reader *, hash_table *));
index 5e954d457e0a8bc6a3325f546904891eda919d90..d5179487637b1bab9b715bd52617966e5e843b16 100644 (file)
@@ -451,6 +451,52 @@ paste_all_tokens (pfile, lhs)
   push_token_context (pfile, NULL, lhs, 1);
 }
 
+/* Returns TRUE if the number of arguments ARGC supplied in an
+   invocation of the MACRO referenced by NODE is valid.  An empty
+   invocation to a macro with no parameters should pass ARGC as zero.
+
+   Note that MACRO cannot necessarily be deduced from NODE, in case
+   NODE was redefined whilst collecting arguments.  */
+bool
+_cpp_arguments_ok (pfile, macro, node, argc)
+     cpp_reader *pfile;
+     cpp_macro *macro;
+     const cpp_hashnode *node;
+     unsigned int argc;
+{
+  if (argc == macro->paramc)
+    return true;
+
+  if (argc < macro->paramc)
+    {
+      /* As an extension, a rest argument is allowed to not appear in
+        the invocation at all.
+        e.g. #define debug(format, args...) something
+        debug("string");
+
+        This is exactly the same as if there had been an empty rest
+        argument - debug("string", ).  */
+
+      if (argc + 1 == macro->paramc && macro->variadic)
+       {
+         if (CPP_PEDANTIC (pfile) && ! macro->syshdr)
+           cpp_error (pfile, DL_PEDWARN,
+                      "ISO C99 requires rest arguments to be used");
+         return true;
+       }
+
+      cpp_error (pfile, DL_ERROR,
+                "macro \"%s\" requires %u arguments, but only %u given",
+                NODE_NAME (node), macro->paramc, argc);
+    }
+  else
+    cpp_error (pfile, DL_ERROR,
+              "macro \"%s\" passed %u arguments, but takes just %u",
+              NODE_NAME (node), argc, macro->paramc);
+
+  return false;
+}
+
 /* Reads and returns the arguments to a function-like macro
    invocation.  Assumes the opening parenthesis has been processed.
    If there is an error, emits an appropriate diagnostic and returns
@@ -466,7 +512,6 @@ collect_args (pfile, node)
   macro_arg *args, *arg;
   const cpp_token *token;
   unsigned int argc;
-  bool error = false;
 
   macro = node->value.macro;
   if (macro->paramc)
@@ -561,47 +606,17 @@ collect_args (pfile, node)
       cpp_error (pfile, DL_ERROR,
                 "unterminated argument list invoking macro \"%s\"",
                 NODE_NAME (node));
-      error = true;
-    }
-  else if (argc < macro->paramc)
-    {
-      /* As an extension, a rest argument is allowed to not appear in
-        the invocation at all.
-        e.g. #define debug(format, args...) something
-        debug("string");
-
-        This is exactly the same as if there had been an empty rest
-        argument - debug("string", ).  */
-
-      if (argc + 1 == macro->paramc && macro->variadic)
-       {
-         if (CPP_PEDANTIC (pfile) && ! macro->syshdr)
-           cpp_error (pfile, DL_PEDWARN,
-                      "ISO C99 requires rest arguments to be used");
-       }
-      else
-       {
-         cpp_error (pfile, DL_ERROR,
-                    "macro \"%s\" requires %u arguments, but only %u given",
-                    NODE_NAME (node), macro->paramc, argc);
-         error = true;
-       }
     }
-  else if (argc > macro->paramc)
+  else
     {
-      /* Empty argument to a macro taking no arguments is OK.  */
-      if (argc != 1 || arg->count)
-       {
-         cpp_error (pfile, DL_ERROR,
-                    "macro \"%s\" passed %u arguments, but takes just %u",
-                    NODE_NAME (node), argc, macro->paramc);
-         error = true;
-       }
+      /* A single empty argument is counted as no argument.  */
+      if (argc == 1 && macro->paramc == 0 && args[0].count == 0)
+       argc = 0;
+      if (_cpp_arguments_ok (pfile, macro, node, argc))
+       return base_buff;
     }
 
-  if (!error)
-    return base_buff;
-
+  /* An error occurred.  */
   _cpp_release_buff (pfile, base_buff);
   return NULL;
 }
@@ -919,10 +934,11 @@ push_token_context (pfile, macro, first, count)
 
 /* Push a traditional macro's replacement text.  */
 void
-_cpp_push_text_context (pfile, macro, start, end)
+_cpp_push_text_context (pfile, macro, start, len)
      cpp_reader *pfile;
      cpp_hashnode *macro;
-     const uchar *start, *end;
+     const uchar *start;
+     size_t len;
 {
   cpp_context *context = next_context (pfile);
 
@@ -930,7 +946,7 @@ _cpp_push_text_context (pfile, macro, start, end)
   context->macro = macro;
   context->buff = NULL;
   CUR (context) = start;
-  RLIMIT (context) = end;
+  RLIMIT (context) = start + len;
 }
 
 /* Expand an argument ARG before replacing parameters in a
index 3886b4522c7e90266446f7f18d0ee775b8b4cc5e..7ba9d924ef03dbfcb10cff4a717eebda96e16315 100644 (file)
@@ -44,6 +44,29 @@ struct block
 #define BLOCK_HEADER_LEN offsetof (struct block, text)
 #define BLOCK_LEN(TEXT_LEN) CPP_ALIGN (BLOCK_HEADER_LEN + TEXT_LEN)
 
+/* Structure holding information about a function-like macro
+   invocation.  */
+struct fun_macro
+{
+  /* Memory buffer holding the trad_arg array.  */
+  _cpp_buff *buff;
+
+  /* An array of size the number of macro parameters + 1, containing
+     the offsets of the start of each macro argument in the output
+     buffer.  The argument continues until the character before the
+     start of the next one.  */
+  size_t *args;
+
+  /* The hashnode of the macro.  */
+  cpp_hashnode *node;
+
+  /* The offset of the macro name in the output buffer.  */
+  size_t offset;
+
+  /* Zero-based index of argument being currently lexed.  */
+  unsigned int argc;
+};
+
 /* Lexing TODO: Handle -C, maybe -CC, and space in escaped newlines.
    Stop cpplex.c from recognizing comments and directives during its
    lexing pass.  Get rid of line_base usage - seems pointless?  Do we
@@ -62,7 +85,10 @@ static void push_replacement_text PARAMS ((cpp_reader *, cpp_hashnode *));
 static bool scan_parameters PARAMS ((cpp_reader *, cpp_macro *));
 static void save_replacement_text PARAMS ((cpp_reader *, cpp_macro *,
                                           unsigned int));
-static unsigned int replacement_length PARAMS ((cpp_macro *));
+static void maybe_start_funlike PARAMS ((cpp_reader *, cpp_hashnode *,
+                                        const uchar *, struct fun_macro *));
+static void save_argument PARAMS ((struct fun_macro *, size_t));
+static void replace_args_and_push PARAMS ((cpp_reader *, struct fun_macro *));
 
 /* Ensures we have N bytes' space in the output buffer, and
    reallocates it if not.  */
@@ -205,10 +231,10 @@ lex_identifier (pfile, cur)
     {
       do
        *out++ = *cur++;
-      while (ISIDNUM (*cur));
+      while (is_numchar (*cur));
       cur = skip_escaped_newlines (pfile, cur);
     }
-  while (ISIDNUM (*cur));
+  while (is_numchar (*cur));
 
   CUR (pfile->context) = cur;
   len = out - pfile->trad_out_cur;
@@ -226,7 +252,7 @@ _cpp_lex_identifier_trad (pfile)
 {
   const uchar *cur = skip_whitespace (pfile, CUR (pfile->context));
 
-  if (!ISIDST (*cur))
+  if (!is_idstart (*cur))
     {
       CUR (pfile->context) = cur;
       return NULL;
@@ -309,12 +335,45 @@ _cpp_read_logical_line_trad (pfile)
   return true;
 }
 
+/* Set up state for finding the opening '(' of a function-like
+   macro.  */
+static void
+maybe_start_funlike (pfile, node, start, macro)
+     cpp_reader *pfile;
+     cpp_hashnode *node;
+     const uchar *start;
+     struct fun_macro *macro;
+{
+  unsigned int n = node->value.macro->paramc + 1;
+
+  if (macro->buff)
+    _cpp_release_buff (pfile, macro->buff);
+  macro->buff = _cpp_get_buff (pfile, n * sizeof (size_t));
+  macro->args = (size_t *) BUFF_FRONT (macro->buff);
+  macro->node = node;
+  macro->offset = start - pfile->trad_out_base;
+  macro->argc = 0;
+
+  pfile->state.parsing_args = 1;
+}
+
+/* Save the OFFSET of the start of the next argument to MACRO.  */
+static void
+save_argument (macro, offset)
+     struct fun_macro *macro;
+     size_t offset;
+{
+  macro->argc++;
+  if (macro->argc <= macro->node->value.macro->paramc)
+    macro->args[macro->argc] = offset;
+}
+
 /* Copies the next logical line in the current buffer to the output
    buffer.  The output is guaranteed to terminate with a NUL
    character.
 
    If MACRO is non-NULL, then we are scanning the replacement list of
-   MACRO, and we call save_replacement_text every time we meet an
+   MACRO, and we call save_replacement_text() every time we meet an
    argument.  */
 static void
 scan_out_logical_line (pfile, macro)
@@ -323,9 +382,11 @@ scan_out_logical_line (pfile, macro)
 {
   cpp_context *context;
   const uchar *cur;
-  unsigned int c, quote = 0;
+  unsigned int c, paren_depth, quote = 0;
   uchar *out;
+  struct fun_macro fmacro;
 
+  fmacro.buff = NULL;
  new_context:
   context = pfile->context;
   cur = CUR (context);
@@ -357,16 +418,22 @@ scan_out_logical_line (pfile, macro)
          cur--;
          if (!pfile->buffer->from_stage3)
            cpp_error (pfile, DL_PEDWARN, "no newline at end of file");
+         if (pfile->state.parsing_args == 2)
+           cpp_error (pfile, DL_ERROR,
+                      "unterminated argument list invoking macro \"%s\"",
+                      NODE_NAME (fmacro.node));
          pfile->line++;
-         goto finish_output;
+         goto done;
 
        case '\r': case '\n':
          cur = handle_newline (pfile, cur - 1);
-         out[-1] = '\0';
-       finish_output:
-         CUR (context) = cur;
-         pfile->trad_out_cur = out - 1;
-         return;
+         if (pfile->state.parsing_args == 2)
+           {
+             /* Newlines in arguments become a space.  */
+             out[-1] = ' ';
+             continue;
+           }
+         goto done;
 
        case '"':
        case '\'':
@@ -418,16 +485,25 @@ scan_out_logical_line (pfile, macro)
              pfile->trad_out_cur = --out;
              node = lex_identifier (pfile, cur - 1);
 
-             if (node->type == NT_MACRO && !pfile->state.prevent_expansion)
+             if (node->type == NT_MACRO
+                 && pfile->state.parsing_args != 2
+                 && !pfile->state.prevent_expansion)
                {
-                 /* Remove the macro name from the output.  */
-                 pfile->trad_out_cur = out;
-                 push_replacement_text (pfile, node);
-                 goto new_context;
+                 if (node->value.macro->fun_like)
+                   maybe_start_funlike (pfile, node, out, &fmacro);
+                 else
+                   {
+                     /* Remove the object-like macro's name from the
+                        output, and push its replacement text.  */
+                     pfile->trad_out_cur = out;
+                     push_replacement_text (pfile, node);
+                     goto new_context;
+                   }
                }
              else if (macro && node->arg_index)
                {
-                 /* Remove the macro name from the output.  */
+                 /* Found a parameter in the replacement text of a
+                    #define.  Remove its name from the output.  */
                  pfile->trad_out_cur = out;
                  save_replacement_text (pfile, macro, node->arg_index);
                }
@@ -437,15 +513,91 @@ scan_out_logical_line (pfile, macro)
            }
          break;
 
+       case '(':
+         if (quote == 0)
+           {
+             paren_depth++;
+             if (pfile->state.parsing_args == 1)
+               {
+                 const uchar *p = pfile->trad_out_base + fmacro.offset;
+
+                 /* Invoke a prior function-like macro if there is only
+                    white space in-between.  */
+                 while (is_numchar (*p))
+                   p++;
+                 while (is_space (*p))
+                   p++;
+
+                 if (p == out - 1)
+                   {
+                     pfile->state.parsing_args = 2;
+                     paren_depth = 1;
+                     out = pfile->trad_out_base + fmacro.offset;
+                     fmacro.args[0] = fmacro.offset;
+                   }
+                 else
+                   pfile->state.parsing_args = 0;
+               }
+           }
+         break;
+
+       case ',':
+         if (quote == 0 && pfile->state.parsing_args == 2 && paren_depth == 1)
+           save_argument (&fmacro, out - pfile->trad_out_base);
+         break;
+
+       case ')':
+         if (quote == 0)
+           {
+             paren_depth--;
+             if (pfile->state.parsing_args == 2 && paren_depth == 0)
+               {
+                 cpp_macro *m = fmacro.node->value.macro;
+
+                 pfile->state.parsing_args = 0;
+                 save_argument (&fmacro, out - pfile->trad_out_base);
+
+                 /* A single whitespace argument is no argument.  */
+                 if (fmacro.argc == 1 && m->paramc == 0)
+                   {
+                     const uchar *p = pfile->trad_out_base;
+                     p += fmacro.args[0];
+                     while (is_space (*p))
+                       p++;
+                     if (p == pfile->trad_out_base + fmacro.args[1])
+                       fmacro.argc = 0;
+                   }
+
+                 if (_cpp_arguments_ok (pfile, m, fmacro.node, fmacro.argc))
+                   {
+                     /* Remove the macro's invocation from the
+                        output, and push its replacement text.  */
+                     pfile->trad_out_cur = (pfile->trad_out_base
+                                            + fmacro.offset);
+                     CUR (context) = cur;
+                     replace_args_and_push (pfile, &fmacro);
+                     goto new_context;
+                   }
+               }
+           }
+         break;
+
        default:
          break;
        }
     }
+
+ done:
+  out[-1] = '\0';
+  CUR (context) = cur;
+  pfile->trad_out_cur = out - 1;
+  if (fmacro.buff)
+    _cpp_release_buff (pfile, fmacro.buff);
 }
 
 /* Push a context holding the replacement text of the macro NODE on
-   the context stack.  Doesn't yet handle special built-ins or
-   function-like macros.  */
+   the context stack.  NODE is either object-like, or a function-like
+   macro with no arguments.  */
 static void
 push_replacement_text (pfile, node)
      cpp_reader *pfile;
@@ -453,9 +605,70 @@ push_replacement_text (pfile, node)
 {
   cpp_macro *macro = node->value.macro;
 
-  _cpp_push_text_context (pfile, node,
-                         macro->exp.text,
-                         macro->exp.text + macro->count);
+  _cpp_push_text_context (pfile, node, macro->exp.text, macro->count);
+}
+
+/* Push a context holding the replacement text of the macro NODE on
+   the context stack.  NODE is either object-like, or a function-like
+   macro with no arguments.  */
+static void
+replace_args_and_push (pfile, fmacro)
+     cpp_reader *pfile;
+     struct fun_macro *fmacro;
+{
+  cpp_macro *macro = fmacro->node->value.macro;
+
+  if (macro->paramc == 0)
+    push_replacement_text (pfile, fmacro->node);
+  else
+    {
+      const uchar *exp;
+      uchar *p;
+      _cpp_buff *buff;
+      size_t len = 0;
+
+      /* Calculate the length of the argument-replaced text.  */
+      for (exp = macro->exp.text;;)
+       {
+         struct block *b = (struct block *) exp;
+
+         len += b->text_len;
+         if (b->arg_index == 0)
+           break;
+         len += (fmacro->args[b->arg_index]
+                 - fmacro->args[b->arg_index - 1] - 1);
+         exp += BLOCK_LEN (b->text_len);
+       }
+
+      /* Allocate room for the expansion plus NUL.  */
+      buff = _cpp_get_buff (pfile, len + 1);
+
+      /* Copy the expansion and replace arguments.  */
+      p = BUFF_FRONT (buff);
+      for (exp = macro->exp.text;;)
+       {
+         struct block *b = (struct block *) exp;
+         size_t arglen;
+
+         memcpy (p, b->text, b->text_len);
+         p += b->text_len;
+         if (b->arg_index == 0)
+           break;
+         arglen = (fmacro->args[b->arg_index]
+                   - fmacro->args[b->arg_index - 1] - 1);
+         memcpy (p, pfile->trad_out_base + fmacro->args[b->arg_index - 1],
+                 arglen);
+         p += arglen;
+         exp += BLOCK_LEN (b->text_len);
+       }
+
+      /* NUL-terminate.  */
+      *p = '\0';
+      _cpp_push_text_context (pfile, fmacro->node, BUFF_FRONT (buff), len);
+
+      /* So we free buffer allocation when macro is left.  */
+      pfile->context->buff = buff;
+    }
 }
 
 /* Read and record the parameters, if any, of a function-like macro
@@ -476,7 +689,7 @@ scan_parameters (pfile, macro)
     {
       cur = skip_whitespace (pfile, cur);
 
-      if (ISIDST (*cur))
+      if (is_idstart (*cur))
        {
          ok = false;
          if (_cpp_save_parameter (pfile, macro, lex_identifier (pfile, cur)))
@@ -500,25 +713,6 @@ scan_parameters (pfile, macro)
   return ok;
 }
 
-/* Calculate the length of the replacement text of MACRO.  */
-static unsigned int
-replacement_length (macro)
-     cpp_macro *macro;
-{
-  unsigned int result = 0;
-  const uchar *exp = macro->exp.text;
-
-  for (;;)
-    {
-      struct block *block = (struct block *) exp;
-
-      result += block->text_len;
-      if (block->arg_index == 0)
-       return result;
-      exp += BLOCK_LEN (block->text_len);
-    }
-}
-
 /* Save the text from pfile->trad_out_base to pfile->trad_out_cur as
    the replacement text for the current macro, followed by argument
    ARG_INDEX, with zero indicating the end of the replacement
@@ -568,10 +762,7 @@ save_replacement_text (pfile, macro, arg_index)
         in the replacement list, excluding the parameter names, and
         save this in macro->count, else store the total bytes in the
         replacement text so far (including block headers).  */
-      if (arg_index == 0)
-       macro->count = replacement_length (macro);
-      else
-       macro->count += blen;
+      macro->count += blen;
     }
 }
 
@@ -585,13 +776,11 @@ _cpp_create_trad_definition (pfile, macro)
   const uchar *cur;
   uchar *limit;
 
-  /* Skip leading whitespace now.  */
-  CUR (pfile->context) = skip_whitespace (pfile, CUR (pfile->context));
-
   /* Is this a function-like macro?  */
   if (* CUR (pfile->context) == '(')
     {
-      /* Setting macro to NULL indicates an error occurred.  */
+      /* Setting macro to NULL indicates an error occurred, and
+        prevents unnecessary work in scan_out_logical_line.  */
       if (!scan_parameters (pfile, macro))
        macro = NULL;
       else
@@ -601,10 +790,11 @@ _cpp_create_trad_definition (pfile, macro)
          BUFF_FRONT (pfile->a_buff) = (uchar *) &macro->params[macro->paramc];
          macro->fun_like = 1;
        }
-
-      CUR (pfile->context) = skip_whitespace (pfile, CUR (pfile->context));
     }
 
+  /* Skip leading whitespace in the replacement text.  */
+  CUR (pfile->context) = skip_whitespace (pfile, CUR (pfile->context));
+
   pfile->trad_out_cur = pfile->trad_out_base;
   pfile->state.prevent_expansion++;
   scan_out_logical_line (pfile, macro);