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