Imported Upstream version 20221229
[platform/upstream/byacc.git] / reader.c
index d581a3c..5e8ca67 100644 (file)
--- a/reader.c
+++ b/reader.c
@@ -1,11 +1,11 @@
-/* $Id: reader.c,v 1.47 2014/04/09 21:09:27 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
 
 /* 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;
 
@@ -42,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;
@@ -57,6 +69,13 @@ 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 */
 
@@ -70,6 +89,7 @@ static bucket *default_destructor[3] =
 static bucket *
 lookup_type_destructor(char *tag)
 {
+    const char fmt[] = "%.*s destructor";
     char name[1024] = "\0";
     bucket *bp, **bpp = &default_destructor[TYPE_SPECIFIED];
 
@@ -80,7 +100,8 @@ lookup_type_destructor(char *tag)
        bpp = &bp->link;
     }
 
-    *bpp = bp = make_bucket(strcat(strcpy(name, tag), " destructor"));
+    sprintf(name, fmt, (int)(sizeof(name) - sizeof(fmt)), tag);
+    *bpp = bp = make_bucket(name);
     bp->tag = tag;
 
     return (bp);
@@ -93,7 +114,7 @@ cachec(int c)
     assert(cinc >= 0);
     if (cinc >= cache_size)
     {
-       cache_size += 256;
+       cache_size += CACHE_SIZE;
        cache = TREALLOC(char, cache, cache_size);
        NO_SPACE(cache);
     }
@@ -101,55 +122,203 @@ cachec(int 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)
        {
-           FREE(line);
-           line = 0;
+       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;
        }
-       cptr = 0;
-       saw_eof = 1;
-       return;
     }
 
-    if (line == 0 || linesize != (LINESIZE + 1))
+    if (ld == ldOK)
     {
-       if (line)
-           FREE(line);
-       linesize = LINESIZE + 1;
-       line = TMALLOC(char, linesize);
-       NO_SPACE(line);
+       size_t need = (size_t)(name_end - name_1st);
+       if ((long)need > (long)input_file_name_len)
+       {
+           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';
+       }
     }
 
-    i = 0;
-    ++lineno;
-    for (;;)
+    if (ld >= ldNUM && ld < ldERR)
     {
-       line[i] = (char)c;
-       if (c == '\n')
-           break;
-       if (++i >= linesize)
+       if (line_1st >= 0)
        {
-           linesize += LINESIZE;
-           line = TREALLOC(char, line, linesize);
-           NO_SPACE(line);
+           lineno = (int)strtol(line + line_1st, NULL, 10) - 1;
        }
-       c = getc(f);
-       if (c == EOF)
+       else
        {
-           line[i] = '\n';
+           lineno = 0;
+       }
+    }
+
+    return (ld == ldOK);
+#undef UNLESS
+}
+
+static void
+get_line(void)
+{
+    FILE *f = input_file;
+
+    do
+    {
+       int c;
+       int i;
+
+       if (saw_eof || (c = getc(f)) == EOF)
+       {
+           if (line)
+           {
+               FREE(line);
+               line = 0;
+           }
+           cptr = 0;
            saw_eof = 1;
-           break;
+           return;
+       }
+
+       if (line == NULL || linesize != (LINESIZE + 1))
+       {
+           if (line)
+               FREE(line);
+           linesize = LINESIZE + 1;
+           line = TMALLOC(char, linesize);
+           NO_SPACE(line);
+       }
+
+       i = 0;
+       ++lineno;
+       for (;;)
+       {
+           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;
 }
@@ -159,8 +328,8 @@ dup_line(void)
 {
     char *p, *s, *t;
 
-    if (line == 0)
-       return (0);
+    if (line == NULL)
+       return (NULL);
     s = line;
     while (*s != '\n')
        ++s;
@@ -178,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 (;;)
@@ -189,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
@@ -205,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);
     }
 
@@ -221,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] == '*')
            {
@@ -253,7 +401,7 @@ nextc(void)
            else if (s[1] == '/')
            {
                get_line();
-               if (line == 0)
+               if (line == NULL)
                    return (EOF);
                s = cptr;
                break;
@@ -266,20 +414,61 @@ nextc(void)
        }
     }
 }
+
+static int
+nextc(void)
+{
+    int ch;
+    int finish = 0;
+
+    do
+    {
+       switch (ch = next_inline())
+       {
+       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;
+       }
+    }
+    while (!finish);
+
+    return ch;
+}
 /* *INDENT-OFF* */
 static struct keyword
 {
-    char name[13];
+    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 }, 
+    { "ident",       IDENT },
+#if defined(YYBTYACC)
+    { "initial-action", INITIAL_ACTION },
+#endif
     { "left",        LEFT },
     { "lex-param",   LEX_PARAM },
 #if defined(YYBTYACC)
@@ -288,11 +477,11 @@ keywords[] = {
     { "nonassoc",    NONASSOC },
     { "parse-param", PARSE_PARAM },
     { "pure-parser", PURE_PARSER },
-    { "right",       RIGHT }, 
+    { "right",       RIGHT },
     { "start",       START },
-    { "term",        TOKEN }, 
-    { "token",       TOKEN }, 
-    { "token-table", TOKEN_TABLE }, 
+    { "term",        TOKEN },
+    { "token",       TOKEN },
+    { "token-table", TOKEN_TABLE },
     { "type",        TYPE },
     { "union",       UNION },
     { "yacc",        POSIX_YACC },
@@ -312,21 +501,22 @@ keyword(void)
 {
     int c;
     char *t_cptr = cptr;
-    struct keyword *key;
 
     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 == '$')
@@ -368,6 +558,7 @@ keyword(void)
            return (NONASSOC);
     }
     syntax_error(lineno, line, t_cptr);
+    /*NOTREACHED */
 }
 
 static void
@@ -405,22 +596,23 @@ static char *
 copy_string(int quote)
 {
     struct mstring *temp = msnew();
-    int c;
-    int s_lineno = lineno;
-    char *s_line = dup_line();
-    char *s_cptr = s_line + (cptr - line - 1);
+    struct ainfo a;
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line - 1);
 
     for (;;)
     {
-       c = *cptr++;
+       int c = *cptr++;
+
        mputc(temp, c);
        if (c == quote)
        {
-           FREE(s_line);
+           FREE(a.a_line);
            return msdone(temp);
        }
        if (c == '\n')
-           unterminated_string(s_lineno, s_line, s_cptr);
+           unterminated_string(&a);
        if (c == '\\')
        {
            c = *cptr++;
@@ -428,8 +620,8 @@ copy_string(int quote)
            if (c == '\n')
            {
                get_line();
-               if (line == 0)
-                   unterminated_string(s_lineno, s_line, s_cptr);
+               if (line == NULL)
+                   unterminated_string(&a);
            }
        }
     }
@@ -456,9 +648,10 @@ copy_comment(void)
     }
     else if (c == '*')
     {
-       int c_lineno = lineno;
-       char *c_line = dup_line();
-       char *c_cptr = c_line + (cptr - line - 1);
+       struct ainfo a;
+       a.a_lineno = lineno;
+       a.a_line = dup_line();
+       a.a_cptr = a.a_line + (cptr - line - 1);
 
        mputc(temp, c);
        ++cptr;
@@ -470,38 +663,160 @@ copy_comment(void)
            {
                mputc(temp, '/');
                ++cptr;
-               FREE(c_line);
+               FREE(a.a_line);
                return msdone(temp);
            }
            if (c == '\n')
            {
                get_line();
-               if (line == 0)
-                   unterminated_comment(c_lineno, c_line, c_cptr);
+               if (line == NULL)
+                   unterminated_comment(&a);
            }
        }
     }
     return msdone(temp);
 }
 
+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;
-    int t_lineno = lineno;
-    char *t_line = dup_line();
-    char *t_cptr = t_line + (cptr - line - 2);
+    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 == 0)
-           unterminated_text(t_lineno, t_line, t_cptr);
+       if (line == NULL)
+           unterminated_text(&a);
     }
-    if (!lflag)
-       fprintf(f, line_format, lineno, input_file_name);
+    fprintf_lineno(f, lineno, input_file_name);
 
   loop:
     c = *cptr++;
@@ -513,7 +828,7 @@ copy_text(void)
        get_line();
        if (line)
            goto loop;
-       unterminated_text(t_lineno, t_line, t_cptr);
+       unterminated_text(&a);
 
     case '\'':
     case '"':
@@ -543,7 +858,7 @@ copy_text(void)
            if (need_newline)
                putc('\n', f);
            ++cptr;
-           FREE(t_line);
+           FREE(a.a_line);
            return;
        }
        /* FALLTHRU */
@@ -576,24 +891,24 @@ copy_union(void)
 {
     int c;
     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:
@@ -603,8 +918,8 @@ copy_union(void)
     {
     case '\n':
        get_line();
-       if (line == 0)
-           unterminated_union(u_lineno, u_line, u_cptr);
+       if (line == NULL)
+           unterminated_union(&a);
        goto loop;
 
     case L_CURL:
@@ -616,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;
@@ -643,92 +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 != L_CURL)
-       goto out;
-    cptr++;
-
-    c = nextc();
-    if (c == EOF)
-       unexpected_EOF();
-    if (c == R_CURL)
-       goto out;
-
-    buf = TMALLOC(char, linesize);
-    NO_SPACE(buf);
-
-    for (i = 0; (c = *cptr++) != R_CURL; i++)
+    if (*buffer != '\0')
     {
-       if (c == '\0')
-           missing_brace();
-       if (c == EOF)
-           unexpected_EOF();
-       buf[i] = (char)c;
-    }
+       char *d = buffer;
+       char *s = after_blanks(d);
 
-    if (i == 0)
-       goto out;
+       while ((*d++ = *s++) != '\0')
+       {
+           ;
+       }
 
-    buf[i--] = '\0';
-    while (i > 0 && isspace(UCH(buf[i])))
-       buf[i--] = '\0';
+       --d;
+       while ((--d != buffer) && isspace(UCH(*d)))
+           *d = '\0';
 
-    if (buf[i] == ']')
-    {
-       int level = 1;
-       while (i >= 0 && level > 0 && buf[i] != '[')
+       for (s = d = buffer; (*d++ = *s++) != '\0';)
        {
-           if (buf[i] == ']')
-               ++level;
-           else if (buf[i] == '[')
-               --level;
-           i--;
+           if (isspace(UCH(*s)))
+           {
+               *s = ' ';
+               while (isspace(UCH(*s)))
+               {
+                   *s++ = ' ';
+               }
+               --s;
+           }
        }
-       if (i <= 0)
-           unexpected_EOF();
-       type2 = i--;
-    }
-    else
-    {
-       type2 = i + 1;
     }
 
-    while (i > 0 && (isalnum(UCH(buf[i])) ||
-                    UCH(buf[i]) == '_'))
-       i--;
+    return (int)strlen(buffer) - 1;
+}
 
-    if (!isspace(UCH(buf[i])) && buf[i] != '*')
-       goto out;
+/*
+ * 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
+    {
+       switch (next_inline())
+       {
+       case 0:
+       case '\n':
+           finish = 1;
+           break;
+       case R_CURL:
+           finish = 1;
+           result = 1;
+           break;
+       }
+       ++cptr;
+    }
+    while (!finish);
+    cptr = save;
+    return result;
+}
 
-    name = i + 1;
+static void
+save_param(int k, char *buffer, int name, int type2)
+{
+    param *head, *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;
@@ -749,9 +1081,181 @@ copy_param(int k)
            parse_param = p;
     }
     p->next = NULL;
+}
+
+/*
+ * 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;
+    int name, type2;
+    int curly = 0;
+    char *buf = 0;
+    int i = -1;
+    size_t buf_size = 0;
+    int st_lineno = lineno;
+    char *comma;
+
+    do
+    {
+       int state = curly;
+       c = next_inline();
+       switch (c)
+       {
+       case EOF:
+           unexpected_EOF();
+           break;
+       case L_CURL:
+           if (curly == 1)
+           {
+               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;
 
-  out:
+  oops:
+    FREE(buf);
     syntax_error(lineno, line, cptr);
 }
 
@@ -775,9 +1279,10 @@ get_literal(void)
     int n;
     char *s;
     bucket *bp;
-    int s_lineno = lineno;
-    char *s_line = dup_line();
-    char *s_cptr = s_line + (cptr - line);
+    struct ainfo a;
+    a.a_lineno = lineno;
+    a.a_line = dup_line();
+    a.a_cptr = a.a_line + (cptr - line);
 
     quote = *cptr++;
     cinc = 0;
@@ -787,7 +1292,7 @@ get_literal(void)
        if (c == quote)
            break;
        if (c == '\n')
-           unterminated_string(s_lineno, s_line, s_cptr);
+           unterminated_string(&a);
        if (c == '\\')
        {
            char *c_cptr = cptr - 1;
@@ -797,8 +1302,8 @@ get_literal(void)
            {
            case '\n':
                get_line();
-               if (line == 0)
-                   unterminated_string(s_lineno, s_line, s_cptr);
+               if (line == NULL)
+                   unterminated_string(&a);
                continue;
 
            case '0':
@@ -870,7 +1375,7 @@ get_literal(void)
        }
        cachec(c);
     }
-    FREE(s_line);
+    FREE(a.a_line);
 
     n = cinc;
     s = TMALLOC(char, n);
@@ -893,7 +1398,7 @@ get_literal(void)
            cachec('\\');
            cachec(c);
        }
-       else if (isprint(c))
+       else if (isprint(UCH(c)))
            cachec(c);
        else
        {
@@ -948,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)
@@ -957,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)
@@ -987,13 +1491,21 @@ 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 *
@@ -1040,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;
@@ -1061,7 +1573,7 @@ get_tag(void)
 
     FREE(t_line);
     havetags = 1;
-    return cache_tag(cache, (size_t) cinc);
+    return cache_tag(cache, (size_t)cinc);
 }
 
 #if defined(YYBTYACC)
@@ -1070,9 +1582,9 @@ scan_id(void)
 {
     char *b = cptr;
 
-    while (isalnum(*cptr) || *cptr == '_' || *cptr == '$')
+    while (IS_NAME2(UCH(*cptr)))
        cptr++;
-    return cache_tag(b, (size_t) (cptr - b));
+    return cache_tag(b, (size_t)(cptr - b));
 }
 #endif
 
@@ -1100,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();
@@ -1122,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;
        }
 
@@ -1130,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)
@@ -1166,7 +1678,7 @@ declare_expect(int assoc)
 
     for (;;)
     {
-       if (isdigit(c))
+       if (isdigit(UCH(c)))
        {
            if (assoc == EXPECT)
                SRexpect = get_number();
@@ -1179,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);
        }
@@ -1197,14 +1709,14 @@ static void
 declare_argtypes(bucket *bp)
 {
     char *tags[MAXARGS];
-    int args = 0, c;
+    int args = 0;
 
     if (bp->args >= 0)
        retyped_warning(bp->name);
     cptr++;                    /* skip open paren */
     for (;;)
     {
-       c = nextc();
+       int c = nextc();
        if (c == EOF)
            unexpected_EOF();
        if (c != '<')
@@ -1234,7 +1746,7 @@ static void
 declare_types(void)
 {
     int c;
-    bucket *bp;
+    bucket *bp = NULL;
     char *tag = NULL;
 
     c = nextc();
@@ -1248,7 +1760,7 @@ declare_types(void)
        c = nextc();
        if (c == EOF)
            unexpected_EOF();
-       if (isalpha(c) || c == '_' || c == '.' || c == '$')
+       if (isalpha(UCH(c)) || c == '_' || c == '.' || c == '$')
        {
            bp = get_name();
 #if defined(YYBTYACC)
@@ -1286,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)
@@ -1299,15 +1811,15 @@ declare_start(void)
 static void
 read_declarations(void)
 {
-    int c, k;
-
-    cache_size = 256;
+    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 != '%')
@@ -1321,6 +1833,10 @@ read_declarations(void)
            copy_ident();
            break;
 
+       case XCODE:
+           copy_code();
+           break;
+
        case TEXT:
            copy_text();
            break;
@@ -1362,6 +1878,10 @@ read_declarations(void)
            token_table = 1;
            break;
 
+       case ERROR_VERBOSE:
+           error_verbose = 1;
+           break;
+
 #if defined(YYBTYACC)
        case LOCATIONS:
            locations = 1;
@@ -1371,8 +1891,15 @@ read_declarations(void)
            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. */
@@ -1454,9 +1981,11 @@ copy_args(int *alen)
     struct mstring *s = msnew();
     int depth = 0, len = 1;
     char c, quote = 0;
-    int a_lineno = lineno;
-    char *a_line = dup_line();
-    char *a_cptr = a_line + (cptr - line - 1);
+    struct ainfo a;
+
+    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)
     {
@@ -1473,9 +2002,9 @@ copy_args(int *alen)
            if (!line)
            {
                if (quote)
-                   unterminated_string(a_lineno, a_line, a_cptr);
+                   unterminated_string(&a);
                else
-                   unterminated_arglist(a_lineno, a_line, a_cptr);
+                   unterminated_arglist(&a);
            }
        }
        else if (quote)
@@ -1500,7 +2029,7 @@ copy_args(int *alen)
     }
     if (alen)
        *alen = len;
-    FREE(a_line);
+    FREE(a.a_line);
     return msdone(s);
 }
 
@@ -1509,17 +2038,17 @@ parse_id(char *p, char **save)
 {
     char *b;
 
-    while (isspace(*p))
+    while (isspace(UCH(*p)))
        if (*p++ == '\n')
            rescan_lineno++;
-    if (!isalpha(*p) && *p != '_')
+    if (!isalpha(UCH(*p)) && *p != '_')
        return NULL;
     b = p;
-    while (isalnum(*p) || *p == '_' || *p == '$')
+    while (IS_NAME2(UCH(*p)))
        p++;
     if (save)
     {
-       *save = cache_tag(b, (size_t) (p - b));
+       *save = cache_tag(b, (size_t)(p - b));
     }
     return p;
 }
@@ -1529,7 +2058,7 @@ parse_int(char *p, int *save)
 {
     int neg = 0, val = 0;
 
-    while (isspace(*p))
+    while (isspace(UCH(*p)))
        if (*p++ == '\n')
            rescan_lineno++;
     if (*p == '-')
@@ -1537,9 +2066,9 @@ parse_int(char *p, int *save)
        neg = 1;
        p++;
     }
-    if (!isdigit(*p))
+    if (!isdigit(UCH(*p)))
        return NULL;
-    while (isdigit(*p))
+    while (isdigit(UCH(*p)))
        val = val * 10 + *p++ - '0';
     if (neg)
        val = -val;
@@ -1554,7 +2083,7 @@ parse_arginfo(bucket *a, char *args, int argslen)
     char *p = args, *tmp;
     int i, redec = 0;
 
-    if (a->args > 0)
+    if (a->args >= 0)
     {
        if (a->args != argslen)
            arg_number_disagree_warning(rescan_lineno, a->name);
@@ -1573,12 +2102,12 @@ parse_arginfo(bucket *a, char *args, int argslen)
        return;
     for (i = 0; i < argslen; i++)
     {
-       while (isspace(*p))
+       while (isspace(UCH(*p)))
            if (*p++ == '\n')
                rescan_lineno++;
        if (*p++ != '$')
            bad_formals();
-       while (isspace(*p))
+       while (isspace(UCH(*p)))
            if (*p++ == '\n')
                rescan_lineno++;
        if (*p == '<')
@@ -1586,7 +2115,7 @@ parse_arginfo(bucket *a, char *args, int argslen)
            havetags = 1;
            if (!(p = parse_id(p + 1, &tmp)))
                bad_formals();
-           while (isspace(*p))
+           while (isspace(UCH(*p)))
                if (*p++ == '\n')
                    rescan_lineno++;
            if (*p++ != '>')
@@ -1603,7 +2132,7 @@ parse_arginfo(bucket *a, char *args, int argslen)
            a->argtags[i] = NULL;
        if (!(p = parse_id(p, &a->argnames[i])))
            bad_formals();
-       while (isspace(*p))
+       while (isspace(UCH(*p)))
            if (*p++ == '\n')
                rescan_lineno++;
        if (*p++)
@@ -1617,7 +2146,7 @@ compile_arg(char **theptr, char *yyvaltag)
 {
     char *p = *theptr;
     struct mstring *c = msnew();
-    int i, j, n;
+    int i, n;
     Value_t *offsets = NULL, maxoffset;
     bucket **rhs;
 
@@ -1631,12 +2160,15 @@ compile_arg(char **theptr, char *yyvaltag)
     }
     if (maxoffset > 0)
     {
-       offsets = TMALLOC(Value_t, maxoffset + 1);
+       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);
     }
-    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)
@@ -1651,7 +2183,7 @@ compile_arg(char **theptr, char *yyvaltag)
            if (*++p == '<')
                if (!(p = parse_id(++p, &tag)) || *p++ != '>')
                    illegal_tag(rescan_lineno, NULL, NULL);
-           if (isdigit(*p) || *p == '-')
+           if (isdigit(UCH(*p)) || *p == '-')
            {
                int val;
                if (!(p = parse_int(p, &val)))
@@ -1663,7 +2195,7 @@ compile_arg(char **theptr, char *yyvaltag)
                    dollar_warning(rescan_lineno, val);
                    i = val - maxoffset;
                }
-               else
+               else if (maxoffset > 0)
                {
                    i = offsets[val];
                    if (!tag && !(tag = rhs[i]->tag) && havetags)
@@ -1675,7 +2207,7 @@ compile_arg(char **theptr, char *yyvaltag)
                else if (havetags)
                    unknown_rhs(val);
            }
-           else if (isalpha(*p) || *p == '_')
+           else if (isalpha(UCH(*p)) || *p == '_')
            {
                char *arg;
                if (!(p = parse_id(p, &arg)))
@@ -1687,8 +2219,8 @@ compile_arg(char **theptr, char *yyvaltag)
                    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);
+               msprintf(c, "yystack.l_mark[%d]",
+                        i - plhs[nrules]->args + 1 - n);
                if (tag)
                    msprintf(c, ".%s", tag);
                else if (havetags)
@@ -1714,6 +2246,95 @@ compile_arg(char **theptr, char *yyvaltag)
     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
 {
@@ -1765,14 +2386,13 @@ clean_arg_cache(void)
        arg_cache[i] = NULL;
     }
 }
-#endif
+#endif /* defined(YYBTYACC) */
 
 static void
 advance_to_start(void)
 {
     int c;
     bucket *bp;
-    char *s_cptr;
     int s_lineno;
 #if defined(YYBTYACC)
     char *args = NULL;
@@ -1781,12 +2401,18 @@ advance_to_start(void)
 
     for (;;)
     {
+       char *s_cptr;
+
        c = nextc();
        if (c != '%')
            break;
        s_cptr = cptr;
        switch (keyword())
        {
+       case XCODE:
+           copy_code();
+           break;
+
        case MARK:
            no_grammar();
 
@@ -1804,7 +2430,7 @@ advance_to_start(void)
     }
 
     c = nextc();
-    if (!isalpha(c) && c != '_' && c != '.' && c != '_')
+    if (!isalpha(UCH(c)) && c != '_' && c != '.' && c != '_')
        syntax_error(lineno, line, cptr);
     bp = get_name();
     if (goal == 0)
@@ -1855,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;
@@ -1886,6 +2510,7 @@ 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;
@@ -1896,7 +2521,7 @@ insert_empty_rule(void)
     bp->args = 0;
 #endif
 
-    nitems = (Value_t) (nitems + 2);
+    nitems = (Value_t)(nitems + 2);
     if (nitems > maxitems)
        expand_items();
     bpp = pitem + nitems - 1;
@@ -1927,13 +2552,13 @@ insert_arg_rule(char *arg, char *tag)
     {
        rule = nrules;
        insert_arg_cache(code, rule);
-       fprintf(f, "case %d:\n", rule - 2);
-       if (!lflag)
-           fprintf(f, line_format, line_number, input_file_name);
-       fprintf(f, "%s;\n", code);
-       fprintf(f, "break;\n");
+       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 = tag;
+       plhs[rule]->tag = cache_tag(tag, strlen(tag));
        plhs[rule]->class = ARGUMENT;
     }
     else
@@ -2004,11 +2629,33 @@ add_symbol(void)
     }
     else if (bp->args != argslen)
        wrong_number_args_warning("", bp->name);
-    if (bp->args > 0 && argslen > 0)
+    if (args != 0)
     {
-       char *ap;
-       int i;
-       for (ap = args, i = 0; i < argslen; i++)
+       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);
     }
@@ -2019,14 +2666,6 @@ add_symbol(void)
     pitem[nitems - 1] = bp;
 }
 
-static char *
-after_blanks(char *s)
-{
-    while (*s != '\0' && isspace(UCH(*s)))
-       ++s;
-    return s;
-}
-
 static void
 copy_action(void)
 {
@@ -2034,33 +2673,34 @@ copy_action(void)
     int i, j, n;
     int depth;
 #if defined(YYBTYACC)
-    int trialaction = 0;
     int haveyyval = 0;
 #endif
     char *tag;
     FILE *f = action_file;
-    int a_lineno = lineno;
-    char *a_line = dup_line();
-    char *a_cptr = a_line + (cptr - line);
+    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
 
-    fprintf(f, "case %d:\n", nrules - 2);
+    begin_case(f, nrules - 2);
 #if defined(YYBTYACC)
     if (backtrack)
     {
-       if (*cptr != L_BRAC)
+       if (!trialaction)
            fprintf(f, "  if (!yytrial)\n");
-       else
-           trialaction = 1;
     }
 #endif
-    if (!lflag)
-       fprintf(f, line_format, lineno, input_file_name);
+    fprintf_lineno(f, lineno, input_file_name);
     if (*cptr == '=')
        ++cptr;
 
@@ -2083,12 +2723,13 @@ copy_action(void)
     {
        offsets = TMALLOC(Value_t, maxoffset + 1);
        NO_SPACE(offsets);
-    }
-    for (j = 0, i++; i < nitems; i++)
-    {
-       if (pitem[i]->class != ARGUMENT)
+
+       for (j = 0, i++; i < nitems; i++)
        {
-           offsets[++j] = (Value_t) (i - nitems + 1);
+           if (pitem[i]->class != ARGUMENT)
+           {
+               offsets[++j] = (Value_t)(i - nitems + 1);
+           }
        }
     }
     rhs = pitem + nitems - 1;
@@ -2114,7 +2755,7 @@ copy_action(void)
                FREE(d_line);
                goto loop;
            }
-           else if (isdigit(c))
+           else if (isdigit(UCH(c)))
            {
                i = get_number();
                if (i == 0)
@@ -2122,10 +2763,12 @@ copy_action(void)
                else if (i > maxoffset)
                {
                    dollar_warning(d_lineno, i);
-                   fprintf(f, "yystack.l_mark[%d].%s", i - maxoffset, tag);
+                   fprintf(f, "yystack.l_mark[%ld].%s",
+                           (long)(i - maxoffset), tag);
                }
                else if (offsets)
-                   fprintf(f, "yystack.l_mark[%d].%s", offsets[i], tag);
+                   fprintf(f, "yystack.l_mark[%ld].%s",
+                           (long)offsets[i], tag);
                FREE(d_line);
                goto loop;
            }
@@ -2138,7 +2781,7 @@ copy_action(void)
                goto loop;
            }
 #if defined(YYBTYACC)
-           else if (isalpha(c) || c == '_')
+           else if (isalpha(UCH(c)) || c == '_')
            {
                char *arg = scan_id();
                for (i = plhs[nrules]->args - 1; i >= 0; i--)
@@ -2146,8 +2789,8 @@ copy_action(void)
                        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);
+               fprintf(f, "yystack.l_mark[%d].%s",
+                       i - plhs[nrules]->args + 1 - n, tag);
                FREE(d_line);
                goto loop;
            }
@@ -2176,14 +2819,14 @@ copy_action(void)
        {
            ++cptr;
            i = get_number();
-           if (havetags)
+           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[%d].%s", offsets[i], tag);
+               fprintf(f, "yystack.l_mark[%ld].%s", (long)offsets[i], tag);
            }
            else
            {
@@ -2192,10 +2835,10 @@ copy_action(void)
                else if (i > maxoffset)
                {
                    dollar_warning(lineno, i);
-                   fprintf(f, "yystack.l_mark[%d]", i - maxoffset);
+                   fprintf(f, "yystack.l_mark[%ld]", (long)(i - maxoffset));
                }
                else if (offsets)
-                   fprintf(f, "yystack.l_mark[%d]", offsets[i]);
+                   fprintf(f, "yystack.l_mark[%ld]", (long)offsets[i]);
            }
            goto loop;
        }
@@ -2209,7 +2852,7 @@ copy_action(void)
            goto loop;
        }
 #if defined(YYBTYACC)
-       else if (isalpha(cptr[1]) || cptr[1] == '_')
+       else if (isalpha(UCH(cptr[1])) || cptr[1] == '_')
        {
            char *arg;
            ++cptr;
@@ -2260,16 +2903,23 @@ copy_action(void)
                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 (isalpha(c) || c == '_' || c == '$')
+    if (IS_NAME1(c))
     {
        do
        {
            putc(c, f);
            c = *++cptr;
        }
-       while (isalnum(c) || c == '_' || c == '$');
+       while (IS_NAME2(c));
        goto loop;
     }
     ++cptr;
@@ -2294,13 +2944,12 @@ copy_action(void)
            if (c == L_CURL && !haveyyval)
            {
                fprintf(f, "  if (!yytrial)\n");
-               if (!lflag)
-                   fprintf(f, line_format, lineno, input_file_name);
+               fprintf_lineno(f, lineno, input_file_name);
                trialaction = 0;
                goto loop;
            }
-           fprintf(f, "\nbreak;\n");
-           FREE(a_line);
+           end_case(f);
+           FREE(a.a_line);
            if (maxoffset > 0)
                FREE(offsets);
            return;
@@ -2314,13 +2963,13 @@ copy_action(void)
        get_line();
        if (line)
            goto loop;
-       unterminated_action(a_lineno, a_line, a_cptr);
+       unterminated_action(&a);
 
     case ';':
        if (depth > 0)
            goto loop;
-       fprintf(f, "\nbreak;\n");
-       free(a_line);
+       end_case(f);
+       free(a.a_line);
        if (maxoffset > 0)
            FREE(offsets);
        return;
@@ -2356,14 +3005,13 @@ copy_action(void)
            if (c == L_CURL && !haveyyval)
            {
                fprintf(f, "  if (!yytrial)\n");
-               if (!lflag)
-                   fprintf(f, line_format, lineno, input_file_name);
+               fprintf_lineno(f, lineno, input_file_name);
                goto loop;
            }
        }
 #endif
-       fprintf(f, "\nbreak;\n");
-       free(a_line);
+       end_case(f);
+       free(a.a_line);
        if (maxoffset > 0)
            FREE(offsets);
        return;
@@ -2391,32 +3039,27 @@ copy_action(void)
 }
 
 #if defined(YYBTYACC)
-static void
-copy_destructor(void)
+static char *
+get_code(struct ainfo *a, const char *loc)
 {
     int c;
     int depth;
     char *tag;
-    bucket *bp;
-    struct mstring *destructor_text = msnew();
-    char *code_text;
-    int a_lineno;
-    char *a_line;
-    char *a_cptr;
+    struct mstring *code_mstr = msnew();
 
     if (!lflag)
-       msprintf(destructor_text, line_format, lineno, input_file_name);
+       msprintf(code_mstr, line_format, lineno, input_file_name);
 
     cptr = after_blanks(cptr);
     if (*cptr == L_CURL)
        /* avoid putting curly-braces in first column, to ease editing */
-       mputc(destructor_text, '\t');
+       mputc(code_mstr, '\t');
     else
        syntax_error(lineno, line, cptr);
 
-    a_lineno = lineno;
-    a_line = dup_line();
-    a_cptr = a_line + (cptr - line);
+    a->a_lineno = lineno;
+    a->a_line = dup_line();
+    a->a_cptr = a->a_line + (cptr - line);
 
     depth = 0;
   loop:
@@ -2434,7 +3077,7 @@ copy_destructor(void)
            c = *cptr;
            if (c == '$')
            {
-               msprintf(destructor_text, "(*val).%s", tag);
+               msprintf(code_mstr, "(*val).%s", tag);
                ++cptr;
                FREE(d_line);
                goto loop;
@@ -2445,7 +3088,7 @@ copy_destructor(void)
        else if (cptr[1] == '$')
        {
            /* process '$$' later; replacement is context dependent */
-           msprintf(destructor_text, "$$");
+           msprintf(code_mstr, "$$");
            cptr += 2;
            goto loop;
        }
@@ -2459,29 +3102,29 @@ copy_destructor(void)
            char *l_cptr = l_line + (cptr - line);
            syntax_error(l_lineno, l_line, l_cptr);
        }
-       msprintf(destructor_text, "(*loc)");
+       msprintf(code_mstr, "%s", loc);
        cptr += 2;
        goto loop;
     }
-    if (isalpha(c) || c == '_' || c == '$')
+    if (IS_NAME1(c))
     {
        do
        {
-           mputc(destructor_text, c);
+           mputc(code_mstr, c);
            c = *++cptr;
        }
-       while (isalnum(c) || c == '_' || c == '$');
+       while (IS_NAME2(c));
        goto loop;
     }
     ++cptr;
-    mputc(destructor_text, c);
+    mputc(code_mstr, c);
     switch (c)
     {
     case '\n':
        get_line();
        if (line)
            goto loop;
-       unterminated_action(a_lineno, a_line, a_cptr);
+       unterminated_action(a);
 
     case L_CURL:
        ++depth;
@@ -2490,13 +3133,13 @@ copy_destructor(void)
     case R_CURL:
        if (--depth > 0)
            goto loop;
-       goto process_symbols;
+       goto out;
 
     case '\'':
     case '"':
        {
            char *s = copy_string(c);
-           msprintf(destructor_text, "%s", s);
+           msprintf(code_mstr, "%s", s);
            free(s);
        }
        goto loop;
@@ -2504,7 +3147,7 @@ copy_destructor(void)
     case '/':
        {
            char *s = copy_comment();
-           msprintf(destructor_text, "%s", s);
+           msprintf(code_mstr, "%s", s);
            free(s);
        }
        goto loop;
@@ -2512,11 +3155,31 @@ copy_destructor(void)
     default:
        goto loop;
     }
-  process_symbols:
-    code_text = msdone(destructor_text);
+  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 (;;)
     {
-       c = nextc();
+       int c = nextc();
        if (c == EOF)
            unexpected_EOF();
        if (c == '<')
@@ -2532,7 +3195,7 @@ copy_destructor(void)
                    default_destructor[UNTYPED_DEFAULT] = bp;
                }
                if (bp->destructor != NULL)
-                   destructor_redeclared_warning(a_lineno, a_line, a_cptr);
+                   destructor_redeclared_warning(&a);
                else
                    /* replace "$$" with "(*val)" in destructor code */
                    bp->destructor = process_destructor_XX(code_text, NULL);
@@ -2548,7 +3211,7 @@ copy_destructor(void)
                    default_destructor[TYPED_DEFAULT] = bp;
                }
                if (bp->destructor != NULL)
-                   destructor_redeclared_warning(a_lineno, a_line, a_cptr);
+                   destructor_redeclared_warning(&a);
                else
                {
                    /* postpone re-processing destructor $$s until end of grammar spec */
@@ -2559,20 +3222,20 @@ copy_destructor(void)
            }
            else
            {                   /* "semantic type" default destructor */
-               tag = get_tag();
+               char *tag = get_tag();
                bp = lookup_type_destructor(tag);
                if (bp->destructor != NULL)
-                   destructor_redeclared_warning(a_lineno, a_line, a_cptr);
+                   destructor_redeclared_warning(&a);
                else
                    /* replace "$$" with "(*val).tag" in destructor code */
                    bp->destructor = process_destructor_XX(code_text, tag);
            }
        }
-       else if (isalpha(c) || c == '_' || c == '.' || c == '$')
+       else if (isalpha(UCH(c)) || c == '_' || c == '.' || c == '$')
        {                       /* "symbol" destructor */
            bp = get_name();
            if (bp->destructor != NULL)
-               destructor_redeclared_warning(a_lineno, a_line, a_cptr);
+               destructor_redeclared_warning(&a);
            else
            {
                /* postpone re-processing destructor $$s until end of grammar spec */
@@ -2584,7 +3247,7 @@ copy_destructor(void)
        else
            break;
     }
-    free(a_line);
+    free(a.a_line);
     free(code_text);
 }
 
@@ -2609,14 +3272,14 @@ process_destructor_XX(char *code, char *tag)
            msprintf(new_code, "(*val).%s", tag);
        goto loop;
     }
-    if (isalpha(c) || c == '_' || c == '$')
+    if (IS_NAME1(c))
     {
        do
        {
            mputc(new_code, c);
            c = *++codeptr;
        }
-       while (isalnum(c) || c == '_' || c == '$');
+       while (IS_NAME2(c));
        goto loop;
     }
     ++codeptr;
@@ -2695,17 +3358,28 @@ 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();
     else
     {
        syntax_error(lineno, line, cptr);
+       /*NOTREACHED */
     }
 
     if (rprec[nrules] != UNDEFINED && bp->prec != rprec[nrules])
@@ -2719,29 +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 == '='
 #if defined(YYBTYACC)
-       else if (c == L_CURL || c == '=' || (backtrack && c == L_BRAC))
-#else
-       else if (c == L_CURL || c == '=')
+                || (backtrack && c == L_BRAC)
 #endif
+           )
+       {
            copy_action();
+       }
        else if (c == '|')
        {
            end_rule();
@@ -2783,7 +3460,8 @@ 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)
@@ -2797,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);
@@ -2827,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)
        {
@@ -2876,8 +3555,8 @@ pack_symbols(void)
        if (bp->class == TERM)
            ++ntokens;
     }
-    start_symbol = (Value_t) ntokens;
-    nvars = (Value_t) (nsyms - ntokens);
+    start_symbol = (Value_t)ntokens;
+    nvars = (Value_t)(nsyms - ntokens);
 
     symbol_name = TMALLOC(char *, nsyms);
     NO_SPACE(symbol_name);
@@ -2912,7 +3591,7 @@ pack_symbols(void)
     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)
@@ -2925,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)
        {
@@ -2936,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)
        {
@@ -3009,7 +3688,7 @@ pack_symbols(void)
     symbol_prec[start_symbol] = 0;
     symbol_assoc[start_symbol] = TOKEN;
 #if defined(YYBTYACC)
-    symbol_pval[start_symbol] = (Value_t) (max_tok_pval + 1);
+    symbol_pval[start_symbol] = (Value_t)(max_tok_pval + 1);
 #endif
     for (++i; i < nsyms; ++i)
     {
@@ -3019,7 +3698,7 @@ pack_symbols(void)
        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);
+       symbol_pval[k] = (Value_t)((max_tok_pval + 1) + v[i]->value + 1);
        if (destructor)
        {
            symbol_destructor[k] = v[i]->destructor;
@@ -3045,8 +3724,6 @@ pack_grammar(void)
 {
     int i;
     Value_t j;
-    Assoc_t assoc;
-    Value_t prec2;
 
     ritem = TMALLOC(Value_t, nitems);
     NO_SPACE(ritem);
@@ -3077,6 +3754,9 @@ 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)
        {
@@ -3106,7 +3786,7 @@ pack_grammar(void)
            }
            ++j;
        }
-       ritem[j] = (Value_t) - i;
+       ritem[j] = (Value_t)-i;
        ++j;
        if (rprec[i] == UNDEFINED)
        {
@@ -3168,11 +3848,11 @@ finalize_destructors(void)
 {
     int i;
     bucket *bp;
-    char *tag;
 
     for (i = 2; i < nsyms; ++i)
     {
-       tag = symbol_type_tag[i];
+       char *tag = symbol_type_tag[i];
+
        if (symbol_destructor[i] == NULL)
        {
            if (tag == NULL)
@@ -3206,8 +3886,10 @@ finalize_destructors(void)
        }
        else
        {                       /* replace "$$" with "(*val)[.tag]" in destructor code */
+           char *destructor_source = symbol_destructor[i];
            symbol_destructor[i]
-               = process_destructor_XX(symbol_destructor[i], tag);
+               = process_destructor_XX(destructor_source, tag);
+           FREE(destructor_source);
        }
     }
     /* 'symbol_type_tag[]' elements are freed by 'free_tags()' */
@@ -3264,7 +3946,7 @@ reader(void)
 
 #ifdef NO_LEAKS
 static param *
-free_declarations(param * list)
+free_declarations(param *list)
 {
     while (list != 0)
     {