Imported Upstream version 20221229
[platform/upstream/byacc.git] / reader.c
index 3ea784c..5e8ca67 100644 (file)
--- a/reader.c
+++ b/reader.c
@@ -1,24 +1,45 @@
-/* $Id: reader.c,v 1.31 2010/11/26 12:30:40 tom Exp $ */
+/* $Id: reader.c,v 1.91 2022/01/09 18:04:58 tom Exp $ */
 
 #include "defs.h"
 
 /*  The line size must be a positive integer.  One hundred was chosen  */
 /*  because few lines in Yacc input grammars exceed 100 characters.    */
 /*  Note that if a line exceeds LINESIZE characters, the line buffer   */
-/*  will be expanded to accomodate it.                                 */
+/*  will be expanded to accommodate it.                                        */
 
 #define LINESIZE 100
 
-#define L_CURL '{'
-#define R_CURL '}'
+#define L_CURL  '{'
+#define R_CURL  '}'
+#define L_PAREN '('
+#define R_PAREN ')'
+#define L_BRAC  '['
+#define R_BRAC  ']'
+
+/* the maximum number of arguments (inherited attributes) to a non-terminal */
+/* this is a hard limit, but seems more than adequate */
+#define MAXARGS        20
+
+#define begin_case(f,n) fprintf(f, "case %d:\n", (int)(n))
+
+#define end_case(f) \
+           fprintf(f, "\n"); \
+           fprintf_lineno(f, 1, ""); \
+           fprintf(f, "break;\n")
 
 static void start_rule(bucket *bp, int s_lineno);
+#if defined(YYBTYACC)
+static void copy_initial_action(void);
+static void copy_destructor(void);
+static char *process_destructor_XX(char *code, char *tag);
+#endif
 
+#define CACHE_SIZE 256
 static char *cache;
 static int cinc, cache_size;
 
 int ntags;
-static int tagmax;
+static int tagmax, havetags;
 static char **tag_table;
 
 static char saw_eof;
@@ -30,6 +51,9 @@ static bucket *goal;
 static Value_t prec;
 static int gensym;
 static char last_was_action;
+#if defined(YYBTYACC)
+static int trialaction;
+#endif
 
 static int maxitems;
 static bucket **pitem;
@@ -45,73 +69,258 @@ char line_format[] = "#line %d \"%s\"\n";
 param *lex_param;
 param *parse_param;
 
+static const char *code_keys[] =
+{
+    "", "requires", "provides", "top", "imports",
+};
+
+struct code_lines code_lines[CODE_MAX];
+
+#if defined(YYBTYACC)
+int destructor = 0;    /* =1 if at least one %destructor */
+
+static bucket *default_destructor[3] =
+{0, 0, 0};
+
+#define UNTYPED_DEFAULT 0
+#define TYPED_DEFAULT   1
+#define TYPE_SPECIFIED  2
+
+static bucket *
+lookup_type_destructor(char *tag)
+{
+    const char fmt[] = "%.*s destructor";
+    char name[1024] = "\0";
+    bucket *bp, **bpp = &default_destructor[TYPE_SPECIFIED];
+
+    while ((bp = *bpp) != NULL)
+    {
+       if (bp->tag == tag)
+           return (bp);
+       bpp = &bp->link;
+    }
+
+    sprintf(name, fmt, (int)(sizeof(name) - sizeof(fmt)), tag);
+    *bpp = bp = make_bucket(name);
+    bp->tag = tag;
+
+    return (bp);
+}
+#endif /* defined(YYBTYACC) */
+
 static void
 cachec(int c)
 {
     assert(cinc >= 0);
     if (cinc >= cache_size)
     {
-       cache_size += 256;
-       cache = REALLOC(cache, cache_size);
+       cache_size += CACHE_SIZE;
+       cache = TREALLOC(char, cache, cache_size);
        NO_SPACE(cache);
     }
     cache[cinc] = (char)c;
     ++cinc;
 }
 
-static void
-get_line(void)
+typedef enum
 {
-    FILE *f = input_file;
-    int c;
-    int i;
+    ldSPC1,
+    ldSPC2,
+    ldNAME,
+    ldSPC3,
+    ldNUM,
+    ldSPC4,
+    ldFILE,
+    ldOK,
+    ldERR
+}
+LINE_DIR;
 
-    if (saw_eof || (c = getc(f)) == EOF)
+/*
+ * Expect this pattern:
+ *     /^[[:space:]]*#[[:space:]]*
+ *       line[[:space:]]+
+ *       [[:digit:]]+
+ *       ([[:space:]]*|[[:space:]]+"[^"]+")/
+ */
+static int
+line_directive(void)
+{
+#define UNLESS(what) if (what) { ld = ldERR; break; }
+    int n;
+    int line_1st = -1;
+    int name_1st = -1;
+    int name_end = -1;
+    LINE_DIR ld = ldSPC1;
+    for (n = 0; (ld <= ldOK) && (line[n] != '\0'); ++n)
     {
-       if (line)
+       int ch = UCH(line[n]);
+       switch (ld)
+       {
+       case ldSPC1:
+           if (isspace(UCH(ch)))
+           {
+               break;
+           }
+           else
+               UNLESS(ch != '#');
+           ld = ldSPC2;
+           break;
+       case ldSPC2:
+           if (isspace(UCH(ch)))
+           {
+               break;
+           }
+           /* FALLTHRU */
+       case ldNAME:
+           UNLESS(strncmp(line + n, "line", 4));
+           n += 4;
+           if (line[n] == '\0')
+           {
+               ld = ldOK;
+               break;
+           }
+           else
+               UNLESS(!isspace(UCH(line[n])));
+           ld = ldSPC3;
+           break;
+       case ldSPC3:
+           if (isspace(UCH(ch)))
+           {
+               break;
+           }
+           else
+               UNLESS(!isdigit(UCH(ch)));
+           line_1st = n;
+           ld = ldNUM;         /* this is needed, but cppcheck says no... */
+           /* FALLTHRU */
+       case ldNUM:
+           if (isdigit(UCH(ch)))
+           {
+               break;
+           }
+           else
+               UNLESS(!isspace(UCH(ch)));
+           ld = ldSPC4;
+           break;
+       case ldSPC4:
+           if (isspace(UCH(ch)))
+           {
+               break;
+           }
+           else
+               UNLESS(ch != '"');
+           UNLESS(line[n + 1] == '"');
+           ld = ldFILE;
+           name_1st = n;
+           break;
+       case ldFILE:
+           if (ch != '"')
+           {
+               break;
+           }
+           ld = ldOK;
+           name_end = n;
+           /* FALLTHRU */
+       case ldERR:
+       case ldOK:
+           break;
+       }
+    }
+
+    if (ld == ldOK)
+    {
+       size_t need = (size_t)(name_end - name_1st);
+       if ((long)need > (long)input_file_name_len)
        {
-           FREE(line);
-           line = 0;
+           input_file_name_len = ((need + 1) * 3) / 2;
+           input_file_name = TREALLOC(char, input_file_name, input_file_name_len);
+           NO_SPACE(input_file_name);
+       }
+       if ((long)need > 0)
+       {
+           memcpy(input_file_name, line + name_1st + 1, need - 1);
+           input_file_name[need - 1] = '\0';
+       }
+       else
+       {
+           input_file_name[0] = '\0';
        }
-       cptr = 0;
-       saw_eof = 1;
-       return;
     }
 
-    if (line == 0 || linesize != (LINESIZE + 1))
+    if (ld >= ldNUM && ld < ldERR)
     {
-       if (line)
-           FREE(line);
-       linesize = LINESIZE + 1;
-       line = MALLOC(linesize);
-       NO_SPACE(line);
+       if (line_1st >= 0)
+       {
+           lineno = (int)strtol(line + line_1st, NULL, 10) - 1;
+       }
+       else
+       {
+           lineno = 0;
+       }
     }
 
-    i = 0;
-    ++lineno;
-    for (;;)
+    return (ld == ldOK);
+#undef UNLESS
+}
+
+static void
+get_line(void)
+{
+    FILE *f = input_file;
+
+    do
     {
-       line[i] = (char)c;
-       if (c == '\n')
+       int c;
+       int i;
+
+       if (saw_eof || (c = getc(f)) == EOF)
        {
-           cptr = line;
+           if (line)
+           {
+               FREE(line);
+               line = 0;
+           }
+           cptr = 0;
+           saw_eof = 1;
            return;
        }
-       if (++i >= linesize)
+
+       if (line == NULL || linesize != (LINESIZE + 1))
        {
-           linesize += LINESIZE;
-           line = REALLOC(line, linesize);
+           if (line)
+               FREE(line);
+           linesize = LINESIZE + 1;
+           line = TMALLOC(char, linesize);
            NO_SPACE(line);
        }
-       c = getc(f);
-       if (c == EOF)
+
+       i = 0;
+       ++lineno;
+       for (;;)
        {
-           line[i] = '\n';
-           saw_eof = 1;
-           cptr = line;
-           return;
+           line[i++] = (char)c;
+           if (c == '\n')
+               break;
+           if ((i + 3) >= linesize)
+           {
+               linesize += LINESIZE;
+               line = TREALLOC(char, line, linesize);
+               NO_SPACE(line);
+           }
+           c = getc(f);
+           if (c == EOF)
+           {
+               line[i++] = '\n';
+               saw_eof = 1;
+               break;
+           }
        }
+       line[i] = '\0';
     }
+    while (line_directive());
+    cptr = line;
+    return;
 }
 
 static char *
@@ -119,12 +328,12 @@ dup_line(void)
 {
     char *p, *s, *t;
 
-    if (line == 0)
-       return (0);
+    if (line == NULL)
+       return (NULL);
     s = line;
     while (*s != '\n')
        ++s;
-    p = MALLOC(s - line + 1);
+    p = TMALLOC(char, s - line + 1);
     NO_SPACE(p);
 
     s = line;
@@ -138,10 +347,10 @@ static void
 skip_comment(void)
 {
     char *s;
-
-    int st_lineno = lineno;
-    char *st_line = dup_line();
-    char *st_cptr = st_line + (cptr - line);
+    struct ainfo a;
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line);
 
     s = cptr + 2;
     for (;;)
@@ -149,14 +358,14 @@ skip_comment(void)
        if (*s == '*' && s[1] == '/')
        {
            cptr = s + 2;
-           FREE(st_line);
+           FREE(a.a_line);
            return;
        }
        if (*s == '\n')
        {
            get_line();
-           if (line == 0)
-               unterminated_comment(st_lineno, st_line, st_cptr);
+           if (line == NULL)
+               unterminated_comment(&a);
            s = cptr;
        }
        else
@@ -165,14 +374,14 @@ skip_comment(void)
 }
 
 static int
-nextc(void)
+next_inline(void)
 {
     char *s;
 
-    if (line == 0)
+    if (line == NULL)
     {
        get_line();
-       if (line == 0)
+       if (line == NULL)
            return (EOF);
     }
 
@@ -181,27 +390,6 @@ nextc(void)
     {
        switch (*s)
        {
-       case '\n':
-           get_line();
-           if (line == 0)
-               return (EOF);
-           s = cptr;
-           break;
-
-       case ' ':
-       case '\t':
-       case '\f':
-       case '\r':
-       case '\v':
-       case ',':
-       case ';':
-           ++s;
-           break;
-
-       case '\\':
-           cptr = s;
-           return ('%');
-
        case '/':
            if (s[1] == '*')
            {
@@ -213,7 +401,7 @@ nextc(void)
            else if (s[1] == '/')
            {
                get_line();
-               if (line == 0)
+               if (line == NULL)
                    return (EOF);
                s = cptr;
                break;
@@ -227,34 +415,85 @@ nextc(void)
     }
 }
 
-/*
- * Compare keyword to cached token, treating '_' and '-' the same.  Some
- * grammars rely upon this misfeature.
- */
 static int
-matchec(const char *name)
+nextc(void)
 {
-    const char *p = cache;
-    const char *q = name;
-    int code = 0;      /* assume mismatch */
+    int ch;
+    int finish = 0;
 
-    while (*p != '\0' && *q != '\0')
+    do
     {
-       char a = *p++;
-       char b = *q++;
-       if (a == '_')
-           a = '-';
-       if (b == '_')
-           b = '-';
-       if (a != b)
-           break;
-       if (*p == '\0' && *q == '\0')
+       switch (ch = next_inline())
        {
-           code = 1;
+       case '\n':
+           get_line();
+           break;
+       case ' ':
+       case '\t':
+       case '\f':
+       case '\r':
+       case '\v':
+       case ',':
+       case ';':
+           ++cptr;
+           break;
+       case '\\':
+           ch = '%';
+           /* FALLTHRU */
+       default:
+           finish = 1;
            break;
        }
     }
-    return code;
+    while (!finish);
+
+    return ch;
+}
+/* *INDENT-OFF* */
+static struct keyword
+{
+    char name[16];
+    int token;
+}
+keywords[] = {
+    { "binary",      NONASSOC },
+    { "code",        XCODE },
+    { "debug",       NONPOSIX_DEBUG },
+#if defined(YYBTYACC)
+    { "destructor",  DESTRUCTOR },
+#endif
+    { "error-verbose",ERROR_VERBOSE },
+    { "expect",      EXPECT },
+    { "expect-rr",   EXPECT_RR },
+    { "ident",       IDENT },
+#if defined(YYBTYACC)
+    { "initial-action", INITIAL_ACTION },
+#endif
+    { "left",        LEFT },
+    { "lex-param",   LEX_PARAM },
+#if defined(YYBTYACC)
+    { "locations",   LOCATIONS },
+#endif
+    { "nonassoc",    NONASSOC },
+    { "parse-param", PARSE_PARAM },
+    { "pure-parser", PURE_PARSER },
+    { "right",       RIGHT },
+    { "start",       START },
+    { "term",        TOKEN },
+    { "token",       TOKEN },
+    { "token-table", TOKEN_TABLE },
+    { "type",        TYPE },
+    { "union",       UNION },
+    { "yacc",        POSIX_YACC },
+};
+/* *INDENT-ON* */
+
+static int
+compare_keys(const void *a, const void *b)
+{
+    const struct keyword *p = (const struct keyword *)a;
+    const struct keyword *q = (const struct keyword *)b;
+    return strcmp(p->name, q->name);
 }
 
 static int
@@ -264,25 +503,31 @@ keyword(void)
     char *t_cptr = cptr;
 
     c = *++cptr;
-    if (isalpha(c))
+    if (isalpha(UCH(c)))
     {
+       struct keyword *key;
+
        cinc = 0;
        for (;;)
        {
-           if (isalpha(c))
+           if (isalpha(UCH(c)))
            {
-               if (isupper(c))
+               if (isupper(UCH(c)))
                    c = tolower(c);
                cachec(c);
            }
-           else if (isdigit(c)
+           else if (isdigit(UCH(c))
                     || c == '-'
-                    || c == '_'
                     || c == '.'
                     || c == '$')
            {
                cachec(c);
            }
+           else if (c == '_')
+           {
+               /* treat keywords spelled with '_' as if it were '-' */
+               cachec('-');
+           }
            else
            {
                break;
@@ -291,34 +536,10 @@ keyword(void)
        }
        cachec(NUL);
 
-       if (matchec("token") || matchec("term"))
-           return (TOKEN);
-       if (matchec("type"))
-           return (TYPE);
-       if (matchec("left"))
-           return (LEFT);
-       if (matchec("right"))
-           return (RIGHT);
-       if (matchec("nonassoc") || matchec("binary"))
-           return (NONASSOC);
-       if (matchec("start"))
-           return (START);
-       if (matchec("union"))
-           return (UNION);
-       if (matchec("ident"))
-           return (IDENT);
-       if (matchec("expect"))
-           return (EXPECT);
-       if (matchec("expect-rr"))
-           return (EXPECT_RR);
-       if (matchec("pure-parser"))
-           return (PURE_PARSER);
-       if (matchec("parse-param"))
-           return (PARSE_PARAM);
-       if (matchec("lex-param"))
-           return (LEX_PARAM);
-       if (matchec("yacc"))
-           return (POSIX_YACC);
+       if ((key = bsearch(cache, keywords,
+                          sizeof(keywords) / sizeof(*key),
+                          sizeof(*key), compare_keys)))
+           return key->token;
     }
     else
     {
@@ -371,129 +592,273 @@ copy_ident(void)
     }
 }
 
-static void
-copy_text(void)
+static char *
+copy_string(int quote)
 {
-    int c;
-    int quote;
-    FILE *f = text_file;
-    int need_newline = 0;
-    int t_lineno = lineno;
-    char *t_line = dup_line();
-    char *t_cptr = t_line + (cptr - line - 2);
-
-    if (*cptr == '\n')
-    {
-       get_line();
-       if (line == 0)
-           unterminated_text(t_lineno, t_line, t_cptr);
-    }
-    if (!lflag)
-       fprintf(f, line_format, lineno, input_file_name);
+    struct mstring *temp = msnew();
+    struct ainfo a;
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line - 1);
 
-  loop:
-    c = *cptr++;
-    switch (c)
+    for (;;)
     {
-    case '\n':
-      next_line:
-       putc('\n', f);
-       need_newline = 0;
-       get_line();
-       if (line)
-           goto loop;
-       unterminated_text(t_lineno, t_line, t_cptr);
+       int c = *cptr++;
 
-    case '\'':
-    case '"':
+       mputc(temp, c);
+       if (c == quote)
        {
-           int s_lineno = lineno;
-           char *s_line = dup_line();
-           char *s_cptr = s_line + (cptr - line - 1);
-
-           quote = c;
-           putc(c, f);
-           for (;;)
-           {
-               c = *cptr++;
-               putc(c, f);
-               if (c == quote)
-               {
-                   need_newline = 1;
-                   FREE(s_line);
-                   goto loop;
-               }
-               if (c == '\n')
-                   unterminated_string(s_lineno, s_line, s_cptr);
-               if (c == '\\')
-               {
-                   c = *cptr++;
-                   putc(c, f);
-                   if (c == '\n')
-                   {
-                       get_line();
-                       if (line == 0)
-                           unterminated_string(s_lineno, s_line, s_cptr);
-                   }
-               }
-           }
+           FREE(a.a_line);
+           return msdone(temp);
        }
-
-    case '/':
-       putc(c, f);
-       need_newline = 1;
-       c = *cptr;
-       if (c == '/')
+       if (c == '\n')
+           unterminated_string(&a);
+       if (c == '\\')
        {
-           putc('*', f);
-           while ((c = *++cptr) != '\n')
+           c = *cptr++;
+           mputc(temp, c);
+           if (c == '\n')
            {
-               if (c == '*' && cptr[1] == '/')
-                   fprintf(f, "* ");
-               else
-                   putc(c, f);
+               get_line();
+               if (line == NULL)
+                   unterminated_string(&a);
            }
-           fprintf(f, "*/");
-           goto next_line;
        }
-       if (c == '*')
+    }
+}
+
+static char *
+copy_comment(void)
+{
+    struct mstring *temp = msnew();
+    int c;
+
+    c = *cptr;
+    if (c == '/')
+    {
+       mputc(temp, '*');
+       while ((c = *++cptr) != '\n')
        {
-           int c_lineno = lineno;
-           char *c_line = dup_line();
-           char *c_cptr = c_line + (cptr - line - 1);
+           mputc(temp, c);
+           if (c == '*' && cptr[1] == '/')
+               mputc(temp, ' ');
+       }
+       mputc(temp, '*');
+       mputc(temp, '/');
+    }
+    else if (c == '*')
+    {
+       struct ainfo a;
+       a.a_lineno = lineno;
+       a.a_line = dup_line();
+       a.a_cptr = a.a_line + (cptr - line - 1);
 
-           putc('*', f);
-           ++cptr;
-           for (;;)
+       mputc(temp, c);
+       ++cptr;
+       for (;;)
+       {
+           c = *cptr++;
+           mputc(temp, c);
+           if (c == '*' && *cptr == '/')
            {
-               c = *cptr++;
-               putc(c, f);
-               if (c == '*' && *cptr == '/')
-               {
-                   putc('/', f);
-                   ++cptr;
-                   FREE(c_line);
-                   goto loop;
-               }
-               if (c == '\n')
-               {
-                   get_line();
-                   if (line == 0)
-                       unterminated_comment(c_lineno, c_line, c_cptr);
-               }
+               mputc(temp, '/');
+               ++cptr;
+               FREE(a.a_line);
+               return msdone(temp);
+           }
+           if (c == '\n')
+           {
+               get_line();
+               if (line == NULL)
+                   unterminated_comment(&a);
            }
        }
-       need_newline = 1;
-       goto loop;
+    }
+    return msdone(temp);
+}
 
-    case '%':
-    case '\\':
+static int
+check_key(int pos)
+{
+    const char *key = code_keys[pos];
+    while (*cptr && *key)
+       if (*key++ != *cptr++)
+           return 0;
+    if (*key || (!isspace(UCH(*cptr)) && *cptr != L_CURL))
+       return 0;
+    cptr--;
+    return 1;
+}
+
+static void
+copy_code(void)
+{
+    int c;
+    int curl;
+    int cline;
+    int on_line = 0;
+    int pos = CODE_HEADER;
+    struct mstring *code_mstr;
+
+    /* read %code <keyword> { */
+    for (;;)
+    {
+       c = *++cptr;
+       if (c == EOF)
+           unexpected_EOF();
+       if (isspace(UCH(c)))
+           continue;
+
+       if (c == L_CURL)
+           break;
+
+       if (pos == CODE_HEADER)
+       {
+           switch (UCH(c))
+           {
+           case 'r':
+               pos = CODE_REQUIRES;
+               break;
+           case 'p':
+               pos = CODE_PROVIDES;
+               break;
+           case 't':
+               pos = CODE_TOP;
+               break;
+           case 'i':
+               pos = CODE_IMPORTS;
+               break;
+           default:
+               break;
+           }
+
+           if (pos == -1 || !check_key(pos))
+           {
+               syntax_error(lineno, line, cptr);
+               /*NOTREACHED */
+           }
+       }
+    }
+
+    cptr++;                    /* skip initial curl */
+    while (*cptr && isspace(UCH(*cptr)))       /* skip space */
+       cptr++;
+    curl = 1;                  /* nesting count */
+
+    /* gather text */
+    code_lines[pos].name = code_keys[pos];
+    if ((cline = (int)code_lines[pos].num) != 0)
+    {
+       code_mstr = msrenew(code_lines[pos].lines);
+    }
+    else
+    {
+       code_mstr = msnew();
+    }
+    cline++;
+    if (!lflag)
+       msprintf(code_mstr, line_format, lineno, input_file_name);
+    for (;;)
+    {
+       c = *cptr++;
+       switch (c)
+       {
+       case '\0':
+           get_line();
+           if (line == NULL)
+           {
+               unexpected_EOF();
+               /*NOTREACHED */
+           }
+           continue;
+       case '\n':
+           cline++;
+           on_line = 0;
+           break;
+       case L_CURL:
+           curl++;
+           break;
+       case R_CURL:
+           if (--curl == 0)
+           {
+               if (on_line > 1)
+               {
+                   mputc(code_mstr, '\n');
+                   cline++;
+               }
+               code_lines[pos].lines = msdone(code_mstr);
+               code_lines[pos].num = (size_t)cline;
+               return;
+           }
+           break;
+       default:
+           break;
+       }
+       mputc(code_mstr, c);
+       on_line++;
+    }
+}
+
+static void
+copy_text(void)
+{
+    int c;
+    FILE *f = text_file;
+    int need_newline = 0;
+    struct ainfo a;
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line - 2);
+
+    if (*cptr == '\n')
+    {
+       get_line();
+       if (line == NULL)
+           unterminated_text(&a);
+    }
+    fprintf_lineno(f, lineno, input_file_name);
+
+  loop:
+    c = *cptr++;
+    switch (c)
+    {
+    case '\n':
+       putc('\n', f);
+       need_newline = 0;
+       get_line();
+       if (line)
+           goto loop;
+       unterminated_text(&a);
+
+    case '\'':
+    case '"':
+       putc(c, f);
+       {
+           char *s = copy_string(c);
+           fputs(s, f);
+           free(s);
+       }
+       need_newline = 1;
+       goto loop;
+
+    case '/':
+       putc(c, f);
+       {
+           char *s = copy_comment();
+           fputs(s, f);
+           free(s);
+       }
+       need_newline = 1;
+       goto loop;
+
+    case '%':
+    case '\\':
        if (*cptr == R_CURL)
        {
            if (need_newline)
                putc('\n', f);
            ++cptr;
-           FREE(t_line);
+           FREE(a.a_line);
            return;
        }
        /* FALLTHRU */
@@ -525,26 +890,25 @@ static void
 copy_union(void)
 {
     int c;
-    int quote;
     int depth;
-    int u_lineno = lineno;
-    char *u_line = dup_line();
-    char *u_cptr = u_line + (cptr - line - 6);
+    struct ainfo a;
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line - 6);
 
     if (unionized)
        over_unionized(cptr - 6);
     unionized = 1;
 
-    if (!lflag)
-       fprintf(text_file, line_format, lineno, input_file_name);
-
     puts_both("#ifdef YYSTYPE\n");
     puts_both("#undef  YYSTYPE_IS_DECLARED\n");
     puts_both("#define YYSTYPE_IS_DECLARED 1\n");
     puts_both("#endif\n");
     puts_both("#ifndef YYSTYPE_IS_DECLARED\n");
     puts_both("#define YYSTYPE_IS_DECLARED 1\n");
-    puts_both("typedef union");
+
+    fprintf_lineno(text_file, lineno, input_file_name);
+    puts_both("typedef union YYSTYPE");
 
     depth = 0;
   loop:
@@ -553,10 +917,9 @@ copy_union(void)
     switch (c)
     {
     case '\n':
-      next_line:
        get_line();
-       if (line == 0)
-           unterminated_union(u_lineno, u_line, u_cptr);
+       if (line == NULL)
+           unterminated_union(&a);
        goto loop;
 
     case L_CURL:
@@ -568,7 +931,7 @@ copy_union(void)
        {
            puts_both(" YYSTYPE;\n");
            puts_both("#endif /* !YYSTYPE_IS_DECLARED */\n");
-           FREE(u_line);
+           FREE(a.a_line);
            return;
        }
        goto loop;
@@ -576,81 +939,17 @@ copy_union(void)
     case '\'':
     case '"':
        {
-           int s_lineno = lineno;
-           char *s_line = dup_line();
-           char *s_cptr = s_line + (cptr - line - 1);
-
-           quote = c;
-           for (;;)
-           {
-               c = *cptr++;
-               putc_both(c);
-               if (c == quote)
-               {
-                   FREE(s_line);
-                   goto loop;
-               }
-               if (c == '\n')
-                   unterminated_string(s_lineno, s_line, s_cptr);
-               if (c == '\\')
-               {
-                   c = *cptr++;
-                   putc_both(c);
-                   if (c == '\n')
-                   {
-                       get_line();
-                       if (line == 0)
-                           unterminated_string(s_lineno, s_line, s_cptr);
-                   }
-               }
-           }
+           char *s = copy_string(c);
+           puts_both(s);
+           free(s);
        }
+       goto loop;
 
     case '/':
-       c = *cptr;
-       if (c == '/')
-       {
-           putc_both('*');
-           while ((c = *++cptr) != '\n')
-           {
-               if (c == '*' && cptr[1] == '/')
-               {
-                   puts_both("* ");
-               }
-               else
-               {
-                   putc_both(c);
-               }
-           }
-           puts_both("*/\n");
-           goto next_line;
-       }
-       if (c == '*')
        {
-           int c_lineno = lineno;
-           char *c_line = dup_line();
-           char *c_cptr = c_line + (cptr - line - 1);
-
-           putc_both('*');
-           ++cptr;
-           for (;;)
-           {
-               c = *cptr++;
-               putc_both(c);
-               if (c == '*' && *cptr == '/')
-               {
-                   putc_both('/');
-                   ++cptr;
-                   FREE(c_line);
-                   goto loop;
-               }
-               if (c == '\n')
-               {
-                   get_line();
-                   if (line == 0)
-                       unterminated_comment(c_lineno, c_line, c_cptr);
-               }
-           }
+           char *s = copy_comment();
+           puts_both(s);
+           free(s);
        }
        goto loop;
 
@@ -659,90 +958,109 @@ copy_union(void)
     }
 }
 
+static char *
+after_blanks(char *s)
+{
+    while (*s != '\0' && isspace(UCH(*s)))
+       ++s;
+    return s;
+}
+
 /*
- * Keep a linked list of parameters
+ * Trim leading/trailing blanks, and collapse multiple embedded blanks to a
+ * single space.  Return index to last character in the buffer.
  */
-static void
-copy_param(int k)
+static int
+trim_blanks(char *buffer)
 {
-    char *buf;
-    int c;
-    param *head, *p;
-    int i;
-    int name, type2;
-
-    c = nextc();
-    if (c == EOF)
-       unexpected_EOF();
-    if (c != '{')
-       goto out;
-    cptr++;
+    if (*buffer != '\0')
+    {
+       char *d = buffer;
+       char *s = after_blanks(d);
 
-    c = nextc();
-    if (c == EOF)
-       unexpected_EOF();
-    if (c == '}')
-       goto out;
+       while ((*d++ = *s++) != '\0')
+       {
+           ;
+       }
 
-    buf = MALLOC(linesize);
-    NO_SPACE(buf);
+       --d;
+       while ((--d != buffer) && isspace(UCH(*d)))
+           *d = '\0';
 
-    for (i = 0; (c = *cptr++) != '}'; i++)
-    {
-       if (c == EOF)
-           unexpected_EOF();
-       buf[i] = (char)c;
+       for (s = d = buffer; (*d++ = *s++) != '\0';)
+       {
+           if (isspace(UCH(*s)))
+           {
+               *s = ' ';
+               while (isspace(UCH(*s)))
+               {
+                   *s++ = ' ';
+               }
+               --s;
+           }
+       }
     }
 
-    if (i == 0)
-       goto out;
-
-    buf[i--] = '\0';
-    while (i >= 0 && isspace(UCH(buf[i])))
-       buf[i--] = '\0';
+    return (int)strlen(buffer) - 1;
+}
 
-    if (buf[i] == ']')
+/*
+ * Scan forward in the current line-buffer looking for a right-curly bracket.
+ *
+ * Parameters begin with a left-curly bracket, and continue until there are no
+ * more interesting characters after the last right-curly bracket on the
+ * current line.  Bison documents parameters as separated like this:
+ *     {type param1} {type2 param2}
+ * but also accepts commas (although some versions of bison mishandle this)
+ *     {type param1,  type2 param2}
+ */
+static int
+more_curly(void)
+{
+    char *save = cptr;
+    int result = 0;
+    int finish = 0;
+    do
     {
-       int level = 1;
-       while (i >= 0 && level > 0 && buf[i] != '[')
+       switch (next_inline())
        {
-           if (buf[i] == ']')
-               ++level;
-           else if (buf[i] == '[')
-               --level;
-           i--;
+       case 0:
+       case '\n':
+           finish = 1;
+           break;
+       case R_CURL:
+           finish = 1;
+           result = 1;
+           break;
        }
-       if (i <= 0)
-           unexpected_EOF();
-       type2 = i--;
-    }
-    else
-    {
-       type2 = i + 1;
+       ++cptr;
     }
+    while (!finish);
+    cptr = save;
+    return result;
+}
 
-    while (i >= 0 && (isalnum(UCH(buf[i])) ||
-                     UCH(buf[i]) == '_'))
-       i--;
-
-    if (!isspace(UCH(buf[i])) && buf[i] != '*')
-       goto out;
-
-    name = i + 1;
+static void
+save_param(int k, char *buffer, int name, int type2)
+{
+    param *head, *p;
 
-    p = MALLOC(sizeof(*p));
+    p = TMALLOC(param, 1);
     NO_SPACE(p);
 
-    p->type2 = strdup(buf + type2);
+    p->type2 = strdup(buffer + type2);
     NO_SPACE(p->type2);
+    buffer[type2] = '\0';
+    (void)trim_blanks(p->type2);
 
-    buf[type2] = '\0';
-
-    p->name = strdup(buf + name);
+    p->name = strdup(buffer + name);
     NO_SPACE(p->name);
+    buffer[name] = '\0';
+    (void)trim_blanks(p->name);
 
-    buf[name] = '\0';
-    p->type = buf;
+    p->type = strdup(buffer);
+    NO_SPACE(p->type);
+    (void)trim_blanks(p->type);
 
     if (k == LEX_PARAM)
        head = lex_param;
@@ -763,58 +1081,231 @@ copy_param(int k)
            parse_param = p;
     }
     p->next = NULL;
-    return;
-
-  out:
-    syntax_error(lineno, line, cptr);
-}
-
-static int
-hexval(int c)
-{
-    if (c >= '0' && c <= '9')
-       return (c - '0');
-    if (c >= 'A' && c <= 'F')
-       return (c - 'A' + 10);
-    if (c >= 'a' && c <= 'f')
-       return (c - 'a' + 10);
-    return (-1);
 }
 
-static bucket *
-get_literal(void)
+/*
+ * Keep a linked list of parameters.  This may be multi-line, if the trailing
+ * right-curly bracket is absent.
+ */
+static void
+copy_param(int k)
 {
-    int c, quote;
-    int i;
-    int n;
-    char *s;
-    bucket *bp;
-    int s_lineno = lineno;
-    char *s_line = dup_line();
-    char *s_cptr = s_line + (cptr - line);
+    int c;
+    int name, type2;
+    int curly = 0;
+    char *buf = 0;
+    int i = -1;
+    size_t buf_size = 0;
+    int st_lineno = lineno;
+    char *comma;
 
-    quote = *cptr++;
-    cinc = 0;
-    for (;;)
+    do
     {
-       c = *cptr++;
-       if (c == quote)
-           break;
-       if (c == '\n')
-           unterminated_string(s_lineno, s_line, s_cptr);
-       if (c == '\\')
+       int state = curly;
+       c = next_inline();
+       switch (c)
        {
-           char *c_cptr = cptr - 1;
-
-           c = *cptr++;
-           switch (c)
+       case EOF:
+           unexpected_EOF();
+           break;
+       case L_CURL:
+           if (curly == 1)
            {
-           case '\n':
-               get_line();
-               if (line == 0)
-                   unterminated_string(s_lineno, s_line, s_cptr);
-               continue;
-
+               goto oops;
+           }
+           curly = 1;
+           st_lineno = lineno;
+           break;
+       case R_CURL:
+           if (curly != 1)
+           {
+               goto oops;
+           }
+           curly = 2;
+           break;
+       case '\n':
+           if (curly == 0)
+           {
+               goto oops;
+           }
+           break;
+       case '%':
+           if ((curly == 1) && (cptr == line))
+           {
+               lineno = st_lineno;
+               missing_brace();
+           }
+           /* FALLTHRU */
+       case '"':
+       case '\'':
+           goto oops;
+       default:
+           if (curly == 0 && !isspace(UCH(c)))
+           {
+               goto oops;
+           }
+           break;
+       }
+       if (buf == 0)
+       {
+           buf_size = (size_t)linesize;
+           buf = TMALLOC(char, buf_size);
+           NO_SPACE(buf);
+       }
+       else if (c == '\n')
+       {
+           char *tmp;
+
+           get_line();
+           if (line == NULL)
+               unexpected_EOF();
+           --cptr;
+           buf_size += (size_t)linesize;
+           tmp = TREALLOC(char, buf, buf_size);
+           NO_SPACE(tmp);
+           buf = tmp;
+       }
+       if (curly)
+       {
+           if ((state == 2) && (c == L_CURL))
+           {
+               buf[++i] = ',';
+           }
+           else if ((state == 2) && isspace(UCH(c)))
+           {
+               ;
+           }
+           else if ((c != L_CURL) && (c != R_CURL))
+           {
+               buf[++i] = (char)c;
+           }
+       }
+       cptr++;
+    }
+    while (curly < 2 || more_curly());
+
+    if (i == 0)
+    {
+       if (curly == 1)
+       {
+           lineno = st_lineno;
+           missing_brace();
+       }
+       goto oops;
+    }
+
+    buf[++i] = '\0';
+    (void)trim_blanks(buf);
+
+    comma = buf - 1;
+    do
+    {
+       char *parms = (comma + 1);
+       comma = strchr(parms, ',');
+       if (comma != 0)
+           *comma = '\0';
+
+       (void)trim_blanks(parms);
+       i = (int)strlen(parms) - 1;
+       if (i < 0)
+       {
+           goto oops;
+       }
+
+       if (parms[i] == ']')
+       {
+           int level = 1;
+           while (i >= 0)
+           {
+               char ch = parms[i--];
+               if (ch == ']')
+               {
+                   ++level;
+               }
+               else if (ch == '[')
+               {
+                   if (--level <= 1)
+                   {
+                       ++i;
+                       break;
+                   }
+               }
+           }
+           if (i <= 0)
+               unexpected_EOF();
+           type2 = i--;
+       }
+       else
+       {
+           type2 = i + 1;
+       }
+
+       while (i > 0 && (isalnum(UCH(parms[i])) || UCH(parms[i]) == '_'))
+           i--;
+
+       if (!isspace(UCH(parms[i])) && parms[i] != '*')
+           goto oops;
+
+       name = i + 1;
+
+       save_param(k, parms, name, type2);
+    }
+    while (comma != 0);
+    FREE(buf);
+    return;
+
+  oops:
+    FREE(buf);
+    syntax_error(lineno, line, cptr);
+}
+
+static int
+hexval(int c)
+{
+    if (c >= '0' && c <= '9')
+       return (c - '0');
+    if (c >= 'A' && c <= 'F')
+       return (c - 'A' + 10);
+    if (c >= 'a' && c <= 'f')
+       return (c - 'a' + 10);
+    return (-1);
+}
+
+static bucket *
+get_literal(void)
+{
+    int c, quote;
+    int i;
+    int n;
+    char *s;
+    bucket *bp;
+    struct ainfo a;
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line);
+
+    quote = *cptr++;
+    cinc = 0;
+    for (;;)
+    {
+       c = *cptr++;
+       if (c == quote)
+           break;
+       if (c == '\n')
+           unterminated_string(&a);
+       if (c == '\\')
+       {
+           char *c_cptr = cptr - 1;
+
+           c = *cptr++;
+           switch (c)
+           {
+           case '\n':
+               get_line();
+               if (line == NULL)
+                   unterminated_string(&a);
+               continue;
+
            case '0':
            case '1':
            case '2':
@@ -884,10 +1375,10 @@ get_literal(void)
        }
        cachec(c);
     }
-    FREE(s_line);
+    FREE(a.a_line);
 
     n = cinc;
-    s = MALLOC(n);
+    s = TMALLOC(char, n);
     NO_SPACE(s);
 
     for (i = 0; i < n; ++i)
@@ -907,7 +1398,7 @@ get_literal(void)
            cachec('\\');
            cachec(c);
        }
-       else if (isprint(c))
+       else if (isprint(UCH(c)))
            cachec(c);
        else
        {
@@ -962,8 +1453,6 @@ get_literal(void)
 static int
 is_reserved(char *name)
 {
-    char *s;
-
     if (strcmp(name, ".") == 0 ||
        strcmp(name, "$accept") == 0 ||
        strcmp(name, "$end") == 0)
@@ -971,7 +1460,8 @@ is_reserved(char *name)
 
     if (name[0] == '$' && name[1] == '$' && isdigit(UCH(name[2])))
     {
-       s = name + 3;
+       char *s = name + 3;
+
        while (isdigit(UCH(*s)))
            ++s;
        if (*s == NUL)
@@ -1001,21 +1491,59 @@ static Value_t
 get_number(void)
 {
     int c;
-    Value_t n;
+    long n;
+    char *base = cptr;
 
     n = 0;
-    for (c = *cptr; isdigit(c); c = *++cptr)
-       n = (Value_t) (10 * n + (c - '0'));
+    for (c = *cptr; isdigit(UCH(c)); c = *++cptr)
+    {
+       n = (10 * n + (c - '0'));
+       if (n > MAXYYINT)
+       {
+           syntax_error(lineno, line, base);
+           /*NOTREACHED */
+       }
+    }
 
-    return (n);
+    return (Value_t)(n);
 }
 
 static char *
-get_tag(void)
+cache_tag(char *tag, size_t len)
 {
-    int c;
     int i;
     char *s;
+
+    for (i = 0; i < ntags; ++i)
+    {
+       if (strncmp(tag, tag_table[i], len) == 0 &&
+           tag_table[i][len] == NUL)
+           return (tag_table[i]);
+    }
+
+    if (ntags >= tagmax)
+    {
+       tagmax += 16;
+       tag_table =
+           (tag_table
+            ? TREALLOC(char *, tag_table, tagmax)
+            : TMALLOC(char *, tagmax));
+       NO_SPACE(tag_table);
+    }
+
+    s = TMALLOC(char, len + 1);
+    NO_SPACE(s);
+
+    strncpy(s, tag, len);
+    s[len] = 0;
+    tag_table[ntags++] = s;
+    return s;
+}
+
+static char *
+get_tag(void)
+{
+    int c;
     int t_lineno = lineno;
     char *t_line = dup_line();
     char *t_cptr = t_line + (cptr - line);
@@ -1024,7 +1552,7 @@ get_tag(void)
     c = nextc();
     if (c == EOF)
        unexpected_EOF();
-    if (!isalpha(c) && c != '_' && c != '$')
+    if (!IS_NAME1(c))
        illegal_tag(t_lineno, t_line, t_cptr);
 
     cinc = 0;
@@ -1043,34 +1571,22 @@ get_tag(void)
        illegal_tag(t_lineno, t_line, t_cptr);
     ++cptr;
 
-    for (i = 0; i < ntags; ++i)
-    {
-       if (strcmp(cache, tag_table[i]) == 0)
-       {
-           FREE(t_line);
-           return (tag_table[i]);
-       }
-    }
-
-    if (ntags >= tagmax)
-    {
-       tagmax += 16;
-       tag_table = (char **)
-           (tag_table
-            ? REALLOC(tag_table, (unsigned)tagmax * sizeof(char *))
-            : MALLOC((unsigned)tagmax * sizeof(char *)));
-       NO_SPACE(tag_table);
-    }
+    FREE(t_line);
+    havetags = 1;
+    return cache_tag(cache, (size_t)cinc);
+}
 
-    s = MALLOC(cinc);
-    NO_SPACE(s);
+#if defined(YYBTYACC)
+static char *
+scan_id(void)
+{
+    char *b = cptr;
 
-    strcpy(s, cache);
-    tag_table[ntags] = s;
-    ++ntags;
-    FREE(t_line);
-    return (s);
+    while (IS_NAME2(UCH(*cptr)))
+       cptr++;
+    return cache_tag(b, (size_t)(cptr - b));
 }
+#endif
 
 static void
 declare_tokens(int assoc)
@@ -1096,7 +1612,7 @@ declare_tokens(int assoc)
 
     for (;;)
     {
-       if (isalpha(c) || c == '_' || c == '.' || c == '$')
+       if (isalpha(UCH(c)) || c == '_' || c == '.' || c == '$')
            bp = get_name();
        else if (c == '\'' || c == '"')
            bp = get_literal();
@@ -1118,7 +1634,7 @@ declare_tokens(int assoc)
        {
            if (bp->prec && prec != bp->prec)
                reprec_warning(bp->name);
-           bp->assoc = (Assoc_t) assoc;
+           bp->assoc = (Assoc_t)assoc;
            bp->prec = prec;
        }
 
@@ -1126,7 +1642,7 @@ declare_tokens(int assoc)
        if (c == EOF)
            unexpected_EOF();
 
-       if (isdigit(c))
+       if (isdigit(UCH(c)))
        {
            value = get_number();
            if (bp->value != UNDEFINED && value != bp->value)
@@ -1162,7 +1678,7 @@ declare_expect(int assoc)
 
     for (;;)
     {
-       if (isdigit(c))
+       if (isdigit(UCH(c)))
        {
            if (assoc == EXPECT)
                SRexpect = get_number();
@@ -1175,7 +1691,7 @@ declare_expect(int assoc)
         * Spaces, tabs, and numbers are ok,
         * words, punc., etc. are syntax errors.
         */
-       else if (c == '\n' || isalpha(c) || !isspace(c))
+       else if (c == '\n' || isalpha(UCH(c)) || !isspace(UCH(c)))
        {
            syntax_error(lineno, line, cptr);
        }
@@ -1188,33 +1704,88 @@ declare_expect(int assoc)
     }
 }
 
+#if defined(YYBTYACC)
+static void
+declare_argtypes(bucket *bp)
+{
+    char *tags[MAXARGS];
+    int args = 0;
+
+    if (bp->args >= 0)
+       retyped_warning(bp->name);
+    cptr++;                    /* skip open paren */
+    for (;;)
+    {
+       int c = nextc();
+       if (c == EOF)
+           unexpected_EOF();
+       if (c != '<')
+           syntax_error(lineno, line, cptr);
+       tags[args++] = get_tag();
+       c = nextc();
+       if (c == R_PAREN)
+           break;
+       if (c == EOF)
+           unexpected_EOF();
+    }
+    cptr++;                    /* skip close paren */
+    bp->args = args;
+    bp->argnames = TMALLOC(char *, args);
+    NO_SPACE(bp->argnames);
+    bp->argtags = CALLOC(sizeof(char *), args + 1);
+    NO_SPACE(bp->argtags);
+    while (--args >= 0)
+    {
+       bp->argtags[args] = tags[args];
+       bp->argnames[args] = NULL;
+    }
+}
+#endif
+
 static void
 declare_types(void)
 {
     int c;
-    bucket *bp;
-    char *tag;
+    bucket *bp = NULL;
+    char *tag = NULL;
 
     c = nextc();
     if (c == EOF)
        unexpected_EOF();
-    if (c != '<')
-       syntax_error(lineno, line, cptr);
-    tag = get_tag();
+    if (c == '<')
+       tag = get_tag();
 
     for (;;)
     {
        c = nextc();
-       if (isalpha(c) || c == '_' || c == '.' || c == '$')
+       if (c == EOF)
+           unexpected_EOF();
+       if (isalpha(UCH(c)) || c == '_' || c == '.' || c == '$')
+       {
            bp = get_name();
+#if defined(YYBTYACC)
+           if (nextc() == L_PAREN)
+               declare_argtypes(bp);
+           else
+               bp->args = 0;
+#endif
+       }
        else if (c == '\'' || c == '"')
+       {
            bp = get_literal();
+#if defined(YYBTYACC)
+           bp->args = 0;
+#endif
+       }
        else
            return;
 
-       if (bp->tag && tag != bp->tag)
-           retyped_warning(bp->name);
-       bp->tag = tag;
+       if (tag)
+       {
+           if (bp->tag && tag != bp->tag)
+               retyped_warning(bp->name);
+           bp->tag = tag;
+       }
     }
 }
 
@@ -1227,7 +1798,7 @@ declare_start(void)
     c = nextc();
     if (c == EOF)
        unexpected_EOF();
-    if (!isalpha(c) && c != '_' && c != '.' && c != '$')
+    if (!isalpha(UCH(c)) && c != '_' && c != '.' && c != '$')
        syntax_error(lineno, line, cptr);
     bp = get_name();
     if (bp->class == TERM)
@@ -1240,15 +1811,15 @@ declare_start(void)
 static void
 read_declarations(void)
 {
-    int c, k;
-
-    cache_size = 256;
-    cache = MALLOC(cache_size);
+    cache_size = CACHE_SIZE;
+    cache = TMALLOC(char, cache_size);
     NO_SPACE(cache);
 
     for (;;)
     {
-       c = nextc();
+       int k;
+       int c = nextc();
+
        if (c == EOF)
            unexpected_EOF();
        if (c != '%')
@@ -1262,6 +1833,10 @@ read_declarations(void)
            copy_ident();
            break;
 
+       case XCODE:
+           copy_code();
+           break;
+
        case TEXT:
            copy_text();
            break;
@@ -1299,6 +1874,32 @@ read_declarations(void)
            copy_param(k);
            break;
 
+       case TOKEN_TABLE:
+           token_table = 1;
+           break;
+
+       case ERROR_VERBOSE:
+           error_verbose = 1;
+           break;
+
+#if defined(YYBTYACC)
+       case LOCATIONS:
+           locations = 1;
+           break;
+
+       case DESTRUCTOR:
+           destructor = 1;
+           copy_destructor();
+           break;
+       case INITIAL_ACTION:
+           copy_initial_action();
+           break;
+#endif
+
+       case NONPOSIX_DEBUG:
+           tflag = 1;
+           break;
+
        case POSIX_YACC:
            /* noop for bison compatibility. byacc is already designed to be posix
             * yacc compatible. */
@@ -1313,7 +1914,7 @@ initialize_grammar(void)
     nitems = 4;
     maxitems = 300;
 
-    pitem = (bucket **)MALLOC((unsigned)maxitems * sizeof(bucket *));
+    pitem = TMALLOC(bucket *, maxitems);
     NO_SPACE(pitem);
 
     pitem[0] = 0;
@@ -1324,21 +1925,21 @@ initialize_grammar(void)
     nrules = 3;
     maxrules = 100;
 
-    plhs = (bucket **)MALLOC((unsigned)maxrules * sizeof(bucket *));
+    plhs = TMALLOC(bucket *, maxrules);
     NO_SPACE(plhs);
 
     plhs[0] = 0;
     plhs[1] = 0;
     plhs[2] = 0;
 
-    rprec = (short *)MALLOC((unsigned)maxrules * sizeof(short));
+    rprec = TMALLOC(Value_t, maxrules);
     NO_SPACE(rprec);
 
     rprec[0] = 0;
     rprec[1] = 0;
     rprec[2] = 0;
 
-    rassoc = (char *)MALLOC((unsigned)maxrules * sizeof(char));
+    rassoc = TMALLOC(Assoc_t, maxrules);
     NO_SPACE(rassoc);
 
     rassoc[0] = TOKEN;
@@ -1350,7 +1951,7 @@ static void
 expand_items(void)
 {
     maxitems += 300;
-    pitem = (bucket **)REALLOC(pitem, (unsigned)maxitems * sizeof(bucket *));
+    pitem = TREALLOC(bucket *, pitem, maxitems);
     NO_SPACE(pitem);
 }
 
@@ -1359,56 +1960,483 @@ expand_rules(void)
 {
     maxrules += 100;
 
-    plhs = (bucket **)REALLOC(plhs, (unsigned)maxrules * sizeof(bucket *));
+    plhs = TREALLOC(bucket *, plhs, maxrules);
     NO_SPACE(plhs);
 
-    rprec = (short *)REALLOC(rprec, (unsigned)maxrules * sizeof(short));
+    rprec = TREALLOC(Value_t, rprec, maxrules);
     NO_SPACE(rprec);
 
-    rassoc = (char *)REALLOC(rassoc, (unsigned)maxrules * sizeof(char));
+    rassoc = TREALLOC(Assoc_t, rassoc, maxrules);
     NO_SPACE(rassoc);
 }
 
-static void
-advance_to_start(void)
+/* set immediately prior to where copy_args() could be called, and incremented by
+   the various routines that will rescan the argument list as appropriate */
+static int rescan_lineno;
+#if defined(YYBTYACC)
+
+static char *
+copy_args(int *alen)
 {
-    int c;
-    bucket *bp;
-    char *s_cptr;
-    int s_lineno;
+    struct mstring *s = msnew();
+    int depth = 0, len = 1;
+    char c, quote = 0;
+    struct ainfo a;
 
-    for (;;)
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line - 1);
+
+    while ((c = *cptr++) != R_PAREN || depth || quote)
     {
-       c = nextc();
-       if (c != '%')
-           break;
-       s_cptr = cptr;
-       switch (keyword())
+       if (c == ',' && !quote && !depth)
        {
-       case MARK:
-           no_grammar();
-
-       case TEXT:
-           copy_text();
-           break;
-
-       case START:
-           declare_start();
-           break;
-
-       default:
-           syntax_error(lineno, line, s_cptr);
+           len++;
+           mputc(s, 0);
+           continue;
        }
-    }
-
-    c = nextc();
-    if (!isalpha(c) && c != '_' && c != '.' && c != '_')
-       syntax_error(lineno, line, cptr);
-    bp = get_name();
-    if (goal == 0)
-    {
-       if (bp->class == TERM)
-           terminal_start(bp->name);
+       mputc(s, c);
+       if (c == '\n')
+       {
+           get_line();
+           if (!line)
+           {
+               if (quote)
+                   unterminated_string(&a);
+               else
+                   unterminated_arglist(&a);
+           }
+       }
+       else if (quote)
+       {
+           if (c == quote)
+               quote = 0;
+           else if (c == '\\')
+           {
+               if (*cptr != '\n')
+                   mputc(s, *cptr++);
+           }
+       }
+       else
+       {
+           if (c == L_PAREN)
+               depth++;
+           else if (c == R_PAREN)
+               depth--;
+           else if (c == '\"' || c == '\'')
+               quote = c;
+       }
+    }
+    if (alen)
+       *alen = len;
+    FREE(a.a_line);
+    return msdone(s);
+}
+
+static char *
+parse_id(char *p, char **save)
+{
+    char *b;
+
+    while (isspace(UCH(*p)))
+       if (*p++ == '\n')
+           rescan_lineno++;
+    if (!isalpha(UCH(*p)) && *p != '_')
+       return NULL;
+    b = p;
+    while (IS_NAME2(UCH(*p)))
+       p++;
+    if (save)
+    {
+       *save = cache_tag(b, (size_t)(p - b));
+    }
+    return p;
+}
+
+static char *
+parse_int(char *p, int *save)
+{
+    int neg = 0, val = 0;
+
+    while (isspace(UCH(*p)))
+       if (*p++ == '\n')
+           rescan_lineno++;
+    if (*p == '-')
+    {
+       neg = 1;
+       p++;
+    }
+    if (!isdigit(UCH(*p)))
+       return NULL;
+    while (isdigit(UCH(*p)))
+       val = val * 10 + *p++ - '0';
+    if (neg)
+       val = -val;
+    if (save)
+       *save = val;
+    return p;
+}
+
+static void
+parse_arginfo(bucket *a, char *args, int argslen)
+{
+    char *p = args, *tmp;
+    int i, redec = 0;
+
+    if (a->args >= 0)
+    {
+       if (a->args != argslen)
+           arg_number_disagree_warning(rescan_lineno, a->name);
+       redec = 1;
+    }
+    else
+    {
+       if ((a->args = argslen) == 0)
+           return;
+       a->argnames = TMALLOC(char *, argslen);
+       NO_SPACE(a->argnames);
+       a->argtags = TMALLOC(char *, argslen);
+       NO_SPACE(a->argtags);
+    }
+    if (!args)
+       return;
+    for (i = 0; i < argslen; i++)
+    {
+       while (isspace(UCH(*p)))
+           if (*p++ == '\n')
+               rescan_lineno++;
+       if (*p++ != '$')
+           bad_formals();
+       while (isspace(UCH(*p)))
+           if (*p++ == '\n')
+               rescan_lineno++;
+       if (*p == '<')
+       {
+           havetags = 1;
+           if (!(p = parse_id(p + 1, &tmp)))
+               bad_formals();
+           while (isspace(UCH(*p)))
+               if (*p++ == '\n')
+                   rescan_lineno++;
+           if (*p++ != '>')
+               bad_formals();
+           if (redec)
+           {
+               if (a->argtags[i] != tmp)
+                   arg_type_disagree_warning(rescan_lineno, i + 1, a->name);
+           }
+           else
+               a->argtags[i] = tmp;
+       }
+       else if (!redec)
+           a->argtags[i] = NULL;
+       if (!(p = parse_id(p, &a->argnames[i])))
+           bad_formals();
+       while (isspace(UCH(*p)))
+           if (*p++ == '\n')
+               rescan_lineno++;
+       if (*p++)
+           bad_formals();
+    }
+    free(args);
+}
+
+static char *
+compile_arg(char **theptr, char *yyvaltag)
+{
+    char *p = *theptr;
+    struct mstring *c = msnew();
+    int i, n;
+    Value_t *offsets = NULL, maxoffset;
+    bucket **rhs;
+
+    maxoffset = 0;
+    n = 0;
+    for (i = nitems - 1; pitem[i]; --i)
+    {
+       n++;
+       if (pitem[i]->class != ARGUMENT)
+           maxoffset++;
+    }
+    if (maxoffset > 0)
+    {
+       int j;
+
+       offsets = TCMALLOC(Value_t, maxoffset + 1);
+       NO_SPACE(offsets);
+
+       for (j = 0, i++; i < nitems; i++)
+           if (pitem[i]->class != ARGUMENT)
+               offsets[++j] = (Value_t)(i - nitems + 1);
+    }
+    rhs = pitem + nitems - 1;
+
+    if (yyvaltag)
+       msprintf(c, "yyval.%s = ", yyvaltag);
+    else
+       msprintf(c, "yyval = ");
+    while (*p)
+    {
+       if (*p == '$')
+       {
+           char *tag = NULL;
+           if (*++p == '<')
+               if (!(p = parse_id(++p, &tag)) || *p++ != '>')
+                   illegal_tag(rescan_lineno, NULL, NULL);
+           if (isdigit(UCH(*p)) || *p == '-')
+           {
+               int val;
+               if (!(p = parse_int(p, &val)))
+                   dollar_error(rescan_lineno, NULL, NULL);
+               if (val <= 0)
+                   i = val - n;
+               else if (val > maxoffset)
+               {
+                   dollar_warning(rescan_lineno, val);
+                   i = val - maxoffset;
+               }
+               else if (maxoffset > 0)
+               {
+                   i = offsets[val];
+                   if (!tag && !(tag = rhs[i]->tag) && havetags)
+                       untyped_rhs(val, rhs[i]->name);
+               }
+               msprintf(c, "yystack.l_mark[%d]", i);
+               if (tag)
+                   msprintf(c, ".%s", tag);
+               else if (havetags)
+                   unknown_rhs(val);
+           }
+           else if (isalpha(UCH(*p)) || *p == '_')
+           {
+               char *arg;
+               if (!(p = parse_id(p, &arg)))
+                   dollar_error(rescan_lineno, NULL, NULL);
+               for (i = plhs[nrules]->args - 1; i >= 0; i--)
+                   if (arg == plhs[nrules]->argnames[i])
+                       break;
+               if (i < 0)
+                   unknown_arg_warning(rescan_lineno, "$", arg, NULL, NULL);
+               else if (!tag)
+                   tag = plhs[nrules]->argtags[i];
+               msprintf(c, "yystack.l_mark[%d]",
+                        i - plhs[nrules]->args + 1 - n);
+               if (tag)
+                   msprintf(c, ".%s", tag);
+               else if (havetags)
+                   untyped_arg_warning(rescan_lineno, "$", arg);
+           }
+           else
+               dollar_error(rescan_lineno, NULL, NULL);
+       }
+       else if (*p == '@')
+       {
+           at_error(rescan_lineno, NULL, NULL);
+       }
+       else
+       {
+           if (*p == '\n')
+               rescan_lineno++;
+           mputc(c, *p++);
+       }
+    }
+    *theptr = p;
+    if (maxoffset > 0)
+       FREE(offsets);
+    return msdone(c);
+}
+
+static int
+can_elide_arg(char **theptr, char *yyvaltag)
+{
+    char *p = *theptr;
+    int rv = 0;
+    int i, n = 0;
+    Value_t *offsets = NULL, maxoffset = 0;
+    bucket **rhs;
+    char *tag = 0;
+
+    if (*p++ != '$')
+       return 0;
+    if (*p == '<')
+    {
+       if (!(p = parse_id(++p, &tag)) || *p++ != '>')
+           return 0;
+    }
+    for (i = nitems - 1; pitem[i]; --i)
+    {
+       n++;
+       if (pitem[i]->class != ARGUMENT)
+           maxoffset++;
+    }
+    if (maxoffset > 0)
+    {
+       int j;
+
+       offsets = TCMALLOC(Value_t, maxoffset + 1);
+       NO_SPACE(offsets);
+
+       for (j = 0, i++; i < nitems; i++)
+           if (pitem[i]->class != ARGUMENT)
+               offsets[++j] = (Value_t)(i - nitems + 1);
+    }
+    rhs = pitem + nitems - 1;
+
+    if (isdigit(UCH(*p)) || *p == '-')
+    {
+       int val;
+       if (!(p = parse_int(p, &val)))
+           rv = 0;
+       else
+       {
+           if (val <= 0)
+               rv = 1 - val + n;
+           else if (val > maxoffset)
+               rv = 0;
+           else
+           {
+               i = offsets[val];
+               rv = 1 - i;
+               if (!tag)
+                   tag = rhs[i]->tag;
+           }
+       }
+    }
+    else if (isalpha(UCH(*p)) || *p == '_')
+    {
+       char *arg;
+       if (!(p = parse_id(p, &arg)))
+       {
+           FREE(offsets);
+           return 0;
+       }
+       for (i = plhs[nrules]->args - 1; i >= 0; i--)
+           if (arg == plhs[nrules]->argnames[i])
+               break;
+       if (i >= 0)
+       {
+           if (!tag)
+               tag = plhs[nrules]->argtags[i];
+           rv = plhs[nrules]->args + n - i;
+       }
+    }
+    if (tag && yyvaltag)
+    {
+       if (strcmp(tag, yyvaltag))
+           rv = 0;
+    }
+    else if (tag || yyvaltag)
+       rv = 0;
+    if (maxoffset > 0)
+       FREE(offsets);
+    if (p == 0 || *p || rv <= 0)
+       return 0;
+    *theptr = p + 1;
+    return rv;
+}
+
+#define ARG_CACHE_SIZE 1024
+static struct arg_cache
+{
+    struct arg_cache *next;
+    char *code;
+    int rule;
+}
+ *arg_cache[ARG_CACHE_SIZE];
+
+static int
+lookup_arg_cache(char *code)
+{
+    struct arg_cache *entry;
+
+    entry = arg_cache[strnshash(code) % ARG_CACHE_SIZE];
+    while (entry)
+    {
+       if (!strnscmp(entry->code, code))
+           return entry->rule;
+       entry = entry->next;
+    }
+    return -1;
+}
+
+static void
+insert_arg_cache(char *code, int rule)
+{
+    struct arg_cache *entry = NEW(struct arg_cache);
+    int i;
+
+    NO_SPACE(entry);
+    i = strnshash(code) % ARG_CACHE_SIZE;
+    entry->code = code;
+    entry->rule = rule;
+    entry->next = arg_cache[i];
+    arg_cache[i] = entry;
+}
+
+static void
+clean_arg_cache(void)
+{
+    struct arg_cache *e, *t;
+    int i;
+
+    for (i = 0; i < ARG_CACHE_SIZE; i++)
+    {
+       for (e = arg_cache[i]; (t = e); e = e->next, FREE(t))
+           free(e->code);
+       arg_cache[i] = NULL;
+    }
+}
+#endif /* defined(YYBTYACC) */
+
+static void
+advance_to_start(void)
+{
+    int c;
+    bucket *bp;
+    int s_lineno;
+#if defined(YYBTYACC)
+    char *args = NULL;
+    int argslen = 0;
+#endif
+
+    for (;;)
+    {
+       char *s_cptr;
+
+       c = nextc();
+       if (c != '%')
+           break;
+       s_cptr = cptr;
+       switch (keyword())
+       {
+       case XCODE:
+           copy_code();
+           break;
+
+       case MARK:
+           no_grammar();
+
+       case TEXT:
+           copy_text();
+           break;
+
+       case START:
+           declare_start();
+           break;
+
+       default:
+           syntax_error(lineno, line, s_cptr);
+       }
+    }
+
+    c = nextc();
+    if (!isalpha(UCH(c)) && c != '_' && c != '.' && c != '_')
+       syntax_error(lineno, line, cptr);
+    bp = get_name();
+    if (goal == 0)
+    {
+       if (bp->class == TERM)
+           terminal_start(bp->name);
        goal = bp;
     }
 
@@ -1416,9 +2444,22 @@ advance_to_start(void)
     c = nextc();
     if (c == EOF)
        unexpected_EOF();
+    rescan_lineno = lineno;    /* line# for possible inherited args rescan */
+#if defined(YYBTYACC)
+    if (c == L_PAREN)
+    {
+       ++cptr;
+       args = copy_args(&argslen);
+       NO_SPACE(args);
+       c = nextc();
+    }
+#endif
     if (c != ':')
        syntax_error(lineno, line, cptr);
     start_rule(bp, s_lineno);
+#if defined(YYBTYACC)
+    parse_arginfo(bp, args, argslen);
+#endif
     ++cptr;
 }
 
@@ -1428,6 +2469,8 @@ start_rule(bucket *bp, int s_lineno)
     if (bp->class == TERM)
        terminal_lhs(s_lineno);
     bp->class = NONTERM;
+    if (!bp->index)
+       bp->index = nrules;
     if (nrules >= maxrules)
        expand_rules();
     plhs[nrules] = bp;
@@ -1438,21 +2481,19 @@ start_rule(bucket *bp, int s_lineno)
 static void
 end_rule(void)
 {
-    int i;
-
     if (!last_was_action && plhs[nrules]->tag)
     {
        if (pitem[nitems - 1])
        {
+           int i;
+
            for (i = nitems - 1; (i > 0) && pitem[i]; --i)
                continue;
            if (pitem[i + 1] == 0 || pitem[i + 1]->tag != plhs[nrules]->tag)
-               default_action_warning();
+               default_action_warning(plhs[nrules]->name);
        }
        else
-       {
-           default_action_warning();
-       }
+           default_action_warning(plhs[nrules]->name);
     }
 
     last_was_action = 0;
@@ -1469,14 +2510,19 @@ insert_empty_rule(void)
     bucket *bp, **bpp;
 
     assert(cache);
+    assert(cache_size >= CACHE_SIZE);
     sprintf(cache, "$$%d", ++gensym);
     bp = make_bucket(cache);
     last_symbol->next = bp;
     last_symbol = bp;
     bp->tag = plhs[nrules]->tag;
-    bp->class = NONTERM;
+    bp->class = ACTION;
+#if defined(YYBTYACC)
+    bp->args = 0;
+#endif
 
-    if ((nitems += 2) > maxitems)
+    nitems = (Value_t)(nitems + 2);
+    if (nitems > maxitems)
        expand_items();
     bpp = pitem + nitems - 1;
     *bpp-- = bp;
@@ -1493,12 +2539,49 @@ insert_empty_rule(void)
     rassoc[nrules - 1] = TOKEN;
 }
 
+#if defined(YYBTYACC)
+static char *
+insert_arg_rule(char *arg, char *tag)
+{
+    int line_number = rescan_lineno;
+    char *code = compile_arg(&arg, tag);
+    int rule = lookup_arg_cache(code);
+    FILE *f = action_file;
+
+    if (rule < 0)
+    {
+       rule = nrules;
+       insert_arg_cache(code, rule);
+       trialaction = 1;        /* arg rules always run in trial mode */
+       begin_case(f, rule - 2);
+       fprintf_lineno(f, line_number, input_file_name);
+       fprintf(f, "%s;", code);
+       end_case(f);
+       insert_empty_rule();
+       plhs[rule]->tag = cache_tag(tag, strlen(tag));
+       plhs[rule]->class = ARGUMENT;
+    }
+    else
+    {
+       if (++nitems > maxitems)
+           expand_items();
+       pitem[nitems - 1] = plhs[rule];
+       free(code);
+    }
+    return arg + 1;
+}
+#endif
+
 static void
 add_symbol(void)
 {
     int c;
     bucket *bp;
     int s_lineno = lineno;
+#if defined(YYBTYACC)
+    char *args = NULL;
+    int argslen = 0;
+#endif
 
     c = *cptr;
     if (c == '\'' || c == '"')
@@ -1507,10 +2590,23 @@ add_symbol(void)
        bp = get_name();
 
     c = nextc();
+    rescan_lineno = lineno;    /* line# for possible inherited args rescan */
+#if defined(YYBTYACC)
+    if (c == L_PAREN)
+    {
+       ++cptr;
+       args = copy_args(&argslen);
+       NO_SPACE(args);
+       c = nextc();
+    }
+#endif
     if (c == ':')
     {
        end_rule();
        start_rule(bp, s_lineno);
+#if defined(YYBTYACC)
+       parse_arginfo(bp, args, argslen);
+#endif
        ++cptr;
        return;
     }
@@ -1519,52 +2615,451 @@ add_symbol(void)
        insert_empty_rule();
     last_was_action = 0;
 
+#if defined(YYBTYACC)
+    if (bp->args < 0)
+       bp->args = argslen;
+    if (argslen == 0 && bp->args > 0 && pitem[nitems - 1] == NULL)
+    {
+       int i;
+       if (plhs[nrules]->args != bp->args)
+           wrong_number_args_warning("default ", bp->name);
+       for (i = bp->args - 1; i >= 0; i--)
+           if (plhs[nrules]->argtags[i] != bp->argtags[i])
+               wrong_type_for_arg_warning(i + 1, bp->name);
+    }
+    else if (bp->args != argslen)
+       wrong_number_args_warning("", bp->name);
+    if (args != 0)
+    {
+       char *ap = args;
+       int i = 0;
+       int elide_cnt = can_elide_arg(&ap, bp->argtags[0]);
+
+       if (elide_cnt > argslen)
+           elide_cnt = 0;
+       if (elide_cnt)
+       {
+           for (i = 1; i < elide_cnt; i++)
+               if (can_elide_arg(&ap, bp->argtags[i]) != elide_cnt - i)
+               {
+                   elide_cnt = 0;
+                   break;
+               }
+       }
+       if (elide_cnt)
+       {
+           assert(i == elide_cnt);
+       }
+       else
+       {
+           ap = args;
+           i = 0;
+       }
+       for (; i < argslen; i++)
+           ap = insert_arg_rule(ap, bp->argtags[i]);
+       free(args);
+    }
+#endif /* defined(YYBTYACC) */
+
     if (++nitems > maxitems)
        expand_items();
     pitem[nitems - 1] = bp;
 }
 
-static char *
-after_blanks(char *s)
+static void
+copy_action(void)
 {
-    while (*s != '\0' && isspace(UCH(*s)))
-       ++s;
-    return s;
+    int c;
+    int i, j, n;
+    int depth;
+#if defined(YYBTYACC)
+    int haveyyval = 0;
+#endif
+    char *tag;
+    FILE *f = action_file;
+    struct ainfo a;
+    Value_t *offsets = NULL, maxoffset;
+    bucket **rhs;
+
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line);
+
+    if (last_was_action)
+       insert_empty_rule();
+    last_was_action = 1;
+#if defined(YYBTYACC)
+    trialaction = (*cptr == L_BRAC);
+#endif
+
+    begin_case(f, nrules - 2);
+#if defined(YYBTYACC)
+    if (backtrack)
+    {
+       if (!trialaction)
+           fprintf(f, "  if (!yytrial)\n");
+    }
+#endif
+    fprintf_lineno(f, lineno, input_file_name);
+    if (*cptr == '=')
+       ++cptr;
+
+    /* avoid putting curly-braces in first column, to ease editing */
+    if (*after_blanks(cptr) == L_CURL)
+    {
+       putc('\t', f);
+       cptr = after_blanks(cptr);
+    }
+
+    maxoffset = 0;
+    n = 0;
+    for (i = nitems - 1; pitem[i]; --i)
+    {
+       ++n;
+       if (pitem[i]->class != ARGUMENT)
+           maxoffset++;
+    }
+    if (maxoffset > 0)
+    {
+       offsets = TMALLOC(Value_t, maxoffset + 1);
+       NO_SPACE(offsets);
+
+       for (j = 0, i++; i < nitems; i++)
+       {
+           if (pitem[i]->class != ARGUMENT)
+           {
+               offsets[++j] = (Value_t)(i - nitems + 1);
+           }
+       }
+    }
+    rhs = pitem + nitems - 1;
+
+    depth = 0;
+  loop:
+    c = *cptr;
+    if (c == '$')
+    {
+       if (cptr[1] == '<')
+       {
+           int d_lineno = lineno;
+           char *d_line = dup_line();
+           char *d_cptr = d_line + (cptr - line);
+
+           ++cptr;
+           tag = get_tag();
+           c = *cptr;
+           if (c == '$')
+           {
+               fprintf(f, "yyval.%s", tag);
+               ++cptr;
+               FREE(d_line);
+               goto loop;
+           }
+           else if (isdigit(UCH(c)))
+           {
+               i = get_number();
+               if (i == 0)
+                   fprintf(f, "yystack.l_mark[%d].%s", -n, tag);
+               else if (i > maxoffset)
+               {
+                   dollar_warning(d_lineno, i);
+                   fprintf(f, "yystack.l_mark[%ld].%s",
+                           (long)(i - maxoffset), tag);
+               }
+               else if (offsets)
+                   fprintf(f, "yystack.l_mark[%ld].%s",
+                           (long)offsets[i], tag);
+               FREE(d_line);
+               goto loop;
+           }
+           else if (c == '-' && isdigit(UCH(cptr[1])))
+           {
+               ++cptr;
+               i = -get_number() - n;
+               fprintf(f, "yystack.l_mark[%d].%s", i, tag);
+               FREE(d_line);
+               goto loop;
+           }
+#if defined(YYBTYACC)
+           else if (isalpha(UCH(c)) || c == '_')
+           {
+               char *arg = scan_id();
+               for (i = plhs[nrules]->args - 1; i >= 0; i--)
+                   if (arg == plhs[nrules]->argnames[i])
+                       break;
+               if (i < 0)
+                   unknown_arg_warning(d_lineno, "$", arg, d_line, d_cptr);
+               fprintf(f, "yystack.l_mark[%d].%s",
+                       i - plhs[nrules]->args + 1 - n, tag);
+               FREE(d_line);
+               goto loop;
+           }
+#endif
+           else
+               dollar_error(d_lineno, d_line, d_cptr);
+       }
+       else if (cptr[1] == '$')
+       {
+           if (havetags)
+           {
+               tag = plhs[nrules]->tag;
+               if (tag == 0)
+                   untyped_lhs();
+               fprintf(f, "yyval.%s", tag);
+           }
+           else
+               fprintf(f, "yyval");
+           cptr += 2;
+#if defined(YYBTYACC)
+           haveyyval = 1;
+#endif
+           goto loop;
+       }
+       else if (isdigit(UCH(cptr[1])))
+       {
+           ++cptr;
+           i = get_number();
+           if (havetags && offsets)
+           {
+               if (i <= 0 || i > maxoffset)
+                   unknown_rhs(i);
+               tag = rhs[offsets[i]]->tag;
+               if (tag == 0)
+                   untyped_rhs(i, rhs[offsets[i]]->name);
+               fprintf(f, "yystack.l_mark[%ld].%s", (long)offsets[i], tag);
+           }
+           else
+           {
+               if (i == 0)
+                   fprintf(f, "yystack.l_mark[%d]", -n);
+               else if (i > maxoffset)
+               {
+                   dollar_warning(lineno, i);
+                   fprintf(f, "yystack.l_mark[%ld]", (long)(i - maxoffset));
+               }
+               else if (offsets)
+                   fprintf(f, "yystack.l_mark[%ld]", (long)offsets[i]);
+           }
+           goto loop;
+       }
+       else if (cptr[1] == '-')
+       {
+           cptr += 2;
+           i = get_number();
+           if (havetags)
+               unknown_rhs(-i);
+           fprintf(f, "yystack.l_mark[%d]", -i - n);
+           goto loop;
+       }
+#if defined(YYBTYACC)
+       else if (isalpha(UCH(cptr[1])) || cptr[1] == '_')
+       {
+           char *arg;
+           ++cptr;
+           arg = scan_id();
+           for (i = plhs[nrules]->args - 1; i >= 0; i--)
+               if (arg == plhs[nrules]->argnames[i])
+                   break;
+           if (i < 0)
+               unknown_arg_warning(lineno, "$", arg, line, cptr);
+           tag = (i < 0 ? NULL : plhs[nrules]->argtags[i]);
+           fprintf(f, "yystack.l_mark[%d]", i - plhs[nrules]->args + 1 - n);
+           if (tag)
+               fprintf(f, ".%s", tag);
+           else if (havetags)
+               untyped_arg_warning(lineno, "$", arg);
+           goto loop;
+       }
+#endif
+    }
+#if defined(YYBTYACC)
+    if (c == '@')
+    {
+       if (!locations)
+       {
+           int l_lineno = lineno;
+           char *l_line = dup_line();
+           char *l_cptr = l_line + (cptr - line);
+           syntax_error(l_lineno, l_line, l_cptr);
+       }
+       if (cptr[1] == '$')
+       {
+           fprintf(f, "yyloc");
+           cptr += 2;
+           goto loop;
+       }
+       else if (isdigit(UCH(cptr[1])))
+       {
+           ++cptr;
+           i = get_number();
+           if (i == 0)
+               fprintf(f, "yystack.p_mark[%d]", -n);
+           else if (i > maxoffset)
+           {
+               at_warning(lineno, i);
+               fprintf(f, "yystack.p_mark[%d]", i - maxoffset);
+           }
+           else if (offsets)
+               fprintf(f, "yystack.p_mark[%d]", offsets[i]);
+           goto loop;
+       }
+       else if (cptr[1] == '-')
+       {
+           cptr += 2;
+           i = get_number();
+           fprintf(f, "yystack.p_mark[%d]", -i - n);
+           goto loop;
+       }
+    }
+#endif
+    if (IS_NAME1(c))
+    {
+       do
+       {
+           putc(c, f);
+           c = *++cptr;
+       }
+       while (IS_NAME2(c));
+       goto loop;
+    }
+    ++cptr;
+#if defined(YYBTYACC)
+    if (backtrack)
+    {
+       if (trialaction && c == L_BRAC && depth == 0)
+       {
+           ++depth;
+           putc(L_CURL, f);
+           goto loop;
+       }
+       if (trialaction && c == R_BRAC && depth == 1)
+       {
+           --depth;
+           putc(R_CURL, f);
+           c = nextc();
+           if (c == L_BRAC && !haveyyval)
+           {
+               goto loop;
+           }
+           if (c == L_CURL && !haveyyval)
+           {
+               fprintf(f, "  if (!yytrial)\n");
+               fprintf_lineno(f, lineno, input_file_name);
+               trialaction = 0;
+               goto loop;
+           }
+           end_case(f);
+           FREE(a.a_line);
+           if (maxoffset > 0)
+               FREE(offsets);
+           return;
+       }
+    }
+#endif
+    putc(c, f);
+    switch (c)
+    {
+    case '\n':
+       get_line();
+       if (line)
+           goto loop;
+       unterminated_action(&a);
+
+    case ';':
+       if (depth > 0)
+           goto loop;
+       end_case(f);
+       free(a.a_line);
+       if (maxoffset > 0)
+           FREE(offsets);
+       return;
+
+#if defined(YYBTYACC)
+    case L_BRAC:
+       if (backtrack)
+           ++depth;
+       goto loop;
+
+    case R_BRAC:
+       if (backtrack)
+           --depth;
+       goto loop;
+#endif
+
+    case L_CURL:
+       ++depth;
+       goto loop;
+
+    case R_CURL:
+       if (--depth > 0)
+           goto loop;
+#if defined(YYBTYACC)
+       if (backtrack)
+       {
+           c = nextc();
+           if (c == L_BRAC && !haveyyval)
+           {
+               trialaction = 1;
+               goto loop;
+           }
+           if (c == L_CURL && !haveyyval)
+           {
+               fprintf(f, "  if (!yytrial)\n");
+               fprintf_lineno(f, lineno, input_file_name);
+               goto loop;
+           }
+       }
+#endif
+       end_case(f);
+       free(a.a_line);
+       if (maxoffset > 0)
+           FREE(offsets);
+       return;
+
+    case '\'':
+    case '"':
+       {
+           char *s = copy_string(c);
+           fputs(s, f);
+           free(s);
+       }
+       goto loop;
+
+    case '/':
+       {
+           char *s = copy_comment();
+           fputs(s, f);
+           free(s);
+       }
+       goto loop;
+
+    default:
+       goto loop;
+    }
 }
 
-static void
-copy_action(void)
+#if defined(YYBTYACC)
+static char *
+get_code(struct ainfo *a, const char *loc)
 {
     int c;
-    int i, n;
     int depth;
-    int quote;
     char *tag;
-    FILE *f = action_file;
-    int a_lineno = lineno;
-    char *a_line = dup_line();
-    char *a_cptr = a_line + (cptr - line);
-
-    if (last_was_action)
-       insert_empty_rule();
-    last_was_action = 1;
+    struct mstring *code_mstr = msnew();
 
-    fprintf(f, "case %d:\n", nrules - 2);
     if (!lflag)
-       fprintf(f, line_format, lineno, input_file_name);
-    if (*cptr == '=')
-       ++cptr;
+       msprintf(code_mstr, line_format, lineno, input_file_name);
 
-    /* avoid putting curly-braces in first column, to ease editing */
-    if (*after_blanks(cptr) == L_CURL)
-    {
-       putc('\t', f);
-       cptr = after_blanks(cptr);
-    }
+    cptr = after_blanks(cptr);
+    if (*cptr == L_CURL)
+       /* avoid putting curly-braces in first column, to ease editing */
+       mputc(code_mstr, '\t');
+    else
+       syntax_error(lineno, line, cptr);
 
-    n = 0;
-    for (i = nitems - 1; pitem[i]; --i)
-       ++n;
+    a->a_lineno = lineno;
+    a->a_line = dup_line();
+    a->a_cptr = a->a_line + (cptr - line);
 
     depth = 0;
   loop:
@@ -1582,25 +3077,8 @@ copy_action(void)
            c = *cptr;
            if (c == '$')
            {
-               fprintf(f, "yyval.%s", tag);
-               ++cptr;
-               FREE(d_line);
-               goto loop;
-           }
-           else if (isdigit(c))
-           {
-               i = get_number();
-               if (i > n)
-                   dollar_warning(d_lineno, i);
-               fprintf(f, "yystack.l_mark[%d].%s", i - n, tag);
-               FREE(d_line);
-               goto loop;
-           }
-           else if (c == '-' && isdigit(UCH(cptr[1])))
-           {
+               msprintf(code_mstr, "(*val).%s", tag);
                ++cptr;
-               i = -get_number() - n;
-               fprintf(f, "yystack.l_mark[%d].%s", i, tag);
                FREE(d_line);
                goto loop;
            }
@@ -1609,76 +3087,44 @@ copy_action(void)
        }
        else if (cptr[1] == '$')
        {
-           if (ntags)
-           {
-               tag = plhs[nrules]->tag;
-               if (tag == 0)
-                   untyped_lhs();
-               fprintf(f, "yyval.%s", tag);
-           }
-           else
-               fprintf(f, "yyval");
+           /* process '$$' later; replacement is context dependent */
+           msprintf(code_mstr, "$$");
            cptr += 2;
            goto loop;
        }
-       else if (isdigit(UCH(cptr[1])))
-       {
-           ++cptr;
-           i = get_number();
-           if (ntags)
-           {
-               if (i <= 0 || i > n)
-                   unknown_rhs(i);
-               tag = pitem[nitems + i - n - 1]->tag;
-               if (tag == 0)
-                   untyped_rhs(i, pitem[nitems + i - n - 1]->name);
-               fprintf(f, "yystack.l_mark[%d].%s", i - n, tag);
-           }
-           else
-           {
-               if (i > n)
-                   dollar_warning(lineno, i);
-               fprintf(f, "yystack.l_mark[%d]", i - n);
-           }
-           goto loop;
-       }
-       else if (cptr[1] == '-')
+    }
+    if (c == '@' && cptr[1] == '$')
+    {
+       if (!locations)
        {
-           cptr += 2;
-           i = get_number();
-           if (ntags)
-               unknown_rhs(-i);
-           fprintf(f, "yystack.l_mark[%d]", -i - n);
-           goto loop;
+           int l_lineno = lineno;
+           char *l_line = dup_line();
+           char *l_cptr = l_line + (cptr - line);
+           syntax_error(l_lineno, l_line, l_cptr);
        }
+       msprintf(code_mstr, "%s", loc);
+       cptr += 2;
+       goto loop;
     }
-    if (isalpha(c) || c == '_' || c == '$')
+    if (IS_NAME1(c))
     {
        do
        {
-           putc(c, f);
+           mputc(code_mstr, c);
            c = *++cptr;
        }
-       while (isalnum(c) || c == '_' || c == '$');
+       while (IS_NAME2(c));
        goto loop;
     }
-    putc(c, f);
     ++cptr;
+    mputc(code_mstr, c);
     switch (c)
     {
     case '\n':
-      next_line:
        get_line();
        if (line)
            goto loop;
-       unterminated_action(a_lineno, a_line, a_cptr);
-
-    case ';':
-       if (depth > 0)
-           goto loop;
-       fprintf(f, "\nbreak;\n");
-       free(a_line);
-       return;
+       unterminated_action(a);
 
     case L_CURL:
        ++depth;
@@ -1687,83 +3133,200 @@ copy_action(void)
     case R_CURL:
        if (--depth > 0)
            goto loop;
-       fprintf(f, "\nbreak;\n");
-       free(a_line);
-       return;
+       goto out;
 
     case '\'':
     case '"':
        {
-           int s_lineno = lineno;
-           char *s_line = dup_line();
-           char *s_cptr = s_line + (cptr - line - 1);
+           char *s = copy_string(c);
+           msprintf(code_mstr, "%s", s);
+           free(s);
+       }
+       goto loop;
 
-           quote = c;
-           for (;;)
-           {
-               c = *cptr++;
-               putc(c, f);
-               if (c == quote)
+    case '/':
+       {
+           char *s = copy_comment();
+           msprintf(code_mstr, "%s", s);
+           free(s);
+       }
+       goto loop;
+
+    default:
+       goto loop;
+    }
+  out:
+    return msdone(code_mstr);
+}
+
+static void
+copy_initial_action(void)
+{
+    struct ainfo a;
+
+    initial_action = get_code(&a, "yyloc");
+    free(a.a_line);
+}
+
+static void
+copy_destructor(void)
+{
+    char *code_text;
+    struct ainfo a;
+    bucket *bp;
+
+    code_text = get_code(&a, "(*loc)");
+
+    for (;;)
+    {
+       int c = nextc();
+       if (c == EOF)
+           unexpected_EOF();
+       if (c == '<')
+       {
+           if (cptr[1] == '>')
+           {                   /* "no semantic type" default destructor */
+               cptr += 2;
+               if ((bp = default_destructor[UNTYPED_DEFAULT]) == NULL)
                {
-                   FREE(s_line);
-                   goto loop;
+                   static char untyped_default[] = "<>";
+                   bp = make_bucket("untyped default");
+                   bp->tag = untyped_default;
+                   default_destructor[UNTYPED_DEFAULT] = bp;
                }
-               if (c == '\n')
-                   unterminated_string(s_lineno, s_line, s_cptr);
-               if (c == '\\')
+               if (bp->destructor != NULL)
+                   destructor_redeclared_warning(&a);
+               else
+                   /* replace "$$" with "(*val)" in destructor code */
+                   bp->destructor = process_destructor_XX(code_text, NULL);
+           }
+           else if (cptr[1] == '*' && cptr[2] == '>')
+           {                   /* "no per-symbol or per-type" default destructor */
+               cptr += 3;
+               if ((bp = default_destructor[TYPED_DEFAULT]) == NULL)
                {
-                   c = *cptr++;
-                   putc(c, f);
-                   if (c == '\n')
-                   {
-                       get_line();
-                       if (line == 0)
-                           unterminated_string(s_lineno, s_line, s_cptr);
-                   }
+                   static char typed_default[] = "<*>";
+                   bp = make_bucket("typed default");
+                   bp->tag = typed_default;
+                   default_destructor[TYPED_DEFAULT] = bp;
                }
+               if (bp->destructor != NULL)
+                   destructor_redeclared_warning(&a);
+               else
+               {
+                   /* postpone re-processing destructor $$s until end of grammar spec */
+                   bp->destructor = TMALLOC(char, strlen(code_text) + 1);
+                   NO_SPACE(bp->destructor);
+                   strcpy(bp->destructor, code_text);
+               }
+           }
+           else
+           {                   /* "semantic type" default destructor */
+               char *tag = get_tag();
+               bp = lookup_type_destructor(tag);
+               if (bp->destructor != NULL)
+                   destructor_redeclared_warning(&a);
+               else
+                   /* replace "$$" with "(*val).tag" in destructor code */
+                   bp->destructor = process_destructor_XX(code_text, tag);
+           }
+       }
+       else if (isalpha(UCH(c)) || c == '_' || c == '.' || c == '$')
+       {                       /* "symbol" destructor */
+           bp = get_name();
+           if (bp->destructor != NULL)
+               destructor_redeclared_warning(&a);
+           else
+           {
+               /* postpone re-processing destructor $$s until end of grammar spec */
+               bp->destructor = TMALLOC(char, strlen(code_text) + 1);
+               NO_SPACE(bp->destructor);
+               strcpy(bp->destructor, code_text);
            }
        }
+       else
+           break;
+    }
+    free(a.a_line);
+    free(code_text);
+}
 
-    case '/':
-       c = *cptr;
-       if (c == '/')
+static char *
+process_destructor_XX(char *code, char *tag)
+{
+    int c;
+    int quote;
+    int depth;
+    struct mstring *new_code = msnew();
+    char *codeptr = code;
+
+    depth = 0;
+  loop:                        /* step thru code */
+    c = *codeptr;
+    if (c == '$' && codeptr[1] == '$')
+    {
+       codeptr += 2;
+       if (tag == NULL)
+           msprintf(new_code, "(*val)");
+       else
+           msprintf(new_code, "(*val).%s", tag);
+       goto loop;
+    }
+    if (IS_NAME1(c))
+    {
+       do
+       {
+           mputc(new_code, c);
+           c = *++codeptr;
+       }
+       while (IS_NAME2(c));
+       goto loop;
+    }
+    ++codeptr;
+    mputc(new_code, c);
+    switch (c)
+    {
+    case L_CURL:
+       ++depth;
+       goto loop;
+
+    case R_CURL:
+       if (--depth > 0)
+           goto loop;
+       return msdone(new_code);
+
+    case '\'':
+    case '"':
+       quote = c;
+       for (;;)
        {
-           putc('*', f);
-           while ((c = *++cptr) != '\n')
+           c = *codeptr++;
+           mputc(new_code, c);
+           if (c == quote)
+               goto loop;
+           if (c == '\\')
            {
-               if (c == '*' && cptr[1] == '/')
-                   fprintf(f, "* ");
-               else
-                   putc(c, f);
+               c = *codeptr++;
+               mputc(new_code, c);
            }
-           fprintf(f, "*/\n");
-           goto next_line;
        }
+
+    case '/':
+       c = *codeptr;
        if (c == '*')
        {
-           int c_lineno = lineno;
-           char *c_line = dup_line();
-           char *c_cptr = c_line + (cptr - line - 1);
-
-           putc('*', f);
-           ++cptr;
+           mputc(new_code, c);
+           ++codeptr;
            for (;;)
            {
-               c = *cptr++;
-               putc(c, f);
-               if (c == '*' && *cptr == '/')
+               c = *codeptr++;
+               mputc(new_code, c);
+               if (c == '*' && *codeptr == '/')
                {
-                   putc('/', f);
-                   ++cptr;
-                   FREE(c_line);
+                   mputc(new_code, '/');
+                   ++codeptr;
                    goto loop;
                }
-               if (c == '\n')
-               {
-                   get_line();
-                   if (line == 0)
-                       unterminated_comment(c_lineno, c_line, c_cptr);
-               }
            }
        }
        goto loop;
@@ -1772,12 +3335,13 @@ copy_action(void)
        goto loop;
     }
 }
+#endif /* defined(YYBTYACC) */
 
 static int
 mark_symbol(void)
 {
     int c;
-    bucket *bp;
+    bucket *bp = NULL;
 
     c = cptr[1];
     if (c == '%' || c == '\\')
@@ -1794,11 +3358,21 @@ mark_symbol(void)
             ((c = cptr[4]) == 'c' || c == 'C') &&
             ((c = cptr[5], !IS_IDENT(c))))
        cptr += 5;
+    else if ((c == 'e' || c == 'E') &&
+            ((c = cptr[2]) == 'm' || c == 'M') &&
+            ((c = cptr[3]) == 'p' || c == 'P') &&
+            ((c = cptr[4]) == 't' || c == 'T') &&
+            ((c = cptr[5]) == 'y' || c == 'Y') &&
+            ((c = cptr[6], !IS_IDENT(c))))
+    {
+       cptr += 6;
+       return (1);
+    }
     else
        syntax_error(lineno, line, cptr);
 
     c = nextc();
-    if (isalpha(c) || c == '_' || c == '.' || c == '$')
+    if (isalpha(UCH(c)) || c == '_' || c == '.' || c == '$')
        bp = get_name();
     else if (c == '\'' || c == '"')
        bp = get_literal();
@@ -1819,25 +3393,32 @@ mark_symbol(void)
 static void
 read_grammar(void)
 {
-    int c;
-
     initialize_grammar();
     advance_to_start();
 
     for (;;)
     {
-       c = nextc();
+       int c = nextc();
+
        if (c == EOF)
            break;
-       if (isalpha(c)
+       if (isalpha(UCH(c))
            || c == '_'
            || c == '.'
            || c == '$'
            || c == '\''
            || c == '"')
+       {
            add_symbol();
-       else if (c == L_CURL || c == '=')
+       }
+       else if (c == L_CURL || c == '='
+#if defined(YYBTYACC)
+                || (backtrack && c == L_BRAC)
+#endif
+           )
+       {
            copy_action();
+       }
        else if (c == '|')
        {
            end_rule();
@@ -1853,6 +3434,10 @@ read_grammar(void)
            syntax_error(lineno, line, cptr);
     }
     end_rule();
+#if defined(YYBTYACC)
+    if (goal->args > 0)
+       start_requires_args(goal->name);
+#endif
 }
 
 static void
@@ -1875,13 +3460,14 @@ static void
 pack_names(void)
 {
     bucket *bp;
-    char *p, *s, *t;
+    char *p;
+    char *t;
 
     name_pool_size = 13;       /* 13 == sizeof("$end") + sizeof("$accept") */
     for (bp = first_symbol; bp; bp = bp->next)
        name_pool_size += strlen(bp->name) + 1;
 
-    name_pool = MALLOC(name_pool_size);
+    name_pool = TMALLOC(char, name_pool_size);
     NO_SPACE(name_pool);
 
     strcpy(name_pool, "$accept");
@@ -1889,8 +3475,9 @@ pack_names(void)
     t = name_pool + 13;
     for (bp = first_symbol; bp; bp = bp->next)
     {
+       char *s = bp->name;
+
        p = t;
-       s = bp->name;
        while ((*t++ = *s++) != 0)
            continue;
        FREE(bp->name);
@@ -1919,14 +3506,14 @@ check_symbols(void)
 static void
 protect_string(char *src, char **des)
 {
-    unsigned len;
-    char *s;
-    char *d;
-
     *des = src;
     if (src)
     {
-       len = 1;
+       char *s;
+       char *d;
+
+       unsigned len = 1;
+
        s = src;
        while (*s)
        {
@@ -1936,7 +3523,7 @@ protect_string(char *src, char **des)
            len++;
        }
 
-       *des = d = (char *)MALLOC(len);
+       *des = d = TMALLOC(char, len);
        NO_SPACE(d);
 
        s = src;
@@ -1956,6 +3543,9 @@ pack_symbols(void)
     bucket *bp;
     bucket **v;
     Value_t i, j, k, n;
+#if defined(YYBTYACC)
+    Value_t max_tok_pval;
+#endif
 
     nsyms = 2;
     ntokens = 1;
@@ -1965,29 +3555,43 @@ pack_symbols(void)
        if (bp->class == TERM)
            ++ntokens;
     }
-    start_symbol = (Value_t) ntokens;
-    nvars = nsyms - ntokens;
+    start_symbol = (Value_t)ntokens;
+    nvars = (Value_t)(nsyms - ntokens);
 
-    symbol_name = (char **)MALLOC((unsigned)nsyms * sizeof(char *));
+    symbol_name = TMALLOC(char *, nsyms);
     NO_SPACE(symbol_name);
 
-    symbol_value = (short *)MALLOC((unsigned)nsyms * sizeof(short));
+    symbol_value = TMALLOC(Value_t, nsyms);
     NO_SPACE(symbol_value);
 
-    symbol_prec = (short *)MALLOC((unsigned)nsyms * sizeof(short));
+    symbol_prec = TMALLOC(Value_t, nsyms);
     NO_SPACE(symbol_prec);
 
-    symbol_assoc = MALLOC(nsyms);
+    symbol_assoc = TMALLOC(char, nsyms);
     NO_SPACE(symbol_assoc);
 
-    v = (bucket **)MALLOC((unsigned)nsyms * sizeof(bucket *));
+#if defined(YYBTYACC)
+    symbol_pval = TMALLOC(Value_t, nsyms);
+    NO_SPACE(symbol_pval);
+
+    if (destructor)
+    {
+       symbol_destructor = CALLOC(sizeof(char *), nsyms);
+       NO_SPACE(symbol_destructor);
+
+       symbol_type_tag = CALLOC(sizeof(char *), nsyms);
+       NO_SPACE(symbol_type_tag);
+    }
+#endif
+
+    v = TMALLOC(bucket *, nsyms);
     NO_SPACE(v);
 
     v[0] = 0;
     v[start_symbol] = 0;
 
     i = 1;
-    j = (Value_t) (start_symbol + 1);
+    j = (Value_t)(start_symbol + 1);
     for (bp = first_symbol; bp; bp = bp->next)
     {
        if (bp->class == TERM)
@@ -2000,8 +3604,8 @@ pack_symbols(void)
     for (i = 1; i < ntokens; ++i)
        v[i]->index = i;
 
-    goal->index = (Index_t) (start_symbol + 1);
-    k = (Value_t) (start_symbol + 2);
+    goal->index = (Index_t)(start_symbol + 1);
+    k = (Value_t)(start_symbol + 2);
     while (++i < nsyms)
        if (v[i] != goal)
        {
@@ -2011,7 +3615,7 @@ pack_symbols(void)
 
     goal->value = 0;
     k = 1;
-    for (i = (Value_t) (start_symbol + 1); i < nsyms; ++i)
+    for (i = (Value_t)(start_symbol + 1); i < nsyms; ++i)
     {
        if (v[i] != goal)
        {
@@ -2058,17 +3662,34 @@ pack_symbols(void)
     symbol_value[0] = 0;
     symbol_prec[0] = 0;
     symbol_assoc[0] = TOKEN;
+#if defined(YYBTYACC)
+    symbol_pval[0] = 0;
+    max_tok_pval = 0;
+#endif
     for (i = 1; i < ntokens; ++i)
     {
        symbol_name[i] = v[i]->name;
        symbol_value[i] = v[i]->value;
        symbol_prec[i] = v[i]->prec;
        symbol_assoc[i] = v[i]->assoc;
+#if defined(YYBTYACC)
+       symbol_pval[i] = v[i]->value;
+       if (symbol_pval[i] > max_tok_pval)
+           max_tok_pval = symbol_pval[i];
+       if (destructor)
+       {
+           symbol_destructor[i] = v[i]->destructor;
+           symbol_type_tag[i] = v[i]->tag;
+       }
+#endif
     }
     symbol_name[start_symbol] = name_pool;
     symbol_value[start_symbol] = -1;
     symbol_prec[start_symbol] = 0;
     symbol_assoc[start_symbol] = TOKEN;
+#if defined(YYBTYACC)
+    symbol_pval[start_symbol] = (Value_t)(max_tok_pval + 1);
+#endif
     for (++i; i < nsyms; ++i)
     {
        k = v[i]->index;
@@ -2076,11 +3697,19 @@ pack_symbols(void)
        symbol_value[k] = v[i]->value;
        symbol_prec[k] = v[i]->prec;
        symbol_assoc[k] = v[i]->assoc;
+#if defined(YYBTYACC)
+       symbol_pval[k] = (Value_t)((max_tok_pval + 1) + v[i]->value + 1);
+       if (destructor)
+       {
+           symbol_destructor[k] = v[i]->destructor;
+           symbol_type_tag[k] = v[i]->tag;
+       }
+#endif
     }
 
     if (gflag)
     {
-       symbol_pname = (char **)MALLOC((unsigned)nsyms * sizeof(char *));
+       symbol_pname = TMALLOC(char *, nsyms);
        NO_SPACE(symbol_pname);
 
        for (i = 0; i < nsyms; ++i)
@@ -2095,22 +3724,20 @@ pack_grammar(void)
 {
     int i;
     Value_t j;
-    Assoc_t assoc;
-    Value_t prec2;
 
-    ritem = (short *)MALLOC((unsigned)nitems * sizeof(short));
+    ritem = TMALLOC(Value_t, nitems);
     NO_SPACE(ritem);
 
-    rlhs = (short *)MALLOC((unsigned)nrules * sizeof(short));
+    rlhs = TMALLOC(Value_t, nrules);
     NO_SPACE(rlhs);
 
-    rrhs = (short *)MALLOC((unsigned)(nrules + 1) * sizeof(short));
+    rrhs = TMALLOC(Value_t, nrules + 1);
     NO_SPACE(rrhs);
 
-    rprec = (short *)REALLOC(rprec, (unsigned)nrules * sizeof(short));
+    rprec = TREALLOC(Value_t, rprec, nrules);
     NO_SPACE(rprec);
 
-    rassoc = REALLOC(rassoc, nrules);
+    rassoc = TREALLOC(Assoc_t, rassoc, nrules);
     NO_SPACE(rassoc);
 
     ritem[0] = -1;
@@ -2127,6 +3754,24 @@ pack_grammar(void)
     j = 4;
     for (i = 3; i < nrules; ++i)
     {
+       Assoc_t assoc;
+       Value_t prec2;
+
+#if defined(YYBTYACC)
+       if (plhs[i]->args > 0)
+       {
+           if (plhs[i]->argnames)
+           {
+               FREE(plhs[i]->argnames);
+               plhs[i]->argnames = NULL;
+           }
+           if (plhs[i]->argtags)
+           {
+               FREE(plhs[i]->argtags);
+               plhs[i]->argtags = NULL;
+           }
+       }
+#endif /* defined(YYBTYACC) */
        rlhs[i] = plhs[i]->index;
        rrhs[i] = j;
        assoc = TOKEN;
@@ -2141,7 +3786,7 @@ pack_grammar(void)
            }
            ++j;
        }
-       ritem[j] = (Value_t) - i;
+       ritem[j] = (Value_t)-i;
        ++j;
        if (rprec[i] == UNDEFINED)
        {
@@ -2153,6 +3798,9 @@ pack_grammar(void)
 
     FREE(plhs);
     FREE(pitem);
+#if defined(YYBTYACC)
+    clean_arg_cache();
+#endif
 }
 
 static void
@@ -2194,26 +3842,111 @@ print_grammar(void)
     }
 }
 
+#if defined(YYBTYACC)
+static void
+finalize_destructors(void)
+{
+    int i;
+    bucket *bp;
+
+    for (i = 2; i < nsyms; ++i)
+    {
+       char *tag = symbol_type_tag[i];
+
+       if (symbol_destructor[i] == NULL)
+       {
+           if (tag == NULL)
+           {                   /* use <> destructor, if there is one */
+               if ((bp = default_destructor[UNTYPED_DEFAULT]) != NULL)
+               {
+                   symbol_destructor[i] = TMALLOC(char,
+                                                  strlen(bp->destructor) + 1);
+                   NO_SPACE(symbol_destructor[i]);
+                   strcpy(symbol_destructor[i], bp->destructor);
+               }
+           }
+           else
+           {                   /* use type destructor for this tag, if there is one */
+               bp = lookup_type_destructor(tag);
+               if (bp->destructor != NULL)
+               {
+                   symbol_destructor[i] = TMALLOC(char,
+                                                  strlen(bp->destructor) + 1);
+                   NO_SPACE(symbol_destructor[i]);
+                   strcpy(symbol_destructor[i], bp->destructor);
+               }
+               else
+               {               /* use <*> destructor, if there is one */
+                   if ((bp = default_destructor[TYPED_DEFAULT]) != NULL)
+                       /* replace "$$" with "(*val).tag" in destructor code */
+                       symbol_destructor[i]
+                           = process_destructor_XX(bp->destructor, tag);
+               }
+           }
+       }
+       else
+       {                       /* replace "$$" with "(*val)[.tag]" in destructor code */
+           char *destructor_source = symbol_destructor[i];
+           symbol_destructor[i]
+               = process_destructor_XX(destructor_source, tag);
+           FREE(destructor_source);
+       }
+    }
+    /* 'symbol_type_tag[]' elements are freed by 'free_tags()' */
+    DO_FREE(symbol_type_tag);  /* no longer needed */
+    if ((bp = default_destructor[UNTYPED_DEFAULT]) != NULL)
+    {
+       FREE(bp->name);
+       /* 'bp->tag' is a static value, don't free */
+       FREE(bp->destructor);
+       FREE(bp);
+    }
+    if ((bp = default_destructor[TYPED_DEFAULT]) != NULL)
+    {
+       FREE(bp->name);
+       /* 'bp->tag' is a static value, don't free */
+       FREE(bp->destructor);
+       FREE(bp);
+    }
+    if ((bp = default_destructor[TYPE_SPECIFIED]) != NULL)
+    {
+       bucket *p;
+       for (; bp; bp = p)
+       {
+           p = bp->link;
+           FREE(bp->name);
+           /* 'bp->tag' freed by 'free_tags()' */
+           FREE(bp->destructor);
+           FREE(bp);
+       }
+    }
+}
+#endif /* defined(YYBTYACC) */
+
 void
 reader(void)
 {
-    write_section(banner);
+    write_section(code_file, banner);
     create_symbol_table();
     read_declarations();
     read_grammar();
     free_symbol_table();
-    free_tags();
     pack_names();
     check_symbols();
     pack_symbols();
     pack_grammar();
     free_symbols();
     print_grammar();
+#if defined(YYBTYACC)
+    if (destructor)
+       finalize_destructors();
+#endif
+    free_tags();
 }
 
 #ifdef NO_LEAKS
 static param *
-free_declarations(param * list)
+free_declarations(param *list)
 {
     while (list != 0)
     {
@@ -2245,5 +3978,10 @@ reader_leaks(void)
     DO_FREE(symbol_prec);
     DO_FREE(symbol_assoc);
     DO_FREE(symbol_value);
+#if defined(YYBTYACC)
+    DO_FREE(symbol_pval);
+    DO_FREE(symbol_destructor);
+    DO_FREE(symbol_type_tag);
+#endif
 }
 #endif