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