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