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