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