xref: /freebsd/contrib/byacc/reader.c (revision 5dae51da3da0cc94d17bd67b308fad304ebec7e0)
1 /* $Id: reader.c,v 1.63 2016/06/07 00:17:51 tom Exp $ */
2 
3 #include "defs.h"
4 
5 /*  The line size must be a positive integer.  One hundred was chosen	*/
6 /*  because few lines in Yacc input grammars exceed 100 characters.	*/
7 /*  Note that if a line exceeds LINESIZE characters, the line buffer	*/
8 /*  will be expanded to accomodate it.					*/
9 
10 #define LINESIZE 100
11 
12 #define L_CURL  '{'
13 #define R_CURL  '}'
14 #define L_PAREN '('
15 #define R_PAREN ')'
16 #define L_BRAC  '['
17 #define R_BRAC  ']'
18 
19 /* the maximum number of arguments (inherited attributes) to a non-terminal */
20 /* this is a hard limit, but seems more than adequate */
21 #define MAXARGS	20
22 
23 static void start_rule(bucket *bp, int s_lineno);
24 #if defined(YYBTYACC)
25 static void copy_destructor(void);
26 static char *process_destructor_XX(char *code, char *tag);
27 #endif
28 
29 #define CACHE_SIZE 256
30 static char *cache;
31 static int cinc, cache_size;
32 
33 int ntags;
34 static int tagmax, havetags;
35 static char **tag_table;
36 
37 static char saw_eof;
38 char unionized;
39 char *cptr, *line;
40 static int linesize;
41 
42 static bucket *goal;
43 static Value_t prec;
44 static int gensym;
45 static char last_was_action;
46 #if defined(YYBTYACC)
47 static int trialaction;
48 #endif
49 
50 static int maxitems;
51 static bucket **pitem;
52 
53 static int maxrules;
54 static bucket **plhs;
55 
56 static size_t name_pool_size;
57 static char *name_pool;
58 
59 char line_format[] = "#line %d \"%s\"\n";
60 
61 param *lex_param;
62 param *parse_param;
63 
64 #if defined(YYBTYACC)
65 int destructor = 0;	/* =1 if at least one %destructor */
66 
67 static bucket *default_destructor[3] =
68 {0, 0, 0};
69 
70 #define UNTYPED_DEFAULT 0
71 #define TYPED_DEFAULT   1
72 #define TYPE_SPECIFIED  2
73 
74 static bucket *
75 lookup_type_destructor(char *tag)
76 {
77     const char fmt[] = "%.*s destructor";
78     char name[1024] = "\0";
79     bucket *bp, **bpp = &default_destructor[TYPE_SPECIFIED];
80 
81     while ((bp = *bpp) != NULL)
82     {
83 	if (bp->tag == tag)
84 	    return (bp);
85 	bpp = &bp->link;
86     }
87 
88     sprintf(name, fmt, (int)(sizeof(name) - sizeof(fmt)), tag);
89     *bpp = bp = make_bucket(name);
90     bp->tag = tag;
91 
92     return (bp);
93 }
94 #endif /* defined(YYBTYACC) */
95 
96 static void
97 cachec(int c)
98 {
99     assert(cinc >= 0);
100     if (cinc >= cache_size)
101     {
102 	cache_size += CACHE_SIZE;
103 	cache = TREALLOC(char, cache, cache_size);
104 	NO_SPACE(cache);
105     }
106     cache[cinc] = (char)c;
107     ++cinc;
108 }
109 
110 static void
111 get_line(void)
112 {
113     FILE *f = input_file;
114     int c;
115     int i;
116 
117     if (saw_eof || (c = getc(f)) == EOF)
118     {
119 	if (line)
120 	{
121 	    FREE(line);
122 	    line = 0;
123 	}
124 	cptr = 0;
125 	saw_eof = 1;
126 	return;
127     }
128 
129     if (line == NULL || linesize != (LINESIZE + 1))
130     {
131 	if (line)
132 	    FREE(line);
133 	linesize = LINESIZE + 1;
134 	line = TMALLOC(char, linesize);
135 	NO_SPACE(line);
136     }
137 
138     i = 0;
139     ++lineno;
140     for (;;)
141     {
142 	line[i++] = (char)c;
143 	if (c == '\n')
144 	    break;
145 	if ((i + 3) >= linesize)
146 	{
147 	    linesize += LINESIZE;
148 	    line = TREALLOC(char, line, linesize);
149 	    NO_SPACE(line);
150 	}
151 	c = getc(f);
152 	if (c == EOF)
153 	{
154 	    line[i++] = '\n';
155 	    saw_eof = 1;
156 	    break;
157 	}
158     }
159     line[i] = '\0';
160     cptr = line;
161     return;
162 }
163 
164 static char *
165 dup_line(void)
166 {
167     char *p, *s, *t;
168 
169     if (line == NULL)
170 	return (NULL);
171     s = line;
172     while (*s != '\n')
173 	++s;
174     p = TMALLOC(char, s - line + 1);
175     NO_SPACE(p);
176 
177     s = line;
178     t = p;
179     while ((*t++ = *s++) != '\n')
180 	continue;
181     return (p);
182 }
183 
184 static void
185 skip_comment(void)
186 {
187     char *s;
188 
189     int st_lineno = lineno;
190     char *st_line = dup_line();
191     char *st_cptr = st_line + (cptr - line);
192 
193     s = cptr + 2;
194     for (;;)
195     {
196 	if (*s == '*' && s[1] == '/')
197 	{
198 	    cptr = s + 2;
199 	    FREE(st_line);
200 	    return;
201 	}
202 	if (*s == '\n')
203 	{
204 	    get_line();
205 	    if (line == NULL)
206 		unterminated_comment(st_lineno, st_line, st_cptr);
207 	    s = cptr;
208 	}
209 	else
210 	    ++s;
211     }
212 }
213 
214 static int
215 next_inline(void)
216 {
217     char *s;
218 
219     if (line == NULL)
220     {
221 	get_line();
222 	if (line == NULL)
223 	    return (EOF);
224     }
225 
226     s = cptr;
227     for (;;)
228     {
229 	switch (*s)
230 	{
231 	case '/':
232 	    if (s[1] == '*')
233 	    {
234 		cptr = s;
235 		skip_comment();
236 		s = cptr;
237 		break;
238 	    }
239 	    else if (s[1] == '/')
240 	    {
241 		get_line();
242 		if (line == NULL)
243 		    return (EOF);
244 		s = cptr;
245 		break;
246 	    }
247 	    /* FALLTHRU */
248 
249 	default:
250 	    cptr = s;
251 	    return (*s);
252 	}
253     }
254 }
255 
256 static int
257 nextc(void)
258 {
259     int ch;
260     int finish = 0;
261 
262     do
263     {
264 	switch (ch = next_inline())
265 	{
266 	case '\n':
267 	    get_line();
268 	    break;
269 	case ' ':
270 	case '\t':
271 	case '\f':
272 	case '\r':
273 	case '\v':
274 	case ',':
275 	case ';':
276 	    ++cptr;
277 	    break;
278 	case '\\':
279 	    ch = '%';
280 	    /* FALLTHRU */
281 	default:
282 	    finish = 1;
283 	    break;
284 	}
285     }
286     while (!finish);
287 
288     return ch;
289 }
290 /* *INDENT-OFF* */
291 static struct keyword
292 {
293     char name[13];
294     int token;
295 }
296 keywords[] = {
297     { "binary",      NONASSOC },
298 #if defined(YYBTYACC)
299     { "destructor",  DESTRUCTOR },
300 #endif
301     { "expect",      EXPECT },
302     { "expect-rr",   EXPECT_RR },
303     { "ident",       IDENT },
304     { "left",        LEFT },
305     { "lex-param",   LEX_PARAM },
306 #if defined(YYBTYACC)
307     { "locations",   LOCATIONS },
308 #endif
309     { "nonassoc",    NONASSOC },
310     { "parse-param", PARSE_PARAM },
311     { "pure-parser", PURE_PARSER },
312     { "right",       RIGHT },
313     { "start",       START },
314     { "term",        TOKEN },
315     { "token",       TOKEN },
316     { "token-table", TOKEN_TABLE },
317     { "type",        TYPE },
318     { "union",       UNION },
319     { "yacc",        POSIX_YACC },
320 };
321 /* *INDENT-ON* */
322 
323 static int
324 compare_keys(const void *a, const void *b)
325 {
326     const struct keyword *p = (const struct keyword *)a;
327     const struct keyword *q = (const struct keyword *)b;
328     return strcmp(p->name, q->name);
329 }
330 
331 static int
332 keyword(void)
333 {
334     int c;
335     char *t_cptr = cptr;
336     struct keyword *key;
337 
338     c = *++cptr;
339     if (isalpha(c))
340     {
341 	cinc = 0;
342 	for (;;)
343 	{
344 	    if (isalpha(c))
345 	    {
346 		if (isupper(c))
347 		    c = tolower(c);
348 		cachec(c);
349 	    }
350 	    else if (isdigit(c)
351 		     || c == '-'
352 		     || c == '.'
353 		     || c == '$')
354 	    {
355 		cachec(c);
356 	    }
357 	    else if (c == '_')
358 	    {
359 		/* treat keywords spelled with '_' as if it were '-' */
360 		cachec('-');
361 	    }
362 	    else
363 	    {
364 		break;
365 	    }
366 	    c = *++cptr;
367 	}
368 	cachec(NUL);
369 
370 	if ((key = bsearch(cache, keywords,
371 			   sizeof(keywords) / sizeof(*key),
372 			   sizeof(*key), compare_keys)))
373 	    return key->token;
374     }
375     else
376     {
377 	++cptr;
378 	if (c == L_CURL)
379 	    return (TEXT);
380 	if (c == '%' || c == '\\')
381 	    return (MARK);
382 	if (c == '<')
383 	    return (LEFT);
384 	if (c == '>')
385 	    return (RIGHT);
386 	if (c == '0')
387 	    return (TOKEN);
388 	if (c == '2')
389 	    return (NONASSOC);
390     }
391     syntax_error(lineno, line, t_cptr);
392     /*NOTREACHED */
393 }
394 
395 static void
396 copy_ident(void)
397 {
398     int c;
399     FILE *f = output_file;
400 
401     c = nextc();
402     if (c == EOF)
403 	unexpected_EOF();
404     if (c != '"')
405 	syntax_error(lineno, line, cptr);
406     ++outline;
407     fprintf(f, "#ident \"");
408     for (;;)
409     {
410 	c = *++cptr;
411 	if (c == '\n')
412 	{
413 	    fprintf(f, "\"\n");
414 	    return;
415 	}
416 	putc(c, f);
417 	if (c == '"')
418 	{
419 	    putc('\n', f);
420 	    ++cptr;
421 	    return;
422 	}
423     }
424 }
425 
426 static char *
427 copy_string(int quote)
428 {
429     struct mstring *temp = msnew();
430     int c;
431     int s_lineno = lineno;
432     char *s_line = dup_line();
433     char *s_cptr = s_line + (cptr - line - 1);
434 
435     for (;;)
436     {
437 	c = *cptr++;
438 	mputc(temp, c);
439 	if (c == quote)
440 	{
441 	    FREE(s_line);
442 	    return msdone(temp);
443 	}
444 	if (c == '\n')
445 	    unterminated_string(s_lineno, s_line, s_cptr);
446 	if (c == '\\')
447 	{
448 	    c = *cptr++;
449 	    mputc(temp, c);
450 	    if (c == '\n')
451 	    {
452 		get_line();
453 		if (line == NULL)
454 		    unterminated_string(s_lineno, s_line, s_cptr);
455 	    }
456 	}
457     }
458 }
459 
460 static char *
461 copy_comment(void)
462 {
463     struct mstring *temp = msnew();
464     int c;
465 
466     c = *cptr;
467     if (c == '/')
468     {
469 	mputc(temp, '*');
470 	while ((c = *++cptr) != '\n')
471 	{
472 	    mputc(temp, c);
473 	    if (c == '*' && cptr[1] == '/')
474 		mputc(temp, ' ');
475 	}
476 	mputc(temp, '*');
477 	mputc(temp, '/');
478     }
479     else if (c == '*')
480     {
481 	int c_lineno = lineno;
482 	char *c_line = dup_line();
483 	char *c_cptr = c_line + (cptr - line - 1);
484 
485 	mputc(temp, c);
486 	++cptr;
487 	for (;;)
488 	{
489 	    c = *cptr++;
490 	    mputc(temp, c);
491 	    if (c == '*' && *cptr == '/')
492 	    {
493 		mputc(temp, '/');
494 		++cptr;
495 		FREE(c_line);
496 		return msdone(temp);
497 	    }
498 	    if (c == '\n')
499 	    {
500 		get_line();
501 		if (line == NULL)
502 		    unterminated_comment(c_lineno, c_line, c_cptr);
503 	    }
504 	}
505     }
506     return msdone(temp);
507 }
508 
509 static void
510 copy_text(void)
511 {
512     int c;
513     FILE *f = text_file;
514     int need_newline = 0;
515     int t_lineno = lineno;
516     char *t_line = dup_line();
517     char *t_cptr = t_line + (cptr - line - 2);
518 
519     if (*cptr == '\n')
520     {
521 	get_line();
522 	if (line == NULL)
523 	    unterminated_text(t_lineno, t_line, t_cptr);
524     }
525     if (!lflag)
526 	fprintf(f, line_format, lineno, input_file_name);
527 
528   loop:
529     c = *cptr++;
530     switch (c)
531     {
532     case '\n':
533 	putc('\n', f);
534 	need_newline = 0;
535 	get_line();
536 	if (line)
537 	    goto loop;
538 	unterminated_text(t_lineno, t_line, t_cptr);
539 
540     case '\'':
541     case '"':
542 	putc(c, f);
543 	{
544 	    char *s = copy_string(c);
545 	    fputs(s, f);
546 	    free(s);
547 	}
548 	need_newline = 1;
549 	goto loop;
550 
551     case '/':
552 	putc(c, f);
553 	{
554 	    char *s = copy_comment();
555 	    fputs(s, f);
556 	    free(s);
557 	}
558 	need_newline = 1;
559 	goto loop;
560 
561     case '%':
562     case '\\':
563 	if (*cptr == R_CURL)
564 	{
565 	    if (need_newline)
566 		putc('\n', f);
567 	    ++cptr;
568 	    FREE(t_line);
569 	    return;
570 	}
571 	/* FALLTHRU */
572 
573     default:
574 	putc(c, f);
575 	need_newline = 1;
576 	goto loop;
577     }
578 }
579 
580 static void
581 puts_both(const char *s)
582 {
583     fputs(s, text_file);
584     if (dflag)
585 	fputs(s, union_file);
586 }
587 
588 static void
589 putc_both(int c)
590 {
591     putc(c, text_file);
592     if (dflag)
593 	putc(c, union_file);
594 }
595 
596 static void
597 copy_union(void)
598 {
599     int c;
600     int depth;
601     int u_lineno = lineno;
602     char *u_line = dup_line();
603     char *u_cptr = u_line + (cptr - line - 6);
604 
605     if (unionized)
606 	over_unionized(cptr - 6);
607     unionized = 1;
608 
609     puts_both("#ifdef YYSTYPE\n");
610     puts_both("#undef  YYSTYPE_IS_DECLARED\n");
611     puts_both("#define YYSTYPE_IS_DECLARED 1\n");
612     puts_both("#endif\n");
613     puts_both("#ifndef YYSTYPE_IS_DECLARED\n");
614     puts_both("#define YYSTYPE_IS_DECLARED 1\n");
615 
616     if (!lflag)
617 	fprintf(text_file, line_format, lineno, input_file_name);
618     puts_both("typedef union");
619 
620     depth = 0;
621   loop:
622     c = *cptr++;
623     putc_both(c);
624     switch (c)
625     {
626     case '\n':
627 	get_line();
628 	if (line == NULL)
629 	    unterminated_union(u_lineno, u_line, u_cptr);
630 	goto loop;
631 
632     case L_CURL:
633 	++depth;
634 	goto loop;
635 
636     case R_CURL:
637 	if (--depth == 0)
638 	{
639 	    puts_both(" YYSTYPE;\n");
640 	    puts_both("#endif /* !YYSTYPE_IS_DECLARED */\n");
641 	    FREE(u_line);
642 	    return;
643 	}
644 	goto loop;
645 
646     case '\'':
647     case '"':
648 	{
649 	    char *s = copy_string(c);
650 	    puts_both(s);
651 	    free(s);
652 	}
653 	goto loop;
654 
655     case '/':
656 	{
657 	    char *s = copy_comment();
658 	    puts_both(s);
659 	    free(s);
660 	}
661 	goto loop;
662 
663     default:
664 	goto loop;
665     }
666 }
667 
668 static char *
669 after_blanks(char *s)
670 {
671     while (*s != '\0' && isspace(UCH(*s)))
672 	++s;
673     return s;
674 }
675 
676 /*
677  * Trim leading/trailing blanks, and collapse multiple embedded blanks to a
678  * single space.  Return index to last character in the buffer.
679  */
680 static int
681 trim_blanks(char *buffer)
682 {
683     if (*buffer != '\0')
684     {
685 	char *d = buffer;
686 	char *s = after_blanks(d);
687 
688 	while ((*d++ = *s++) != '\0')
689 	{
690 	    ;
691 	}
692 
693 	--d;
694 	while ((--d != buffer) && isspace(UCH(*d)))
695 	    *d = '\0';
696 
697 	for (s = d = buffer; (*d++ = *s++) != '\0';)
698 	{
699 	    if (isspace(UCH(*s)))
700 	    {
701 		*s = ' ';
702 		while (isspace(UCH(*s)))
703 		{
704 		    *s++ = ' ';
705 		}
706 		--s;
707 	    }
708 	}
709     }
710 
711     return (int)strlen(buffer) - 1;
712 }
713 
714 /*
715  * Scan forward in the current line-buffer looking for a right-curly bracket.
716  *
717  * Parameters begin with a left-curly bracket, and continue until there are no
718  * more interesting characters after the last right-curly bracket on the
719  * current line.  Bison documents parameters as separated like this:
720  *	{type param1} {type2 param2}
721  * but also accepts commas (although some versions of bison mishandle this)
722  *	{type param1,  type2 param2}
723  */
724 static int
725 more_curly(void)
726 {
727     char *save = cptr;
728     int result = 0;
729     int finish = 0;
730     do
731     {
732 	switch (next_inline())
733 	{
734 	case 0:
735 	case '\n':
736 	    finish = 1;
737 	    break;
738 	case R_CURL:
739 	    finish = 1;
740 	    result = 1;
741 	    break;
742 	}
743 	++cptr;
744     }
745     while (!finish);
746     cptr = save;
747     return result;
748 }
749 
750 static void
751 save_param(int k, char *buffer, int name, int type2)
752 {
753     param *head, *p;
754 
755     p = TMALLOC(param, 1);
756     NO_SPACE(p);
757 
758     p->type2 = strdup(buffer + type2);
759     NO_SPACE(p->type2);
760     buffer[type2] = '\0';
761     (void)trim_blanks(p->type2);
762 
763     p->name = strdup(buffer + name);
764     NO_SPACE(p->name);
765     buffer[name] = '\0';
766     (void)trim_blanks(p->name);
767 
768     p->type = strdup(buffer);
769     NO_SPACE(p->type);
770     (void)trim_blanks(p->type);
771 
772     if (k == LEX_PARAM)
773 	head = lex_param;
774     else
775 	head = parse_param;
776 
777     if (head != NULL)
778     {
779 	while (head->next)
780 	    head = head->next;
781 	head->next = p;
782     }
783     else
784     {
785 	if (k == LEX_PARAM)
786 	    lex_param = p;
787 	else
788 	    parse_param = p;
789     }
790     p->next = NULL;
791 }
792 
793 /*
794  * Keep a linked list of parameters.  This may be multi-line, if the trailing
795  * right-curly bracket is absent.
796  */
797 static void
798 copy_param(int k)
799 {
800     int c;
801     int name, type2;
802     int curly = 0;
803     char *buf = 0;
804     int i = -1;
805     size_t buf_size = 0;
806     int st_lineno = lineno;
807     char *comma;
808 
809     do
810     {
811 	int state = curly;
812 	c = next_inline();
813 	switch (c)
814 	{
815 	case EOF:
816 	    unexpected_EOF();
817 	    break;
818 	case L_CURL:
819 	    if (curly == 1)
820 	    {
821 		goto oops;
822 	    }
823 	    curly = 1;
824 	    st_lineno = lineno;
825 	    break;
826 	case R_CURL:
827 	    if (curly != 1)
828 	    {
829 		goto oops;
830 	    }
831 	    curly = 2;
832 	    break;
833 	case '\n':
834 	    if (curly == 0)
835 	    {
836 		goto oops;
837 	    }
838 	    break;
839 	case '%':
840 	    if ((curly == 1) && (cptr == line))
841 	    {
842 		lineno = st_lineno;
843 		missing_brace();
844 	    }
845 	    /* FALLTHRU */
846 	case '"':
847 	case '\'':
848 	    goto oops;
849 	default:
850 	    if (curly == 0 && !isspace(UCH(c)))
851 	    {
852 		goto oops;
853 	    }
854 	    break;
855 	}
856 	if (buf == 0)
857 	{
858 	    buf_size = (size_t) linesize;
859 	    buf = TMALLOC(char, buf_size);
860 	}
861 	else if (c == '\n')
862 	{
863 	    get_line();
864 	    if (line == NULL)
865 		unexpected_EOF();
866 	    --cptr;
867 	    buf_size += (size_t) linesize;
868 	    buf = TREALLOC(char, buf, buf_size);
869 	}
870 	NO_SPACE(buf);
871 	if (curly)
872 	{
873 	    if ((state == 2) && (c == L_CURL))
874 	    {
875 		buf[++i] = ',';
876 	    }
877 	    else if ((state == 2) && isspace(UCH(c)))
878 	    {
879 		;
880 	    }
881 	    else if ((c != L_CURL) && (c != R_CURL))
882 	    {
883 		buf[++i] = (char)c;
884 	    }
885 	}
886 	cptr++;
887     }
888     while (curly < 2 || more_curly());
889 
890     if (i == 0)
891     {
892 	if (curly == 1)
893 	{
894 	    lineno = st_lineno;
895 	    missing_brace();
896 	}
897 	goto oops;
898     }
899 
900     buf[i--] = '\0';
901     (void)trim_blanks(buf);
902 
903     comma = buf - 1;
904     do
905     {
906 	char *parms = (comma + 1);
907 	comma = strchr(parms, ',');
908 	if (comma != 0)
909 	    *comma = '\0';
910 
911 	(void)trim_blanks(parms);
912 	i = (int)strlen(parms) - 1;
913 	if (i < 0)
914 	{
915 	    goto oops;
916 	}
917 
918 	if (parms[i] == ']')
919 	{
920 	    int level = 1;
921 	    while (i >= 0 && level > 0 && parms[i] != '[')
922 	    {
923 		if (parms[i] == ']')
924 		    ++level;
925 		else if (parms[i] == '[')
926 		    --level;
927 		i--;
928 	    }
929 	    if (i <= 0)
930 		unexpected_EOF();
931 	    type2 = i--;
932 	}
933 	else
934 	{
935 	    type2 = i + 1;
936 	}
937 
938 	while (i > 0 && (isalnum(UCH(parms[i])) || UCH(parms[i]) == '_'))
939 	    i--;
940 
941 	if (!isspace(UCH(parms[i])) && parms[i] != '*')
942 	    goto oops;
943 
944 	name = i + 1;
945 
946 	save_param(k, parms, name, type2);
947     }
948     while (comma != 0);
949     FREE(buf);
950     return;
951 
952   oops:
953     FREE(buf);
954     syntax_error(lineno, line, cptr);
955 }
956 
957 static int
958 hexval(int c)
959 {
960     if (c >= '0' && c <= '9')
961 	return (c - '0');
962     if (c >= 'A' && c <= 'F')
963 	return (c - 'A' + 10);
964     if (c >= 'a' && c <= 'f')
965 	return (c - 'a' + 10);
966     return (-1);
967 }
968 
969 static bucket *
970 get_literal(void)
971 {
972     int c, quote;
973     int i;
974     int n;
975     char *s;
976     bucket *bp;
977     int s_lineno = lineno;
978     char *s_line = dup_line();
979     char *s_cptr = s_line + (cptr - line);
980 
981     quote = *cptr++;
982     cinc = 0;
983     for (;;)
984     {
985 	c = *cptr++;
986 	if (c == quote)
987 	    break;
988 	if (c == '\n')
989 	    unterminated_string(s_lineno, s_line, s_cptr);
990 	if (c == '\\')
991 	{
992 	    char *c_cptr = cptr - 1;
993 
994 	    c = *cptr++;
995 	    switch (c)
996 	    {
997 	    case '\n':
998 		get_line();
999 		if (line == NULL)
1000 		    unterminated_string(s_lineno, s_line, s_cptr);
1001 		continue;
1002 
1003 	    case '0':
1004 	    case '1':
1005 	    case '2':
1006 	    case '3':
1007 	    case '4':
1008 	    case '5':
1009 	    case '6':
1010 	    case '7':
1011 		n = c - '0';
1012 		c = *cptr;
1013 		if (IS_OCTAL(c))
1014 		{
1015 		    n = (n << 3) + (c - '0');
1016 		    c = *++cptr;
1017 		    if (IS_OCTAL(c))
1018 		    {
1019 			n = (n << 3) + (c - '0');
1020 			++cptr;
1021 		    }
1022 		}
1023 		if (n > MAXCHAR)
1024 		    illegal_character(c_cptr);
1025 		c = n;
1026 		break;
1027 
1028 	    case 'x':
1029 		c = *cptr++;
1030 		n = hexval(c);
1031 		if (n < 0 || n >= 16)
1032 		    illegal_character(c_cptr);
1033 		for (;;)
1034 		{
1035 		    c = *cptr;
1036 		    i = hexval(c);
1037 		    if (i < 0 || i >= 16)
1038 			break;
1039 		    ++cptr;
1040 		    n = (n << 4) + i;
1041 		    if (n > MAXCHAR)
1042 			illegal_character(c_cptr);
1043 		}
1044 		c = n;
1045 		break;
1046 
1047 	    case 'a':
1048 		c = 7;
1049 		break;
1050 	    case 'b':
1051 		c = '\b';
1052 		break;
1053 	    case 'f':
1054 		c = '\f';
1055 		break;
1056 	    case 'n':
1057 		c = '\n';
1058 		break;
1059 	    case 'r':
1060 		c = '\r';
1061 		break;
1062 	    case 't':
1063 		c = '\t';
1064 		break;
1065 	    case 'v':
1066 		c = '\v';
1067 		break;
1068 	    }
1069 	}
1070 	cachec(c);
1071     }
1072     FREE(s_line);
1073 
1074     n = cinc;
1075     s = TMALLOC(char, n);
1076     NO_SPACE(s);
1077 
1078     for (i = 0; i < n; ++i)
1079 	s[i] = cache[i];
1080 
1081     cinc = 0;
1082     if (n == 1)
1083 	cachec('\'');
1084     else
1085 	cachec('"');
1086 
1087     for (i = 0; i < n; ++i)
1088     {
1089 	c = UCH(s[i]);
1090 	if (c == '\\' || c == cache[0])
1091 	{
1092 	    cachec('\\');
1093 	    cachec(c);
1094 	}
1095 	else if (isprint(c))
1096 	    cachec(c);
1097 	else
1098 	{
1099 	    cachec('\\');
1100 	    switch (c)
1101 	    {
1102 	    case 7:
1103 		cachec('a');
1104 		break;
1105 	    case '\b':
1106 		cachec('b');
1107 		break;
1108 	    case '\f':
1109 		cachec('f');
1110 		break;
1111 	    case '\n':
1112 		cachec('n');
1113 		break;
1114 	    case '\r':
1115 		cachec('r');
1116 		break;
1117 	    case '\t':
1118 		cachec('t');
1119 		break;
1120 	    case '\v':
1121 		cachec('v');
1122 		break;
1123 	    default:
1124 		cachec(((c >> 6) & 7) + '0');
1125 		cachec(((c >> 3) & 7) + '0');
1126 		cachec((c & 7) + '0');
1127 		break;
1128 	    }
1129 	}
1130     }
1131 
1132     if (n == 1)
1133 	cachec('\'');
1134     else
1135 	cachec('"');
1136 
1137     cachec(NUL);
1138     bp = lookup(cache);
1139     bp->class = TERM;
1140     if (n == 1 && bp->value == UNDEFINED)
1141 	bp->value = UCH(*s);
1142     FREE(s);
1143 
1144     return (bp);
1145 }
1146 
1147 static int
1148 is_reserved(char *name)
1149 {
1150     char *s;
1151 
1152     if (strcmp(name, ".") == 0 ||
1153 	strcmp(name, "$accept") == 0 ||
1154 	strcmp(name, "$end") == 0)
1155 	return (1);
1156 
1157     if (name[0] == '$' && name[1] == '$' && isdigit(UCH(name[2])))
1158     {
1159 	s = name + 3;
1160 	while (isdigit(UCH(*s)))
1161 	    ++s;
1162 	if (*s == NUL)
1163 	    return (1);
1164     }
1165 
1166     return (0);
1167 }
1168 
1169 static bucket *
1170 get_name(void)
1171 {
1172     int c;
1173 
1174     cinc = 0;
1175     for (c = *cptr; IS_IDENT(c); c = *++cptr)
1176 	cachec(c);
1177     cachec(NUL);
1178 
1179     if (is_reserved(cache))
1180 	used_reserved(cache);
1181 
1182     return (lookup(cache));
1183 }
1184 
1185 static Value_t
1186 get_number(void)
1187 {
1188     int c;
1189     Value_t n;
1190 
1191     n = 0;
1192     for (c = *cptr; isdigit(c); c = *++cptr)
1193 	n = (Value_t)(10 * n + (c - '0'));
1194 
1195     return (n);
1196 }
1197 
1198 static char *
1199 cache_tag(char *tag, size_t len)
1200 {
1201     int i;
1202     char *s;
1203 
1204     for (i = 0; i < ntags; ++i)
1205     {
1206 	if (strncmp(tag, tag_table[i], len) == 0 &&
1207 	    tag_table[i][len] == NUL)
1208 	    return (tag_table[i]);
1209     }
1210 
1211     if (ntags >= tagmax)
1212     {
1213 	tagmax += 16;
1214 	tag_table =
1215 	    (tag_table
1216 	     ? TREALLOC(char *, tag_table, tagmax)
1217 	     : TMALLOC(char *, tagmax));
1218 	NO_SPACE(tag_table);
1219     }
1220 
1221     s = TMALLOC(char, len + 1);
1222     NO_SPACE(s);
1223 
1224     strncpy(s, tag, len);
1225     s[len] = 0;
1226     tag_table[ntags++] = s;
1227     return s;
1228 }
1229 
1230 static char *
1231 get_tag(void)
1232 {
1233     int c;
1234     int t_lineno = lineno;
1235     char *t_line = dup_line();
1236     char *t_cptr = t_line + (cptr - line);
1237 
1238     ++cptr;
1239     c = nextc();
1240     if (c == EOF)
1241 	unexpected_EOF();
1242     if (!isalpha(c) && c != '_' && c != '$')
1243 	illegal_tag(t_lineno, t_line, t_cptr);
1244 
1245     cinc = 0;
1246     do
1247     {
1248 	cachec(c);
1249 	c = *++cptr;
1250     }
1251     while (IS_IDENT(c));
1252     cachec(NUL);
1253 
1254     c = nextc();
1255     if (c == EOF)
1256 	unexpected_EOF();
1257     if (c != '>')
1258 	illegal_tag(t_lineno, t_line, t_cptr);
1259     ++cptr;
1260 
1261     FREE(t_line);
1262     havetags = 1;
1263     return cache_tag(cache, (size_t) cinc);
1264 }
1265 
1266 #if defined(YYBTYACC)
1267 static char *
1268 scan_id(void)
1269 {
1270     char *b = cptr;
1271 
1272     while (isalnum(*cptr) || *cptr == '_' || *cptr == '$')
1273 	cptr++;
1274     return cache_tag(b, (size_t) (cptr - b));
1275 }
1276 #endif
1277 
1278 static void
1279 declare_tokens(int assoc)
1280 {
1281     int c;
1282     bucket *bp;
1283     Value_t value;
1284     char *tag = 0;
1285 
1286     if (assoc != TOKEN)
1287 	++prec;
1288 
1289     c = nextc();
1290     if (c == EOF)
1291 	unexpected_EOF();
1292     if (c == '<')
1293     {
1294 	tag = get_tag();
1295 	c = nextc();
1296 	if (c == EOF)
1297 	    unexpected_EOF();
1298     }
1299 
1300     for (;;)
1301     {
1302 	if (isalpha(c) || c == '_' || c == '.' || c == '$')
1303 	    bp = get_name();
1304 	else if (c == '\'' || c == '"')
1305 	    bp = get_literal();
1306 	else
1307 	    return;
1308 
1309 	if (bp == goal)
1310 	    tokenized_start(bp->name);
1311 	bp->class = TERM;
1312 
1313 	if (tag)
1314 	{
1315 	    if (bp->tag && tag != bp->tag)
1316 		retyped_warning(bp->name);
1317 	    bp->tag = tag;
1318 	}
1319 
1320 	if (assoc != TOKEN)
1321 	{
1322 	    if (bp->prec && prec != bp->prec)
1323 		reprec_warning(bp->name);
1324 	    bp->assoc = (Assoc_t)assoc;
1325 	    bp->prec = prec;
1326 	}
1327 
1328 	c = nextc();
1329 	if (c == EOF)
1330 	    unexpected_EOF();
1331 
1332 	value = UNDEFINED;
1333 	if (isdigit(c))
1334 	{
1335 	    value = get_number();
1336 	    if (bp->value != UNDEFINED && value != bp->value)
1337 		revalued_warning(bp->name);
1338 	    bp->value = value;
1339 	    c = nextc();
1340 	    if (c == EOF)
1341 		unexpected_EOF();
1342 	}
1343     }
1344 }
1345 
1346 /*
1347  * %expect requires special handling
1348  * as it really isn't part of the yacc
1349  * grammar only a flag for yacc proper.
1350  */
1351 static void
1352 declare_expect(int assoc)
1353 {
1354     int c;
1355 
1356     if (assoc != EXPECT && assoc != EXPECT_RR)
1357 	++prec;
1358 
1359     /*
1360      * Stay away from nextc - doesn't
1361      * detect EOL and will read to EOF.
1362      */
1363     c = *++cptr;
1364     if (c == EOF)
1365 	unexpected_EOF();
1366 
1367     for (;;)
1368     {
1369 	if (isdigit(c))
1370 	{
1371 	    if (assoc == EXPECT)
1372 		SRexpect = get_number();
1373 	    else
1374 		RRexpect = get_number();
1375 	    break;
1376 	}
1377 	/*
1378 	 * Looking for number before EOL.
1379 	 * Spaces, tabs, and numbers are ok,
1380 	 * words, punc., etc. are syntax errors.
1381 	 */
1382 	else if (c == '\n' || isalpha(c) || !isspace(c))
1383 	{
1384 	    syntax_error(lineno, line, cptr);
1385 	}
1386 	else
1387 	{
1388 	    c = *++cptr;
1389 	    if (c == EOF)
1390 		unexpected_EOF();
1391 	}
1392     }
1393 }
1394 
1395 #if defined(YYBTYACC)
1396 static void
1397 declare_argtypes(bucket *bp)
1398 {
1399     char *tags[MAXARGS];
1400     int args = 0, c;
1401 
1402     if (bp->args >= 0)
1403 	retyped_warning(bp->name);
1404     cptr++;			/* skip open paren */
1405     for (;;)
1406     {
1407 	c = nextc();
1408 	if (c == EOF)
1409 	    unexpected_EOF();
1410 	if (c != '<')
1411 	    syntax_error(lineno, line, cptr);
1412 	tags[args++] = get_tag();
1413 	c = nextc();
1414 	if (c == R_PAREN)
1415 	    break;
1416 	if (c == EOF)
1417 	    unexpected_EOF();
1418     }
1419     cptr++;			/* skip close paren */
1420     bp->args = args;
1421     bp->argnames = TMALLOC(char *, args);
1422     NO_SPACE(bp->argnames);
1423     bp->argtags = CALLOC(sizeof(char *), args + 1);
1424     NO_SPACE(bp->argtags);
1425     while (--args >= 0)
1426     {
1427 	bp->argtags[args] = tags[args];
1428 	bp->argnames[args] = NULL;
1429     }
1430 }
1431 #endif
1432 
1433 static void
1434 declare_types(void)
1435 {
1436     int c;
1437     bucket *bp = NULL;
1438     char *tag = NULL;
1439 
1440     c = nextc();
1441     if (c == EOF)
1442 	unexpected_EOF();
1443     if (c == '<')
1444 	tag = get_tag();
1445 
1446     for (;;)
1447     {
1448 	c = nextc();
1449 	if (c == EOF)
1450 	    unexpected_EOF();
1451 	if (isalpha(c) || c == '_' || c == '.' || c == '$')
1452 	{
1453 	    bp = get_name();
1454 #if defined(YYBTYACC)
1455 	    if (nextc() == L_PAREN)
1456 		declare_argtypes(bp);
1457 	    else
1458 		bp->args = 0;
1459 #endif
1460 	}
1461 	else if (c == '\'' || c == '"')
1462 	{
1463 	    bp = get_literal();
1464 #if defined(YYBTYACC)
1465 	    bp->args = 0;
1466 #endif
1467 	}
1468 	else
1469 	    return;
1470 
1471 	if (tag)
1472 	{
1473 	    if (bp->tag && tag != bp->tag)
1474 		retyped_warning(bp->name);
1475 	    bp->tag = tag;
1476 	}
1477     }
1478 }
1479 
1480 static void
1481 declare_start(void)
1482 {
1483     int c;
1484     bucket *bp;
1485 
1486     c = nextc();
1487     if (c == EOF)
1488 	unexpected_EOF();
1489     if (!isalpha(c) && c != '_' && c != '.' && c != '$')
1490 	syntax_error(lineno, line, cptr);
1491     bp = get_name();
1492     if (bp->class == TERM)
1493 	terminal_start(bp->name);
1494     if (goal && goal != bp)
1495 	restarted_warning();
1496     goal = bp;
1497 }
1498 
1499 static void
1500 read_declarations(void)
1501 {
1502     int c, k;
1503 
1504     cache_size = CACHE_SIZE;
1505     cache = TMALLOC(char, cache_size);
1506     NO_SPACE(cache);
1507 
1508     for (;;)
1509     {
1510 	c = nextc();
1511 	if (c == EOF)
1512 	    unexpected_EOF();
1513 	if (c != '%')
1514 	    syntax_error(lineno, line, cptr);
1515 	switch (k = keyword())
1516 	{
1517 	case MARK:
1518 	    return;
1519 
1520 	case IDENT:
1521 	    copy_ident();
1522 	    break;
1523 
1524 	case TEXT:
1525 	    copy_text();
1526 	    break;
1527 
1528 	case UNION:
1529 	    copy_union();
1530 	    break;
1531 
1532 	case TOKEN:
1533 	case LEFT:
1534 	case RIGHT:
1535 	case NONASSOC:
1536 	    declare_tokens(k);
1537 	    break;
1538 
1539 	case EXPECT:
1540 	case EXPECT_RR:
1541 	    declare_expect(k);
1542 	    break;
1543 
1544 	case TYPE:
1545 	    declare_types();
1546 	    break;
1547 
1548 	case START:
1549 	    declare_start();
1550 	    break;
1551 
1552 	case PURE_PARSER:
1553 	    pure_parser = 1;
1554 	    break;
1555 
1556 	case PARSE_PARAM:
1557 	case LEX_PARAM:
1558 	    copy_param(k);
1559 	    break;
1560 
1561 	case TOKEN_TABLE:
1562 	    token_table = 1;
1563 	    break;
1564 
1565 #if defined(YYBTYACC)
1566 	case LOCATIONS:
1567 	    locations = 1;
1568 	    break;
1569 
1570 	case DESTRUCTOR:
1571 	    destructor = 1;
1572 	    copy_destructor();
1573 	    break;
1574 #endif
1575 
1576 	case POSIX_YACC:
1577 	    /* noop for bison compatibility. byacc is already designed to be posix
1578 	     * yacc compatible. */
1579 	    break;
1580 	}
1581     }
1582 }
1583 
1584 static void
1585 initialize_grammar(void)
1586 {
1587     nitems = 4;
1588     maxitems = 300;
1589 
1590     pitem = TMALLOC(bucket *, maxitems);
1591     NO_SPACE(pitem);
1592 
1593     pitem[0] = 0;
1594     pitem[1] = 0;
1595     pitem[2] = 0;
1596     pitem[3] = 0;
1597 
1598     nrules = 3;
1599     maxrules = 100;
1600 
1601     plhs = TMALLOC(bucket *, maxrules);
1602     NO_SPACE(plhs);
1603 
1604     plhs[0] = 0;
1605     plhs[1] = 0;
1606     plhs[2] = 0;
1607 
1608     rprec = TMALLOC(Value_t, maxrules);
1609     NO_SPACE(rprec);
1610 
1611     rprec[0] = 0;
1612     rprec[1] = 0;
1613     rprec[2] = 0;
1614 
1615     rassoc = TMALLOC(Assoc_t, maxrules);
1616     NO_SPACE(rassoc);
1617 
1618     rassoc[0] = TOKEN;
1619     rassoc[1] = TOKEN;
1620     rassoc[2] = TOKEN;
1621 }
1622 
1623 static void
1624 expand_items(void)
1625 {
1626     maxitems += 300;
1627     pitem = TREALLOC(bucket *, pitem, maxitems);
1628     NO_SPACE(pitem);
1629 }
1630 
1631 static void
1632 expand_rules(void)
1633 {
1634     maxrules += 100;
1635 
1636     plhs = TREALLOC(bucket *, plhs, maxrules);
1637     NO_SPACE(plhs);
1638 
1639     rprec = TREALLOC(Value_t, rprec, maxrules);
1640     NO_SPACE(rprec);
1641 
1642     rassoc = TREALLOC(Assoc_t, rassoc, maxrules);
1643     NO_SPACE(rassoc);
1644 }
1645 
1646 /* set immediately prior to where copy_args() could be called, and incremented by
1647    the various routines that will rescan the argument list as appropriate */
1648 static int rescan_lineno;
1649 #if defined(YYBTYACC)
1650 
1651 static char *
1652 copy_args(int *alen)
1653 {
1654     struct mstring *s = msnew();
1655     int depth = 0, len = 1;
1656     char c, quote = 0;
1657     int a_lineno = lineno;
1658     char *a_line = dup_line();
1659     char *a_cptr = a_line + (cptr - line - 1);
1660 
1661     while ((c = *cptr++) != R_PAREN || depth || quote)
1662     {
1663 	if (c == ',' && !quote && !depth)
1664 	{
1665 	    len++;
1666 	    mputc(s, 0);
1667 	    continue;
1668 	}
1669 	mputc(s, c);
1670 	if (c == '\n')
1671 	{
1672 	    get_line();
1673 	    if (!line)
1674 	    {
1675 		if (quote)
1676 		    unterminated_string(a_lineno, a_line, a_cptr);
1677 		else
1678 		    unterminated_arglist(a_lineno, a_line, a_cptr);
1679 	    }
1680 	}
1681 	else if (quote)
1682 	{
1683 	    if (c == quote)
1684 		quote = 0;
1685 	    else if (c == '\\')
1686 	    {
1687 		if (*cptr != '\n')
1688 		    mputc(s, *cptr++);
1689 	    }
1690 	}
1691 	else
1692 	{
1693 	    if (c == L_PAREN)
1694 		depth++;
1695 	    else if (c == R_PAREN)
1696 		depth--;
1697 	    else if (c == '\"' || c == '\'')
1698 		quote = c;
1699 	}
1700     }
1701     if (alen)
1702 	*alen = len;
1703     FREE(a_line);
1704     return msdone(s);
1705 }
1706 
1707 static char *
1708 parse_id(char *p, char **save)
1709 {
1710     char *b;
1711 
1712     while (isspace(*p))
1713 	if (*p++ == '\n')
1714 	    rescan_lineno++;
1715     if (!isalpha(*p) && *p != '_')
1716 	return NULL;
1717     b = p;
1718     while (isalnum(*p) || *p == '_' || *p == '$')
1719 	p++;
1720     if (save)
1721     {
1722 	*save = cache_tag(b, (size_t) (p - b));
1723     }
1724     return p;
1725 }
1726 
1727 static char *
1728 parse_int(char *p, int *save)
1729 {
1730     int neg = 0, val = 0;
1731 
1732     while (isspace(*p))
1733 	if (*p++ == '\n')
1734 	    rescan_lineno++;
1735     if (*p == '-')
1736     {
1737 	neg = 1;
1738 	p++;
1739     }
1740     if (!isdigit(*p))
1741 	return NULL;
1742     while (isdigit(*p))
1743 	val = val * 10 + *p++ - '0';
1744     if (neg)
1745 	val = -val;
1746     if (save)
1747 	*save = val;
1748     return p;
1749 }
1750 
1751 static void
1752 parse_arginfo(bucket *a, char *args, int argslen)
1753 {
1754     char *p = args, *tmp;
1755     int i, redec = 0;
1756 
1757     if (a->args >= 0)
1758     {
1759 	if (a->args != argslen)
1760 	    arg_number_disagree_warning(rescan_lineno, a->name);
1761 	redec = 1;
1762     }
1763     else
1764     {
1765 	if ((a->args = argslen) == 0)
1766 	    return;
1767 	a->argnames = TMALLOC(char *, argslen);
1768 	NO_SPACE(a->argnames);
1769 	a->argtags = TMALLOC(char *, argslen);
1770 	NO_SPACE(a->argtags);
1771     }
1772     if (!args)
1773 	return;
1774     for (i = 0; i < argslen; i++)
1775     {
1776 	while (isspace(*p))
1777 	    if (*p++ == '\n')
1778 		rescan_lineno++;
1779 	if (*p++ != '$')
1780 	    bad_formals();
1781 	while (isspace(*p))
1782 	    if (*p++ == '\n')
1783 		rescan_lineno++;
1784 	if (*p == '<')
1785 	{
1786 	    havetags = 1;
1787 	    if (!(p = parse_id(p + 1, &tmp)))
1788 		bad_formals();
1789 	    while (isspace(*p))
1790 		if (*p++ == '\n')
1791 		    rescan_lineno++;
1792 	    if (*p++ != '>')
1793 		bad_formals();
1794 	    if (redec)
1795 	    {
1796 		if (a->argtags[i] != tmp)
1797 		    arg_type_disagree_warning(rescan_lineno, i + 1, a->name);
1798 	    }
1799 	    else
1800 		a->argtags[i] = tmp;
1801 	}
1802 	else if (!redec)
1803 	    a->argtags[i] = NULL;
1804 	if (!(p = parse_id(p, &a->argnames[i])))
1805 	    bad_formals();
1806 	while (isspace(*p))
1807 	    if (*p++ == '\n')
1808 		rescan_lineno++;
1809 	if (*p++)
1810 	    bad_formals();
1811     }
1812     free(args);
1813 }
1814 
1815 static char *
1816 compile_arg(char **theptr, char *yyvaltag)
1817 {
1818     char *p = *theptr;
1819     struct mstring *c = msnew();
1820     int i, j, n;
1821     Value_t *offsets = NULL, maxoffset;
1822     bucket **rhs;
1823 
1824     maxoffset = 0;
1825     n = 0;
1826     for (i = nitems - 1; pitem[i]; --i)
1827     {
1828 	n++;
1829 	if (pitem[i]->class != ARGUMENT)
1830 	    maxoffset++;
1831     }
1832     if (maxoffset > 0)
1833     {
1834 	offsets = TMALLOC(Value_t, maxoffset + 1);
1835 	NO_SPACE(offsets);
1836 
1837 	for (j = 0, i++; i < nitems; i++)
1838 	    if (pitem[i]->class != ARGUMENT)
1839 		offsets[++j] = (Value_t)(i - nitems + 1);
1840     }
1841     rhs = pitem + nitems - 1;
1842 
1843     if (yyvaltag)
1844 	msprintf(c, "yyval.%s = ", yyvaltag);
1845     else
1846 	msprintf(c, "yyval = ");
1847     while (*p)
1848     {
1849 	if (*p == '$')
1850 	{
1851 	    char *tag = NULL;
1852 	    if (*++p == '<')
1853 		if (!(p = parse_id(++p, &tag)) || *p++ != '>')
1854 		    illegal_tag(rescan_lineno, NULL, NULL);
1855 	    if (isdigit(*p) || *p == '-')
1856 	    {
1857 		int val;
1858 		if (!(p = parse_int(p, &val)))
1859 		    dollar_error(rescan_lineno, NULL, NULL);
1860 		if (val <= 0)
1861 		    i = val - n;
1862 		else if (val > maxoffset)
1863 		{
1864 		    dollar_warning(rescan_lineno, val);
1865 		    i = val - maxoffset;
1866 		}
1867 		else if (maxoffset > 0)
1868 		{
1869 		    i = offsets[val];
1870 		    if (!tag && !(tag = rhs[i]->tag) && havetags)
1871 			untyped_rhs(val, rhs[i]->name);
1872 		}
1873 		msprintf(c, "yystack.l_mark[%d]", i);
1874 		if (tag)
1875 		    msprintf(c, ".%s", tag);
1876 		else if (havetags)
1877 		    unknown_rhs(val);
1878 	    }
1879 	    else if (isalpha(*p) || *p == '_')
1880 	    {
1881 		char *arg;
1882 		if (!(p = parse_id(p, &arg)))
1883 		    dollar_error(rescan_lineno, NULL, NULL);
1884 		for (i = plhs[nrules]->args - 1; i >= 0; i--)
1885 		    if (arg == plhs[nrules]->argnames[i])
1886 			break;
1887 		if (i < 0)
1888 		    unknown_arg_warning(rescan_lineno, "$", arg, NULL, NULL);
1889 		else if (!tag)
1890 		    tag = plhs[nrules]->argtags[i];
1891 		msprintf(c, "yystack.l_mark[%d]",
1892 			 i - plhs[nrules]->args + 1 - n);
1893 		if (tag)
1894 		    msprintf(c, ".%s", tag);
1895 		else if (havetags)
1896 		    untyped_arg_warning(rescan_lineno, "$", arg);
1897 	    }
1898 	    else
1899 		dollar_error(rescan_lineno, NULL, NULL);
1900 	}
1901 	else if (*p == '@')
1902 	{
1903 	    at_error(rescan_lineno, NULL, NULL);
1904 	}
1905 	else
1906 	{
1907 	    if (*p == '\n')
1908 		rescan_lineno++;
1909 	    mputc(c, *p++);
1910 	}
1911     }
1912     *theptr = p;
1913     if (maxoffset > 0)
1914 	FREE(offsets);
1915     return msdone(c);
1916 }
1917 
1918 static int
1919 can_elide_arg(char **theptr, char *yyvaltag)
1920 {
1921     char *p = *theptr;
1922     int rv = 0;
1923     int i, j, n = 0;
1924     Value_t *offsets = NULL, maxoffset = 0;
1925     bucket **rhs;
1926     char *tag = 0;
1927 
1928     if (*p++ != '$')
1929 	return 0;
1930     if (*p == '<')
1931     {
1932 	if (!(p = parse_id(++p, &tag)) || *p++ != '>')
1933 	    return 0;
1934     }
1935     for (i = nitems - 1; pitem[i]; --i)
1936     {
1937 	n++;
1938 	if (pitem[i]->class != ARGUMENT)
1939 	    maxoffset++;
1940     }
1941     if (maxoffset > 0)
1942     {
1943 	offsets = TMALLOC(Value_t, maxoffset + 1);
1944 	NO_SPACE(offsets);
1945 
1946 	for (j = 0, i++; i < nitems; i++)
1947 	    if (pitem[i]->class != ARGUMENT)
1948 		offsets[++j] = (Value_t)(i - nitems + 1);
1949     }
1950     rhs = pitem + nitems - 1;
1951 
1952     if (isdigit(*p) || *p == '-')
1953     {
1954 	int val;
1955 	if (!(p = parse_int(p, &val)))
1956 	    rv = 0;
1957 	else
1958 	{
1959 	    if (val <= 0)
1960 		rv = 1 - val + n;
1961 	    else if (val > maxoffset)
1962 		rv = 0;
1963 	    else
1964 	    {
1965 		i = offsets[val];
1966 		rv = 1 - i;
1967 		if (!tag)
1968 		    tag = rhs[i]->tag;
1969 	    }
1970 	}
1971     }
1972     else if (isalpha(*p) || *p == '_')
1973     {
1974 	char *arg;
1975 	if (!(p = parse_id(p, &arg)))
1976 	    return 0;
1977 	for (i = plhs[nrules]->args - 1; i >= 0; i--)
1978 	    if (arg == plhs[nrules]->argnames[i])
1979 		break;
1980 	if (i >= 0)
1981 	{
1982 	    if (!tag)
1983 		tag = plhs[nrules]->argtags[i];
1984 	    rv = plhs[nrules]->args + n - i;
1985 	}
1986     }
1987     if (tag && yyvaltag)
1988     {
1989 	if (strcmp(tag, yyvaltag))
1990 	    rv = 0;
1991     }
1992     else if (tag || yyvaltag)
1993 	rv = 0;
1994     if (maxoffset > 0)
1995 	FREE(offsets);
1996     if (*p || rv <= 0)
1997 	return 0;
1998     *theptr = p + 1;
1999     return rv;
2000 }
2001 
2002 #define ARG_CACHE_SIZE	1024
2003 static struct arg_cache
2004 {
2005     struct arg_cache *next;
2006     char *code;
2007     int rule;
2008 }
2009  *arg_cache[ARG_CACHE_SIZE];
2010 
2011 static int
2012 lookup_arg_cache(char *code)
2013 {
2014     struct arg_cache *entry;
2015 
2016     entry = arg_cache[strnshash(code) % ARG_CACHE_SIZE];
2017     while (entry)
2018     {
2019 	if (!strnscmp(entry->code, code))
2020 	    return entry->rule;
2021 	entry = entry->next;
2022     }
2023     return -1;
2024 }
2025 
2026 static void
2027 insert_arg_cache(char *code, int rule)
2028 {
2029     struct arg_cache *entry = NEW(struct arg_cache);
2030     int i;
2031 
2032     NO_SPACE(entry);
2033     i = strnshash(code) % ARG_CACHE_SIZE;
2034     entry->code = code;
2035     entry->rule = rule;
2036     entry->next = arg_cache[i];
2037     arg_cache[i] = entry;
2038 }
2039 
2040 static void
2041 clean_arg_cache(void)
2042 {
2043     struct arg_cache *e, *t;
2044     int i;
2045 
2046     for (i = 0; i < ARG_CACHE_SIZE; i++)
2047     {
2048 	for (e = arg_cache[i]; (t = e); e = e->next, FREE(t))
2049 	    free(e->code);
2050 	arg_cache[i] = NULL;
2051     }
2052 }
2053 #endif /* defined(YYBTYACC) */
2054 
2055 static void
2056 advance_to_start(void)
2057 {
2058     int c;
2059     bucket *bp;
2060     char *s_cptr;
2061     int s_lineno;
2062 #if defined(YYBTYACC)
2063     char *args = NULL;
2064     int argslen = 0;
2065 #endif
2066 
2067     for (;;)
2068     {
2069 	c = nextc();
2070 	if (c != '%')
2071 	    break;
2072 	s_cptr = cptr;
2073 	switch (keyword())
2074 	{
2075 	case MARK:
2076 	    no_grammar();
2077 
2078 	case TEXT:
2079 	    copy_text();
2080 	    break;
2081 
2082 	case START:
2083 	    declare_start();
2084 	    break;
2085 
2086 	default:
2087 	    syntax_error(lineno, line, s_cptr);
2088 	}
2089     }
2090 
2091     c = nextc();
2092     if (!isalpha(c) && c != '_' && c != '.' && c != '_')
2093 	syntax_error(lineno, line, cptr);
2094     bp = get_name();
2095     if (goal == 0)
2096     {
2097 	if (bp->class == TERM)
2098 	    terminal_start(bp->name);
2099 	goal = bp;
2100     }
2101 
2102     s_lineno = lineno;
2103     c = nextc();
2104     if (c == EOF)
2105 	unexpected_EOF();
2106     rescan_lineno = lineno;	/* line# for possible inherited args rescan */
2107 #if defined(YYBTYACC)
2108     if (c == L_PAREN)
2109     {
2110 	++cptr;
2111 	args = copy_args(&argslen);
2112 	NO_SPACE(args);
2113 	c = nextc();
2114     }
2115 #endif
2116     if (c != ':')
2117 	syntax_error(lineno, line, cptr);
2118     start_rule(bp, s_lineno);
2119 #if defined(YYBTYACC)
2120     parse_arginfo(bp, args, argslen);
2121 #endif
2122     ++cptr;
2123 }
2124 
2125 static void
2126 start_rule(bucket *bp, int s_lineno)
2127 {
2128     if (bp->class == TERM)
2129 	terminal_lhs(s_lineno);
2130     bp->class = NONTERM;
2131     if (!bp->index)
2132 	bp->index = nrules;
2133     if (nrules >= maxrules)
2134 	expand_rules();
2135     plhs[nrules] = bp;
2136     rprec[nrules] = UNDEFINED;
2137     rassoc[nrules] = TOKEN;
2138 }
2139 
2140 static void
2141 end_rule(void)
2142 {
2143     int i;
2144 
2145     if (!last_was_action && plhs[nrules]->tag)
2146     {
2147 	if (pitem[nitems - 1])
2148 	{
2149 	    for (i = nitems - 1; (i > 0) && pitem[i]; --i)
2150 		continue;
2151 	    if (pitem[i + 1] == 0 || pitem[i + 1]->tag != plhs[nrules]->tag)
2152 		default_action_warning(plhs[nrules]->name);
2153 	}
2154 	else
2155 	    default_action_warning(plhs[nrules]->name);
2156     }
2157 
2158     last_was_action = 0;
2159     if (nitems >= maxitems)
2160 	expand_items();
2161     pitem[nitems] = 0;
2162     ++nitems;
2163     ++nrules;
2164 }
2165 
2166 static void
2167 insert_empty_rule(void)
2168 {
2169     bucket *bp, **bpp;
2170 
2171     assert(cache);
2172     assert(cache_size >= CACHE_SIZE);
2173     sprintf(cache, "$$%d", ++gensym);
2174     bp = make_bucket(cache);
2175     last_symbol->next = bp;
2176     last_symbol = bp;
2177     bp->tag = plhs[nrules]->tag;
2178     bp->class = ACTION;
2179 #if defined(YYBTYACC)
2180     bp->args = 0;
2181 #endif
2182 
2183     nitems = (Value_t)(nitems + 2);
2184     if (nitems > maxitems)
2185 	expand_items();
2186     bpp = pitem + nitems - 1;
2187     *bpp-- = bp;
2188     while ((bpp[0] = bpp[-1]) != 0)
2189 	--bpp;
2190 
2191     if (++nrules >= maxrules)
2192 	expand_rules();
2193     plhs[nrules] = plhs[nrules - 1];
2194     plhs[nrules - 1] = bp;
2195     rprec[nrules] = rprec[nrules - 1];
2196     rprec[nrules - 1] = 0;
2197     rassoc[nrules] = rassoc[nrules - 1];
2198     rassoc[nrules - 1] = TOKEN;
2199 }
2200 
2201 #if defined(YYBTYACC)
2202 static char *
2203 insert_arg_rule(char *arg, char *tag)
2204 {
2205     int line_number = rescan_lineno;
2206     char *code = compile_arg(&arg, tag);
2207     int rule = lookup_arg_cache(code);
2208     FILE *f = action_file;
2209 
2210     if (rule < 0)
2211     {
2212 	rule = nrules;
2213 	insert_arg_cache(code, rule);
2214 	trialaction = 1;	/* arg rules always run in trial mode */
2215 	fprintf(f, "case %d:\n", rule - 2);
2216 	if (!lflag)
2217 	    fprintf(f, line_format, line_number, input_file_name);
2218 	fprintf(f, "%s;\n", code);
2219 	fprintf(f, "break;\n");
2220 	insert_empty_rule();
2221 	plhs[rule]->tag = cache_tag(tag, strlen(tag));
2222 	plhs[rule]->class = ARGUMENT;
2223     }
2224     else
2225     {
2226 	if (++nitems > maxitems)
2227 	    expand_items();
2228 	pitem[nitems - 1] = plhs[rule];
2229 	free(code);
2230     }
2231     return arg + 1;
2232 }
2233 #endif
2234 
2235 static void
2236 add_symbol(void)
2237 {
2238     int c;
2239     bucket *bp;
2240     int s_lineno = lineno;
2241 #if defined(YYBTYACC)
2242     char *args = NULL;
2243     int argslen = 0;
2244 #endif
2245 
2246     c = *cptr;
2247     if (c == '\'' || c == '"')
2248 	bp = get_literal();
2249     else
2250 	bp = get_name();
2251 
2252     c = nextc();
2253     rescan_lineno = lineno;	/* line# for possible inherited args rescan */
2254 #if defined(YYBTYACC)
2255     if (c == L_PAREN)
2256     {
2257 	++cptr;
2258 	args = copy_args(&argslen);
2259 	NO_SPACE(args);
2260 	c = nextc();
2261     }
2262 #endif
2263     if (c == ':')
2264     {
2265 	end_rule();
2266 	start_rule(bp, s_lineno);
2267 #if defined(YYBTYACC)
2268 	parse_arginfo(bp, args, argslen);
2269 #endif
2270 	++cptr;
2271 	return;
2272     }
2273 
2274     if (last_was_action)
2275 	insert_empty_rule();
2276     last_was_action = 0;
2277 
2278 #if defined(YYBTYACC)
2279     if (bp->args < 0)
2280 	bp->args = argslen;
2281     if (argslen == 0 && bp->args > 0 && pitem[nitems - 1] == NULL)
2282     {
2283 	int i;
2284 	if (plhs[nrules]->args != bp->args)
2285 	    wrong_number_args_warning("default ", bp->name);
2286 	for (i = bp->args - 1; i >= 0; i--)
2287 	    if (plhs[nrules]->argtags[i] != bp->argtags[i])
2288 		wrong_type_for_arg_warning(i + 1, bp->name);
2289     }
2290     else if (bp->args != argslen)
2291 	wrong_number_args_warning("", bp->name);
2292     if (args != 0)
2293     {
2294 	char *ap = args;
2295 	int i = 0;
2296 	int elide_cnt = can_elide_arg(&ap, bp->argtags[0]);
2297 
2298 	if (elide_cnt > argslen)
2299 	    elide_cnt = 0;
2300 	if (elide_cnt)
2301 	{
2302 	    for (i = 1; i < elide_cnt; i++)
2303 		if (can_elide_arg(&ap, bp->argtags[i]) != elide_cnt - i)
2304 		{
2305 		    elide_cnt = 0;
2306 		    break;
2307 		}
2308 	}
2309 	if (elide_cnt)
2310 	{
2311 	    assert(i == elide_cnt);
2312 	}
2313 	else
2314 	{
2315 	    ap = args;
2316 	    i = 0;
2317 	}
2318 	for (; i < argslen; i++)
2319 	    ap = insert_arg_rule(ap, bp->argtags[i]);
2320 	free(args);
2321     }
2322 #endif /* defined(YYBTYACC) */
2323 
2324     if (++nitems > maxitems)
2325 	expand_items();
2326     pitem[nitems - 1] = bp;
2327 }
2328 
2329 static void
2330 copy_action(void)
2331 {
2332     int c;
2333     int i, j, n;
2334     int depth;
2335 #if defined(YYBTYACC)
2336     int haveyyval = 0;
2337 #endif
2338     char *tag;
2339     FILE *f = action_file;
2340     int a_lineno = lineno;
2341     char *a_line = dup_line();
2342     char *a_cptr = a_line + (cptr - line);
2343     Value_t *offsets = NULL, maxoffset;
2344     bucket **rhs;
2345 
2346     if (last_was_action)
2347 	insert_empty_rule();
2348     last_was_action = 1;
2349 #if defined(YYBTYACC)
2350     trialaction = (*cptr == L_BRAC);
2351 #endif
2352 
2353     fprintf(f, "case %d:\n", nrules - 2);
2354 #if defined(YYBTYACC)
2355     if (backtrack)
2356     {
2357 	if (!trialaction)
2358 	    fprintf(f, "  if (!yytrial)\n");
2359     }
2360 #endif
2361     if (!lflag)
2362 	fprintf(f, line_format, lineno, input_file_name);
2363     if (*cptr == '=')
2364 	++cptr;
2365 
2366     /* avoid putting curly-braces in first column, to ease editing */
2367     if (*after_blanks(cptr) == L_CURL)
2368     {
2369 	putc('\t', f);
2370 	cptr = after_blanks(cptr);
2371     }
2372 
2373     maxoffset = 0;
2374     n = 0;
2375     for (i = nitems - 1; pitem[i]; --i)
2376     {
2377 	++n;
2378 	if (pitem[i]->class != ARGUMENT)
2379 	    maxoffset++;
2380     }
2381     if (maxoffset > 0)
2382     {
2383 	offsets = TMALLOC(Value_t, maxoffset + 1);
2384 	NO_SPACE(offsets);
2385 
2386 	for (j = 0, i++; i < nitems; i++)
2387 	{
2388 	    if (pitem[i]->class != ARGUMENT)
2389 	    {
2390 		offsets[++j] = (Value_t)(i - nitems + 1);
2391 	    }
2392 	}
2393     }
2394     rhs = pitem + nitems - 1;
2395 
2396     depth = 0;
2397   loop:
2398     c = *cptr;
2399     if (c == '$')
2400     {
2401 	if (cptr[1] == '<')
2402 	{
2403 	    int d_lineno = lineno;
2404 	    char *d_line = dup_line();
2405 	    char *d_cptr = d_line + (cptr - line);
2406 
2407 	    ++cptr;
2408 	    tag = get_tag();
2409 	    c = *cptr;
2410 	    if (c == '$')
2411 	    {
2412 		fprintf(f, "yyval.%s", tag);
2413 		++cptr;
2414 		FREE(d_line);
2415 		goto loop;
2416 	    }
2417 	    else if (isdigit(c))
2418 	    {
2419 		i = get_number();
2420 		if (i == 0)
2421 		    fprintf(f, "yystack.l_mark[%d].%s", -n, tag);
2422 		else if (i > maxoffset)
2423 		{
2424 		    dollar_warning(d_lineno, i);
2425 		    fprintf(f, "yystack.l_mark[%d].%s", i - maxoffset, tag);
2426 		}
2427 		else if (offsets)
2428 		    fprintf(f, "yystack.l_mark[%d].%s", offsets[i], tag);
2429 		FREE(d_line);
2430 		goto loop;
2431 	    }
2432 	    else if (c == '-' && isdigit(UCH(cptr[1])))
2433 	    {
2434 		++cptr;
2435 		i = -get_number() - n;
2436 		fprintf(f, "yystack.l_mark[%d].%s", i, tag);
2437 		FREE(d_line);
2438 		goto loop;
2439 	    }
2440 #if defined(YYBTYACC)
2441 	    else if (isalpha(c) || c == '_')
2442 	    {
2443 		char *arg = scan_id();
2444 		for (i = plhs[nrules]->args - 1; i >= 0; i--)
2445 		    if (arg == plhs[nrules]->argnames[i])
2446 			break;
2447 		if (i < 0)
2448 		    unknown_arg_warning(d_lineno, "$", arg, d_line, d_cptr);
2449 		fprintf(f, "yystack.l_mark[%d].%s",
2450 			i - plhs[nrules]->args + 1 - n, tag);
2451 		FREE(d_line);
2452 		goto loop;
2453 	    }
2454 #endif
2455 	    else
2456 		dollar_error(d_lineno, d_line, d_cptr);
2457 	}
2458 	else if (cptr[1] == '$')
2459 	{
2460 	    if (havetags)
2461 	    {
2462 		tag = plhs[nrules]->tag;
2463 		if (tag == 0)
2464 		    untyped_lhs();
2465 		fprintf(f, "yyval.%s", tag);
2466 	    }
2467 	    else
2468 		fprintf(f, "yyval");
2469 	    cptr += 2;
2470 #if defined(YYBTYACC)
2471 	    haveyyval = 1;
2472 #endif
2473 	    goto loop;
2474 	}
2475 	else if (isdigit(UCH(cptr[1])))
2476 	{
2477 	    ++cptr;
2478 	    i = get_number();
2479 	    if (havetags && offsets)
2480 	    {
2481 		if (i <= 0 || i > maxoffset)
2482 		    unknown_rhs(i);
2483 		tag = rhs[offsets[i]]->tag;
2484 		if (tag == 0)
2485 		    untyped_rhs(i, rhs[offsets[i]]->name);
2486 		fprintf(f, "yystack.l_mark[%d].%s", offsets[i], tag);
2487 	    }
2488 	    else
2489 	    {
2490 		if (i == 0)
2491 		    fprintf(f, "yystack.l_mark[%d]", -n);
2492 		else if (i > maxoffset)
2493 		{
2494 		    dollar_warning(lineno, i);
2495 		    fprintf(f, "yystack.l_mark[%d]", i - maxoffset);
2496 		}
2497 		else if (offsets)
2498 		    fprintf(f, "yystack.l_mark[%d]", offsets[i]);
2499 	    }
2500 	    goto loop;
2501 	}
2502 	else if (cptr[1] == '-')
2503 	{
2504 	    cptr += 2;
2505 	    i = get_number();
2506 	    if (havetags)
2507 		unknown_rhs(-i);
2508 	    fprintf(f, "yystack.l_mark[%d]", -i - n);
2509 	    goto loop;
2510 	}
2511 #if defined(YYBTYACC)
2512 	else if (isalpha(cptr[1]) || cptr[1] == '_')
2513 	{
2514 	    char *arg;
2515 	    ++cptr;
2516 	    arg = scan_id();
2517 	    for (i = plhs[nrules]->args - 1; i >= 0; i--)
2518 		if (arg == plhs[nrules]->argnames[i])
2519 		    break;
2520 	    if (i < 0)
2521 		unknown_arg_warning(lineno, "$", arg, line, cptr);
2522 	    tag = (i < 0 ? NULL : plhs[nrules]->argtags[i]);
2523 	    fprintf(f, "yystack.l_mark[%d]", i - plhs[nrules]->args + 1 - n);
2524 	    if (tag)
2525 		fprintf(f, ".%s", tag);
2526 	    else if (havetags)
2527 		untyped_arg_warning(lineno, "$", arg);
2528 	    goto loop;
2529 	}
2530 #endif
2531     }
2532 #if defined(YYBTYACC)
2533     if (c == '@')
2534     {
2535 	if (!locations)
2536 	{
2537 	    int l_lineno = lineno;
2538 	    char *l_line = dup_line();
2539 	    char *l_cptr = l_line + (cptr - line);
2540 	    syntax_error(l_lineno, l_line, l_cptr);
2541 	}
2542 	if (cptr[1] == '$')
2543 	{
2544 	    fprintf(f, "yyloc");
2545 	    cptr += 2;
2546 	    goto loop;
2547 	}
2548 	else if (isdigit(UCH(cptr[1])))
2549 	{
2550 	    ++cptr;
2551 	    i = get_number();
2552 	    if (i == 0)
2553 		fprintf(f, "yystack.p_mark[%d]", -n);
2554 	    else if (i > maxoffset)
2555 	    {
2556 		at_warning(lineno, i);
2557 		fprintf(f, "yystack.p_mark[%d]", i - maxoffset);
2558 	    }
2559 	    else if (offsets)
2560 		fprintf(f, "yystack.p_mark[%d]", offsets[i]);
2561 	    goto loop;
2562 	}
2563 	else if (cptr[1] == '-')
2564 	{
2565 	    cptr += 2;
2566 	    i = get_number();
2567 	    fprintf(f, "yystack.p_mark[%d]", -i - n);
2568 	    goto loop;
2569 	}
2570     }
2571 #endif
2572     if (isalpha(c) || c == '_' || c == '$')
2573     {
2574 	do
2575 	{
2576 	    putc(c, f);
2577 	    c = *++cptr;
2578 	}
2579 	while (isalnum(c) || c == '_' || c == '$');
2580 	goto loop;
2581     }
2582     ++cptr;
2583 #if defined(YYBTYACC)
2584     if (backtrack)
2585     {
2586 	if (trialaction && c == L_BRAC && depth == 0)
2587 	{
2588 	    ++depth;
2589 	    putc(L_CURL, f);
2590 	    goto loop;
2591 	}
2592 	if (trialaction && c == R_BRAC && depth == 1)
2593 	{
2594 	    --depth;
2595 	    putc(R_CURL, f);
2596 	    c = nextc();
2597 	    if (c == L_BRAC && !haveyyval)
2598 	    {
2599 		goto loop;
2600 	    }
2601 	    if (c == L_CURL && !haveyyval)
2602 	    {
2603 		fprintf(f, "  if (!yytrial)\n");
2604 		if (!lflag)
2605 		    fprintf(f, line_format, lineno, input_file_name);
2606 		trialaction = 0;
2607 		goto loop;
2608 	    }
2609 	    fprintf(f, "\nbreak;\n");
2610 	    FREE(a_line);
2611 	    if (maxoffset > 0)
2612 		FREE(offsets);
2613 	    return;
2614 	}
2615     }
2616 #endif
2617     putc(c, f);
2618     switch (c)
2619     {
2620     case '\n':
2621 	get_line();
2622 	if (line)
2623 	    goto loop;
2624 	unterminated_action(a_lineno, a_line, a_cptr);
2625 
2626     case ';':
2627 	if (depth > 0)
2628 	    goto loop;
2629 	fprintf(f, "\nbreak;\n");
2630 	FREE(a_line);
2631 	if (maxoffset > 0)
2632 	    FREE(offsets);
2633 	return;
2634 
2635 #if defined(YYBTYACC)
2636     case L_BRAC:
2637 	if (backtrack)
2638 	    ++depth;
2639 	goto loop;
2640 
2641     case R_BRAC:
2642 	if (backtrack)
2643 	    --depth;
2644 	goto loop;
2645 #endif
2646 
2647     case L_CURL:
2648 	++depth;
2649 	goto loop;
2650 
2651     case R_CURL:
2652 	if (--depth > 0)
2653 	    goto loop;
2654 #if defined(YYBTYACC)
2655 	if (backtrack)
2656 	{
2657 	    c = nextc();
2658 	    if (c == L_BRAC && !haveyyval)
2659 	    {
2660 		trialaction = 1;
2661 		goto loop;
2662 	    }
2663 	    if (c == L_CURL && !haveyyval)
2664 	    {
2665 		fprintf(f, "  if (!yytrial)\n");
2666 		if (!lflag)
2667 		    fprintf(f, line_format, lineno, input_file_name);
2668 		goto loop;
2669 	    }
2670 	}
2671 #endif
2672 	fprintf(f, "\nbreak;\n");
2673 	FREE(a_line);
2674 	if (maxoffset > 0)
2675 	    FREE(offsets);
2676 	return;
2677 
2678     case '\'':
2679     case '"':
2680 	{
2681 	    char *s = copy_string(c);
2682 	    fputs(s, f);
2683 	    free(s);
2684 	}
2685 	goto loop;
2686 
2687     case '/':
2688 	{
2689 	    char *s = copy_comment();
2690 	    fputs(s, f);
2691 	    free(s);
2692 	}
2693 	goto loop;
2694 
2695     default:
2696 	goto loop;
2697     }
2698 }
2699 
2700 #if defined(YYBTYACC)
2701 static void
2702 copy_destructor(void)
2703 {
2704     int c;
2705     int depth;
2706     char *tag;
2707     bucket *bp;
2708     struct mstring *destructor_text = msnew();
2709     char *code_text;
2710     int a_lineno;
2711     char *a_line;
2712     char *a_cptr;
2713 
2714     if (!lflag)
2715 	msprintf(destructor_text, line_format, lineno, input_file_name);
2716 
2717     cptr = after_blanks(cptr);
2718     if (*cptr == L_CURL)
2719 	/* avoid putting curly-braces in first column, to ease editing */
2720 	mputc(destructor_text, '\t');
2721     else
2722 	syntax_error(lineno, line, cptr);
2723 
2724     a_lineno = lineno;
2725     a_line = dup_line();
2726     a_cptr = a_line + (cptr - line);
2727 
2728     depth = 0;
2729   loop:
2730     c = *cptr;
2731     if (c == '$')
2732     {
2733 	if (cptr[1] == '<')
2734 	{
2735 	    int d_lineno = lineno;
2736 	    char *d_line = dup_line();
2737 	    char *d_cptr = d_line + (cptr - line);
2738 
2739 	    ++cptr;
2740 	    tag = get_tag();
2741 	    c = *cptr;
2742 	    if (c == '$')
2743 	    {
2744 		msprintf(destructor_text, "(*val).%s", tag);
2745 		++cptr;
2746 		FREE(d_line);
2747 		goto loop;
2748 	    }
2749 	    else
2750 		dollar_error(d_lineno, d_line, d_cptr);
2751 	}
2752 	else if (cptr[1] == '$')
2753 	{
2754 	    /* process '$$' later; replacement is context dependent */
2755 	    msprintf(destructor_text, "$$");
2756 	    cptr += 2;
2757 	    goto loop;
2758 	}
2759     }
2760     if (c == '@' && cptr[1] == '$')
2761     {
2762 	if (!locations)
2763 	{
2764 	    int l_lineno = lineno;
2765 	    char *l_line = dup_line();
2766 	    char *l_cptr = l_line + (cptr - line);
2767 	    syntax_error(l_lineno, l_line, l_cptr);
2768 	}
2769 	msprintf(destructor_text, "(*loc)");
2770 	cptr += 2;
2771 	goto loop;
2772     }
2773     if (isalpha(c) || c == '_' || c == '$')
2774     {
2775 	do
2776 	{
2777 	    mputc(destructor_text, c);
2778 	    c = *++cptr;
2779 	}
2780 	while (isalnum(c) || c == '_' || c == '$');
2781 	goto loop;
2782     }
2783     ++cptr;
2784     mputc(destructor_text, c);
2785     switch (c)
2786     {
2787     case '\n':
2788 	get_line();
2789 	if (line)
2790 	    goto loop;
2791 	unterminated_action(a_lineno, a_line, a_cptr);
2792 
2793     case L_CURL:
2794 	++depth;
2795 	goto loop;
2796 
2797     case R_CURL:
2798 	if (--depth > 0)
2799 	    goto loop;
2800 	goto process_symbols;
2801 
2802     case '\'':
2803     case '"':
2804 	{
2805 	    char *s = copy_string(c);
2806 	    msprintf(destructor_text, "%s", s);
2807 	    free(s);
2808 	}
2809 	goto loop;
2810 
2811     case '/':
2812 	{
2813 	    char *s = copy_comment();
2814 	    msprintf(destructor_text, "%s", s);
2815 	    free(s);
2816 	}
2817 	goto loop;
2818 
2819     default:
2820 	goto loop;
2821     }
2822   process_symbols:
2823     code_text = msdone(destructor_text);
2824     for (;;)
2825     {
2826 	c = nextc();
2827 	if (c == EOF)
2828 	    unexpected_EOF();
2829 	if (c == '<')
2830 	{
2831 	    if (cptr[1] == '>')
2832 	    {			/* "no semantic type" default destructor */
2833 		cptr += 2;
2834 		if ((bp = default_destructor[UNTYPED_DEFAULT]) == NULL)
2835 		{
2836 		    static char untyped_default[] = "<>";
2837 		    bp = make_bucket("untyped default");
2838 		    bp->tag = untyped_default;
2839 		    default_destructor[UNTYPED_DEFAULT] = bp;
2840 		}
2841 		if (bp->destructor != NULL)
2842 		    destructor_redeclared_warning(a_lineno, a_line, a_cptr);
2843 		else
2844 		    /* replace "$$" with "(*val)" in destructor code */
2845 		    bp->destructor = process_destructor_XX(code_text, NULL);
2846 	    }
2847 	    else if (cptr[1] == '*' && cptr[2] == '>')
2848 	    {			/* "no per-symbol or per-type" default destructor */
2849 		cptr += 3;
2850 		if ((bp = default_destructor[TYPED_DEFAULT]) == NULL)
2851 		{
2852 		    static char typed_default[] = "<*>";
2853 		    bp = make_bucket("typed default");
2854 		    bp->tag = typed_default;
2855 		    default_destructor[TYPED_DEFAULT] = bp;
2856 		}
2857 		if (bp->destructor != NULL)
2858 		    destructor_redeclared_warning(a_lineno, a_line, a_cptr);
2859 		else
2860 		{
2861 		    /* postpone re-processing destructor $$s until end of grammar spec */
2862 		    bp->destructor = TMALLOC(char, strlen(code_text) + 1);
2863 		    NO_SPACE(bp->destructor);
2864 		    strcpy(bp->destructor, code_text);
2865 		}
2866 	    }
2867 	    else
2868 	    {			/* "semantic type" default destructor */
2869 		tag = get_tag();
2870 		bp = lookup_type_destructor(tag);
2871 		if (bp->destructor != NULL)
2872 		    destructor_redeclared_warning(a_lineno, a_line, a_cptr);
2873 		else
2874 		    /* replace "$$" with "(*val).tag" in destructor code */
2875 		    bp->destructor = process_destructor_XX(code_text, tag);
2876 	    }
2877 	}
2878 	else if (isalpha(c) || c == '_' || c == '.' || c == '$')
2879 	{			/* "symbol" destructor */
2880 	    bp = get_name();
2881 	    if (bp->destructor != NULL)
2882 		destructor_redeclared_warning(a_lineno, a_line, a_cptr);
2883 	    else
2884 	    {
2885 		/* postpone re-processing destructor $$s until end of grammar spec */
2886 		bp->destructor = TMALLOC(char, strlen(code_text) + 1);
2887 		NO_SPACE(bp->destructor);
2888 		strcpy(bp->destructor, code_text);
2889 	    }
2890 	}
2891 	else
2892 	    break;
2893     }
2894     free(a_line);
2895     free(code_text);
2896 }
2897 
2898 static char *
2899 process_destructor_XX(char *code, char *tag)
2900 {
2901     int c;
2902     int quote;
2903     int depth;
2904     struct mstring *new_code = msnew();
2905     char *codeptr = code;
2906 
2907     depth = 0;
2908   loop:			/* step thru code */
2909     c = *codeptr;
2910     if (c == '$' && codeptr[1] == '$')
2911     {
2912 	codeptr += 2;
2913 	if (tag == NULL)
2914 	    msprintf(new_code, "(*val)");
2915 	else
2916 	    msprintf(new_code, "(*val).%s", tag);
2917 	goto loop;
2918     }
2919     if (isalpha(c) || c == '_' || c == '$')
2920     {
2921 	do
2922 	{
2923 	    mputc(new_code, c);
2924 	    c = *++codeptr;
2925 	}
2926 	while (isalnum(c) || c == '_' || c == '$');
2927 	goto loop;
2928     }
2929     ++codeptr;
2930     mputc(new_code, c);
2931     switch (c)
2932     {
2933     case L_CURL:
2934 	++depth;
2935 	goto loop;
2936 
2937     case R_CURL:
2938 	if (--depth > 0)
2939 	    goto loop;
2940 	return msdone(new_code);
2941 
2942     case '\'':
2943     case '"':
2944 	quote = c;
2945 	for (;;)
2946 	{
2947 	    c = *codeptr++;
2948 	    mputc(new_code, c);
2949 	    if (c == quote)
2950 		goto loop;
2951 	    if (c == '\\')
2952 	    {
2953 		c = *codeptr++;
2954 		mputc(new_code, c);
2955 	    }
2956 	}
2957 
2958     case '/':
2959 	c = *codeptr;
2960 	if (c == '*')
2961 	{
2962 	    mputc(new_code, c);
2963 	    ++codeptr;
2964 	    for (;;)
2965 	    {
2966 		c = *codeptr++;
2967 		mputc(new_code, c);
2968 		if (c == '*' && *codeptr == '/')
2969 		{
2970 		    mputc(new_code, '/');
2971 		    ++codeptr;
2972 		    goto loop;
2973 		}
2974 	    }
2975 	}
2976 	goto loop;
2977 
2978     default:
2979 	goto loop;
2980     }
2981 }
2982 #endif /* defined(YYBTYACC) */
2983 
2984 static int
2985 mark_symbol(void)
2986 {
2987     int c;
2988     bucket *bp = NULL;
2989 
2990     c = cptr[1];
2991     if (c == '%' || c == '\\')
2992     {
2993 	cptr += 2;
2994 	return (1);
2995     }
2996 
2997     if (c == '=')
2998 	cptr += 2;
2999     else if ((c == 'p' || c == 'P') &&
3000 	     ((c = cptr[2]) == 'r' || c == 'R') &&
3001 	     ((c = cptr[3]) == 'e' || c == 'E') &&
3002 	     ((c = cptr[4]) == 'c' || c == 'C') &&
3003 	     ((c = cptr[5], !IS_IDENT(c))))
3004 	cptr += 5;
3005     else
3006 	syntax_error(lineno, line, cptr);
3007 
3008     c = nextc();
3009     if (isalpha(c) || c == '_' || c == '.' || c == '$')
3010 	bp = get_name();
3011     else if (c == '\'' || c == '"')
3012 	bp = get_literal();
3013     else
3014     {
3015 	syntax_error(lineno, line, cptr);
3016 	/*NOTREACHED */
3017     }
3018 
3019     if (rprec[nrules] != UNDEFINED && bp->prec != rprec[nrules])
3020 	prec_redeclared();
3021 
3022     rprec[nrules] = bp->prec;
3023     rassoc[nrules] = bp->assoc;
3024     return (0);
3025 }
3026 
3027 static void
3028 read_grammar(void)
3029 {
3030     int c;
3031 
3032     initialize_grammar();
3033     advance_to_start();
3034 
3035     for (;;)
3036     {
3037 	c = nextc();
3038 	if (c == EOF)
3039 	    break;
3040 	if (isalpha(c)
3041 	    || c == '_'
3042 	    || c == '.'
3043 	    || c == '$'
3044 	    || c == '\''
3045 	    || c == '"')
3046 	    add_symbol();
3047 #if defined(YYBTYACC)
3048 	else if (c == L_CURL || c == '=' || (backtrack && c == L_BRAC))
3049 #else
3050 	else if (c == L_CURL || c == '=')
3051 #endif
3052 	    copy_action();
3053 	else if (c == '|')
3054 	{
3055 	    end_rule();
3056 	    start_rule(plhs[nrules - 1], 0);
3057 	    ++cptr;
3058 	}
3059 	else if (c == '%')
3060 	{
3061 	    if (mark_symbol())
3062 		break;
3063 	}
3064 	else
3065 	    syntax_error(lineno, line, cptr);
3066     }
3067     end_rule();
3068 #if defined(YYBTYACC)
3069     if (goal->args > 0)
3070 	start_requires_args(goal->name);
3071 #endif
3072 }
3073 
3074 static void
3075 free_tags(void)
3076 {
3077     int i;
3078 
3079     if (tag_table == 0)
3080 	return;
3081 
3082     for (i = 0; i < ntags; ++i)
3083     {
3084 	assert(tag_table[i]);
3085 	FREE(tag_table[i]);
3086     }
3087     FREE(tag_table);
3088 }
3089 
3090 static void
3091 pack_names(void)
3092 {
3093     bucket *bp;
3094     char *p, *s, *t;
3095 
3096     name_pool_size = 13;	/* 13 == sizeof("$end") + sizeof("$accept") */
3097     for (bp = first_symbol; bp; bp = bp->next)
3098 	name_pool_size += strlen(bp->name) + 1;
3099 
3100     name_pool = TMALLOC(char, name_pool_size);
3101     NO_SPACE(name_pool);
3102 
3103     strcpy(name_pool, "$accept");
3104     strcpy(name_pool + 8, "$end");
3105     t = name_pool + 13;
3106     for (bp = first_symbol; bp; bp = bp->next)
3107     {
3108 	p = t;
3109 	s = bp->name;
3110 	while ((*t++ = *s++) != 0)
3111 	    continue;
3112 	FREE(bp->name);
3113 	bp->name = p;
3114     }
3115 }
3116 
3117 static void
3118 check_symbols(void)
3119 {
3120     bucket *bp;
3121 
3122     if (goal->class == UNKNOWN)
3123 	undefined_goal(goal->name);
3124 
3125     for (bp = first_symbol; bp; bp = bp->next)
3126     {
3127 	if (bp->class == UNKNOWN)
3128 	{
3129 	    undefined_symbol_warning(bp->name);
3130 	    bp->class = TERM;
3131 	}
3132     }
3133 }
3134 
3135 static void
3136 protect_string(char *src, char **des)
3137 {
3138     unsigned len;
3139     char *s;
3140     char *d;
3141 
3142     *des = src;
3143     if (src)
3144     {
3145 	len = 1;
3146 	s = src;
3147 	while (*s)
3148 	{
3149 	    if ('\\' == *s || '"' == *s)
3150 		len++;
3151 	    s++;
3152 	    len++;
3153 	}
3154 
3155 	*des = d = TMALLOC(char, len);
3156 	NO_SPACE(d);
3157 
3158 	s = src;
3159 	while (*s)
3160 	{
3161 	    if ('\\' == *s || '"' == *s)
3162 		*d++ = '\\';
3163 	    *d++ = *s++;
3164 	}
3165 	*d = '\0';
3166     }
3167 }
3168 
3169 static void
3170 pack_symbols(void)
3171 {
3172     bucket *bp;
3173     bucket **v;
3174     Value_t i, j, k, n;
3175 #if defined(YYBTYACC)
3176     Value_t max_tok_pval;
3177 #endif
3178 
3179     nsyms = 2;
3180     ntokens = 1;
3181     for (bp = first_symbol; bp; bp = bp->next)
3182     {
3183 	++nsyms;
3184 	if (bp->class == TERM)
3185 	    ++ntokens;
3186     }
3187     start_symbol = (Value_t)ntokens;
3188     nvars = (Value_t)(nsyms - ntokens);
3189 
3190     symbol_name = TMALLOC(char *, nsyms);
3191     NO_SPACE(symbol_name);
3192 
3193     symbol_value = TMALLOC(Value_t, nsyms);
3194     NO_SPACE(symbol_value);
3195 
3196     symbol_prec = TMALLOC(Value_t, nsyms);
3197     NO_SPACE(symbol_prec);
3198 
3199     symbol_assoc = TMALLOC(char, nsyms);
3200     NO_SPACE(symbol_assoc);
3201 
3202 #if defined(YYBTYACC)
3203     symbol_pval = TMALLOC(Value_t, nsyms);
3204     NO_SPACE(symbol_pval);
3205 
3206     if (destructor)
3207     {
3208 	symbol_destructor = CALLOC(sizeof(char *), nsyms);
3209 	NO_SPACE(symbol_destructor);
3210 
3211 	symbol_type_tag = CALLOC(sizeof(char *), nsyms);
3212 	NO_SPACE(symbol_type_tag);
3213     }
3214 #endif
3215 
3216     v = TMALLOC(bucket *, nsyms);
3217     NO_SPACE(v);
3218 
3219     v[0] = 0;
3220     v[start_symbol] = 0;
3221 
3222     i = 1;
3223     j = (Value_t)(start_symbol + 1);
3224     for (bp = first_symbol; bp; bp = bp->next)
3225     {
3226 	if (bp->class == TERM)
3227 	    v[i++] = bp;
3228 	else
3229 	    v[j++] = bp;
3230     }
3231     assert(i == ntokens && j == nsyms);
3232 
3233     for (i = 1; i < ntokens; ++i)
3234 	v[i]->index = i;
3235 
3236     goal->index = (Index_t)(start_symbol + 1);
3237     k = (Value_t)(start_symbol + 2);
3238     while (++i < nsyms)
3239 	if (v[i] != goal)
3240 	{
3241 	    v[i]->index = k;
3242 	    ++k;
3243 	}
3244 
3245     goal->value = 0;
3246     k = 1;
3247     for (i = (Value_t)(start_symbol + 1); i < nsyms; ++i)
3248     {
3249 	if (v[i] != goal)
3250 	{
3251 	    v[i]->value = k;
3252 	    ++k;
3253 	}
3254     }
3255 
3256     k = 0;
3257     for (i = 1; i < ntokens; ++i)
3258     {
3259 	n = v[i]->value;
3260 	if (n > 256)
3261 	{
3262 	    for (j = k++; j > 0 && symbol_value[j - 1] > n; --j)
3263 		symbol_value[j] = symbol_value[j - 1];
3264 	    symbol_value[j] = n;
3265 	}
3266     }
3267 
3268     assert(v[1] != 0);
3269 
3270     if (v[1]->value == UNDEFINED)
3271 	v[1]->value = 256;
3272 
3273     j = 0;
3274     n = 257;
3275     for (i = 2; i < ntokens; ++i)
3276     {
3277 	if (v[i]->value == UNDEFINED)
3278 	{
3279 	    while (j < k && n == symbol_value[j])
3280 	    {
3281 		while (++j < k && n == symbol_value[j])
3282 		    continue;
3283 		++n;
3284 	    }
3285 	    v[i]->value = n;
3286 	    ++n;
3287 	}
3288     }
3289 
3290     symbol_name[0] = name_pool + 8;
3291     symbol_value[0] = 0;
3292     symbol_prec[0] = 0;
3293     symbol_assoc[0] = TOKEN;
3294 #if defined(YYBTYACC)
3295     symbol_pval[0] = 0;
3296     max_tok_pval = 0;
3297 #endif
3298     for (i = 1; i < ntokens; ++i)
3299     {
3300 	symbol_name[i] = v[i]->name;
3301 	symbol_value[i] = v[i]->value;
3302 	symbol_prec[i] = v[i]->prec;
3303 	symbol_assoc[i] = v[i]->assoc;
3304 #if defined(YYBTYACC)
3305 	symbol_pval[i] = v[i]->value;
3306 	if (symbol_pval[i] > max_tok_pval)
3307 	    max_tok_pval = symbol_pval[i];
3308 	if (destructor)
3309 	{
3310 	    symbol_destructor[i] = v[i]->destructor;
3311 	    symbol_type_tag[i] = v[i]->tag;
3312 	}
3313 #endif
3314     }
3315     symbol_name[start_symbol] = name_pool;
3316     symbol_value[start_symbol] = -1;
3317     symbol_prec[start_symbol] = 0;
3318     symbol_assoc[start_symbol] = TOKEN;
3319 #if defined(YYBTYACC)
3320     symbol_pval[start_symbol] = (Value_t)(max_tok_pval + 1);
3321 #endif
3322     for (++i; i < nsyms; ++i)
3323     {
3324 	k = v[i]->index;
3325 	symbol_name[k] = v[i]->name;
3326 	symbol_value[k] = v[i]->value;
3327 	symbol_prec[k] = v[i]->prec;
3328 	symbol_assoc[k] = v[i]->assoc;
3329 #if defined(YYBTYACC)
3330 	symbol_pval[k] = (Value_t)((max_tok_pval + 1) + v[i]->value + 1);
3331 	if (destructor)
3332 	{
3333 	    symbol_destructor[k] = v[i]->destructor;
3334 	    symbol_type_tag[k] = v[i]->tag;
3335 	}
3336 #endif
3337     }
3338 
3339     if (gflag)
3340     {
3341 	symbol_pname = TMALLOC(char *, nsyms);
3342 	NO_SPACE(symbol_pname);
3343 
3344 	for (i = 0; i < nsyms; ++i)
3345 	    protect_string(symbol_name[i], &(symbol_pname[i]));
3346     }
3347 
3348     FREE(v);
3349 }
3350 
3351 static void
3352 pack_grammar(void)
3353 {
3354     int i;
3355     Value_t j;
3356     Assoc_t assoc;
3357     Value_t prec2;
3358 
3359     ritem = TMALLOC(Value_t, nitems);
3360     NO_SPACE(ritem);
3361 
3362     rlhs = TMALLOC(Value_t, nrules);
3363     NO_SPACE(rlhs);
3364 
3365     rrhs = TMALLOC(Value_t, nrules + 1);
3366     NO_SPACE(rrhs);
3367 
3368     rprec = TREALLOC(Value_t, rprec, nrules);
3369     NO_SPACE(rprec);
3370 
3371     rassoc = TREALLOC(Assoc_t, rassoc, nrules);
3372     NO_SPACE(rassoc);
3373 
3374     ritem[0] = -1;
3375     ritem[1] = goal->index;
3376     ritem[2] = 0;
3377     ritem[3] = -2;
3378     rlhs[0] = 0;
3379     rlhs[1] = 0;
3380     rlhs[2] = start_symbol;
3381     rrhs[0] = 0;
3382     rrhs[1] = 0;
3383     rrhs[2] = 1;
3384 
3385     j = 4;
3386     for (i = 3; i < nrules; ++i)
3387     {
3388 #if defined(YYBTYACC)
3389 	if (plhs[i]->args > 0)
3390 	{
3391 	    if (plhs[i]->argnames)
3392 	    {
3393 		FREE(plhs[i]->argnames);
3394 		plhs[i]->argnames = NULL;
3395 	    }
3396 	    if (plhs[i]->argtags)
3397 	    {
3398 		FREE(plhs[i]->argtags);
3399 		plhs[i]->argtags = NULL;
3400 	    }
3401 	}
3402 #endif /* defined(YYBTYACC) */
3403 	rlhs[i] = plhs[i]->index;
3404 	rrhs[i] = j;
3405 	assoc = TOKEN;
3406 	prec2 = 0;
3407 	while (pitem[j])
3408 	{
3409 	    ritem[j] = pitem[j]->index;
3410 	    if (pitem[j]->class == TERM)
3411 	    {
3412 		prec2 = pitem[j]->prec;
3413 		assoc = pitem[j]->assoc;
3414 	    }
3415 	    ++j;
3416 	}
3417 	ritem[j] = (Value_t)-i;
3418 	++j;
3419 	if (rprec[i] == UNDEFINED)
3420 	{
3421 	    rprec[i] = prec2;
3422 	    rassoc[i] = assoc;
3423 	}
3424     }
3425     rrhs[i] = j;
3426 
3427     FREE(plhs);
3428     FREE(pitem);
3429 #if defined(YYBTYACC)
3430     clean_arg_cache();
3431 #endif
3432 }
3433 
3434 static void
3435 print_grammar(void)
3436 {
3437     int i, k;
3438     size_t j, spacing = 0;
3439     FILE *f = verbose_file;
3440 
3441     if (!vflag)
3442 	return;
3443 
3444     k = 1;
3445     for (i = 2; i < nrules; ++i)
3446     {
3447 	if (rlhs[i] != rlhs[i - 1])
3448 	{
3449 	    if (i != 2)
3450 		fprintf(f, "\n");
3451 	    fprintf(f, "%4d  %s :", i - 2, symbol_name[rlhs[i]]);
3452 	    spacing = strlen(symbol_name[rlhs[i]]) + 1;
3453 	}
3454 	else
3455 	{
3456 	    fprintf(f, "%4d  ", i - 2);
3457 	    j = spacing;
3458 	    while (j-- != 0)
3459 		putc(' ', f);
3460 	    putc('|', f);
3461 	}
3462 
3463 	while (ritem[k] >= 0)
3464 	{
3465 	    fprintf(f, " %s", symbol_name[ritem[k]]);
3466 	    ++k;
3467 	}
3468 	++k;
3469 	putc('\n', f);
3470     }
3471 }
3472 
3473 #if defined(YYBTYACC)
3474 static void
3475 finalize_destructors(void)
3476 {
3477     int i;
3478     bucket *bp;
3479     char *tag;
3480 
3481     for (i = 2; i < nsyms; ++i)
3482     {
3483 	tag = symbol_type_tag[i];
3484 	if (symbol_destructor[i] == NULL)
3485 	{
3486 	    if (tag == NULL)
3487 	    {			/* use <> destructor, if there is one */
3488 		if ((bp = default_destructor[UNTYPED_DEFAULT]) != NULL)
3489 		{
3490 		    symbol_destructor[i] = TMALLOC(char,
3491 						   strlen(bp->destructor) + 1);
3492 		    NO_SPACE(symbol_destructor[i]);
3493 		    strcpy(symbol_destructor[i], bp->destructor);
3494 		}
3495 	    }
3496 	    else
3497 	    {			/* use type destructor for this tag, if there is one */
3498 		bp = lookup_type_destructor(tag);
3499 		if (bp->destructor != NULL)
3500 		{
3501 		    symbol_destructor[i] = TMALLOC(char,
3502 						   strlen(bp->destructor) + 1);
3503 		    NO_SPACE(symbol_destructor[i]);
3504 		    strcpy(symbol_destructor[i], bp->destructor);
3505 		}
3506 		else
3507 		{		/* use <*> destructor, if there is one */
3508 		    if ((bp = default_destructor[TYPED_DEFAULT]) != NULL)
3509 			/* replace "$$" with "(*val).tag" in destructor code */
3510 			symbol_destructor[i]
3511 			    = process_destructor_XX(bp->destructor, tag);
3512 		}
3513 	    }
3514 	}
3515 	else
3516 	{			/* replace "$$" with "(*val)[.tag]" in destructor code */
3517 	    symbol_destructor[i]
3518 		= process_destructor_XX(symbol_destructor[i], tag);
3519 	}
3520     }
3521     /* 'symbol_type_tag[]' elements are freed by 'free_tags()' */
3522     DO_FREE(symbol_type_tag);	/* no longer needed */
3523     if ((bp = default_destructor[UNTYPED_DEFAULT]) != NULL)
3524     {
3525 	FREE(bp->name);
3526 	/* 'bp->tag' is a static value, don't free */
3527 	FREE(bp->destructor);
3528 	FREE(bp);
3529     }
3530     if ((bp = default_destructor[TYPED_DEFAULT]) != NULL)
3531     {
3532 	FREE(bp->name);
3533 	/* 'bp->tag' is a static value, don't free */
3534 	FREE(bp->destructor);
3535 	FREE(bp);
3536     }
3537     if ((bp = default_destructor[TYPE_SPECIFIED]) != NULL)
3538     {
3539 	bucket *p;
3540 	for (; bp; bp = p)
3541 	{
3542 	    p = bp->link;
3543 	    FREE(bp->name);
3544 	    /* 'bp->tag' freed by 'free_tags()' */
3545 	    FREE(bp->destructor);
3546 	    FREE(bp);
3547 	}
3548     }
3549 }
3550 #endif /* defined(YYBTYACC) */
3551 
3552 void
3553 reader(void)
3554 {
3555     write_section(code_file, banner);
3556     create_symbol_table();
3557     read_declarations();
3558     read_grammar();
3559     free_symbol_table();
3560     pack_names();
3561     check_symbols();
3562     pack_symbols();
3563     pack_grammar();
3564     free_symbols();
3565     print_grammar();
3566 #if defined(YYBTYACC)
3567     if (destructor)
3568 	finalize_destructors();
3569 #endif
3570     free_tags();
3571 }
3572 
3573 #ifdef NO_LEAKS
3574 static param *
3575 free_declarations(param *list)
3576 {
3577     while (list != 0)
3578     {
3579 	param *next = list->next;
3580 	free(list->type);
3581 	free(list->name);
3582 	free(list->type2);
3583 	free(list);
3584 	list = next;
3585     }
3586     return list;
3587 }
3588 
3589 void
3590 reader_leaks(void)
3591 {
3592     lex_param = free_declarations(lex_param);
3593     parse_param = free_declarations(parse_param);
3594 
3595     DO_FREE(line);
3596     DO_FREE(rrhs);
3597     DO_FREE(rlhs);
3598     DO_FREE(rprec);
3599     DO_FREE(ritem);
3600     DO_FREE(rassoc);
3601     DO_FREE(cache);
3602     DO_FREE(name_pool);
3603     DO_FREE(symbol_name);
3604     DO_FREE(symbol_prec);
3605     DO_FREE(symbol_assoc);
3606     DO_FREE(symbol_value);
3607 #if defined(YYBTYACC)
3608     DO_FREE(symbol_pval);
3609     DO_FREE(symbol_destructor);
3610     DO_FREE(symbol_type_tag);
3611 #endif
3612 }
3613 #endif
3614