]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb
authorTom Tromey <tromey@redhat.com>
Sat, 27 Sep 2008 21:40:49 +0000 (21:40 +0000)
committerTom Tromey <tromey@redhat.com>
Sat, 27 Sep 2008 21:40:49 +0000 (21:40 +0000)
* NEWS: Update.
* macrocmd.c (extract_identifier): Add is_parameter argument.
(macro_define_command): Update.
(macro_undef_command): Likewise.
* macroexp.c (stringify): New function.
(find_parameter): Likewise.
(gather_arguments): Add nargs argument.  Handle varargs.
(substitute_args): Add is_varargs and va_arg_name arguments.
Handle varargs, splicing, stringification.  Use find_parameter.
(expand): Handle varargs.
gdb/doc
* gdb.texinfo (Macros): Remove text about stringification,
varargs, and splicing.
gdb/testsuite
* gdb.base/macscp.exp: Add tests for stringification, splicing,
and varargs.

gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/macrocmd.c
gdb/macroexp.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/macscp.exp

index 667d6be27ae013df8170bda51a3d9987a33b4500..b8e1027ca1cdfc71213b2fd50cf9e08cd7068ea2 100644 (file)
@@ -1,3 +1,16 @@
+2008-09-27  Tom Tromey  <tromey@redhat.com>
+
+       * NEWS: Update.
+       * macrocmd.c (extract_identifier): Add is_parameter argument.
+       (macro_define_command): Update.
+       (macro_undef_command): Likewise.
+       * macroexp.c (stringify): New function.
+       (find_parameter): Likewise.
+       (gather_arguments): Add nargs argument.  Handle varargs.
+       (substitute_args): Add is_varargs and va_arg_name arguments.
+       Handle varargs, splicing, stringification.  Use find_parameter.
+       (expand): Handle varargs.
+
 2008-09-27  Tom Tromey  <tromey@redhat.com>
 
        * scm-lang.c (scm_language_defn): Update.
index 4c9af8295a13bd1a370989af01e76d5aa50529aa..46aa14481d25424225e0175e454d6b6c844eb847 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -22,6 +22,10 @@ For instance, consider:
 If the user types TAB at the end of this command line, the available
 completions will be "f1" and "f2".
 
+* GDB now supports the token-splicing (##) and stringification (#)
+operators when expanding macros.  It also supports variable-arity
+macros.
+
 * New remote packets
 
 qSearch:memory:
index b75da649b5a5dd2773e100507b3d45fec7d9840b..6d1c713887e42419251397ba49c8f28080f8fe71 100644 (file)
@@ -1,3 +1,8 @@
+2008-09-27  Tom Tromey  <tromey@redhat.com>
+
+       * gdb.texinfo (Macros): Remove text about stringification,
+       varargs, and splicing.
+
 2008-09-27  Tom Tromey  <tromey@redhat.com>
 
        * gdbint.texinfo (Language Support): Remove text about omitting
index d46f8489656edc1bde781a0d5b32eb0d07f01b05..9db0ff8a764f5c84ae302bcfda1ede34bbd2c7e9 100644 (file)
@@ -8133,10 +8133,6 @@ uses the macros in scope at that frame's source code line.  Otherwise,
 @value{GDBN} uses the macros in scope at the current listing location;
 see @ref{List}.
 
-At the moment, @value{GDBN} does not support the @code{##}
-token-splicing operator, the @code{#} stringification operator, or
-variable-arity macros.
-
 Whenever @value{GDBN} evaluates an expression, it always expands any
 macro invocations present in the expression.  @value{GDBN} also provides
 the following commands for working with macros explicitly.
index 8213c0d703b8e536a0e9a53bc30debb92c415bba..c9ab440f794b4514abfd222cc6049c9101ae0455 100644 (file)
@@ -197,18 +197,37 @@ skip_ws (char **expp)
     ++*expp;
 }
 
+/* Try to find the bounds of an identifier.  If an identifier is
+   found, returns a newly allocated string; otherwise returns NULL.
+   EXPP is a pointer to an input string; it is updated to point to the
+   text following the identifier.  If IS_PARAMETER is true, this
+   function will also allow "..." forms as used in varargs macro
+   parameters.  */
+
 static char *
-extract_identifier (char **expp)
+extract_identifier (char **expp, int is_parameter)
 {
   char *result;
   char *p = *expp;
   unsigned int len;
-  if (! *p || ! macro_is_identifier_nondigit (*p))
-    return NULL;
-  for (++p;
-       *p && (macro_is_identifier_nondigit (*p) || macro_is_digit (*p));
-       ++p)
-    ;
+
+  if (is_parameter && !strncmp (p, "...", 3))
+    {
+      /* Ok.  */
+    }
+  else
+    {
+      if (! *p || ! macro_is_identifier_nondigit (*p))
+       return NULL;
+      for (++p;
+          *p && (macro_is_identifier_nondigit (*p) || macro_is_digit (*p));
+          ++p)
+       ;
+    }
+
+  if (is_parameter && !strncmp (p, "...", 3))      
+    p += 3;
+
   len = p - *expp;
   result = (char *) xmalloc (len + 1);
   memcpy (result, *expp, len);
@@ -246,7 +265,7 @@ macro_define_command (char *exp, int from_tty)
   memset (&new_macro, 0, sizeof (struct macro_definition));
 
   skip_ws (&exp);
-  name = extract_identifier (&exp);
+  name = extract_identifier (&exp, 0);
   if (! name)
     error (_("Invalid macro name."));
   if (*exp == '(')
@@ -274,7 +293,7 @@ macro_define_command (char *exp, int from_tty)
              /* Must update new_macro as well... */
              new_macro.argv = (const char * const *) argv;
            }
-         argv[new_macro.argc] = extract_identifier (&exp);
+         argv[new_macro.argc] = extract_identifier (&exp, 1);
          if (! argv[new_macro.argc])
            error (_("Macro is missing an argument."));
          ++new_macro.argc;
@@ -317,7 +336,7 @@ macro_undef_command (char *exp, int from_tty)
     error (_("usage: macro undef NAME"));
 
   skip_ws (&exp);
-  name = extract_identifier (&exp);
+  name = extract_identifier (&exp, 0);
   if (! name)
     error (_("Invalid macro name."));
   macro_undef (macro_main (macro_user_macros), -1, name);
index 8102bc081f00b366a9e12e4a1487a6f73a755b71..7fb23ce65b3e90fd7bbfbf5a10f80cba59b64302 100644 (file)
@@ -625,6 +625,52 @@ append_tokens_without_splicing (struct macro_buffer *dest,
                   _("unable to avoid splicing tokens during macro expansion"));
 }
 
+/* Stringify an argument, and insert it into DEST.  ARG is the text to
+   stringify; it is LEN bytes long.  */
+
+static void
+stringify (struct macro_buffer *dest, char *arg, int len)
+{
+  /* Trim initial whitespace from ARG.  */
+  while (len > 0 && macro_is_whitespace (*arg))
+    {
+      ++arg;
+      --len;
+    }
+
+  /* Trim trailing whitespace from ARG.  */
+  while (len > 0 && macro_is_whitespace (arg[len - 1]))
+    --len;
+
+  /* Insert the string.  */
+  appendc (dest, '"');
+  while (len > 0)
+    {
+      /* We could try to handle strange cases here, like control
+        characters, but there doesn't seem to be much point.  */
+      if (macro_is_whitespace (*arg))
+       {
+         /* Replace a sequence of whitespace with a single space.  */
+         appendc (dest, ' ');
+         while (len > 1 && macro_is_whitespace (arg[1]))
+           {
+             ++arg;
+             --len;
+           }
+       }
+      else if (*arg == '\\' || *arg == '"')
+       {
+         appendc (dest, '\\');
+         appendc (dest, *arg);
+       }
+      else
+       appendc (dest, *arg);
+      ++arg;
+      --len;
+    }
+  appendc (dest, '"');
+  dest->last_token = dest->len;
+}
 
 \f
 /* Expanding macros!  */
@@ -674,6 +720,11 @@ currently_rescanning (struct macro_name_list *list, const char *name)
 
    If SRC doesn't contain a properly terminated argument list, then
    raise an error.
+   
+   For a variadic macro, NARGS holds the number of formal arguments to
+   the macro.  For a GNU-style variadic macro, this should be the
+   number of named arguments.  For a non-variadic macro, NARGS should
+   be -1.
 
    Otherwise, return a pointer to the first element of an array of
    macro buffers referring to the argument texts, and set *ARGC_P to
@@ -694,7 +745,8 @@ currently_rescanning (struct macro_name_list *list, const char *name)
    following the invocation.  */
 
 static struct macro_buffer *
-gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
+gather_arguments (const char *name, struct macro_buffer *src,
+                 int nargs, int *argc_p)
 {
   struct macro_buffer tok;
   int args_len, args_size;
@@ -760,6 +812,20 @@ gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
                  the end of the argument list.  */
               if (depth == 0)
                 {
+                 /* In the varargs case, the last argument may be
+                    missing.  Add an empty argument in this case.  */
+                 if (nargs != -1 && args_len == nargs - 1)
+                   {
+                     /* Make sure we have room for the argument.  */
+                     if (args_len >= args_size)
+                       {
+                         args_size++;
+                         args = xrealloc (args, sizeof (*args) * args_size);
+                       }
+                     arg = &args[args_len++];
+                     set_token (arg, src->text, src->text);
+                   }
+
                   discard_cleanups (back_to);
                   *argc_p = args_len;
                   return args;
@@ -769,8 +835,11 @@ gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
             }
 
           /* If tok is a comma at top level, then that's the end of
-             the current argument.  */
-          else if (tok.len == 1 && tok.text[0] == ',' && depth == 0)
+             the current argument.  However, if we are handling a
+             variadic macro and we are computing the last argument, we
+             want to include the comma and remaining tokens.  */
+          else if (tok.len == 1 && tok.text[0] == ',' && depth == 0
+                  && (nargs == -1 || args_len < nargs))
             break;
 
           /* Extend the current argument to enclose this token.  If
@@ -801,17 +870,57 @@ static void scan (struct macro_buffer *dest,
                   void *lookup_baton);
 
 
+/* A helper function for substitute_args.
+   
+   ARGV is a vector of all the arguments; ARGC is the number of
+   arguments.  IS_VARARGS is true if the macro being substituted is a
+   varargs macro; in this case VA_ARG_NAME is the name of the
+   "variable" argument.  VA_ARG_NAME is ignored if IS_VARARGS is
+   false.
+
+   If the token TOK is the name of a parameter, return the parameter's
+   index.  If TOK is not an argument, return -1.  */
+
+static int
+find_parameter (const struct macro_buffer *tok,
+               int is_varargs, const struct macro_buffer *va_arg_name,
+               int argc, const char * const *argv)
+{
+  int i;
+
+  if (! tok->is_identifier)
+    return -1;
+
+  for (i = 0; i < argc; ++i)
+    if (tok->len == strlen (argv[i]) && ! memcmp (tok->text, argv[i], tok->len))
+      return i;
+
+  if (is_varargs && tok->len == va_arg_name->len
+      && ! memcmp (tok->text, va_arg_name->text, tok->len))
+    return argc - 1;
+
+  return -1;
+}
 /* Given the macro definition DEF, being invoked with the actual
    arguments given by ARGC and ARGV, substitute the arguments into the
    replacement list, and store the result in DEST.
 
+   IS_VARARGS should be true if DEF is a varargs macro.  In this case,
+   VA_ARG_NAME should be the name of the "variable" argument -- either
+   __VA_ARGS__ for c99-style varargs, or the final argument name, for
+   GNU-style varargs.  If IS_VARARGS is false, this parameter is
+   ignored.
+
    If it is necessary to expand macro invocations in one of the
    arguments, use LOOKUP_FUNC and LOOKUP_BATON to find the macro
    definitions, and don't expand invocations of the macros listed in
    NO_LOOP.  */
+
 static void
 substitute_args (struct macro_buffer *dest, 
                  struct macro_definition *def,
+                int is_varargs, const struct macro_buffer *va_arg_name,
                  int argc, struct macro_buffer *argv,
                  struct macro_name_list *no_loop,
                  macro_lookup_ftype *lookup_func,
@@ -819,6 +928,17 @@ substitute_args (struct macro_buffer *dest,
 {
   /* A macro buffer for the macro's replacement list.  */
   struct macro_buffer replacement_list;
+  /* The token we are currently considering.  */
+  struct macro_buffer tok;
+  /* The replacement list's pointer from just before TOK was lexed.  */
+  char *original_rl_start;
+  /* We have a single lookahead token to handle token splicing.  */
+  struct macro_buffer lookahead;
+  /* The lookahead token might not be valid.  */
+  int lookahead_valid;
+  /* The replacement list's pointer from just before LOOKAHEAD was
+     lexed.  */
+  char *lookahead_rl_start;
 
   init_shared_buffer (&replacement_list, (char *) def->replacement,
                       strlen (def->replacement));
@@ -826,16 +946,14 @@ substitute_args (struct macro_buffer *dest,
   gdb_assert (dest->len == 0);
   dest->last_token = 0;
 
+  original_rl_start = replacement_list.text;
+  if (! get_token (&tok, &replacement_list))
+    return;
+  lookahead_rl_start = replacement_list.text;
+  lookahead_valid = get_token (&lookahead, &replacement_list);
+
   for (;;)
     {
-      struct macro_buffer tok;
-      char *original_rl_start = replacement_list.text;
-      int substituted = 0;
-      
-      /* Find the next token in the replacement list.  */
-      if (! get_token (&tok, &replacement_list))
-        break;
-
       /* Just for aesthetics.  If we skipped some whitespace, copy
          that to DEST.  */
       if (tok.text > original_rl_start)
@@ -847,46 +965,161 @@ substitute_args (struct macro_buffer *dest,
       /* Is this token the stringification operator?  */
       if (tok.len == 1
           && tok.text[0] == '#')
-        error (_("Stringification is not implemented yet."));
+       {
+         int arg;
 
-      /* Is this token the splicing operator?  */
-      if (tok.len == 2
-          && tok.text[0] == '#'
-          && tok.text[1] == '#')
-        error (_("Token splicing is not implemented yet."));
+         if (!lookahead_valid)
+           error (_("Stringification operator requires an argument."));
 
-      /* Is this token an identifier?  */
-      if (tok.is_identifier)
-        {
-          int i;
-
-          /* Is it the magic varargs parameter?  */
-          if (tok.len == 11
-              && ! memcmp (tok.text, "__VA_ARGS__", 11))
-            error (_("Variable-arity macros not implemented yet."));
-
-          /* Is it one of the parameters?  */
-          for (i = 0; i < def->argc; i++)
-            if (tok.len == strlen (def->argv[i])
-                && ! memcmp (tok.text, def->argv[i], tok.len))
-              {
-                struct macro_buffer arg_src;
-
-                /* Expand any macro invocations in the argument text,
-                   and append the result to dest.  Remember that scan
-                   mutates its source, so we need to scan a new buffer
-                   referring to the argument's text, not the argument
-                   itself.  */
-                init_shared_buffer (&arg_src, argv[i].text, argv[i].len);
-                scan (dest, &arg_src, no_loop, lookup_func, lookup_baton);
-                substituted = 1;
-                break;
-              }
-        }
+         arg = find_parameter (&lookahead, is_varargs, va_arg_name,
+                               def->argc, def->argv);
+         if (arg == -1)
+           error (_("Argument to stringification operator must name "
+                    "a macro parameter."));
 
-      /* If it wasn't a parameter, then just copy it across.  */
-      if (! substituted)
-        append_tokens_without_splicing (dest, &tok);
+         stringify (dest, argv[arg].text, argv[arg].len);
+
+         /* Read one token and let the loop iteration code handle the
+            rest.  */
+         lookahead_rl_start = replacement_list.text;
+         lookahead_valid = get_token (&lookahead, &replacement_list);
+       }
+      /* Is this token the splicing operator?  */
+      else if (tok.len == 2
+              && tok.text[0] == '#'
+              && tok.text[1] == '#')
+       error (_("Stray splicing operator"));
+      /* Is the next token the splicing operator?  */
+      else if (lookahead_valid
+              && lookahead.len == 2
+              && lookahead.text[0] == '#'
+              && lookahead.text[1] == '#')
+       {
+         int arg, finished = 0;
+         int prev_was_comma = 0;
+
+         /* Note that GCC warns if the result of splicing is not a
+            token.  In the debugger there doesn't seem to be much
+            benefit from doing this.  */
+
+         /* Insert the first token.  */
+         if (tok.len == 1 && tok.text[0] == ',')
+           prev_was_comma = 1;
+         else
+           {
+             int arg = find_parameter (&tok, is_varargs, va_arg_name,
+                                       def->argc, def->argv);
+             if (arg != -1)
+               appendmem (dest, argv[arg].text, argv[arg].len);
+             else
+               appendmem (dest, tok.text, tok.len);
+           }
+
+         /* Apply a possible sequence of ## operators.  */
+         for (;;)
+           {
+             if (! get_token (&tok, &replacement_list))
+               error (_("Splicing operator at end of macro"));
+
+             /* Handle a comma before a ##.  If we are handling
+                varargs, and the token on the right hand side is the
+                varargs marker, and the final argument is empty or
+                missing, then drop the comma.  This is a GNU
+                extension.  There is one ambiguous case here,
+                involving pedantic behavior with an empty argument,
+                but we settle that in favor of GNU-style (GCC uses an
+                option).  If we aren't dealing with varargs, we
+                simply insert the comma.  */
+             if (prev_was_comma)
+               {
+                 if (! (is_varargs
+                        && tok.len == va_arg_name->len
+                        && !memcmp (tok.text, va_arg_name->text, tok.len)
+                        && argv[argc - 1].len == 0))
+                   appendmem (dest, ",", 1);
+                 prev_was_comma = 0;
+               }
+
+             /* Insert the token.  If it is a parameter, insert the
+                argument.  If it is a comma, treat it specially.  */
+             if (tok.len == 1 && tok.text[0] == ',')
+               prev_was_comma = 1;
+             else
+               {
+                 int arg = find_parameter (&tok, is_varargs, va_arg_name,
+                                           def->argc, def->argv);
+                 if (arg != -1)
+                   appendmem (dest, argv[arg].text, argv[arg].len);
+                 else
+                   appendmem (dest, tok.text, tok.len);
+               }
+
+             /* Now read another token.  If it is another splice, we
+                loop.  */
+             original_rl_start = replacement_list.text;
+             if (! get_token (&tok, &replacement_list))
+               {
+                 finished = 1;
+                 break;
+               }
+
+             if (! (tok.len == 2
+                    && tok.text[0] == '#'
+                    && tok.text[1] == '#'))
+               break;
+           }
+
+         if (prev_was_comma)
+           {
+             /* We saw a comma.  Insert it now.  */
+             appendmem (dest, ",", 1);
+           }
+
+          dest->last_token = dest->len;
+         if (finished)
+           lookahead_valid = 0;
+         else
+           {
+             /* Set up for the loop iterator.  */
+             lookahead = tok;
+             lookahead_rl_start = original_rl_start;
+             lookahead_valid = 1;
+           }
+       }
+      else
+       {
+         /* Is this token an identifier?  */
+         int substituted = 0;
+         int arg = find_parameter (&tok, is_varargs, va_arg_name,
+                                   def->argc, def->argv);
+
+         if (arg != -1)
+           {
+             struct macro_buffer arg_src;
+
+             /* Expand any macro invocations in the argument text,
+                and append the result to dest.  Remember that scan
+                mutates its source, so we need to scan a new buffer
+                referring to the argument's text, not the argument
+                itself.  */
+             init_shared_buffer (&arg_src, argv[arg].text, argv[arg].len);
+             scan (dest, &arg_src, no_loop, lookup_func, lookup_baton);
+             substituted = 1;
+           }
+
+         /* If it wasn't a parameter, then just copy it across.  */
+         if (! substituted)
+           append_tokens_without_splicing (dest, &tok);
+       }
+
+      if (! lookahead_valid)
+       break;
+
+      tok = lookahead;
+      original_rl_start = lookahead_rl_start;
+
+      lookahead_rl_start = replacement_list.text;
+      lookahead_valid = get_token (&lookahead, &replacement_list);
     }
 }
 
@@ -937,13 +1170,39 @@ expand (const char *id,
       struct macro_buffer *argv = NULL;
       struct macro_buffer substituted;
       struct macro_buffer substituted_src;
-
-      if (def->argc >= 1
-          && strcmp (def->argv[def->argc - 1], "...") == 0)
-        error (_("Varargs macros not implemented yet."));
+      struct macro_buffer va_arg_name;
+      int is_varargs = 0;
+
+      if (def->argc >= 1)
+       {
+         if (strcmp (def->argv[def->argc - 1], "...") == 0)
+           {
+             /* In C99-style varargs, substitution is done using
+                __VA_ARGS__.  */
+             init_shared_buffer (&va_arg_name, "__VA_ARGS__",
+                                 strlen ("__VA_ARGS__"));
+             is_varargs = 1;
+           }
+         else
+           {
+             int len = strlen (def->argv[def->argc - 1]);
+             if (len > 3
+                 && strcmp (def->argv[def->argc - 1] + len - 3, "...") == 0)
+               {
+                 /* In GNU-style varargs, the name of the
+                    substitution parameter is the name of the formal
+                    argument without the "...".  */
+                 init_shared_buffer (&va_arg_name,
+                                     (char *) def->argv[def->argc - 1],
+                                     len - 3);
+                 is_varargs = 1;
+               }
+           }
+       }
 
       make_cleanup (free_current_contents, &argv);
-      argv = gather_arguments (id, src, &argc);
+      argv = gather_arguments (id, src, is_varargs ? def->argc : -1,
+                              &argc);
 
       /* If we couldn't find any argument list, then we don't expand
          this macro.  */
@@ -957,12 +1216,16 @@ expand (const char *id,
          this macro.  */
       if (argc != def->argc)
         {
+         if (is_varargs && argc >= def->argc - 1)
+           {
+             /* Ok.  */
+           }
           /* Remember that a sequence of tokens like "foo()" is a
              valid invocation of a macro expecting either zero or one
              arguments.  */
-          if (! (argc == 1
-                 && argv[0].len == 0
-                 && def->argc == 0))
+          else if (! (argc == 1
+                     && argv[0].len == 0
+                     && def->argc == 0))
             error (_("Wrong number of arguments to macro `%s' "
                    "(expected %d, got %d)."),
                    id, def->argc, argc);
@@ -976,8 +1239,8 @@ expand (const char *id,
          expand an argument until we see how it's being used.  */
       init_buffer (&substituted, 0);
       make_cleanup (cleanup_macro_buffer, &substituted);
-      substitute_args (&substituted, def, argc, argv, no_loop,
-                       lookup_func, lookup_baton);
+      substitute_args (&substituted, def, is_varargs, &va_arg_name,
+                      argc, argv, no_loop, lookup_func, lookup_baton);
 
       /* Now `substituted' is the macro's replacement list, with all
          argument values substituted into it properly.  Re-scan it for
index d2478c17a91d7a3e24f072de12b4e4813dc7a384..dec9671c47739a34b5bb357f6f5efeb8203a7dcd 100644 (file)
@@ -1,3 +1,8 @@
+2008-09-27  Tom Tromey  <tromey@redhat.com>
+
+       * gdb.base/macscp.exp: Add tests for stringification, splicing,
+       and varargs.
+
 2008-09-22  Pedro Alves  <pedro@codesourcery.com>
 
        * lib/mi-support.exp (mi_expect_interrupt): New.
index 3424714adde2cebf97ff9dfbb66f8a6dba2cbc12..d9fd97ccd867d3677e845edcf918baca6401a392 100644 (file)
@@ -480,7 +480,100 @@ gdb_test "macro undef" \
     "usage: macro undef.*" \
     "macro undef with no arguments"
 
-# Regression test; this used to emit the wrong error.
+# Splicing tests.
+
 gdb_test "macro expand SPLICE(x, y)" \
-  "Token splicing is not implemented yet." \
-  "macro splicing lexes correctly"
+  "expands to: xy" \
+  "basic macro splicing"
+
+gdb_test "macro define robotinvasion 2010" \
+  "" \
+  "define splice helper"
+
+gdb_test "macro expand SPLICE(robot, invasion)" \
+  "expands to: *2010" \
+  "splicing plus expansion"
+
+# Varargs tests.
+
+gdb_test "macro define va_c99(...) fprintf (stderr, __VA_ARGS__)" \
+  "" \
+  "define first varargs helper"
+
+gdb_test "macro define va2_c99(x, y, ...) fprintf (stderr, x, y, __VA_ARGS__)" \
+  "" \
+  "define second varargs helper"
+
+gdb_test "macro define va_gnu(args...) fprintf (stderr, args)" \
+  "" \
+  "define third varargs helper"
+
+gdb_test "macro define va2_gnu(args...) fprintf (stderr, ## args)" \
+  "" \
+  "define fourth varargs helper"
+
+gdb_test "macro expand va_c99(one, two, three)" \
+  "expands to: *fprintf \\(stderr, *one, two, three\\)" \
+  "c99 varargs expansion"
+
+gdb_test "macro expand va_c99()" \
+  "expands to: *fprintf \\(stderr, *\\)" \
+  "c99 varargs expansion without an argument"
+
+gdb_test "macro expand va2_c99(one, two, three, four)" \
+  "expands to: *fprintf \\(stderr, *one, two, three, four\\)" \
+  "c99 varargs expansion, multiple formal arguments"
+
+gdb_test "macro expand va_gnu(one, two, three, four)" \
+  "expands to: *fprintf \\(stderr, *one, two, three, four\\)" \
+  "gnu varargs expansion"
+
+gdb_test "macro expand va_gnu()" \
+  "expands to: *fprintf \\(stderr, *\\)" \
+  "gnu varargs expansion without an argument"
+
+gdb_test "macro expand va2_gnu()" \
+  "expands to: *fprintf \\(stderr\\)" \
+  "gnu varargs expansion special splicing without an argument"
+
+# Stringification tests.
+
+gdb_test "macro define str(x) #x" \
+  "" \
+  "define stringification macro"
+
+gdb_test "macro define maude 5" \
+  "" \
+  "define first stringification helper"
+
+gdb_test "macro define xstr(x) str(x)" \
+  "" \
+  "define second stringification helper"
+
+gdb_test "print str(5)" \
+  " = \"5\"" \
+  "simple stringify"
+
+gdb_test "print str(hi bob)" \
+  " = \"hi bob\"" \
+  "stringify with one space"
+
+gdb_test "print str(  hi  bob  )" \
+  " = \"hi bob\"" \
+  "stringify with many spaces"
+
+gdb_test "print str(hi \"bob\")" \
+  " = \"hi \\\\\"bob\\\\\"\"" \
+  "stringify with quotes"
+
+gdb_test "print str(hi \\bob\\)" \
+  " = \"hi \\\\\\\\bob\\\\\\\\\"" \
+  "stringify with backslashes"
+
+gdb_test "print str(maude)" \
+  " = \"maude\"" \
+  "stringify without substitution"
+
+gdb_test "print xstr(maude)" \
+  " = \"5\"" \
+  "stringify with substitution"