#include "config.h"
#ifndef __GNUC__
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
# ifdef _AIX
#pragma alloca
# else
# ifndef alloca
# if !defined (__STDC__) && !defined (__hpux)
extern char *alloca ();
# else
extern void *alloca ();
# endif
# endif
# endif
# endif
#endif
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include "libiberty.h"
#include "safe-ctype.h"
#include "sb.h"
#include "hash.h"
#include "macro.h"
#include "asintl.h"
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 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);
static const char *macro_expand_body
(sb *, sb *, formal_entry *, struct hash_control *, int);
static const char *macro_expand (int, sb *, macro_entry *, sb *);
#define ISWHITE(x) ((x) == ' ' || (x) == '\t')
#define ISSEP(x) \
((x) == ' ' || (x) == '\t' || (x) == ',' || (x) == '"' || (x) == ';' \
|| (x) == ')' || (x) == '(' \
|| ((macro_alternate || macro_mri) && ((x) == '<' || (x) == '>')))
#define ISBASE(x) \
((x) == 'b' || (x) == 'B' \
|| (x) == 'q' || (x) == 'Q' \
|| (x) == 'h' || (x) == 'H' \
|| (x) == 'd' || (x) == 'D')
struct hash_control *macro_hash;
int macro_defined;
static int macro_alternate;
static int macro_mri;
static int macro_strip_at;
static int (*macro_expr) (const char *, int, sb *, int *);
static int macro_number;
void
macro_init (int alternate, int mri, int strip_at,
int (*expr) (const char *, int, sb *, int *))
{
macro_hash = hash_new ();
macro_defined = 0;
macro_alternate = alternate;
macro_mri = mri;
macro_strip_at = strip_at;
macro_expr = expr;
}
void
macro_mri_mode (int mri)
{
macro_mri = mri;
}
int
buffer_and_nest (const char *from, const char *to, sb *ptr,
int (*get_line) (sb *))
{
int from_len = strlen (from);
int to_len = strlen (to);
int depth = 1;
int line_start = ptr->len;
int more = get_line (ptr);
while (more)
{
int i = line_start;
if (! macro_alternate && ! macro_mri)
{
while (i < ptr->len && ISWHITE (ptr->ptr[i]))
i++;
while (i < ptr->len
&& (ISALNUM (ptr->ptr[i])
|| ptr->ptr[i] == '_'
|| ptr->ptr[i] == '$'))
i++;
if (i < ptr->len
&& ptr->ptr[i] == ':')
i++;
}
while (i < ptr->len && ISWHITE (ptr->ptr[i]))
i++;
if (i < ptr->len && (ptr->ptr[i] == '.'
|| macro_alternate
|| macro_mri))
{
if (ptr->ptr[i] == '.')
i++;
if (strncasecmp (ptr->ptr + i, from, from_len) == 0
&& (ptr->len == (i + from_len)
|| ! ISALNUM (ptr->ptr[i + from_len])))
depth++;
if (strncasecmp (ptr->ptr + i, to, to_len) == 0
&& (ptr->len == (i + to_len)
|| ! ISALNUM (ptr->ptr[i + to_len])))
{
depth--;
if (depth == 0)
{
ptr->len = line_start;
break;
}
}
}
sb_add_char (ptr, more);
line_start = ptr->len;
more = get_line (ptr);
}
return depth == 0;
}
static int
get_token (int idx, sb *in, sb *name)
{
if (idx < in->len
&& (ISALPHA (in->ptr[idx])
|| in->ptr[idx] == '_'
|| in->ptr[idx] == '$'))
{
sb_add_char (name, in->ptr[idx++]);
while (idx < in->len
&& (ISALNUM (in->ptr[idx])
|| in->ptr[idx] == '_'
|| in->ptr[idx] == '$'))
{
sb_add_char (name, in->ptr[idx++]);
}
}
if (macro_alternate && idx < in->len && in->ptr[idx] == '&')
idx++;
return idx;
}
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))
|| (in->ptr[idx] == '\'' && macro_alternate)))
{
if (in->ptr[idx] == '<')
{
int nest = 0;
idx++;
while ((in->ptr[idx] != '>' || nest)
&& idx < in->len)
{
if (in->ptr[idx] == '!')
{
idx++;
sb_add_char (acc, in->ptr[idx++]);
}
else
{
if (in->ptr[idx] == '>')
nest--;
if (in->ptr[idx] == '<')
nest++;
sb_add_char (acc, in->ptr[idx++]);
}
}
idx++;
}
else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'')
{
char tchar = in->ptr[idx];
int escaped = 0;
idx++;
while (idx < in->len)
{
if (in->ptr[idx - 1] == '\\')
escaped ^= 1;
else
escaped = 0;
if (macro_alternate && in->ptr[idx] == '!')
{
idx ++;
sb_add_char (acc, in->ptr[idx]);
idx ++;
}
else if (escaped && in->ptr[idx] == tchar)
{
sb_add_char (acc, tchar);
idx ++;
}
else
{
if (in->ptr[idx] == tchar)
{
idx ++;
if (idx >= in->len || in->ptr[idx] != tchar)
break;
}
sb_add_char (acc, in->ptr[idx]);
idx ++;
}
}
}
}
return idx;
}
static int
get_any_string (int idx, sb *in, sb *out, int expand, int pretend_quoted)
{
sb_reset (out);
idx = sb_skip_white (idx, in);
if (idx < in->len)
{
if (in->len > idx + 2 && in->ptr[idx + 1] == '\'' && ISBASE (in->ptr[idx]))
{
while (!ISSEP (in->ptr[idx]))
sb_add_char (out, in->ptr[idx++]);
}
else if (in->ptr[idx] == '%'
&& macro_alternate
&& expand)
{
int val;
char buf[20];
idx = (*macro_expr) (_("% operator needs absolute expression"),
idx + 1,
in,
&val);
sprintf (buf, "%d", val);
sb_add_string (out, buf);
}
else if (in->ptr[idx] == '"'
|| (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
|| (macro_alternate && in->ptr[idx] == '\''))
{
if (macro_alternate
&& ! macro_strip_at
&& expand)
{
sb_add_char (out, '\"');
idx = getstring (idx, in, out);
sb_add_char (out, '\"');
}
else
{
idx = getstring (idx, in, out);
}
}
else
{
while (idx < in->len
&& (in->ptr[idx] == '"'
|| in->ptr[idx] == '\''
|| pretend_quoted
|| (in->ptr[idx] != ' '
&& 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];
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;
}
sb_add_char (out, in->ptr[idx++]);
}
}
}
return idx;
}
static int
do_formals (macro_entry *macro, int idx, sb *in)
{
formal_entry **p = ¯o->formals;
macro->formal_count = 0;
macro->formal_hash = hash_new ();
while (idx < in->len)
{
formal_entry *formal;
formal = (formal_entry *) xmalloc (sizeof (formal_entry));
sb_new (&formal->name);
sb_new (&formal->def);
sb_new (&formal->actual);
idx = sb_skip_white (idx, in);
idx = get_token (idx, in, &formal->name);
if (formal->name.len == 0)
break;
idx = sb_skip_white (idx, in);
if (formal->name.len)
{
if (idx < in->len && in->ptr[idx] == '=')
{
idx = get_any_string (idx + 1, in, &formal->def, 1, 0);
}
}
hash_jam (macro->formal_hash, sb_terminate (&formal->name), formal);
formal->index = macro->formal_count;
idx = sb_skip_comma (idx, in);
macro->formal_count++;
*p = formal;
p = &formal->next;
*p = NULL;
}
if (macro_mri)
{
formal_entry *formal;
const char *name;
formal = (formal_entry *) xmalloc (sizeof (formal_entry));
sb_new (&formal->name);
sb_new (&formal->def);
sb_new (&formal->actual);
if (macro_strip_at)
name = "$NARG";
else
name = "NARG";
sb_add_string (&formal->name, name);
hash_jam (macro->formal_hash, name, formal);
formal->index = NARG_INDEX;
*p = formal;
formal->next = NULL;
}
return idx;
}
const char *
define_macro (int idx, sb *in, sb *label,
int (*get_line) (sb *), const char **namep)
{
macro_entry *macro;
sb name;
const char *namestr;
macro = (macro_entry *) xmalloc (sizeof (macro_entry));
sb_new (¯o->sub);
sb_new (&name);
macro->formal_count = 0;
macro->formals = 0;
idx = sb_skip_white (idx, in);
if (! buffer_and_nest ("MACRO", "ENDM", ¯o->sub, get_line))
return _("unexpected end of file in macro definition");
if (label != NULL && label->len != 0)
{
sb_add_sb (&name, label);
if (idx < in->len && in->ptr[idx] == '(')
{
idx = do_formals (macro, idx + 1, in);
if (in->ptr[idx] != ')')
return _("missing ) after formals");
}
else
{
idx = do_formals (macro, idx, in);
}
}
else
{
idx = get_token (idx, in, &name);
idx = sb_skip_comma (idx, in);
idx = do_formals (macro, idx, in);
}
for (idx = 0; idx < name.len; idx++)
name.ptr[idx] = TOLOWER (name.ptr[idx]);
namestr = sb_terminate (&name);
hash_jam (macro_hash, namestr, (PTR) macro);
macro_defined = 1;
if (namep != NULL)
*namep = namestr;
return NULL;
}
static int
get_apost_token (int idx, sb *in, sb *name, int kind)
{
idx = get_token (idx, in, name);
if (idx < in->len
&& in->ptr[idx] == kind
&& (! macro_mri || macro_strip_at)
&& (! macro_strip_at || kind == '@'))
idx++;
return idx;
}
static int
sub_actual (int start, sb *in, sb *t, struct hash_control *formal_hash,
int kind, sb *out, int copyifnotthere)
{
int src;
formal_entry *ptr;
src = get_apost_token (start, in, t, kind);
if (macro_strip_at
&& kind == '@'
&& (src == start || in->ptr[src - 1] != '@'))
ptr = NULL;
else
ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (t));
if (ptr)
{
if (ptr->actual.len)
{
sb_add_sb (out, &ptr->actual);
}
else
{
sb_add_sb (out, &ptr->def);
}
}
else if (kind == '&')
{
sb_add_char (out, '&');
sb_add_sb (out, t);
}
else if (copyifnotthere)
{
sb_add_sb (out, t);
}
else
{
sb_add_char (out, '\\');
sb_add_sb (out, t);
}
return src;
}
static const char *
macro_expand_body (sb *in, sb *out, formal_entry *formals,
struct hash_control *formal_hash, int locals)
{
sb t;
int src = 0;
int inquote = 0;
formal_entry *loclist = NULL;
sb_new (&t);
while (src < in->len)
{
if (in->ptr[src] == '&')
{
sb_reset (&t);
if (macro_mri)
{
if (src + 1 < in->len && in->ptr[src + 1] == '&')
src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
else
sb_add_char (out, in->ptr[src++]);
}
else
{
src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
}
}
else if (in->ptr[src] == '\\')
{
src++;
if (in->ptr[src] == '(')
{
src++;
while (src < in->len && in->ptr[src] != ')')
{
sb_add_char (out, in->ptr[src++]);
}
if (in->ptr[src] == ')')
src++;
else
return _("missplaced )");
}
else if (in->ptr[src] == '@')
{
char buffer[10];
src++;
sprintf (buffer, "%d", macro_number);
sb_add_string (out, buffer);
}
else if (in->ptr[src] == '&')
{
sb_add_char (out, '\\');
sb_add_char (out, '&');
src++;
}
else if (macro_mri && ISALNUM (in->ptr[src]))
{
int ind;
formal_entry *f;
if (ISDIGIT (in->ptr[src]))
ind = in->ptr[src] - '0';
else if (ISUPPER (in->ptr[src]))
ind = in->ptr[src] - 'A' + 10;
else
ind = in->ptr[src] - 'a' + 10;
++src;
for (f = formals; f != NULL; f = f->next)
{
if (f->index == ind - 1)
{
if (f->actual.len != 0)
sb_add_sb (out, &f->actual);
else
sb_add_sb (out, &f->def);
break;
}
}
}
else
{
sb_reset (&t);
src = sub_actual (src, in, &t, formal_hash, '\'', out, 0);
}
}
else if ((macro_alternate || macro_mri)
&& (ISALPHA (in->ptr[src])
|| in->ptr[src] == '_'
|| in->ptr[src] == '$')
&& (! inquote
|| ! macro_strip_at
|| (src > 0 && in->ptr[src - 1] == '@')))
{
if (! locals
|| src + 5 >= in->len
|| strncasecmp (in->ptr + src, "LOCAL", 5) != 0
|| ! ISWHITE (in->ptr[src + 5]))
{
sb_reset (&t);
src = sub_actual (src, in, &t, formal_hash,
(macro_strip_at && inquote) ? '@' : '\'',
out, 1);
}
else
{
formal_entry *f;
src = sb_skip_white (src + 5, in);
while (in->ptr[src] != '\n')
{
static int loccnt;
char buf[20];
const char *err;
f = (formal_entry *) xmalloc (sizeof (formal_entry));
sb_new (&f->name);
sb_new (&f->def);
sb_new (&f->actual);
f->index = LOCAL_INDEX;
f->next = loclist;
loclist = f;
src = get_token (src, in, &f->name);
++loccnt;
sprintf (buf, "LL%04x", loccnt);
sb_add_string (&f->actual, buf);
err = hash_jam (formal_hash, sb_terminate (&f->name), f);
if (err != NULL)
return err;
src = sb_skip_comma (src, in);
}
}
}
else if (in->ptr[src] == '"'
|| (macro_mri && in->ptr[src] == '\''))
{
inquote = !inquote;
sb_add_char (out, in->ptr[src++]);
}
else if (in->ptr[src] == '@' && macro_strip_at)
{
++src;
if (src < in->len
&& in->ptr[src] == '@')
{
sb_add_char (out, '@');
++src;
}
}
else if (macro_mri
&& in->ptr[src] == '='
&& src + 1 < in->len
&& in->ptr[src + 1] == '=')
{
formal_entry *ptr;
sb_reset (&t);
src = get_token (src + 2, in, &t);
ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (&t));
if (ptr == NULL)
{
sb_add_char (out, '=');
sb_add_char (out, '=');
sb_add_sb (out, &t);
}
else
{
if (ptr->actual.len)
{
sb_add_string (out, "-1");
}
else
{
sb_add_char (out, '0');
}
}
}
else
{
sb_add_char (out, in->ptr[src++]);
}
}
sb_kill (&t);
while (loclist != NULL)
{
formal_entry *f;
f = loclist->next;
hash_jam (formal_hash, sb_terminate (&loclist->name), NULL);
sb_kill (&loclist->name);
sb_kill (&loclist->def);
sb_kill (&loclist->actual);
free (loclist);
loclist = f;
}
return NULL;
}
static const char *
macro_expand (int idx, sb *in, macro_entry *m, sb *out)
{
sb t;
formal_entry *ptr;
formal_entry *f;
int is_positional = 0;
int is_keyword = 0;
int narg = 0;
const char *err;
sb_new (&t);
for (f = m->formals; f; f = f->next)
sb_reset (&f->actual);
f = m->formals;
while (f != NULL && f->index < 0)
f = f->next;
if (macro_mri)
{
if (idx < in->len && in->ptr[idx] == '.')
{
idx++;
if ( idx < in->len
&& in->ptr[idx] != ' '
&& in->ptr[idx] != '\t')
{
formal_entry *n;
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 = sb_skip_white (idx, in);
while (idx < in->len)
{
int scan;
scan = idx;
while (scan < in->len
&& !ISSEP (in->ptr[scan])
&& !(macro_mri && in->ptr[scan] == '\'')
&& (!macro_alternate && in->ptr[scan] != '='))
scan++;
if (scan < in->len && !macro_alternate && in->ptr[scan] == '=')
{
is_keyword = 1;
sb_reset (&t);
idx = get_token (idx, in, &t);
if (in->ptr[idx] != '=')
return _("confusion in formal parameters");
ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
if (!ptr)
return _("macro formal argument does not exist");
else
{
sb_reset (&ptr->actual);
idx = get_any_string (idx + 1, in, &ptr->actual, 0, 0);
if (ptr->actual.len > 0)
++narg;
}
}
else
{
is_positional = 1;
if (is_keyword)
return _("can't mix positional and keyword arguments");
if (!f)
{
formal_entry **pf;
int c;
if (!macro_mri)
return _("too many positional arguments");
f = (formal_entry *) xmalloc (sizeof (formal_entry));
sb_new (&f->name);
sb_new (&f->def);
sb_new (&f->actual);
f->next = NULL;
c = -1;
for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next)
if ((*pf)->index >= c)
c = (*pf)->index + 1;
if (c == -1)
c = 0;
*pf = f;
f->index = c;
}
sb_reset (&f->actual);
idx = get_any_string (idx, in, &f->actual, 1, 0);
if (f->actual.len > 0)
++narg;
do
{
f = f->next;
}
while (f != NULL && f->index < 0);
}
if (! macro_mri)
idx = sb_skip_comma (idx, in);
else
{
if (in->ptr[idx] == ',')
++idx;
if (ISWHITE (in->ptr[idx]))
break;
}
}
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));
sb_reset (&ptr->actual);
sprintf (buffer, "%d", narg);
sb_add_string (&ptr->actual, buffer);
}
err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, 1);
if (err != NULL)
return err;
if (macro_mri)
{
formal_entry **pf;
pf = &m->formals;
while (*pf != NULL)
{
if ((*pf)->name.len != 0)
pf = &(*pf)->next;
else
{
sb_kill (&(*pf)->name);
sb_kill (&(*pf)->def);
sb_kill (&(*pf)->actual);
f = (*pf)->next;
free (*pf);
*pf = f;
}
}
}
sb_kill (&t);
macro_number++;
return NULL;
}
int
check_macro (const char *line, sb *expand,
const char **error, macro_entry **info)
{
const char *s;
char *copy, *cs;
macro_entry *macro;
sb line_sb;
if (! ISALPHA (*line)
&& *line != '_'
&& *line != '$'
&& (! macro_mri || *line != '.'))
return 0;
s = line + 1;
while (ISALNUM (*s)
|| *s == '_'
|| *s == '$')
++s;
copy = (char *) alloca (s - line + 1);
memcpy (copy, line, s - line);
copy[s - line] = '\0';
for (cs = copy; *cs != '\0'; cs++)
*cs = TOLOWER (*cs);
macro = (macro_entry *) hash_find (macro_hash, copy);
if (macro == NULL)
return 0;
sb_new (&line_sb);
while (*s != '\0' && *s != '\n' && *s != '\r')
sb_add_char (&line_sb, *s++);
sb_new (expand);
*error = macro_expand (0, &line_sb, macro, expand);
sb_kill (&line_sb);
if (info)
*info = macro;
return 1;
}
void
delete_macro (const char *name)
{
hash_delete (macro_hash, name);
}
const char *
expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
{
const char *mn;
sb sub;
formal_entry f;
struct hash_control *h;
const char *err;
if (irpc)
mn = "IRPC";
else
mn = "IRP";
idx = sb_skip_white (idx, in);
sb_new (&sub);
if (! buffer_and_nest (mn, "ENDR", &sub, get_line))
return _("unexpected end of file in irp or irpc");
sb_new (&f.name);
sb_new (&f.def);
sb_new (&f.actual);
idx = get_token (idx, in, &f.name);
if (f.name.len == 0)
return _("missing model parameter");
h = hash_new ();
err = hash_jam (h, sb_terminate (&f.name), &f);
if (err != NULL)
return err;
f.index = 1;
f.next = NULL;
sb_reset (out);
idx = sb_skip_comma (idx, in);
if (idx >= in->len)
{
err = macro_expand_body (&sub, out, &f, h, 0);
if (err != NULL)
return err;
}
else
{
if (irpc && in->ptr[idx] == '"')
++idx;
while (idx < in->len)
{
if (!irpc)
idx = get_any_string (idx, in, &f.actual, 1, 0);
else
{
if (in->ptr[idx] == '"')
{
int nxt;
nxt = sb_skip_white (idx + 1, in);
if (nxt >= in->len)
{
idx = nxt;
break;
}
}
sb_reset (&f.actual);
sb_add_char (&f.actual, in->ptr[idx]);
++idx;
}
err = macro_expand_body (&sub, out, &f, h, 0);
if (err != NULL)
return err;
if (!irpc)
idx = sb_skip_comma (idx, in);
else
idx = sb_skip_white (idx, in);
}
}
hash_die (h);
sb_kill (&sub);
return NULL;
}