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