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