* expr.c (operand): Handle 08 and 09 in MRI mode.
authorIan Lance Taylor <ian@airs.com>
Thu, 7 Sep 1995 21:54:13 +0000 (21:54 +0000)
committerIan Lance Taylor <ian@airs.com>
Thu, 7 Sep 1995 21:54:13 +0000 (21:54 +0000)
* macro.c (ISSEP): Remove duplicated `"' character.
  (get_any_string): Copy some characters for which ISSEP is true:
';', '>', '(', ')'.  Otherwise we can get in an infinite loop.
* read.c (s_space): In MRI mode, the expressions stop at the first
unquoted space.
(cons_worker): In MRI mode, restore the terminating character at
the end of the function.

gas/ChangeLog
gas/expr.c
gas/macro.c [new file with mode: 0644]
gas/read.c

index 52f457f..676aadc 100644 (file)
@@ -1,5 +1,14 @@
 Thu Sep  7 12:33:58 1995  Ian Lance Taylor  <ian@cygnus.com>
 
+       * expr.c (operand): Handle 08 and 09 in MRI mode.
+       * macro.c (ISSEP): Remove duplicated `"' character.
+       (get_any_string): Copy some characters for which ISSEP is true:
+       ';', '>', '(', ')'.  Otherwise we can get in an infinite loop.
+       * read.c (s_space): In MRI mode, the expressions stop at the first
+       unquoted space.
+       (cons_worker): In MRI mode, restore the terminating character at
+       the end of the function.
+
        * read.c (cons_worker): Don't use #elif; old compilers don't
        support it.
 
index b730667..ebc580f 100644 (file)
@@ -561,6 +561,14 @@ operand (expressionP)
       c = *input_line_pointer;
       switch (c)
        {
+       case '8':
+       case '9':
+         if (flag_mri)
+           {
+             integer_constant (0, expressionP);
+             break;
+           }
+         /* Fall through.  */
        default:
        default_case:
          if (c && strchr (FLT_CHARS, c))
@@ -821,6 +829,49 @@ operand (expressionP)
          expressionP->X_add_number = 0;
          break;
        }
+      else if ((strncasecmp (input_line_pointer, "startof.", 8) == 0
+               && ! is_part_of_name (input_line_pointer[8]))
+              || (strncasecmp (input_line_pointer, "sizeof.", 7) == 0
+                  && ! is_part_of_name (input_line_pointer[7])))
+       {
+         int start;
+
+         start = (input_line_pointer[1] == 't'
+                  || input_line_pointer[1] == 'T');
+         input_line_pointer += start ? 8 : 7;
+         SKIP_WHITESPACE ();
+         if (*input_line_pointer != '(')
+           as_bad ("syntax error in .startof. or .sizeof.");
+         else
+           {
+             char *buf;
+
+             ++input_line_pointer;
+             SKIP_WHITESPACE ();
+             name = input_line_pointer;
+             c = get_symbol_end ();
+
+             buf = (char *) xmalloc (strlen (name) + 10);
+             if (start)
+               sprintf (buf, ".startof.%s", name);
+             else
+               sprintf (buf, ".sizeof.%s", name);
+             symbolP = symbol_make (buf);
+             free (buf);
+
+             expressionP->X_op = O_symbol;
+             expressionP->X_add_symbol = symbolP;
+             expressionP->X_add_number = 0;
+
+             *input_line_pointer = c;
+             SKIP_WHITESPACE ();
+             if (*input_line_pointer != ')')
+               as_bad ("syntax error in .startof. or .sizeof.");
+             else
+               ++input_line_pointer;
+           }
+         break;
+       }
       else
        {
          goto isname;
@@ -1016,13 +1067,13 @@ clean_up_expression (expressionP)
 #undef __
 #define __ O_illegal
 
-static const operatorT op_encoding[256] =
+static operatorT op_encoding[256] =
 {                              /* maps ASCII->operators */
 
   __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
   __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
 
-  __, O_bit_or_not, O_bit_not, __, __, O_modulus, O_bit_and, __,
+  __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __,
   __, __, O_multiply, O_add, __, O_subtract, __, O_divide,
   __, __, __, __, __, __, __, __,
   __, __, __, __, O_lt, __, O_gt, __,
@@ -1097,6 +1148,7 @@ expr_begin ()
       op_rank[O_multiply] = 3;
       op_rank[O_divide] = 3;
       op_rank[O_modulus] = 3;
+      op_encoding['"'] = O_bit_not;
     }
 }
 \f
@@ -1385,7 +1437,10 @@ get_symbol_end ()
 {
   char c;
 
-  while (is_part_of_name (c = *input_line_pointer++))
+  /* We accept \001 in a name in case this is being called with a
+     constructed string.  */
+  while (is_part_of_name (c = *input_line_pointer++)
+        || c == '\001')
     ;
   *--input_line_pointer = 0;
   return (c);
diff --git a/gas/macro.c b/gas/macro.c
new file mode 100644 (file)
index 0000000..13dd4af
--- /dev/null
@@ -0,0 +1,1126 @@
+/* macro.c - macro support for gas and gasp
+   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+
+   Written by Steve and Judy Chamberlain of Cygnus Support,
+      sac@cygnus.com
+
+   This file is part of GAS, the GNU Assembler.
+
+   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)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include "sb.h"
+#include "hash.h"
+#include "macro.h"
+
+/* The routines in this file handle macro definition and expansion.
+   They are called by both gasp and gas.  */
+
+/* Structures used to store macros. 
+
+   Each macro knows its name and included text.  It gets built with a
+   list of formal arguments, and also keeps a hash table which points
+   into the list to speed up formal search.  Each formal knows its
+   name and its default value.  Each time the macro is expanded, the
+   formals get the actual values attatched to them. */
+
+/* describe the formal arguments to a macro */
+
+typedef struct formal_struct
+  {
+    struct formal_struct *next;        /* next formal in list */
+    sb name;                   /* name of the formal */
+    sb def;                    /* the default value */
+    sb actual;                 /* the actual argument (changed on each expansion) */
+    int index;                 /* the index of the formal 0..formal_count-1 */
+  }
+formal_entry;
+
+/* Other values found in the index field of a formal_entry.  */
+#define QUAL_INDEX (-1)
+#define NARG_INDEX (-2)
+#define LOCAL_INDEX (-3)
+
+/* describe the macro. */
+
+typedef struct macro_struct
+  {
+    sb sub;                    /* substitution text. */
+    int formal_count;          /* number of formal args. */
+    formal_entry *formals;     /* pointer to list of formal_structs */
+    struct hash_control *formal_hash; /* hash table of formals. */
+  }
+macro_entry;
+
+/* Internal functions.  */
+
+static int get_token PARAMS ((int, sb *, sb *));
+static int getstring PARAMS ((int, sb *, sb *));
+static int get_any_string PARAMS ((int, sb *, sb *, int, int));
+static int do_formals PARAMS ((macro_entry *, int, sb *));
+static int get_apost_token PARAMS ((int, sb *, sb *, int));
+static int sub_actual
+  PARAMS ((int, sb *, sb *, struct hash_control *, int, sb *, int));
+static const char *macro_expand_body
+  PARAMS ((sb *, sb *, formal_entry *, struct hash_control *, int, int));
+static const char *macro_expand PARAMS ((int, sb *, macro_entry *, sb *, int));
+
+#define ISWHITE(x) ((x) == ' ' || (x) == '\t')
+
+#define ISSEP(x) \
+ ((x) == ' ' || (x) == '\t' || (x) == ',' || (x) == '"' || (x) == ';' \
+  || (x) == '<' || (x) == '>' || (x) == ')' || (x) == '(')
+
+#define ISBASE(x) \
+  ((x) == 'b' || (x) == 'B' \
+   || (x) == 'q' || (x) == 'Q' \
+   || (x) == 'h' || (x) == 'H' \
+   || (x) == 'd' || (x) == 'D')
+
+/* The macro hash table.  */
+
+static struct hash_control *macro_hash;
+
+/* Whether any macros have been defined.  */
+
+int macro_defined;
+
+/* Whether we are in GASP alternate mode.  */
+
+static int macro_alternate;
+
+/* Whether we are in MRI mode.  */
+
+static int macro_mri;
+
+/* Function to use to parse an expression.  */
+
+static int (*macro_expr) PARAMS ((const char *, int, sb *, int *));
+
+/* Number of macro expansions that have been done.  */
+
+static int macro_number;
+
+/* Initialize macro processing.  */
+
+void
+macro_init (alternate, mri, expr)
+     int alternate;
+     int mri;
+     int (*expr) PARAMS ((const char *, int, sb *, int *));
+{
+  macro_hash = hash_new ();
+  macro_defined = 0;
+  macro_alternate = alternate;
+  macro_mri = mri;
+  macro_expr = expr;
+}
+
+/* Read input lines till we get to a TO string.
+   Increase nesting depth if we get a FROM string.
+   Put the results into sb at PTR.
+   Add a new input line to an sb using GET_LINE.
+   Return 1 on success, 0 on unexpected EOF.  */
+
+int
+buffer_and_nest (from, to, ptr, get_line)
+     const char *from;
+     const char *to;
+     sb *ptr;
+     int (*get_line) PARAMS ((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)
+    {
+      /* Try and find the first pseudo op on the line */
+      int i = line_start;
+
+      if (! macro_alternate && ! macro_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 cant tell what's a label and
+            whats a pseudoop */
+
+         /* Skip leading whitespace */
+         while (i < ptr->len && ISWHITE (ptr->ptr[i]))
+           i++;
+
+         /* Skip over a label */
+         while (i < ptr->len
+                && (isalnum ((unsigned char) ptr->ptr[i])
+                    || ptr->ptr[i] == '_'
+                    || ptr->ptr[i] == '$'))
+           i++;
+
+         /* And a colon */
+         if (i < ptr->len
+             && ptr->ptr[i] == ':')
+           i++;
+
+       }
+      /* Skip trailing whitespace */
+      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)
+           depth++;
+         if (strncasecmp (ptr->ptr + i, to, to_len) == 0)
+           {
+             depth--;
+             if (depth == 0)
+               {
+                 /* Reset the string to not include the ending rune */
+                 ptr->len = line_start;
+                 break;
+               }
+           }
+       }
+
+      /* Add a CR to the end and keep running */
+      sb_add_char (ptr, '\n');
+      line_start = ptr->len;
+      more = get_line (ptr);
+    }
+
+  /* Return 1 on success, 0 on unexpected EOF.  */
+  return depth == 0;
+}
+
+/* Pick up a token.  */
+
+static int
+get_token (idx, in, name)
+     int idx;
+     sb *in;
+     sb *name;
+{
+  if (idx < in->len
+      && (isalpha ((unsigned char) in->ptr[idx])
+         || in->ptr[idx] == '_'
+         || in->ptr[idx] == '$'))
+    {
+      sb_add_char (name, in->ptr[idx++]);
+      while (idx < in->len
+            && (isalnum ((unsigned char) in->ptr[idx])
+                || in->ptr[idx] == '_'
+                || in->ptr[idx] == '$'))
+       {
+         sb_add_char (name, in->ptr[idx++]);
+       }
+    }
+  /* Ignore trailing & */
+  if (macro_alternate && idx < in->len && in->ptr[idx] == '&')
+    idx++;
+  return idx;
+}
+
+/* Pick up a string.  */
+
+static int
+getstring (idx, in, acc)
+     int idx;
+     sb *in;
+     sb *acc;
+{
+  idx = sb_skip_white (idx, in);
+
+  while (idx < in->len
+        && (in->ptr[idx] == '"' 
+            || in->ptr[idx] == '<' 
+            || (in->ptr[idx] == '\'' && macro_alternate)))
+    {
+      if (in->ptr[idx] == '<')
+       {
+         if (macro_alternate || macro_mri)
+           {
+             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
+           {
+             int code;
+             idx++;
+             idx = ((*macro_expr)
+                    ("character code in string must be absolute expression",
+                     idx, in, &code));
+             sb_add_char (acc, code);
+
+#if 0
+             if (in->ptr[idx] != '>')
+               ERROR ((stderr, "Missing > for character code.\n"));
+#endif
+             idx++;
+           }
+       }
+      else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'')
+       {
+         char tchar = in->ptr[idx];
+         idx++;
+         while (idx < in->len)
+           {
+             if (macro_alternate && in->ptr[idx] == '!')
+               {
+                 idx++  ;
+                 sb_add_char (acc, in->ptr[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;
+}
+
+/* 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
+*/
+
+static int
+get_any_string (idx, in, out, expand, pretend_quoted)
+     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 > 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];
+         /* Turns the next expression into a string */
+         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 && in->ptr[idx] == '\''))
+       {
+         if (macro_alternate && expand)
+           {
+             /* Keep the quotes */
+             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] != '<')))
+           {
+             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;
+}
+
+/* Pick up the formal parameters of a macro definition.  */
+
+static int
+do_formals (macro, idx, in)
+     macro_entry *macro;
+     int idx;
+     sb *in;
+{
+  formal_entry **p = &macro->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)
+       {
+         /* This is a formal */
+         if (idx < in->len && in->ptr[idx] == '=')
+           {
+             /* Got a default */
+             idx = get_any_string (idx + 1, in, &formal->def, 1, 0);
+           }
+       }
+
+      /* Add to macro's hash table */
+      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;
+
+      /* 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);
+
+      sb_add_string (&formal->name, "NARG");
+
+      /* Add to macro's hash table */
+      hash_jam (macro->formal_hash, "NARG", formal);
+
+      formal->index = NARG_INDEX;
+      *p = formal;
+      formal->next = NULL;
+    }
+
+  return idx;
+}
+
+/* Define a new macro.  Returns NULL on success, otherwise returns an
+   error message.  */
+
+const char *
+define_macro (idx, in, label, get_line)
+     int idx;
+     sb *in;
+     sb *label;
+     int (*get_line) PARAMS ((sb *));
+{
+  macro_entry *macro;
+  sb name;
+
+  macro = (macro_entry *) xmalloc (sizeof (macro_entry));
+  sb_new (&macro->sub);
+  sb_new (&name);
+
+  macro->formal_count = 0;
+  macro->formals = 0;
+
+  idx = sb_skip_white (idx, in);
+  if (! buffer_and_nest ("MACRO", "ENDM", &macro->sub, get_line))
+    return "unexpected end of file in macro definition";
+  if (label != NULL && label->len != 0)
+    {
+      sb_add_sb (&name, label);
+      if (in->ptr[idx] == '(')
+       {
+         /* It's the label: MACRO (formals,...)  sort */
+         idx = do_formals (macro, idx + 1, in);
+         if (in->ptr[idx] != ')')
+           return "missing ) after formals";
+       }
+      else
+       {
+         /* It's the label: MACRO formals,...  sort */
+         idx = do_formals (macro, idx, in);
+       }
+    }
+  else
+    {
+      idx = get_token (idx, in, &name);
+      idx = sb_skip_white (idx, in);
+      idx = do_formals (macro, idx, in);
+    }
+
+  /* and stick it in the macro hash table */
+  for (idx = 0; idx < name.len; idx++)
+    if (isupper (name.ptr[idx]))
+      name.ptr[idx] = tolower (name.ptr[idx]);
+  hash_jam (macro_hash, sb_terminate (&name), (PTR) macro);
+
+  macro_defined = 1;
+
+  return NULL;
+}
+
+/* Scan a token, and then skip KIND.  */
+
+static int
+get_apost_token (idx, in, name, kind)
+     int idx;
+     sb *in;
+     sb *name;
+     int kind;
+{
+  idx = get_token (idx, in, name);
+  if (idx < in->len && in->ptr[idx] == kind && ! macro_mri)
+    idx++;
+  return idx;
+}
+
+/* Substitute the actual value for a formal parameter.  */
+
+static int
+sub_actual (src, in, t, formal_hash, kind, out, copyifnotthere)
+     int src;
+     sb *in;
+     sb *t;
+     struct hash_control *formal_hash;
+     int kind;
+     sb *out;
+     int copyifnotthere;
+{
+  formal_entry *ptr;
+
+  src = get_apost_token (src, in, t, kind);
+  /* See if it's in the macro's hash table */
+  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 (copyifnotthere)
+    {
+      sb_add_sb (out, t);
+    }
+  else 
+    {
+      sb_add_char (out, '\\');
+      sb_add_sb (out, t);
+    }
+  return src;
+}
+
+/* Expand the body of a macro.  */
+
+static const char *
+macro_expand_body (in, out, formals, formal_hash, comment_char, locals)
+     sb *in;
+     sb *out;
+     formal_entry *formals;
+     struct hash_control *formal_hash;
+     int comment_char;
+     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 && src + 1 < in->len && in->ptr[src + 1] == '&')
+           {
+             src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
+           }
+         else
+           {
+             src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
+           }
+       }
+      else if (in->ptr[src] == '\\')
+       {
+         src++;
+         if (in->ptr[src] == comment_char && comment_char != '\0')
+           {
+             /* This is a comment, just drop the rest of the line */
+             while (src < in->len
+                    && in->ptr[src] != '\n')
+               src++;
+           }
+         else if (in->ptr[src] == '(')
+           {
+             /* Sub in till the next ')' literally */
+             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] == '@')
+           {
+             /* Sub in the macro invocation number */
+
+             char buffer[6];
+             src++;
+             sprintf (buffer, "%05d", macro_number);
+             sb_add_string (out, buffer);
+           }
+         else if (in->ptr[src] == '&')
+           {
+             /* This is a preprocessor variable name, we don't do them
+                here */
+             sb_add_char (out, '\\');
+             sb_add_char (out, '&');
+             src++;
+           }
+         else if (macro_mri
+                  && isalnum ((unsigned char) in->ptr[src]))
+           {
+             int ind;
+             formal_entry *f;
+
+             if (isdigit ((unsigned char) in->ptr[src]))
+               ind = in->ptr[src] - '0';
+             else if (isupper ((unsigned char) 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 ((unsigned char) in->ptr[src])
+                  || in->ptr[src] == '_'
+                  || in->ptr[src] == '$'))
+       {
+         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, '\'', out, 1);
+           }
+         else
+           {
+             formal_entry *f;
+
+             src = sb_skip_white (src + 5, in);
+             while (in->ptr[src] != '\n' && in->ptr[src] != comment_char)
+               {
+                 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 (comment_char != '\0'
+              && in->ptr[src] == comment_char
+              && src + 1 < in->len
+              && in->ptr[src + 1] == comment_char
+              && !inquote)
+       {
+         /* Two comment chars in a row cause the rest of the line to
+             be dropped.  */
+         while (src < in->len && in->ptr[src] != '\n')
+           src++;
+       }
+      else if (in->ptr[src] == '"'
+              || (macro_mri && in->ptr[src] == '\''))
+       {
+         inquote = !inquote;
+         sb_add_char (out, in->ptr[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)
+           return "macro formal argument does not exist";
+         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;
+      sb_kill (&loclist->name);
+      sb_kill (&loclist->def);
+      sb_kill (&loclist->actual);
+      free (loclist);
+      loclist = f;
+    }
+
+  return NULL;
+}
+
+/* Assign values to the formal parameters of a macro, and expand the
+   body.  */
+
+static const char *
+macro_expand (idx, in, m, out, comment_char)
+     int idx;
+     sb *in;
+     macro_entry *m;
+     sb *out;
+     int comment_char;
+{
+  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);
+  
+  /* Reset any old value the actuals may have */
+  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)
+    {
+      /* The macro may be called with an optional qualifier, which may
+         be referred to in the macro body as \0.  */
+      if (idx < in->len && in->ptr[idx] == '.')
+       {
+         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 + 1, in, &n->actual, 1, 0);
+       }
+    }
+
+  /* Peel off the actuals and store them away in the hash tables' actuals */
+  idx = sb_skip_white (idx, in);
+  while (idx < in->len && in->ptr[idx] != comment_char)
+    {
+      int scan;
+
+      /* Look and see if it's a positional or keyword arg */
+      scan = idx;
+      while (scan < in->len
+            && !ISSEP (in->ptr[scan])
+            && (!macro_alternate && in->ptr[scan] != '='))
+       scan++;
+      if (scan < in->len && !macro_alternate && in->ptr[scan] == '=')
+       {
+         is_keyword = 1;
+         if (is_positional)
+           return "can't mix positional and keyword arguments";
+
+         /* This is a keyword arg, fetch the formal name and
+            then the actual stuff */
+         sb_reset (&t);
+         idx = get_token (idx, in, &t);
+         if (in->ptr[idx] != '=')
+           return "confusion in formal parameters";
+
+         /* 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";
+         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 > 0)
+               ++narg;
+           }
+       }
+      else
+       {
+         /* This is a positional arg */
+         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);
+       }
+
+      idx = sb_skip_comma (idx, in);
+    }
+
+  if (macro_mri)
+    {
+      char buffer[20];
+
+      sb_reset (&t);
+      sb_add_string (&t, "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,
+                          comment_char, 1);
+  if (err != NULL)
+    return err;
+
+  /* Discard any unnamed formal arguments.  */
+  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;
+}
+
+/* Check for a macro.  If one is found, put the expansion into
+   *EXPAND.  COMMENT_CHAR is the comment character--this is used by
+   gasp.  Return 1 if a macro is found, 0 otherwise.  */
+
+int
+check_macro (line, expand, comment_char, error)
+     const char *line;
+     sb *expand;
+     int comment_char;
+     const char **error;
+{
+  const char *s;
+  char *copy, *cs;
+  macro_entry *macro;
+  sb line_sb;
+
+  if (! isalpha ((unsigned char) *line)
+      && *line != '_'
+      && *line != '$'
+      && (! macro_mri || *line != '.'))
+    return 0;
+
+  s = line + 1;
+  while (isalnum ((unsigned char) *s)
+        || *s == '_'
+        || *s == '$')
+    ++s;
+
+  copy = (char *) xmalloc (s - line + 1);
+  memcpy (copy, line, s - line);
+  copy[s - line] = '\0';
+  for (cs = copy; *cs != '\0'; cs++)
+    if (isupper (*cs))
+      *cs = tolower (*cs);
+
+  macro = (macro_entry *) hash_find (macro_hash, copy);
+
+  if (macro == NULL)
+    return 0;
+
+  /* Wrap the line up in an sb.  */
+  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, comment_char);
+
+  sb_kill (&line_sb);
+
+  return 1;
+}
+
+/* Handle the MRI IRP and IRPC pseudo-ops.  These are handled as a
+   combined macro definition and execution.  This returns NULL on
+   success, or an error message otherwise.  */
+
+const char *
+expand_irp (irpc, idx, in, out, get_line, comment_char)
+     int irpc;
+     int idx;
+     sb *in;
+     sb *out;
+     int (*get_line) PARAMS ((sb *));
+     int comment_char;
+{
+  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 || in->ptr[idx] == comment_char)
+    {
+      /* Expand once with a null string.  */
+      err = macro_expand_body (&sub, out, &f, h, comment_char, 0);
+      if (err != NULL)
+       return err;
+    }
+  else
+    {
+      while (idx < in->len && in->ptr[idx] != comment_char)
+       {
+         if (!irpc)
+           idx = get_any_string (idx, in, &f.actual, 1, 0);
+         else
+           {
+             sb_reset (&f.actual);
+             sb_add_char (&f.actual, in->ptr[idx]);
+             ++idx;
+           }
+         err = macro_expand_body (&sub, out, &f, h, comment_char, 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;
+}
index 3b313eb..f7a4f55 100644 (file)
@@ -2030,11 +2030,32 @@ s_space (mult)
   expressionS exp;
   long temp_fill;
   char *p = 0;
+  char *stop = NULL;
+  char stopc;
 
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
 #endif
 
+  /* In MRI mode, the operands end at the first unquoted space.  */
+  if (flag_mri)
+    {
+      char *s;
+      int inquote = 0;
+
+      for (s = input_line_pointer;
+          ((! is_end_of_line[(unsigned char) *s] && *s != ' ' && *s != '\t')
+           || inquote);
+          s++)
+       {
+         if (*s == '\'')
+           inquote = ! inquote;
+       }
+      stop = s;
+      stopc = *stop;
+      *stop = '\0';
+    }
+
   /* Just like .fill, but temp_size = 1 */
   expression (&exp);
   if (exp.X_op == O_constant)
@@ -2049,16 +2070,14 @@ s_space (mult)
          if (! flag_mri || repeat < 0)
            as_warn (".space repeat count is %s, ignored",
                     repeat ? "negative" : "zero");
-         ignore_rest_of_line ();
-         return;
+         goto getout;
        }
 
       /* If we are in the absolute section, just bump the offset.  */
       if (now_seg == absolute_section)
        {
          abs_section_offset += repeat;
-         demand_empty_rest_of_line ();
-         return;
+         goto getout;
        }
 
       /* If we are secretly in an MRI common section, then creating
@@ -2067,8 +2086,7 @@ s_space (mult)
        {
          S_SET_VALUE (mri_common_symbol,
                       S_GET_VALUE (mri_common_symbol) + repeat);
-         demand_empty_rest_of_line ();
-         return;
+         goto getout;
        }
 
       if (!need_pass_2)
@@ -2105,6 +2123,16 @@ s_space (mult)
     {
       *p = temp_fill;
     }
+
+ getout:
+  if (flag_mri)
+    {
+      input_line_pointer = stop;
+      *stop = stopc;
+      while (! is_end_of_line[(unsigned char) *input_line_pointer])
+       ++input_line_pointer;
+    }
+
   demand_empty_rest_of_line ();
 }
 
@@ -2394,6 +2422,7 @@ cons_worker (nbytes, rva)
   int c;
   expressionS exp;
   char *stop = NULL;
+  char stopc;
 
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
@@ -2420,6 +2449,8 @@ cons_worker (nbytes, rva)
            inquote = ! inquote;
        }
       stop = s;
+      stopc = *stop;
+      *stop = '\0';
     }
 
   c = 0;
@@ -2467,6 +2498,7 @@ cons_worker (nbytes, rva)
   if (flag_mri)
     {
       input_line_pointer = stop;
+      *stop = stopc;
       while (! is_end_of_line[(unsigned char) *input_line_pointer])
        ++input_line_pointer;
     }