This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFA: implement all missing macro expansion features


Joel> I will let you decide what makes the most sense. 

I don't really care, so I made it an error.

In the interest of full disclosure, there is another pedantic error
case that this code does not handle: it is invalid to attempt to paste
two things which do not form a token.  I did not implement checking
for this -- but perhaps you would want that?

I think this patch addresses all your concerns.  I also incorporated a
documentation update, which was sitting unsubmitted in my patch queue.

Tom

2008-09-20  Tom Tromey  <tromey@redhat.com>

	* 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-20  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (Macros): Remove text about stringification,
	varargs, and splicing.

2008-09-20  Tom Tromey  <tromey@redhat.com>

	* gdb.base/macscp.exp: Add tests for stringification, splicing,
	and varargs.

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4a36fec..d8a87ef 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -8086,10 +8086,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.
diff --git a/gdb/macrocmd.c b/gdb/macrocmd.c
index 8213c0d..c9ab440 100644
--- a/gdb/macrocmd.c
+++ b/gdb/macrocmd.c
@@ -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);
diff --git a/gdb/macroexp.c b/gdb/macroexp.c
index 8102bc0..7fb23ce 100644
--- a/gdb/macroexp.c
+++ b/gdb/macroexp.c
@@ -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;
+}
 
 
 /* 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
diff --git a/gdb/testsuite/gdb.base/macscp.exp b/gdb/testsuite/gdb.base/macscp.exp
index 3424714..d9fd97c 100644
--- a/gdb/testsuite/gdb.base/macscp.exp
+++ b/gdb/testsuite/gdb.base/macscp.exp
@@ -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"


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