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