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