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