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