/* macro.c - macro support for gas
Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
- 2004, 2005 Free Software Foundation, Inc.
+ 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
Written by Steve and Judy Chamberlain of Cygnus Support,
sac@cygnus.com
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
+ the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
02110-1301, USA. */
-#include "config.h"
-
-#ifndef __GNUC__
-# if HAVE_ALLOCA_H
-# include <alloca.h>
-# else
-# ifdef _AIX
-/* Indented so that pre-ansi C compilers will ignore it, rather than
- choke on it. Some versions of AIX require this to be the first
- thing in the file. */
- #pragma alloca
-# else
-# ifndef alloca /* predefined by HP cc +Olibcalls */
-# if !defined (__STDC__) && !defined (__hpux)
-extern char *alloca ();
-# else
-extern void *alloca ();
-# endif /* __STDC__, __hpux */
-# endif /* alloca */
-# endif /* _AIX */
-# endif /* HAVE_ALLOCA_H */
-#endif /* __GNUC__ */
-
-#include <stdio.h>
-#ifdef HAVE_STRING_H
-#include <string.h>
-#else
-#include <strings.h>
-#endif
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
#include "as.h"
-#include "libiberty.h"
#include "safe-ctype.h"
#include "sb.h"
-#include "hash.h"
#include "macro.h"
-#include "asintl.h"
-
/* The routines in this file handle macro definition and expansion.
They are called by gas. */
static int get_token (int, sb *, sb *);
static int getstring (int, sb *, sb *);
-static int get_any_string (int, sb *, sb *, int, int);
+static int get_any_string (int, sb *, sb *);
+static formal_entry *new_formal (void);
+static void del_formal (formal_entry *);
static int do_formals (macro_entry *, int, sb *);
static int get_apost_token (int, sb *, sb *, int);
static int sub_actual (int, sb *, sb *, struct hash_control *, int, sb *, int);
while (more)
{
- /* Try and find the first pseudo op on the line. */
+ /* Try to find the first pseudo op on the line. */
int i = line_start;
- if (! NO_PSEUDO_DOT && ! flag_m68k_mri)
- {
- /* With normal syntax we can suck what we want till we get
- to the dot. With the alternate, labels have to start in
- the first column, since we can't tell what's a label and
- whats a pseudoop. */
+ /* With normal syntax we can suck what we want till we get
+ to the dot. With the alternate, labels have to start in
+ the first column, since we can't tell what's a label and
+ what's a pseudoop. */
- if (! LABELS_WITHOUT_COLONS)
- {
- /* Skip leading whitespace. */
- while (i < ptr->len && ISWHITE (ptr->ptr[i]))
- i++;
- }
+ if (! LABELS_WITHOUT_COLONS)
+ {
+ /* Skip leading whitespace. */
+ while (i < ptr->len && ISWHITE (ptr->ptr[i]))
+ i++;
+ }
- for (;;)
+ for (;;)
+ {
+ /* Skip over a label, if any. */
+ if (i >= ptr->len || ! is_name_beginner (ptr->ptr[i]))
+ break;
+ i++;
+ while (i < ptr->len && is_part_of_name (ptr->ptr[i]))
+ i++;
+ if (i < ptr->len && is_name_ender (ptr->ptr[i]))
+ i++;
+ if (LABELS_WITHOUT_COLONS)
+ break;
+ /* Skip whitespace. */
+ while (i < ptr->len && ISWHITE (ptr->ptr[i]))
+ i++;
+ /* Check for the colon. */
+ if (i >= ptr->len || ptr->ptr[i] != ':')
{
- /* Skip over a label, if any. */
- if (i >= ptr->len || ! is_name_beginner (ptr->ptr[i]))
- break;
- i++;
- while (i < ptr->len && is_part_of_name (ptr->ptr[i]))
- i++;
- if (i < ptr->len && is_name_ender (ptr->ptr[i]))
- i++;
- if (LABELS_WITHOUT_COLONS)
- break;
- /* Skip whitespace. */
- while (i < ptr->len && ISWHITE (ptr->ptr[i]))
- i++;
- /* Check for the colon. */
- if (i >= ptr->len || ptr->ptr[i] != ':')
- {
- i = line_start;
- break;
- }
- i++;
- line_start = i;
+ i = line_start;
+ break;
}
-
+ i++;
+ line_start = i;
}
+
/* Skip trailing whitespace. */
while (i < ptr->len && ISWHITE (ptr->ptr[i]))
i++;
static int
getstring (int idx, sb *in, sb *acc)
{
- idx = sb_skip_white (idx, in);
-
while (idx < in->len
&& (in->ptr[idx] == '"'
|| (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
/* Fetch string from the input stream,
rules:
'Bxyx<whitespace> -> return 'Bxyza
- %<char> -> return string of decimal value of x
- "<string>" -> return string
- xyx<whitespace> -> return xyz
-*/
+ %<expr> -> return string of decimal value of <expr>
+ "string" -> return string
+ (string) -> return (string-including-whitespaces)
+ xyx<whitespace> -> return xyz. */
static int
-get_any_string (int idx, sb *in, sb *out, int expand, int pretend_quoted)
+get_any_string (int idx, sb *in, sb *out)
{
sb_reset (out);
idx = sb_skip_white (idx, in);
while (!ISSEP (in->ptr[idx]))
sb_add_char (out, in->ptr[idx++]);
}
- else if (in->ptr[idx] == '%'
- && macro_alternate
- && expand)
+ else if (in->ptr[idx] == '%' && macro_alternate)
{
int val;
char buf[20];
+
/* Turns the next expression into a string. */
/* xgettext: no-c-format */
idx = (*macro_expr) (_("% operator needs absolute expression"),
|| (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
|| (macro_alternate && in->ptr[idx] == '\''))
{
- if (macro_alternate
- && ! macro_strip_at
- && expand)
+ if (macro_alternate && ! macro_strip_at && in->ptr[idx] != '<')
{
/* Keep the quotes. */
- sb_add_char (out, '\"');
-
+ sb_add_char (out, '"');
idx = getstring (idx, in, out);
- sb_add_char (out, '\"');
+ sb_add_char (out, '"');
}
else
{
}
else
{
+ char *br_buf = xmalloc(1);
+ char *in_br = br_buf;
+
+ *in_br = '\0';
while (idx < in->len
- && (in->ptr[idx] == '"'
- || in->ptr[idx] == '\''
- || pretend_quoted
+ && (*in_br
|| (in->ptr[idx] != ' '
- && in->ptr[idx] != '\t'
- && in->ptr[idx] != ','
- && (in->ptr[idx] != '<'
- || (! macro_alternate && ! macro_mri)))))
+ && in->ptr[idx] != '\t'))
+ && in->ptr[idx] != ','
+ && (in->ptr[idx] != '<'
+ || (! macro_alternate && ! macro_mri)))
{
- if (in->ptr[idx] == '"'
- || in->ptr[idx] == '\'')
+ char tchar = in->ptr[idx];
+
+ switch (tchar)
{
- char tchar = in->ptr[idx];
+ case '"':
+ case '\'':
sb_add_char (out, in->ptr[idx++]);
while (idx < in->len
&& in->ptr[idx] != tchar)
sb_add_char (out, in->ptr[idx++]);
if (idx == in->len)
return idx;
+ break;
+ case '(':
+ case '[':
+ if (in_br > br_buf)
+ --in_br;
+ else
+ {
+ br_buf = xmalloc(strlen(in_br) + 2);
+ strcpy(br_buf + 1, in_br);
+ free(in_br);
+ in_br = br_buf;
+ }
+ *in_br = tchar;
+ break;
+ case ')':
+ if (*in_br == '(')
+ ++in_br;
+ break;
+ case ']':
+ if (*in_br == '[')
+ ++in_br;
+ break;
}
- sb_add_char (out, in->ptr[idx++]);
+ sb_add_char (out, tchar);
+ ++idx;
}
+ free(br_buf);
}
}
return idx;
}
+/* Allocate a new formal. */
+
+static formal_entry *
+new_formal (void)
+{
+ formal_entry *formal;
+
+ formal = xmalloc (sizeof (formal_entry));
+
+ sb_new (&formal->name);
+ sb_new (&formal->def);
+ sb_new (&formal->actual);
+ formal->next = NULL;
+ formal->type = FORMAL_OPTIONAL;
+ return formal;
+}
+
+/* Free a formal. */
+
+static void
+del_formal (formal_entry *formal)
+{
+ sb_kill (&formal->actual);
+ sb_kill (&formal->def);
+ sb_kill (&formal->name);
+ free (formal);
+}
+
/* Pick up the formal parameters of a macro definition. */
static int
idx = sb_skip_white (idx, in);
while (idx < in->len)
{
- formal_entry *formal;
+ formal_entry *formal = new_formal ();
int cidx;
- formal = (formal_entry *) xmalloc (sizeof (formal_entry));
-
- sb_new (&formal->name);
- sb_new (&formal->def);
- sb_new (&formal->actual);
-
idx = get_token (idx, in, &formal->name);
if (formal->name.len == 0)
{
}
idx = sb_skip_white (idx, in);
/* This is a formal. */
+ name = sb_terminate (&formal->name);
+ if (! macro_mri
+ && idx < in->len
+ && in->ptr[idx] == ':'
+ && (! is_name_beginner (':')
+ || idx + 1 >= in->len
+ || ! is_part_of_name (in->ptr[idx + 1])))
+ {
+ /* Got a qualifier. */
+ sb qual;
+
+ sb_new (&qual);
+ idx = get_token (sb_skip_white (idx + 1, in), in, &qual);
+ sb_terminate (&qual);
+ if (qual.len == 0)
+ as_bad_where (macro->file,
+ macro->line,
+ _("Missing parameter qualifier for `%s' in macro `%s'"),
+ name,
+ macro->name);
+ else if (strcmp (qual.ptr, "req") == 0)
+ formal->type = FORMAL_REQUIRED;
+ else if (strcmp (qual.ptr, "vararg") == 0)
+ formal->type = FORMAL_VARARG;
+ else
+ as_bad_where (macro->file,
+ macro->line,
+ _("`%s' is not a valid parameter qualifier for `%s' in macro `%s'"),
+ qual.ptr,
+ name,
+ macro->name);
+ sb_kill (&qual);
+ idx = sb_skip_white (idx, in);
+ }
if (idx < in->len && in->ptr[idx] == '=')
{
/* Got a default. */
- idx = get_any_string (idx + 1, in, &formal->def, 1, 0);
+ idx = get_any_string (idx + 1, in, &formal->def);
idx = sb_skip_white (idx, in);
+ if (formal->type == FORMAL_REQUIRED)
+ {
+ sb_reset (&formal->def);
+ as_warn_where (macro->file,
+ macro->line,
+ _("Pointless default value for required parameter `%s' in macro `%s'"),
+ name,
+ macro->name);
+ }
}
/* Add to macro's hash table. */
- name = sb_terminate (&formal->name);
if (! hash_find (macro->formal_hash, name))
hash_jam (macro->formal_hash, name, formal);
else
macro->name);
formal->index = macro->formal_count++;
+ *p = formal;
+ p = &formal->next;
+ if (formal->type == FORMAL_VARARG)
+ break;
cidx = idx;
idx = sb_skip_comma (idx, in);
if (idx != cidx && idx >= in->len)
idx = cidx;
break;
}
- *p = formal;
- p = &formal->next;
- *p = NULL;
}
if (macro_mri)
{
- formal_entry *formal;
+ formal_entry *formal = new_formal ();
/* Add a special NARG formal, which macro_expand will set to the
number of arguments. */
- formal = (formal_entry *) xmalloc (sizeof (formal_entry));
-
- sb_new (&formal->name);
- sb_new (&formal->def);
- sb_new (&formal->actual);
-
/* The same MRI assemblers which treat '@' characters also use
the name $NARG. At least until we find an exception. */
if (macro_strip_at)
formal->index = NARG_INDEX;
*p = formal;
- formal->next = NULL;
}
return idx;
if (hash_find (macro_hash, macro->name))
error = _("Macro `%s' was already defined");
if (!error)
- error = hash_jam (macro_hash, macro->name, (PTR) macro);
+ error = hash_jam (macro_hash, macro->name, (void *) macro);
if (namep != NULL)
*namep = macro->name;
while (in->ptr[src] != '\n')
{
const char *name;
- formal_entry *f;
+ formal_entry *f = new_formal ();
- f = (formal_entry *) xmalloc (sizeof (formal_entry));
- sb_new (&f->name);
src = get_token (src, in, &f->name);
name = sb_terminate (&f->name);
if (! hash_find (formal_hash, name))
static int loccnt;
char buf[20];
- sb_new (&f->def);
- sb_new (&f->actual);
f->index = LOCAL_INDEX;
f->next = loclist;
loclist = f;
macro->line + macro_line,
_("`%s' was already used as parameter (or another local) name"),
name);
- sb_kill (&f->name);
- free (f);
+ del_formal (f);
}
src = sb_skip_comma (src, in);
while (loclist != NULL)
{
formal_entry *f;
+ const char *name;
f = loclist->next;
- /* Setting the value to NULL effectively deletes the entry. We
- avoid calling hash_delete because it doesn't reclaim memory. */
- hash_jam (formal_hash, sb_terminate (&loclist->name), NULL);
- sb_kill (&loclist->name);
- sb_kill (&loclist->def);
- sb_kill (&loclist->actual);
- free (loclist);
+ name = sb_terminate (&loclist->name);
+ hash_delete (formal_hash, name, f == NULL);
+ del_formal (loclist);
loclist = f;
}
sb t;
formal_entry *ptr;
formal_entry *f;
- int is_positional = 0;
int is_keyword = 0;
int narg = 0;
- const char *err;
+ const char *err = NULL;
sb_new (&t);
&& in->ptr[idx] != ' '
&& in->ptr[idx] != '\t')
{
- formal_entry *n;
+ formal_entry *n = new_formal ();
- n = (formal_entry *) xmalloc (sizeof (formal_entry));
- sb_new (&n->name);
- sb_new (&n->def);
- sb_new (&n->actual);
n->index = QUAL_INDEX;
n->next = m->formals;
m->formals = n;
- idx = get_any_string (idx, in, &n->actual, 1, 0);
+ idx = get_any_string (idx, in, &n->actual);
}
}
}
sb_reset (&t);
idx = get_token (idx, in, &t);
if (in->ptr[idx] != '=')
- return _("confusion in formal parameters");
+ {
+ err = _("confusion in formal parameters");
+ break;
+ }
/* Lookup the formal in the macro's list. */
ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
if (!ptr)
- return _("macro formal argument does not exist");
+ as_bad (_("Parameter named `%s' does not exist for macro `%s'"),
+ t.ptr,
+ m->name);
else
{
/* Insert this value into the right place. */
- sb_reset (&ptr->actual);
- idx = get_any_string (idx + 1, in, &ptr->actual, 0, 0);
+ if (ptr->actual.len)
+ {
+ as_warn (_("Value for parameter `%s' of macro `%s' was already specified"),
+ ptr->name.ptr,
+ m->name);
+ sb_reset (&ptr->actual);
+ }
+ idx = get_any_string (idx + 1, in, &ptr->actual);
if (ptr->actual.len > 0)
++narg;
}
}
else
{
- /* This is a positional arg. */
- is_positional = 1;
if (is_keyword)
- return _("can't mix positional and keyword arguments");
+ {
+ err = _("can't mix positional and keyword arguments");
+ break;
+ }
if (!f)
{
int c;
if (!macro_mri)
- return _("too many positional arguments");
+ {
+ err = _("too many positional arguments");
+ break;
+ }
- f = (formal_entry *) xmalloc (sizeof (formal_entry));
- sb_new (&f->name);
- sb_new (&f->def);
- sb_new (&f->actual);
- f->next = NULL;
+ f = new_formal ();
c = -1;
for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next)
f->index = c;
}
- sb_reset (&f->actual);
- idx = get_any_string (idx, in, &f->actual, 1, 0);
+ if (f->type != FORMAL_VARARG)
+ idx = get_any_string (idx, in, &f->actual);
+ else
+ {
+ sb_add_buffer (&f->actual, in->ptr + idx, in->len - idx);
+ idx = in->len;
+ }
if (f->actual.len > 0)
++narg;
do
}
}
- if (macro_mri)
+ if (! err)
{
- char buffer[20];
-
- sb_reset (&t);
- sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG");
- ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
- sb_reset (&ptr->actual);
- sprintf (buffer, "%d", narg);
- sb_add_string (&ptr->actual, buffer);
- }
+ for (ptr = m->formals; ptr; ptr = ptr->next)
+ {
+ if (ptr->type == FORMAL_REQUIRED && ptr->actual.len == 0)
+ as_bad (_("Missing value for required parameter `%s' of macro `%s'"),
+ ptr->name.ptr,
+ m->name);
+ }
- err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m);
+ if (macro_mri)
+ {
+ char buffer[20];
+
+ sb_reset (&t);
+ sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG");
+ ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
+ sprintf (buffer, "%d", narg);
+ sb_add_string (&ptr->actual, buffer);
+ }
+
+ err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m);
+ }
/* Discard any unnamed formal arguments. */
if (macro_mri)
pf = &(*pf)->next;
else
{
- sb_kill (&(*pf)->name);
- sb_kill (&(*pf)->def);
- sb_kill (&(*pf)->actual);
f = (*pf)->next;
- free (*pf);
+ del_formal (*pf);
*pf = f;
}
}
for (formal = macro->formals; formal; )
{
- void *ptr;
+ formal_entry *f;
- sb_kill (&formal->name);
- sb_kill (&formal->def);
- sb_kill (&formal->actual);
- ptr = formal;
+ f = formal;
formal = formal->next;
- free (ptr);
+ del_formal (f);
}
hash_die (macro->formal_hash);
sb_kill (¯o->sub);
copy[i] = TOLOWER (name[i]);
copy[i] = '\0';
- /* Since hash_delete doesn't free memory, just clear out the entry. */
+ /* We can only ask hash_delete to free memory if we are deleting
+ macros in reverse order to their definition.
+ So just clear out the entry. */
if ((macro = hash_find (macro_hash, copy)) != NULL)
{
hash_jam (macro_hash, copy, NULL);
f.index = 1;
f.next = NULL;
+ f.type = FORMAL_OPTIONAL;
sb_reset (out);
}
else
{
+ bfd_boolean in_quotes = FALSE;
+
if (irpc && in->ptr[idx] == '"')
- ++idx;
+ {
+ in_quotes = TRUE;
+ ++idx;
+ }
+
while (idx < in->len)
{
if (!irpc)
- idx = get_any_string (idx, in, &f.actual, 1, 0);
+ idx = get_any_string (idx, in, &f.actual);
else
{
if (in->ptr[idx] == '"')
{
int nxt;
+ if (irpc)
+ in_quotes = ! in_quotes;
+
nxt = sb_skip_white (idx + 1, in);
if (nxt >= in->len)
{
sb_add_char (&f.actual, in->ptr[idx]);
++idx;
}
+
err = macro_expand_body (&sub, out, &f, h, 0);
if (err != NULL)
break;
if (!irpc)
idx = sb_skip_comma (idx, in);
- else
+ else if (! in_quotes)
idx = sb_skip_white (idx, in);
}
}
hash_die (h);
+ sb_kill (&f.actual);
+ sb_kill (&f.def);
+ sb_kill (&f.name);
sb_kill (&sub);
return err;