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