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