* 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.
This commit is contained in:
Tom Tromey 2008-09-27 21:40:49 +00:00
parent 5c6ce71d76
commit 2fae03e85b
8 changed files with 468 additions and 70 deletions

View 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> 2008-09-27 Tom Tromey <tromey@redhat.com>
* scm-lang.c (scm_language_defn): Update. * scm-lang.c (scm_language_defn): Update.

View file

@ -22,6 +22,10 @@ For instance, consider:
If the user types TAB at the end of this command line, the available If the user types TAB at the end of this command line, the available
completions will be "f1" and "f2". 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 * New remote packets
qSearch:memory: qSearch:memory:

View 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> 2008-09-27 Tom Tromey <tromey@redhat.com>
* gdbint.texinfo (Language Support): Remove text about omitting * gdbint.texinfo (Language Support): Remove text about omitting

View 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; @value{GDBN} uses the macros in scope at the current listing location;
see @ref{List}. 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 Whenever @value{GDBN} evaluates an expression, it always expands any
macro invocations present in the expression. @value{GDBN} also provides macro invocations present in the expression. @value{GDBN} also provides
the following commands for working with macros explicitly. the following commands for working with macros explicitly.

View file

@ -197,18 +197,37 @@ skip_ws (char **expp)
++*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 * static char *
extract_identifier (char **expp) extract_identifier (char **expp, int is_parameter)
{ {
char *result; char *result;
char *p = *expp; char *p = *expp;
unsigned int len; unsigned int len;
if (! *p || ! macro_is_identifier_nondigit (*p))
return NULL; if (is_parameter && !strncmp (p, "...", 3))
for (++p; {
*p && (macro_is_identifier_nondigit (*p) || macro_is_digit (*p)); /* Ok. */
++p) }
; 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; len = p - *expp;
result = (char *) xmalloc (len + 1); result = (char *) xmalloc (len + 1);
memcpy (result, *expp, len); memcpy (result, *expp, len);
@ -246,7 +265,7 @@ macro_define_command (char *exp, int from_tty)
memset (&new_macro, 0, sizeof (struct macro_definition)); memset (&new_macro, 0, sizeof (struct macro_definition));
skip_ws (&exp); skip_ws (&exp);
name = extract_identifier (&exp); name = extract_identifier (&exp, 0);
if (! name) if (! name)
error (_("Invalid macro name.")); error (_("Invalid macro name."));
if (*exp == '(') if (*exp == '(')
@ -274,7 +293,7 @@ macro_define_command (char *exp, int from_tty)
/* Must update new_macro as well... */ /* Must update new_macro as well... */
new_macro.argv = (const char * const *) argv; 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]) if (! argv[new_macro.argc])
error (_("Macro is missing an argument.")); error (_("Macro is missing an argument."));
++new_macro.argc; ++new_macro.argc;
@ -317,7 +336,7 @@ macro_undef_command (char *exp, int from_tty)
error (_("usage: macro undef NAME")); error (_("usage: macro undef NAME"));
skip_ws (&exp); skip_ws (&exp);
name = extract_identifier (&exp); name = extract_identifier (&exp, 0);
if (! name) if (! name)
error (_("Invalid macro name.")); error (_("Invalid macro name."));
macro_undef (macro_main (macro_user_macros), -1, name); macro_undef (macro_main (macro_user_macros), -1, name);

View file

@ -625,6 +625,52 @@ append_tokens_without_splicing (struct macro_buffer *dest,
_("unable to avoid splicing tokens during macro expansion")); _("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! */ /* 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 If SRC doesn't contain a properly terminated argument list, then
raise an error. 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 Otherwise, return a pointer to the first element of an array of
macro buffers referring to the argument texts, and set *ARGC_P to 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. */ following the invocation. */
static struct macro_buffer * 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; struct macro_buffer tok;
int args_len, args_size; 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. */ the end of the argument list. */
if (depth == 0) 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); discard_cleanups (back_to);
*argc_p = args_len; *argc_p = args_len;
return args; 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 /* If tok is a comma at top level, then that's the end of
the current argument. */ the current argument. However, if we are handling a
else if (tok.len == 1 && tok.text[0] == ',' && depth == 0) 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; break;
/* Extend the current argument to enclose this token. If /* Extend the current argument to enclose this token. If
@ -801,17 +870,57 @@ static void scan (struct macro_buffer *dest,
void *lookup_baton); 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 /* Given the macro definition DEF, being invoked with the actual
arguments given by ARGC and ARGV, substitute the arguments into the arguments given by ARGC and ARGV, substitute the arguments into the
replacement list, and store the result in DEST. 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 If it is necessary to expand macro invocations in one of the
arguments, use LOOKUP_FUNC and LOOKUP_BATON to find the macro arguments, use LOOKUP_FUNC and LOOKUP_BATON to find the macro
definitions, and don't expand invocations of the macros listed in definitions, and don't expand invocations of the macros listed in
NO_LOOP. */ NO_LOOP. */
static void static void
substitute_args (struct macro_buffer *dest, substitute_args (struct macro_buffer *dest,
struct macro_definition *def, struct macro_definition *def,
int is_varargs, const struct macro_buffer *va_arg_name,
int argc, struct macro_buffer *argv, int argc, struct macro_buffer *argv,
struct macro_name_list *no_loop, struct macro_name_list *no_loop,
macro_lookup_ftype *lookup_func, macro_lookup_ftype *lookup_func,
@ -819,6 +928,17 @@ substitute_args (struct macro_buffer *dest,
{ {
/* A macro buffer for the macro's replacement list. */ /* A macro buffer for the macro's replacement list. */
struct macro_buffer 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, init_shared_buffer (&replacement_list, (char *) def->replacement,
strlen (def->replacement)); strlen (def->replacement));
@ -826,16 +946,14 @@ substitute_args (struct macro_buffer *dest,
gdb_assert (dest->len == 0); gdb_assert (dest->len == 0);
dest->last_token = 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 (;;) 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 /* Just for aesthetics. If we skipped some whitespace, copy
that to DEST. */ that to DEST. */
if (tok.text > original_rl_start) if (tok.text > original_rl_start)
@ -847,46 +965,161 @@ substitute_args (struct macro_buffer *dest,
/* Is this token the stringification operator? */ /* Is this token the stringification operator? */
if (tok.len == 1 if (tok.len == 1
&& tok.text[0] == '#') && tok.text[0] == '#')
error (_("Stringification is not implemented yet.")); {
int arg;
if (!lookahead_valid)
error (_("Stringification operator requires an argument."));
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."));
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? */ /* Is this token the splicing operator? */
if (tok.len == 2 else if (tok.len == 2
&& tok.text[0] == '#' && tok.text[0] == '#'
&& tok.text[1] == '#') && tok.text[1] == '#')
error (_("Token splicing is not implemented yet.")); 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;
/* Is this token an identifier? */ /* Note that GCC warns if the result of splicing is not a
if (tok.is_identifier) token. In the debugger there doesn't seem to be much
{ benefit from doing this. */
int i;
/* Is it the magic varargs parameter? */ /* Insert the first token. */
if (tok.len == 11 if (tok.len == 1 && tok.text[0] == ',')
&& ! memcmp (tok.text, "__VA_ARGS__", 11)) prev_was_comma = 1;
error (_("Variable-arity macros not implemented yet.")); 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);
}
/* Is it one of the parameters? */ /* Apply a possible sequence of ## operators. */
for (i = 0; i < def->argc; i++) for (;;)
if (tok.len == strlen (def->argv[i]) {
&& ! memcmp (tok.text, def->argv[i], tok.len)) if (! get_token (&tok, &replacement_list))
{ error (_("Splicing operator at end of macro"));
struct macro_buffer arg_src;
/* Expand any macro invocations in the argument text, /* Handle a comma before a ##. If we are handling
and append the result to dest. Remember that scan varargs, and the token on the right hand side is the
mutates its source, so we need to scan a new buffer varargs marker, and the final argument is empty or
referring to the argument's text, not the argument missing, then drop the comma. This is a GNU
itself. */ extension. There is one ambiguous case here,
init_shared_buffer (&arg_src, argv[i].text, argv[i].len); involving pedantic behavior with an empty argument,
scan (dest, &arg_src, no_loop, lookup_func, lookup_baton); but we settle that in favor of GNU-style (GCC uses an
substituted = 1; option). If we aren't dealing with varargs, we
break; 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;
}
/* If it wasn't a parameter, then just copy it across. */ /* Insert the token. If it is a parameter, insert the
if (! substituted) argument. If it is a comma, treat it specially. */
append_tokens_without_splicing (dest, &tok); 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 *argv = NULL;
struct macro_buffer substituted; struct macro_buffer substituted;
struct macro_buffer substituted_src; struct macro_buffer substituted_src;
struct macro_buffer va_arg_name;
int is_varargs = 0;
if (def->argc >= 1 if (def->argc >= 1)
&& strcmp (def->argv[def->argc - 1], "...") == 0) {
error (_("Varargs macros not implemented yet.")); 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); 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 /* If we couldn't find any argument list, then we don't expand
this macro. */ this macro. */
@ -957,12 +1216,16 @@ expand (const char *id,
this macro. */ this macro. */
if (argc != def->argc) if (argc != def->argc)
{ {
if (is_varargs && argc >= def->argc - 1)
{
/* Ok. */
}
/* Remember that a sequence of tokens like "foo()" is a /* Remember that a sequence of tokens like "foo()" is a
valid invocation of a macro expecting either zero or one valid invocation of a macro expecting either zero or one
arguments. */ arguments. */
if (! (argc == 1 else if (! (argc == 1
&& argv[0].len == 0 && argv[0].len == 0
&& def->argc == 0)) && def->argc == 0))
error (_("Wrong number of arguments to macro `%s' " error (_("Wrong number of arguments to macro `%s' "
"(expected %d, got %d)."), "(expected %d, got %d)."),
id, def->argc, argc); id, def->argc, argc);
@ -976,8 +1239,8 @@ expand (const char *id,
expand an argument until we see how it's being used. */ expand an argument until we see how it's being used. */
init_buffer (&substituted, 0); init_buffer (&substituted, 0);
make_cleanup (cleanup_macro_buffer, &substituted); make_cleanup (cleanup_macro_buffer, &substituted);
substitute_args (&substituted, def, argc, argv, no_loop, substitute_args (&substituted, def, is_varargs, &va_arg_name,
lookup_func, lookup_baton); argc, argv, no_loop, lookup_func, lookup_baton);
/* Now `substituted' is the macro's replacement list, with all /* Now `substituted' is the macro's replacement list, with all
argument values substituted into it properly. Re-scan it for argument values substituted into it properly. Re-scan it for

View 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> 2008-09-22 Pedro Alves <pedro@codesourcery.com>
* lib/mi-support.exp (mi_expect_interrupt): New. * lib/mi-support.exp (mi_expect_interrupt): New.

View file

@ -480,7 +480,100 @@ gdb_test "macro undef" \
"usage: macro undef.*" \ "usage: macro undef.*" \
"macro undef with no arguments" "macro undef with no arguments"
# Regression test; this used to emit the wrong error. # Splicing tests.
gdb_test "macro expand SPLICE(x, y)" \ gdb_test "macro expand SPLICE(x, y)" \
"Token splicing is not implemented yet." \ "expands to: xy" \
"macro splicing lexes correctly" "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"