macro.c revision 38889
133965Sjdp/* macro.c - macro support for gas and gasp
238889Sjdp   Copyright (C) 1994, 95, 96, 97, 1998 Free Software Foundation, Inc.
333965Sjdp
433965Sjdp   Written by Steve and Judy Chamberlain of Cygnus Support,
533965Sjdp      sac@cygnus.com
633965Sjdp
733965Sjdp   This file is part of GAS, the GNU Assembler.
833965Sjdp
933965Sjdp   GAS is free software; you can redistribute it and/or modify
1033965Sjdp   it under the terms of the GNU General Public License as published by
1133965Sjdp   the Free Software Foundation; either version 2, or (at your option)
1233965Sjdp   any later version.
1333965Sjdp
1433965Sjdp   GAS is distributed in the hope that it will be useful,
1533965Sjdp   but WITHOUT ANY WARRANTY; without even the implied warranty of
1633965Sjdp   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1733965Sjdp   GNU General Public License for more details.
1833965Sjdp
1933965Sjdp   You should have received a copy of the GNU General Public License
2033965Sjdp   along with GAS; see the file COPYING.  If not, write to the Free
2133965Sjdp   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
2233965Sjdp   02111-1307, USA. */
2333965Sjdp
2433965Sjdp#include "config.h"
2538889Sjdp
2638889Sjdp/* AIX requires this to be the first thing in the file.  */
2738889Sjdp#ifdef __GNUC__
2838889Sjdp# ifndef alloca
2938889Sjdp#  ifdef __STDC__
3038889Sjdpextern void *alloca ();
3138889Sjdp#  else
3238889Sjdpextern char *alloca ();
3338889Sjdp#  endif
3438889Sjdp# endif
3538889Sjdp#else
3638889Sjdp# if HAVE_ALLOCA_H
3738889Sjdp#  include <alloca.h>
3838889Sjdp# else
3938889Sjdp#  ifdef _AIX
4038889Sjdp #pragma alloca
4138889Sjdp#  else
4238889Sjdp#   ifndef alloca /* predefined by HP cc +Olibcalls */
4338889Sjdp#    if !defined (__STDC__) && !defined (__hpux)
4438889Sjdpextern char *alloca ();
4538889Sjdp#    else
4638889Sjdpextern void *alloca ();
4738889Sjdp#    endif /* __STDC__, __hpux */
4838889Sjdp#   endif /* alloca */
4938889Sjdp#  endif /* _AIX */
5038889Sjdp# endif /* HAVE_ALLOCA_H */
5138889Sjdp#endif
5238889Sjdp
5333965Sjdp#include <stdio.h>
5433965Sjdp#ifdef HAVE_STRING_H
5533965Sjdp#include <string.h>
5633965Sjdp#else
5733965Sjdp#include <strings.h>
5833965Sjdp#endif
5933965Sjdp#include <ctype.h>
6033965Sjdp#ifdef HAVE_STDLIB_H
6133965Sjdp#include <stdlib.h>
6233965Sjdp#endif
6333965Sjdp#include "libiberty.h"
6433965Sjdp#include "sb.h"
6533965Sjdp#include "hash.h"
6633965Sjdp#include "macro.h"
6733965Sjdp
6833965Sjdp/* The routines in this file handle macro definition and expansion.
6933965Sjdp   They are called by both gasp and gas.  */
7033965Sjdp
7133965Sjdp/* Structures used to store macros.
7233965Sjdp
7333965Sjdp   Each macro knows its name and included text.  It gets built with a
7433965Sjdp   list of formal arguments, and also keeps a hash table which points
7533965Sjdp   into the list to speed up formal search.  Each formal knows its
7633965Sjdp   name and its default value.  Each time the macro is expanded, the
7733965Sjdp   formals get the actual values attatched to them. */
7833965Sjdp
7933965Sjdp/* describe the formal arguments to a macro */
8033965Sjdp
8133965Sjdptypedef struct formal_struct
8233965Sjdp  {
8333965Sjdp    struct formal_struct *next;	/* next formal in list */
8433965Sjdp    sb name;			/* name of the formal */
8533965Sjdp    sb def;			/* the default value */
8633965Sjdp    sb actual;			/* the actual argument (changed on each expansion) */
8733965Sjdp    int index;			/* the index of the formal 0..formal_count-1 */
8833965Sjdp  }
8933965Sjdpformal_entry;
9033965Sjdp
9133965Sjdp/* Other values found in the index field of a formal_entry.  */
9233965Sjdp#define QUAL_INDEX (-1)
9333965Sjdp#define NARG_INDEX (-2)
9433965Sjdp#define LOCAL_INDEX (-3)
9533965Sjdp
9633965Sjdp/* describe the macro. */
9733965Sjdp
9833965Sjdptypedef struct macro_struct
9933965Sjdp  {
10033965Sjdp    sb sub;			/* substitution text. */
10133965Sjdp    int formal_count;		/* number of formal args. */
10233965Sjdp    formal_entry *formals;	/* pointer to list of formal_structs */
10333965Sjdp    struct hash_control *formal_hash; /* hash table of formals. */
10433965Sjdp  }
10533965Sjdpmacro_entry;
10633965Sjdp
10733965Sjdp/* Internal functions.  */
10833965Sjdp
10933965Sjdpstatic int get_token PARAMS ((int, sb *, sb *));
11033965Sjdpstatic int getstring PARAMS ((int, sb *, sb *));
11133965Sjdpstatic int get_any_string PARAMS ((int, sb *, sb *, int, int));
11233965Sjdpstatic int do_formals PARAMS ((macro_entry *, int, sb *));
11333965Sjdpstatic int get_apost_token PARAMS ((int, sb *, sb *, int));
11433965Sjdpstatic int sub_actual
11533965Sjdp  PARAMS ((int, sb *, sb *, struct hash_control *, int, sb *, int));
11633965Sjdpstatic const char *macro_expand_body
11733965Sjdp  PARAMS ((sb *, sb *, formal_entry *, struct hash_control *, int, int));
11833965Sjdpstatic const char *macro_expand PARAMS ((int, sb *, macro_entry *, sb *, int));
11933965Sjdp
12033965Sjdp#define ISWHITE(x) ((x) == ' ' || (x) == '\t')
12133965Sjdp
12233965Sjdp#define ISSEP(x) \
12333965Sjdp ((x) == ' ' || (x) == '\t' || (x) == ',' || (x) == '"' || (x) == ';' \
12438889Sjdp  || (x) == ')' || (x) == '(' \
12538889Sjdp  || ((macro_alternate || macro_mri) && ((x) == '<' || (x) == '>')))
12633965Sjdp
12733965Sjdp#define ISBASE(x) \
12833965Sjdp  ((x) == 'b' || (x) == 'B' \
12933965Sjdp   || (x) == 'q' || (x) == 'Q' \
13033965Sjdp   || (x) == 'h' || (x) == 'H' \
13133965Sjdp   || (x) == 'd' || (x) == 'D')
13233965Sjdp
13333965Sjdp/* The macro hash table.  */
13433965Sjdp
13533965Sjdpstatic struct hash_control *macro_hash;
13633965Sjdp
13733965Sjdp/* Whether any macros have been defined.  */
13833965Sjdp
13933965Sjdpint macro_defined;
14033965Sjdp
14133965Sjdp/* Whether we are in GASP alternate mode.  */
14233965Sjdp
14333965Sjdpstatic int macro_alternate;
14433965Sjdp
14533965Sjdp/* Whether we are in MRI mode.  */
14633965Sjdp
14733965Sjdpstatic int macro_mri;
14833965Sjdp
14933965Sjdp/* Whether we should strip '@' characters.  */
15033965Sjdp
15133965Sjdpstatic int macro_strip_at;
15233965Sjdp
15333965Sjdp/* Function to use to parse an expression.  */
15433965Sjdp
15533965Sjdpstatic int (*macro_expr) PARAMS ((const char *, int, sb *, int *));
15633965Sjdp
15733965Sjdp/* Number of macro expansions that have been done.  */
15833965Sjdp
15933965Sjdpstatic int macro_number;
16033965Sjdp
16133965Sjdp/* Initialize macro processing.  */
16233965Sjdp
16333965Sjdpvoid
16433965Sjdpmacro_init (alternate, mri, strip_at, expr)
16533965Sjdp     int alternate;
16633965Sjdp     int mri;
16733965Sjdp     int strip_at;
16833965Sjdp     int (*expr) PARAMS ((const char *, int, sb *, int *));
16933965Sjdp{
17033965Sjdp  macro_hash = hash_new ();
17133965Sjdp  macro_defined = 0;
17233965Sjdp  macro_alternate = alternate;
17333965Sjdp  macro_mri = mri;
17433965Sjdp  macro_strip_at = strip_at;
17533965Sjdp  macro_expr = expr;
17633965Sjdp}
17733965Sjdp
17833965Sjdp/* Read input lines till we get to a TO string.
17933965Sjdp   Increase nesting depth if we get a FROM string.
18033965Sjdp   Put the results into sb at PTR.
18133965Sjdp   Add a new input line to an sb using GET_LINE.
18233965Sjdp   Return 1 on success, 0 on unexpected EOF.  */
18333965Sjdp
18433965Sjdpint
18533965Sjdpbuffer_and_nest (from, to, ptr, get_line)
18633965Sjdp     const char *from;
18733965Sjdp     const char *to;
18833965Sjdp     sb *ptr;
18933965Sjdp     int (*get_line) PARAMS ((sb *));
19033965Sjdp{
19133965Sjdp  int from_len = strlen (from);
19233965Sjdp  int to_len = strlen (to);
19333965Sjdp  int depth = 1;
19433965Sjdp  int line_start = ptr->len;
19533965Sjdp
19633965Sjdp  int more = get_line (ptr);
19733965Sjdp
19833965Sjdp  while (more)
19933965Sjdp    {
20033965Sjdp      /* Try and find the first pseudo op on the line */
20133965Sjdp      int i = line_start;
20233965Sjdp
20333965Sjdp      if (! macro_alternate && ! macro_mri)
20433965Sjdp	{
20533965Sjdp	  /* With normal syntax we can suck what we want till we get
20633965Sjdp	     to the dot.  With the alternate, labels have to start in
20733965Sjdp	     the first column, since we cant tell what's a label and
20833965Sjdp	     whats a pseudoop */
20933965Sjdp
21033965Sjdp	  /* Skip leading whitespace */
21133965Sjdp	  while (i < ptr->len && ISWHITE (ptr->ptr[i]))
21233965Sjdp	    i++;
21333965Sjdp
21433965Sjdp	  /* Skip over a label */
21533965Sjdp	  while (i < ptr->len
21633965Sjdp		 && (isalnum ((unsigned char) ptr->ptr[i])
21733965Sjdp		     || ptr->ptr[i] == '_'
21833965Sjdp		     || ptr->ptr[i] == '$'))
21933965Sjdp	    i++;
22033965Sjdp
22133965Sjdp	  /* And a colon */
22233965Sjdp	  if (i < ptr->len
22333965Sjdp	      && ptr->ptr[i] == ':')
22433965Sjdp	    i++;
22533965Sjdp
22633965Sjdp	}
22733965Sjdp      /* Skip trailing whitespace */
22833965Sjdp      while (i < ptr->len && ISWHITE (ptr->ptr[i]))
22933965Sjdp	i++;
23033965Sjdp
23133965Sjdp      if (i < ptr->len && (ptr->ptr[i] == '.'
23233965Sjdp			   || macro_alternate
23333965Sjdp			   || macro_mri))
23433965Sjdp	{
23533965Sjdp	  if (ptr->ptr[i] == '.')
23633965Sjdp	      i++;
23733965Sjdp	  if (strncasecmp (ptr->ptr + i, from, from_len) == 0)
23833965Sjdp	    depth++;
23933965Sjdp	  if (strncasecmp (ptr->ptr + i, to, to_len) == 0)
24033965Sjdp	    {
24133965Sjdp	      depth--;
24233965Sjdp	      if (depth == 0)
24333965Sjdp		{
24433965Sjdp		  /* Reset the string to not include the ending rune */
24533965Sjdp		  ptr->len = line_start;
24633965Sjdp		  break;
24733965Sjdp		}
24833965Sjdp	    }
24933965Sjdp	}
25033965Sjdp
25133965Sjdp      /* Add a CR to the end and keep running */
25233965Sjdp      sb_add_char (ptr, '\n');
25333965Sjdp      line_start = ptr->len;
25433965Sjdp      more = get_line (ptr);
25533965Sjdp    }
25633965Sjdp
25733965Sjdp  /* Return 1 on success, 0 on unexpected EOF.  */
25833965Sjdp  return depth == 0;
25933965Sjdp}
26033965Sjdp
26133965Sjdp/* Pick up a token.  */
26233965Sjdp
26333965Sjdpstatic int
26433965Sjdpget_token (idx, in, name)
26533965Sjdp     int idx;
26633965Sjdp     sb *in;
26733965Sjdp     sb *name;
26833965Sjdp{
26933965Sjdp  if (idx < in->len
27033965Sjdp      && (isalpha ((unsigned char) in->ptr[idx])
27133965Sjdp	  || in->ptr[idx] == '_'
27233965Sjdp	  || in->ptr[idx] == '$'))
27333965Sjdp    {
27433965Sjdp      sb_add_char (name, in->ptr[idx++]);
27533965Sjdp      while (idx < in->len
27633965Sjdp	     && (isalnum ((unsigned char) in->ptr[idx])
27733965Sjdp		 || in->ptr[idx] == '_'
27833965Sjdp		 || in->ptr[idx] == '$'))
27933965Sjdp	{
28033965Sjdp	  sb_add_char (name, in->ptr[idx++]);
28133965Sjdp	}
28233965Sjdp    }
28333965Sjdp  /* Ignore trailing & */
28433965Sjdp  if (macro_alternate && idx < in->len && in->ptr[idx] == '&')
28533965Sjdp    idx++;
28633965Sjdp  return idx;
28733965Sjdp}
28833965Sjdp
28933965Sjdp/* Pick up a string.  */
29033965Sjdp
29133965Sjdpstatic int
29233965Sjdpgetstring (idx, in, acc)
29333965Sjdp     int idx;
29433965Sjdp     sb *in;
29533965Sjdp     sb *acc;
29633965Sjdp{
29733965Sjdp  idx = sb_skip_white (idx, in);
29833965Sjdp
29933965Sjdp  while (idx < in->len
30033965Sjdp	 && (in->ptr[idx] == '"'
30138889Sjdp	     || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
30233965Sjdp	     || (in->ptr[idx] == '\'' && macro_alternate)))
30333965Sjdp    {
30433965Sjdp      if (in->ptr[idx] == '<')
30533965Sjdp	{
30638889Sjdp	  int nest = 0;
30738889Sjdp	  idx++;
30838889Sjdp	  while ((in->ptr[idx] != '>' || nest)
30938889Sjdp		 && idx < in->len)
31033965Sjdp	    {
31138889Sjdp	      if (in->ptr[idx] == '!')
31233965Sjdp		{
31338889Sjdp		  idx++  ;
31438889Sjdp		  sb_add_char (acc, in->ptr[idx++]);
31533965Sjdp		}
31638889Sjdp	      else
31738889Sjdp		{
31838889Sjdp		  if (in->ptr[idx] == '>')
31938889Sjdp		    nest--;
32038889Sjdp		  if (in->ptr[idx] == '<')
32138889Sjdp		    nest++;
32238889Sjdp		  sb_add_char (acc, in->ptr[idx++]);
32338889Sjdp		}
32433965Sjdp	    }
32538889Sjdp	  idx++;
32633965Sjdp	}
32733965Sjdp      else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'')
32833965Sjdp	{
32933965Sjdp	  char tchar = in->ptr[idx];
33033965Sjdp	  idx++;
33133965Sjdp	  while (idx < in->len)
33233965Sjdp	    {
33333965Sjdp	      if (macro_alternate && in->ptr[idx] == '!')
33433965Sjdp		{
33533965Sjdp		  idx++  ;
33633965Sjdp		  sb_add_char (acc, in->ptr[idx++]);
33733965Sjdp		}
33833965Sjdp	      else
33933965Sjdp		{
34033965Sjdp		  if (in->ptr[idx] == tchar)
34133965Sjdp		    {
34233965Sjdp		      idx++;
34333965Sjdp		      if (idx >= in->len || in->ptr[idx] != tchar)
34433965Sjdp			break;
34533965Sjdp		    }
34633965Sjdp		  sb_add_char (acc, in->ptr[idx]);
34733965Sjdp		  idx++;
34833965Sjdp		}
34933965Sjdp	    }
35033965Sjdp	}
35133965Sjdp    }
35233965Sjdp
35333965Sjdp  return idx;
35433965Sjdp}
35533965Sjdp
35633965Sjdp/* Fetch string from the input stream,
35733965Sjdp   rules:
35833965Sjdp    'Bxyx<whitespace>  	-> return 'Bxyza
35933965Sjdp    %<char>		-> return string of decimal value of x
36033965Sjdp    "<string>"		-> return string
36133965Sjdp    xyx<whitespace>     -> return xyz
36233965Sjdp*/
36333965Sjdp
36433965Sjdpstatic int
36533965Sjdpget_any_string (idx, in, out, expand, pretend_quoted)
36633965Sjdp     int idx;
36733965Sjdp     sb *in;
36833965Sjdp     sb *out;
36933965Sjdp     int expand;
37033965Sjdp     int pretend_quoted;
37133965Sjdp{
37233965Sjdp  sb_reset (out);
37333965Sjdp  idx = sb_skip_white (idx, in);
37433965Sjdp
37533965Sjdp  if (idx < in->len)
37633965Sjdp    {
37733965Sjdp      if (in->len > 2 && in->ptr[idx+1] == '\'' && ISBASE (in->ptr[idx]))
37833965Sjdp	{
37933965Sjdp	  while (!ISSEP (in->ptr[idx]))
38033965Sjdp	    sb_add_char (out, in->ptr[idx++]);
38133965Sjdp	}
38233965Sjdp      else if (in->ptr[idx] == '%'
38333965Sjdp	       && macro_alternate
38433965Sjdp	       && expand)
38533965Sjdp	{
38633965Sjdp	  int val;
38733965Sjdp	  char buf[20];
38833965Sjdp	  /* Turns the next expression into a string */
38933965Sjdp	  idx = (*macro_expr) ("% operator needs absolute expression",
39033965Sjdp			       idx + 1,
39133965Sjdp			       in,
39233965Sjdp			       &val);
39333965Sjdp	  sprintf(buf, "%d", val);
39433965Sjdp	  sb_add_string (out, buf);
39533965Sjdp	}
39633965Sjdp      else if (in->ptr[idx] == '"'
39738889Sjdp	       || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
39833965Sjdp	       || (macro_alternate && in->ptr[idx] == '\''))
39933965Sjdp	{
40033965Sjdp	  if (macro_alternate
40133965Sjdp	      && ! macro_strip_at
40233965Sjdp	      && expand)
40333965Sjdp	    {
40433965Sjdp	      /* Keep the quotes */
40533965Sjdp	      sb_add_char (out,  '\"');
40633965Sjdp
40733965Sjdp	      idx = getstring (idx, in, out);
40833965Sjdp	      sb_add_char (out,  '\"');
40933965Sjdp	    }
41033965Sjdp	  else
41133965Sjdp	    {
41233965Sjdp	      idx = getstring (idx, in, out);
41333965Sjdp	    }
41433965Sjdp	}
41533965Sjdp      else
41633965Sjdp	{
41733965Sjdp	  while (idx < in->len
41833965Sjdp		 && (in->ptr[idx] == '"'
41933965Sjdp		     || in->ptr[idx] == '\''
42033965Sjdp		     || pretend_quoted
42133965Sjdp		     || (in->ptr[idx] != ' '
42233965Sjdp			 && in->ptr[idx] != '\t'
42333965Sjdp			 && in->ptr[idx] != ','
42438889Sjdp			 && (in->ptr[idx] != '<'
42538889Sjdp			     || (! macro_alternate && ! macro_mri)))))
42633965Sjdp	    {
42733965Sjdp	      if (in->ptr[idx] == '"'
42833965Sjdp		  || in->ptr[idx] == '\'')
42933965Sjdp		{
43033965Sjdp		  char tchar = in->ptr[idx];
43133965Sjdp		  sb_add_char (out, in->ptr[idx++]);
43233965Sjdp		  while (idx < in->len
43333965Sjdp			 && in->ptr[idx] != tchar)
43433965Sjdp		    sb_add_char (out, in->ptr[idx++]);
43533965Sjdp		  if (idx == in->len)
43633965Sjdp		    return idx;
43733965Sjdp		}
43833965Sjdp	      sb_add_char (out, in->ptr[idx++]);
43933965Sjdp	    }
44033965Sjdp	}
44133965Sjdp    }
44233965Sjdp
44333965Sjdp  return idx;
44433965Sjdp}
44533965Sjdp
44633965Sjdp/* Pick up the formal parameters of a macro definition.  */
44733965Sjdp
44833965Sjdpstatic int
44933965Sjdpdo_formals (macro, idx, in)
45033965Sjdp     macro_entry *macro;
45133965Sjdp     int idx;
45233965Sjdp     sb *in;
45333965Sjdp{
45433965Sjdp  formal_entry **p = &macro->formals;
45533965Sjdp
45633965Sjdp  macro->formal_count = 0;
45733965Sjdp  macro->formal_hash = hash_new ();
45833965Sjdp  while (idx < in->len)
45933965Sjdp    {
46033965Sjdp      formal_entry *formal;
46133965Sjdp
46233965Sjdp      formal = (formal_entry *) xmalloc (sizeof (formal_entry));
46333965Sjdp
46433965Sjdp      sb_new (&formal->name);
46533965Sjdp      sb_new (&formal->def);
46633965Sjdp      sb_new (&formal->actual);
46733965Sjdp
46833965Sjdp      idx = sb_skip_white (idx, in);
46933965Sjdp      idx = get_token (idx, in, &formal->name);
47033965Sjdp      if (formal->name.len == 0)
47133965Sjdp	break;
47233965Sjdp      idx = sb_skip_white (idx, in);
47333965Sjdp      if (formal->name.len)
47433965Sjdp	{
47533965Sjdp	  /* This is a formal */
47633965Sjdp	  if (idx < in->len && in->ptr[idx] == '=')
47733965Sjdp	    {
47833965Sjdp	      /* Got a default */
47933965Sjdp	      idx = get_any_string (idx + 1, in, &formal->def, 1, 0);
48033965Sjdp	    }
48133965Sjdp	}
48233965Sjdp
48333965Sjdp      /* Add to macro's hash table */
48433965Sjdp      hash_jam (macro->formal_hash, sb_terminate (&formal->name), formal);
48533965Sjdp
48633965Sjdp      formal->index = macro->formal_count;
48733965Sjdp      idx = sb_skip_comma (idx, in);
48833965Sjdp      macro->formal_count++;
48933965Sjdp      *p = formal;
49033965Sjdp      p = &formal->next;
49133965Sjdp      *p = NULL;
49233965Sjdp    }
49333965Sjdp
49433965Sjdp  if (macro_mri)
49533965Sjdp    {
49633965Sjdp      formal_entry *formal;
49733965Sjdp      const char *name;
49833965Sjdp
49933965Sjdp      /* Add a special NARG formal, which macro_expand will set to the
50033965Sjdp         number of arguments.  */
50133965Sjdp      formal = (formal_entry *) xmalloc (sizeof (formal_entry));
50233965Sjdp
50333965Sjdp      sb_new (&formal->name);
50433965Sjdp      sb_new (&formal->def);
50533965Sjdp      sb_new (&formal->actual);
50633965Sjdp
50733965Sjdp      /* The same MRI assemblers which treat '@' characters also use
50833965Sjdp         the name $NARG.  At least until we find an exception.  */
50933965Sjdp      if (macro_strip_at)
51033965Sjdp	name = "$NARG";
51133965Sjdp      else
51233965Sjdp	name = "NARG";
51333965Sjdp
51433965Sjdp      sb_add_string (&formal->name, name);
51533965Sjdp
51633965Sjdp      /* Add to macro's hash table */
51733965Sjdp      hash_jam (macro->formal_hash, name, formal);
51833965Sjdp
51933965Sjdp      formal->index = NARG_INDEX;
52033965Sjdp      *p = formal;
52133965Sjdp      formal->next = NULL;
52233965Sjdp    }
52333965Sjdp
52433965Sjdp  return idx;
52533965Sjdp}
52633965Sjdp
52733965Sjdp/* Define a new macro.  Returns NULL on success, otherwise returns an
52833965Sjdp   error message.  If NAMEP is not NULL, *NAMEP is set to the name of
52933965Sjdp   the macro which was defined.  */
53033965Sjdp
53133965Sjdpconst char *
53233965Sjdpdefine_macro (idx, in, label, get_line, namep)
53333965Sjdp     int idx;
53433965Sjdp     sb *in;
53533965Sjdp     sb *label;
53633965Sjdp     int (*get_line) PARAMS ((sb *));
53733965Sjdp     const char **namep;
53833965Sjdp{
53933965Sjdp  macro_entry *macro;
54033965Sjdp  sb name;
54133965Sjdp  const char *namestr;
54233965Sjdp
54333965Sjdp  macro = (macro_entry *) xmalloc (sizeof (macro_entry));
54433965Sjdp  sb_new (&macro->sub);
54533965Sjdp  sb_new (&name);
54633965Sjdp
54733965Sjdp  macro->formal_count = 0;
54833965Sjdp  macro->formals = 0;
54933965Sjdp
55033965Sjdp  idx = sb_skip_white (idx, in);
55133965Sjdp  if (! buffer_and_nest ("MACRO", "ENDM", &macro->sub, get_line))
55233965Sjdp    return "unexpected end of file in macro definition";
55333965Sjdp  if (label != NULL && label->len != 0)
55433965Sjdp    {
55533965Sjdp      sb_add_sb (&name, label);
55638889Sjdp      if (idx < in->len && in->ptr[idx] == '(')
55733965Sjdp	{
55833965Sjdp	  /* It's the label: MACRO (formals,...)  sort */
55933965Sjdp	  idx = do_formals (macro, idx + 1, in);
56033965Sjdp	  if (in->ptr[idx] != ')')
56133965Sjdp	    return "missing ) after formals";
56233965Sjdp	}
56333965Sjdp      else
56433965Sjdp	{
56533965Sjdp	  /* It's the label: MACRO formals,...  sort */
56633965Sjdp	  idx = do_formals (macro, idx, in);
56733965Sjdp	}
56833965Sjdp    }
56933965Sjdp  else
57033965Sjdp    {
57133965Sjdp      idx = get_token (idx, in, &name);
57233965Sjdp      idx = sb_skip_comma (idx, in);
57333965Sjdp      idx = do_formals (macro, idx, in);
57433965Sjdp    }
57533965Sjdp
57633965Sjdp  /* and stick it in the macro hash table */
57733965Sjdp  for (idx = 0; idx < name.len; idx++)
57838889Sjdp    if (isupper ((unsigned char) name.ptr[idx]))
57933965Sjdp      name.ptr[idx] = tolower (name.ptr[idx]);
58033965Sjdp  namestr = sb_terminate (&name);
58133965Sjdp  hash_jam (macro_hash, namestr, (PTR) macro);
58233965Sjdp
58333965Sjdp  macro_defined = 1;
58433965Sjdp
58533965Sjdp  if (namep != NULL)
58633965Sjdp    *namep = namestr;
58733965Sjdp
58833965Sjdp  return NULL;
58933965Sjdp}
59033965Sjdp
59133965Sjdp/* Scan a token, and then skip KIND.  */
59233965Sjdp
59333965Sjdpstatic int
59433965Sjdpget_apost_token (idx, in, name, kind)
59533965Sjdp     int idx;
59633965Sjdp     sb *in;
59733965Sjdp     sb *name;
59833965Sjdp     int kind;
59933965Sjdp{
60033965Sjdp  idx = get_token (idx, in, name);
60133965Sjdp  if (idx < in->len
60233965Sjdp      && in->ptr[idx] == kind
60333965Sjdp      && (! macro_mri || macro_strip_at)
60433965Sjdp      && (! macro_strip_at || kind == '@'))
60533965Sjdp    idx++;
60633965Sjdp  return idx;
60733965Sjdp}
60833965Sjdp
60933965Sjdp/* Substitute the actual value for a formal parameter.  */
61033965Sjdp
61133965Sjdpstatic int
61233965Sjdpsub_actual (start, in, t, formal_hash, kind, out, copyifnotthere)
61333965Sjdp     int start;
61433965Sjdp     sb *in;
61533965Sjdp     sb *t;
61633965Sjdp     struct hash_control *formal_hash;
61733965Sjdp     int kind;
61833965Sjdp     sb *out;
61933965Sjdp     int copyifnotthere;
62033965Sjdp{
62133965Sjdp  int src;
62233965Sjdp  formal_entry *ptr;
62333965Sjdp
62433965Sjdp  src = get_apost_token (start, in, t, kind);
62533965Sjdp  /* See if it's in the macro's hash table, unless this is
62633965Sjdp     macro_strip_at and kind is '@' and the token did not end in '@'.  */
62733965Sjdp  if (macro_strip_at
62833965Sjdp      && kind == '@'
62933965Sjdp      && (src == start || in->ptr[src - 1] != '@'))
63033965Sjdp    ptr = NULL;
63133965Sjdp  else
63233965Sjdp    ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (t));
63333965Sjdp  if (ptr)
63433965Sjdp    {
63533965Sjdp      if (ptr->actual.len)
63633965Sjdp	{
63733965Sjdp	  sb_add_sb (out, &ptr->actual);
63833965Sjdp	}
63933965Sjdp      else
64033965Sjdp	{
64133965Sjdp	  sb_add_sb (out, &ptr->def);
64233965Sjdp	}
64333965Sjdp    }
64438889Sjdp  else if (kind == '&')
64538889Sjdp    {
64638889Sjdp      /* Doing this permits people to use & in macro bodies.  */
64738889Sjdp      sb_add_char (out, '&');
64838889Sjdp    }
64933965Sjdp  else if (copyifnotthere)
65033965Sjdp    {
65133965Sjdp      sb_add_sb (out, t);
65233965Sjdp    }
65333965Sjdp  else
65433965Sjdp    {
65533965Sjdp      sb_add_char (out, '\\');
65633965Sjdp      sb_add_sb (out, t);
65733965Sjdp    }
65833965Sjdp  return src;
65933965Sjdp}
66033965Sjdp
66133965Sjdp/* Expand the body of a macro.  */
66233965Sjdp
66333965Sjdpstatic const char *
66433965Sjdpmacro_expand_body (in, out, formals, formal_hash, comment_char, locals)
66533965Sjdp     sb *in;
66633965Sjdp     sb *out;
66733965Sjdp     formal_entry *formals;
66833965Sjdp     struct hash_control *formal_hash;
66933965Sjdp     int comment_char;
67033965Sjdp     int locals;
67133965Sjdp{
67233965Sjdp  sb t;
67333965Sjdp  int src = 0;
67433965Sjdp  int inquote = 0;
67533965Sjdp  formal_entry *loclist = NULL;
67633965Sjdp
67733965Sjdp  sb_new (&t);
67833965Sjdp
67933965Sjdp  while (src < in->len)
68033965Sjdp    {
68133965Sjdp      if (in->ptr[src] == '&')
68233965Sjdp	{
68333965Sjdp	  sb_reset (&t);
68433965Sjdp	  if (macro_mri)
68533965Sjdp	    {
68633965Sjdp	      if (src + 1 < in->len && in->ptr[src + 1] == '&')
68733965Sjdp		src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
68833965Sjdp	      else
68933965Sjdp		sb_add_char (out, in->ptr[src++]);
69033965Sjdp	    }
69133965Sjdp	  else
69233965Sjdp	    {
69338889Sjdp	      /* FIXME: Why do we do this?  */
69433965Sjdp	      src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
69533965Sjdp	    }
69633965Sjdp	}
69733965Sjdp      else if (in->ptr[src] == '\\')
69833965Sjdp	{
69933965Sjdp	  src++;
70033965Sjdp	  if (in->ptr[src] == comment_char && comment_char != '\0')
70133965Sjdp	    {
70233965Sjdp	      /* This is a comment, just drop the rest of the line */
70333965Sjdp	      while (src < in->len
70433965Sjdp		     && in->ptr[src] != '\n')
70533965Sjdp		src++;
70633965Sjdp	    }
70733965Sjdp	  else if (in->ptr[src] == '(')
70833965Sjdp	    {
70933965Sjdp	      /* Sub in till the next ')' literally */
71033965Sjdp	      src++;
71133965Sjdp	      while (src < in->len && in->ptr[src] != ')')
71233965Sjdp		{
71333965Sjdp		  sb_add_char (out, in->ptr[src++]);
71433965Sjdp		}
71533965Sjdp	      if (in->ptr[src] == ')')
71633965Sjdp		src++;
71733965Sjdp	      else
71833965Sjdp		return "missplaced )";
71933965Sjdp	    }
72033965Sjdp	  else if (in->ptr[src] == '@')
72133965Sjdp	    {
72233965Sjdp	      /* Sub in the macro invocation number */
72333965Sjdp
72438889Sjdp	      char buffer[10];
72533965Sjdp	      src++;
72633965Sjdp	      sprintf (buffer, "%05d", macro_number);
72733965Sjdp	      sb_add_string (out, buffer);
72833965Sjdp	    }
72933965Sjdp	  else if (in->ptr[src] == '&')
73033965Sjdp	    {
73133965Sjdp	      /* This is a preprocessor variable name, we don't do them
73233965Sjdp		 here */
73333965Sjdp	      sb_add_char (out, '\\');
73433965Sjdp	      sb_add_char (out, '&');
73533965Sjdp	      src++;
73633965Sjdp	    }
73733965Sjdp	  else if (macro_mri
73833965Sjdp		   && isalnum ((unsigned char) in->ptr[src]))
73933965Sjdp	    {
74033965Sjdp	      int ind;
74133965Sjdp	      formal_entry *f;
74233965Sjdp
74333965Sjdp	      if (isdigit ((unsigned char) in->ptr[src]))
74433965Sjdp		ind = in->ptr[src] - '0';
74533965Sjdp	      else if (isupper ((unsigned char) in->ptr[src]))
74633965Sjdp		ind = in->ptr[src] - 'A' + 10;
74733965Sjdp	      else
74833965Sjdp		ind = in->ptr[src] - 'a' + 10;
74933965Sjdp	      ++src;
75033965Sjdp	      for (f = formals; f != NULL; f = f->next)
75133965Sjdp		{
75233965Sjdp		  if (f->index == ind - 1)
75333965Sjdp		    {
75433965Sjdp		      if (f->actual.len != 0)
75533965Sjdp			sb_add_sb (out, &f->actual);
75633965Sjdp		      else
75733965Sjdp			sb_add_sb (out, &f->def);
75833965Sjdp		      break;
75933965Sjdp		    }
76033965Sjdp		}
76133965Sjdp	    }
76233965Sjdp	  else
76333965Sjdp	    {
76433965Sjdp	      sb_reset (&t);
76533965Sjdp	      src = sub_actual (src, in, &t, formal_hash, '\'', out, 0);
76633965Sjdp	    }
76733965Sjdp	}
76833965Sjdp      else if ((macro_alternate || macro_mri)
76933965Sjdp	       && (isalpha ((unsigned char) in->ptr[src])
77033965Sjdp		   || in->ptr[src] == '_'
77133965Sjdp		   || in->ptr[src] == '$')
77233965Sjdp	       && (! inquote
77333965Sjdp		   || ! macro_strip_at
77433965Sjdp		   || (src > 0 && in->ptr[src - 1] == '@')))
77533965Sjdp	{
77633965Sjdp	  if (! locals
77733965Sjdp	      || src + 5 >= in->len
77833965Sjdp	      || strncasecmp (in->ptr + src, "LOCAL", 5) != 0
77933965Sjdp	      || ! ISWHITE (in->ptr[src + 5]))
78033965Sjdp	    {
78133965Sjdp	      sb_reset (&t);
78233965Sjdp	      src = sub_actual (src, in, &t, formal_hash,
78333965Sjdp				(macro_strip_at && inquote) ? '@' : '\'',
78433965Sjdp				out, 1);
78533965Sjdp	    }
78633965Sjdp	  else
78733965Sjdp	    {
78833965Sjdp	      formal_entry *f;
78933965Sjdp
79033965Sjdp	      src = sb_skip_white (src + 5, in);
79133965Sjdp	      while (in->ptr[src] != '\n' && in->ptr[src] != comment_char)
79233965Sjdp		{
79333965Sjdp		  static int loccnt;
79433965Sjdp		  char buf[20];
79533965Sjdp		  const char *err;
79633965Sjdp
79733965Sjdp		  f = (formal_entry *) xmalloc (sizeof (formal_entry));
79833965Sjdp		  sb_new (&f->name);
79933965Sjdp		  sb_new (&f->def);
80033965Sjdp		  sb_new (&f->actual);
80133965Sjdp		  f->index = LOCAL_INDEX;
80233965Sjdp		  f->next = loclist;
80333965Sjdp		  loclist = f;
80433965Sjdp
80533965Sjdp		  src = get_token (src, in, &f->name);
80633965Sjdp		  ++loccnt;
80733965Sjdp		  sprintf (buf, "LL%04x", loccnt);
80833965Sjdp		  sb_add_string (&f->actual, buf);
80933965Sjdp
81033965Sjdp		  err = hash_jam (formal_hash, sb_terminate (&f->name), f);
81133965Sjdp		  if (err != NULL)
81233965Sjdp		    return err;
81333965Sjdp
81433965Sjdp		  src = sb_skip_comma (src, in);
81533965Sjdp		}
81633965Sjdp	    }
81733965Sjdp	}
81833965Sjdp      else if (comment_char != '\0'
81933965Sjdp	       && in->ptr[src] == comment_char
82033965Sjdp	       && src + 1 < in->len
82133965Sjdp	       && in->ptr[src + 1] == comment_char
82233965Sjdp	       && !inquote)
82333965Sjdp	{
82433965Sjdp	  /* Two comment chars in a row cause the rest of the line to
82533965Sjdp             be dropped.  */
82633965Sjdp	  while (src < in->len && in->ptr[src] != '\n')
82733965Sjdp	    src++;
82833965Sjdp	}
82933965Sjdp      else if (in->ptr[src] == '"'
83033965Sjdp	       || (macro_mri && in->ptr[src] == '\''))
83133965Sjdp	{
83233965Sjdp	  inquote = !inquote;
83333965Sjdp	  sb_add_char (out, in->ptr[src++]);
83433965Sjdp	}
83533965Sjdp      else if (in->ptr[src] == '@' && macro_strip_at)
83633965Sjdp	{
83733965Sjdp	  ++src;
83833965Sjdp	  if (src < in->len
83933965Sjdp	      && in->ptr[src] == '@')
84033965Sjdp	    {
84133965Sjdp	      sb_add_char (out, '@');
84233965Sjdp	      ++src;
84333965Sjdp	    }
84433965Sjdp	}
84533965Sjdp      else if (macro_mri
84633965Sjdp	       && in->ptr[src] == '='
84733965Sjdp	       && src + 1 < in->len
84833965Sjdp	       && in->ptr[src + 1] == '=')
84933965Sjdp	{
85033965Sjdp	  formal_entry *ptr;
85133965Sjdp
85233965Sjdp	  sb_reset (&t);
85333965Sjdp	  src = get_token (src + 2, in, &t);
85433965Sjdp	  ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (&t));
85533965Sjdp	  if (ptr == NULL)
85633965Sjdp	    {
85733965Sjdp	      /* FIXME: We should really return a warning string here,
85833965Sjdp                 but we can't, because the == might be in the MRI
85933965Sjdp                 comment field, and, since the nature of the MRI
86033965Sjdp                 comment field depends upon the exact instruction
86133965Sjdp                 being used, we don't have enough information here to
86233965Sjdp                 figure out whether it is or not.  Instead, we leave
86333965Sjdp                 the == in place, which should cause a syntax error if
86433965Sjdp                 it is not in a comment.  */
86533965Sjdp	      sb_add_char (out, '=');
86633965Sjdp	      sb_add_char (out, '=');
86733965Sjdp	      sb_add_sb (out, &t);
86833965Sjdp	    }
86933965Sjdp	  else
87033965Sjdp	    {
87133965Sjdp	      if (ptr->actual.len)
87233965Sjdp		{
87333965Sjdp		  sb_add_string (out, "-1");
87433965Sjdp		}
87533965Sjdp	      else
87633965Sjdp		{
87733965Sjdp		  sb_add_char (out, '0');
87833965Sjdp		}
87933965Sjdp	    }
88033965Sjdp	}
88133965Sjdp      else
88233965Sjdp	{
88333965Sjdp	  sb_add_char (out, in->ptr[src++]);
88433965Sjdp	}
88533965Sjdp    }
88633965Sjdp
88733965Sjdp  sb_kill (&t);
88833965Sjdp
88933965Sjdp  while (loclist != NULL)
89033965Sjdp    {
89133965Sjdp      formal_entry *f;
89233965Sjdp
89333965Sjdp      f = loclist->next;
89433965Sjdp      hash_delete (formal_hash, sb_terminate (&loclist->name));
89533965Sjdp      sb_kill (&loclist->name);
89633965Sjdp      sb_kill (&loclist->def);
89733965Sjdp      sb_kill (&loclist->actual);
89833965Sjdp      free (loclist);
89933965Sjdp      loclist = f;
90033965Sjdp    }
90133965Sjdp
90233965Sjdp  return NULL;
90333965Sjdp}
90433965Sjdp
90533965Sjdp/* Assign values to the formal parameters of a macro, and expand the
90633965Sjdp   body.  */
90733965Sjdp
90833965Sjdpstatic const char *
90933965Sjdpmacro_expand (idx, in, m, out, comment_char)
91033965Sjdp     int idx;
91133965Sjdp     sb *in;
91233965Sjdp     macro_entry *m;
91333965Sjdp     sb *out;
91433965Sjdp     int comment_char;
91533965Sjdp{
91633965Sjdp  sb t;
91733965Sjdp  formal_entry *ptr;
91833965Sjdp  formal_entry *f;
91933965Sjdp  int is_positional = 0;
92033965Sjdp  int is_keyword = 0;
92133965Sjdp  int narg = 0;
92233965Sjdp  const char *err;
92333965Sjdp
92433965Sjdp  sb_new (&t);
92533965Sjdp
92633965Sjdp  /* Reset any old value the actuals may have */
92733965Sjdp  for (f = m->formals; f; f = f->next)
92833965Sjdp      sb_reset (&f->actual);
92933965Sjdp  f = m->formals;
93033965Sjdp  while (f != NULL && f->index < 0)
93133965Sjdp    f = f->next;
93233965Sjdp
93333965Sjdp  if (macro_mri)
93433965Sjdp    {
93533965Sjdp      /* The macro may be called with an optional qualifier, which may
93633965Sjdp         be referred to in the macro body as \0.  */
93733965Sjdp      if (idx < in->len && in->ptr[idx] == '.')
93833965Sjdp	{
93933965Sjdp	  formal_entry *n;
94033965Sjdp
94133965Sjdp	  n = (formal_entry *) xmalloc (sizeof (formal_entry));
94233965Sjdp	  sb_new (&n->name);
94333965Sjdp	  sb_new (&n->def);
94433965Sjdp	  sb_new (&n->actual);
94533965Sjdp	  n->index = QUAL_INDEX;
94633965Sjdp
94733965Sjdp	  n->next = m->formals;
94833965Sjdp	  m->formals = n;
94933965Sjdp
95033965Sjdp	  idx = get_any_string (idx + 1, in, &n->actual, 1, 0);
95133965Sjdp	}
95233965Sjdp    }
95333965Sjdp
95433965Sjdp  /* Peel off the actuals and store them away in the hash tables' actuals */
95533965Sjdp  idx = sb_skip_white (idx, in);
95633965Sjdp  while (idx < in->len && in->ptr[idx] != comment_char)
95733965Sjdp    {
95833965Sjdp      int scan;
95933965Sjdp
96033965Sjdp      /* Look and see if it's a positional or keyword arg */
96133965Sjdp      scan = idx;
96233965Sjdp      while (scan < in->len
96333965Sjdp	     && !ISSEP (in->ptr[scan])
96438889Sjdp	     && !(macro_mri && in->ptr[scan] == '\'')
96533965Sjdp	     && (!macro_alternate && in->ptr[scan] != '='))
96633965Sjdp	scan++;
96733965Sjdp      if (scan < in->len && !macro_alternate && in->ptr[scan] == '=')
96833965Sjdp	{
96933965Sjdp	  is_keyword = 1;
97033965Sjdp
97138889Sjdp	  /* It's OK to go from positional to keyword.  */
97238889Sjdp
97333965Sjdp	  /* This is a keyword arg, fetch the formal name and
97433965Sjdp	     then the actual stuff */
97533965Sjdp	  sb_reset (&t);
97633965Sjdp	  idx = get_token (idx, in, &t);
97733965Sjdp	  if (in->ptr[idx] != '=')
97833965Sjdp	    return "confusion in formal parameters";
97933965Sjdp
98033965Sjdp	  /* Lookup the formal in the macro's list */
98133965Sjdp	  ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
98233965Sjdp	  if (!ptr)
98333965Sjdp	    return "macro formal argument does not exist";
98433965Sjdp	  else
98533965Sjdp	    {
98633965Sjdp	      /* Insert this value into the right place */
98733965Sjdp	      sb_reset (&ptr->actual);
98833965Sjdp	      idx = get_any_string (idx + 1, in, &ptr->actual, 0, 0);
98933965Sjdp	      if (ptr->actual.len > 0)
99033965Sjdp		++narg;
99133965Sjdp	    }
99233965Sjdp	}
99333965Sjdp      else
99433965Sjdp	{
99533965Sjdp	  /* This is a positional arg */
99633965Sjdp	  is_positional = 1;
99733965Sjdp	  if (is_keyword)
99833965Sjdp	    return "can't mix positional and keyword arguments";
99933965Sjdp
100033965Sjdp	  if (!f)
100133965Sjdp	    {
100233965Sjdp	      formal_entry **pf;
100333965Sjdp	      int c;
100433965Sjdp
100533965Sjdp	      if (!macro_mri)
100633965Sjdp		return "too many positional arguments";
100733965Sjdp
100833965Sjdp	      f = (formal_entry *) xmalloc (sizeof (formal_entry));
100933965Sjdp	      sb_new (&f->name);
101033965Sjdp	      sb_new (&f->def);
101133965Sjdp	      sb_new (&f->actual);
101233965Sjdp	      f->next = NULL;
101333965Sjdp
101433965Sjdp	      c = -1;
101533965Sjdp	      for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next)
101633965Sjdp		if ((*pf)->index >= c)
101733965Sjdp		  c = (*pf)->index + 1;
101833965Sjdp	      if (c == -1)
101933965Sjdp		c = 0;
102033965Sjdp	      *pf = f;
102133965Sjdp	      f->index = c;
102233965Sjdp	    }
102333965Sjdp
102433965Sjdp	  sb_reset (&f->actual);
102533965Sjdp	  idx = get_any_string (idx, in, &f->actual, 1, 0);
102633965Sjdp	  if (f->actual.len > 0)
102733965Sjdp	    ++narg;
102833965Sjdp	  do
102933965Sjdp	    {
103033965Sjdp	      f = f->next;
103133965Sjdp	    }
103233965Sjdp	  while (f != NULL && f->index < 0);
103333965Sjdp	}
103433965Sjdp
103533965Sjdp      if (! macro_mri)
103633965Sjdp	idx = sb_skip_comma (idx, in);
103733965Sjdp      else
103833965Sjdp	{
103933965Sjdp	  if (in->ptr[idx] == ',')
104033965Sjdp	    ++idx;
104133965Sjdp	  if (ISWHITE (in->ptr[idx]))
104233965Sjdp	    break;
104333965Sjdp	}
104433965Sjdp    }
104533965Sjdp
104633965Sjdp  if (macro_mri)
104733965Sjdp    {
104833965Sjdp      char buffer[20];
104933965Sjdp
105033965Sjdp      sb_reset (&t);
105133965Sjdp      sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG");
105233965Sjdp      ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
105333965Sjdp      sb_reset (&ptr->actual);
105433965Sjdp      sprintf (buffer, "%d", narg);
105533965Sjdp      sb_add_string (&ptr->actual, buffer);
105633965Sjdp    }
105733965Sjdp
105833965Sjdp  err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash,
105933965Sjdp			   comment_char, 1);
106033965Sjdp  if (err != NULL)
106133965Sjdp    return err;
106233965Sjdp
106333965Sjdp  /* Discard any unnamed formal arguments.  */
106433965Sjdp  if (macro_mri)
106533965Sjdp    {
106633965Sjdp      formal_entry **pf;
106733965Sjdp
106833965Sjdp      pf = &m->formals;
106933965Sjdp      while (*pf != NULL)
107033965Sjdp	{
107133965Sjdp	  if ((*pf)->name.len != 0)
107233965Sjdp	    pf = &(*pf)->next;
107333965Sjdp	  else
107433965Sjdp	    {
107533965Sjdp	      sb_kill (&(*pf)->name);
107633965Sjdp	      sb_kill (&(*pf)->def);
107733965Sjdp	      sb_kill (&(*pf)->actual);
107833965Sjdp	      f = (*pf)->next;
107933965Sjdp	      free (*pf);
108033965Sjdp	      *pf = f;
108133965Sjdp	    }
108233965Sjdp	}
108333965Sjdp    }
108433965Sjdp
108533965Sjdp  sb_kill (&t);
108633965Sjdp  macro_number++;
108733965Sjdp
108833965Sjdp  return NULL;
108933965Sjdp}
109033965Sjdp
109133965Sjdp/* Check for a macro.  If one is found, put the expansion into
109233965Sjdp   *EXPAND.  COMMENT_CHAR is the comment character--this is used by
109333965Sjdp   gasp.  Return 1 if a macro is found, 0 otherwise.  */
109433965Sjdp
109533965Sjdpint
109633965Sjdpcheck_macro (line, expand, comment_char, error)
109733965Sjdp     const char *line;
109833965Sjdp     sb *expand;
109933965Sjdp     int comment_char;
110033965Sjdp     const char **error;
110133965Sjdp{
110233965Sjdp  const char *s;
110333965Sjdp  char *copy, *cs;
110433965Sjdp  macro_entry *macro;
110533965Sjdp  sb line_sb;
110633965Sjdp
110733965Sjdp  if (! isalpha ((unsigned char) *line)
110833965Sjdp      && *line != '_'
110933965Sjdp      && *line != '$'
111033965Sjdp      && (! macro_mri || *line != '.'))
111133965Sjdp    return 0;
111233965Sjdp
111333965Sjdp  s = line + 1;
111433965Sjdp  while (isalnum ((unsigned char) *s)
111533965Sjdp	 || *s == '_'
111633965Sjdp	 || *s == '$')
111733965Sjdp    ++s;
111833965Sjdp
111938889Sjdp  copy = (char *) alloca (s - line + 1);
112033965Sjdp  memcpy (copy, line, s - line);
112133965Sjdp  copy[s - line] = '\0';
112233965Sjdp  for (cs = copy; *cs != '\0'; cs++)
112338889Sjdp    if (isupper ((unsigned char) *cs))
112433965Sjdp      *cs = tolower (*cs);
112533965Sjdp
112633965Sjdp  macro = (macro_entry *) hash_find (macro_hash, copy);
112733965Sjdp
112833965Sjdp  if (macro == NULL)
112933965Sjdp    return 0;
113033965Sjdp
113133965Sjdp  /* Wrap the line up in an sb.  */
113233965Sjdp  sb_new (&line_sb);
113333965Sjdp  while (*s != '\0' && *s != '\n' && *s != '\r')
113433965Sjdp    sb_add_char (&line_sb, *s++);
113533965Sjdp
113633965Sjdp  sb_new (expand);
113733965Sjdp  *error = macro_expand (0, &line_sb, macro, expand, comment_char);
113833965Sjdp
113933965Sjdp  sb_kill (&line_sb);
114033965Sjdp
114133965Sjdp  return 1;
114233965Sjdp}
114333965Sjdp
114433965Sjdp/* Delete a macro.  */
114533965Sjdp
114633965Sjdpvoid
114733965Sjdpdelete_macro (name)
114833965Sjdp     const char *name;
114933965Sjdp{
115033965Sjdp  hash_delete (macro_hash, name);
115133965Sjdp}
115233965Sjdp
115333965Sjdp/* Handle the MRI IRP and IRPC pseudo-ops.  These are handled as a
115433965Sjdp   combined macro definition and execution.  This returns NULL on
115533965Sjdp   success, or an error message otherwise.  */
115633965Sjdp
115733965Sjdpconst char *
115833965Sjdpexpand_irp (irpc, idx, in, out, get_line, comment_char)
115933965Sjdp     int irpc;
116033965Sjdp     int idx;
116133965Sjdp     sb *in;
116233965Sjdp     sb *out;
116333965Sjdp     int (*get_line) PARAMS ((sb *));
116433965Sjdp     int comment_char;
116533965Sjdp{
116633965Sjdp  const char *mn;
116733965Sjdp  sb sub;
116833965Sjdp  formal_entry f;
116933965Sjdp  struct hash_control *h;
117033965Sjdp  const char *err;
117133965Sjdp
117233965Sjdp  if (irpc)
117333965Sjdp    mn = "IRPC";
117433965Sjdp  else
117533965Sjdp    mn = "IRP";
117633965Sjdp
117733965Sjdp  idx = sb_skip_white (idx, in);
117833965Sjdp
117933965Sjdp  sb_new (&sub);
118033965Sjdp  if (! buffer_and_nest (mn, "ENDR", &sub, get_line))
118133965Sjdp    return "unexpected end of file in irp or irpc";
118233965Sjdp
118333965Sjdp  sb_new (&f.name);
118433965Sjdp  sb_new (&f.def);
118533965Sjdp  sb_new (&f.actual);
118633965Sjdp
118733965Sjdp  idx = get_token (idx, in, &f.name);
118833965Sjdp  if (f.name.len == 0)
118933965Sjdp    return "missing model parameter";
119033965Sjdp
119133965Sjdp  h = hash_new ();
119233965Sjdp  err = hash_jam (h, sb_terminate (&f.name), &f);
119333965Sjdp  if (err != NULL)
119433965Sjdp    return err;
119533965Sjdp
119633965Sjdp  f.index = 1;
119733965Sjdp  f.next = NULL;
119833965Sjdp
119933965Sjdp  sb_reset (out);
120033965Sjdp
120133965Sjdp  idx = sb_skip_comma (idx, in);
120233965Sjdp  if (idx >= in->len || in->ptr[idx] == comment_char)
120333965Sjdp    {
120433965Sjdp      /* Expand once with a null string.  */
120533965Sjdp      err = macro_expand_body (&sub, out, &f, h, comment_char, 0);
120633965Sjdp      if (err != NULL)
120733965Sjdp	return err;
120833965Sjdp    }
120933965Sjdp  else
121033965Sjdp    {
121133965Sjdp      if (irpc && in->ptr[idx] == '"')
121233965Sjdp	++idx;
121333965Sjdp      while (idx < in->len && in->ptr[idx] != comment_char)
121433965Sjdp	{
121533965Sjdp	  if (!irpc)
121633965Sjdp	    idx = get_any_string (idx, in, &f.actual, 1, 0);
121733965Sjdp	  else
121833965Sjdp	    {
121933965Sjdp	      if (in->ptr[idx] == '"')
122033965Sjdp		{
122133965Sjdp		  int nxt;
122233965Sjdp
122333965Sjdp		  nxt = sb_skip_white (idx + 1, in);
122433965Sjdp		  if (nxt >= in->len || in->ptr[nxt] == comment_char)
122533965Sjdp		    {
122633965Sjdp		      idx = nxt;
122733965Sjdp		      break;
122833965Sjdp		    }
122933965Sjdp		}
123033965Sjdp	      sb_reset (&f.actual);
123133965Sjdp	      sb_add_char (&f.actual, in->ptr[idx]);
123233965Sjdp	      ++idx;
123333965Sjdp	    }
123433965Sjdp	  err = macro_expand_body (&sub, out, &f, h, comment_char, 0);
123533965Sjdp	  if (err != NULL)
123633965Sjdp	    return err;
123733965Sjdp	  if (!irpc)
123833965Sjdp	    idx = sb_skip_comma (idx, in);
123933965Sjdp	  else
124033965Sjdp	    idx = sb_skip_white (idx, in);
124133965Sjdp	}
124233965Sjdp    }
124333965Sjdp
124433965Sjdp  hash_die (h);
124533965Sjdp  sb_kill (&sub);
124633965Sjdp
124733965Sjdp  return NULL;
124833965Sjdp}
1249