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