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