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