27 |
#include "files.h" |
#include "files.h" |
28 |
#include "symtab.h" |
#include "symtab.h" |
29 |
#include "options.h" |
#include "options.h" |
|
#include "lex.h" |
|
30 |
#include "gram.h" |
#include "gram.h" |
31 |
#include "complain.h" |
#include "complain.h" |
32 |
#include "output.h" |
#include "output.h" |
34 |
#include "conflicts.h" |
#include "conflicts.h" |
35 |
#include "muscle_tab.h" |
#include "muscle_tab.h" |
36 |
|
|
|
typedef struct symbol_list |
|
|
{ |
|
|
struct symbol_list *next; |
|
|
symbol_t *sym; |
|
|
int line; |
|
|
|
|
|
/* The action is attached to the LHS of a rule. */ |
|
|
const char *action; |
|
|
int action_line; |
|
|
|
|
|
symbol_t *ruleprec; |
|
|
} symbol_list; |
|
|
|
|
37 |
int lineno; |
int lineno; |
38 |
static symbol_list *grammar = NULL; |
static symbol_list *grammar = NULL; |
39 |
static int start_flag = 0; |
static int start_flag = 0; |
40 |
|
|
41 |
/* Nonzero if %union has been seen. */ |
/* Nonzero if %union has been seen. */ |
42 |
static int typed = 0; |
int typed = 0; |
|
|
|
|
/* Incremented for each %left, %right or %nonassoc seen */ |
|
|
static int lastprec = 0; |
|
43 |
|
|
44 |
static symbol_list * |
static symbol_list * |
45 |
symbol_list_new (symbol_t *sym) |
symbol_list_new (symbol_t *sym) |
54 |
return res; |
return res; |
55 |
} |
} |
56 |
|
|
|
/*===================\ |
|
|
| Low level lexing. | |
|
|
\===================*/ |
|
|
|
|
|
static void |
|
|
skip_to_char (int target) |
|
|
{ |
|
|
int c; |
|
|
if (target == '\n') |
|
|
complain (_(" Skipping to next \\n")); |
|
|
else |
|
|
complain (_(" Skipping to next %c"), target); |
|
|
|
|
|
do |
|
|
c = skip_white_space (); |
|
|
while (c != target && c != EOF); |
|
|
if (c != EOF) |
|
|
ungetc (c, finput); |
|
|
} |
|
|
|
|
|
|
|
|
/*---------------------------------------------------------. |
|
|
| Read a signed integer from STREAM and return its value. | |
|
|
`---------------------------------------------------------*/ |
|
|
|
|
|
static inline int |
|
|
read_signed_integer (FILE *stream) |
|
|
{ |
|
|
int c = getc (stream); |
|
|
int sign = 1; |
|
|
int n = 0; |
|
|
|
|
|
if (c == '-') |
|
|
{ |
|
|
c = getc (stream); |
|
|
sign = -1; |
|
|
} |
|
|
|
|
|
while (isdigit (c)) |
|
|
{ |
|
|
n = 10 * n + (c - '0'); |
|
|
c = getc (stream); |
|
|
} |
|
|
|
|
|
ungetc (c, stream); |
|
|
|
|
|
return sign * n; |
|
|
} |
|
57 |
|
|
58 |
/*--------------------------------------------------------------. |
/*--------------------------------------------------------------. |
59 |
| Get the data type (alternative in the union) of the value for | |
| Get the data type (alternative in the union) of the value for | |
60 |
| symbol N in rule RULE. | |
| symbol N in rule RULE. | |
61 |
`--------------------------------------------------------------*/ |
`--------------------------------------------------------------*/ |
62 |
|
|
63 |
static char * |
char * |
64 |
get_type_name (int n, symbol_list *rule) |
get_type_name (int n, symbol_list *rule) |
65 |
{ |
{ |
66 |
int i; |
int i; |
88 |
|
|
89 |
return rp->sym->type_name; |
return rp->sym->type_name; |
90 |
} |
} |
|
|
|
|
/*------------------------------------------------------------------. |
|
|
| Copy the character C to OOUT, and insert quadigraphs when needed. | |
|
|
`------------------------------------------------------------------*/ |
|
|
|
|
|
static inline void |
|
|
copy_character (struct obstack *oout, int c) |
|
|
{ |
|
|
switch (c) |
|
|
{ |
|
|
case '[': |
|
|
obstack_sgrow (oout, "@<:@"); |
|
|
break; |
|
|
|
|
|
case ']': |
|
|
obstack_sgrow (oout, "@:>@"); |
|
|
break; |
|
|
|
|
|
default: |
|
|
obstack_1grow (oout, c); |
|
|
} |
|
|
} |
|
|
|
|
|
/*------------------------------------------------------------. |
|
|
| Dump the string from FIN to OOUT if non null. MATCH is the | |
|
|
| delimiter of the string (either ' or "). | |
|
|
`------------------------------------------------------------*/ |
|
|
|
|
|
static inline void |
|
|
copy_string2 (FILE *fin, struct obstack *oout, int match, int store) |
|
|
{ |
|
|
int c; |
|
|
|
|
|
if (store) |
|
|
obstack_1grow (oout, match); |
|
|
|
|
|
c = getc (fin); |
|
|
|
|
|
while (c != match) |
|
|
{ |
|
|
if (c == EOF) |
|
|
fatal (_("unterminated string at end of file")); |
|
|
if (c == '\n') |
|
|
{ |
|
|
complain (_("unterminated string")); |
|
|
ungetc (c, fin); |
|
|
c = match; /* invent terminator */ |
|
|
continue; |
|
|
} |
|
|
|
|
|
copy_character (oout, c); |
|
|
|
|
|
if (c == '\\') |
|
|
{ |
|
|
c = getc (fin); |
|
|
if (c == EOF) |
|
|
fatal (_("unterminated string at end of file")); |
|
|
copy_character (oout, c); |
|
|
|
|
|
if (c == '\n') |
|
|
++lineno; |
|
|
} |
|
|
|
|
|
c = getc (fin); |
|
|
} |
|
|
|
|
|
if (store) |
|
|
obstack_1grow (oout, c); |
|
|
} |
|
|
|
|
|
/* FIXME. */ |
|
|
|
|
|
static inline void |
|
|
copy_string (FILE *fin, struct obstack *oout, int match) |
|
|
{ |
|
|
copy_string2 (fin, oout, match, 1); |
|
|
} |
|
|
|
|
|
/* FIXME. */ |
|
|
|
|
|
static inline void |
|
|
copy_identifier (FILE *fin, struct obstack *oout) |
|
|
{ |
|
|
int c; |
|
|
|
|
|
while (isalnum (c = getc (fin)) || c == '_') |
|
|
obstack_1grow (oout, c); |
|
|
|
|
|
ungetc (c, fin); |
|
|
} |
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------. |
|
|
| Dump the wannabee comment from IN to OOUT. In fact we just saw a | |
|
|
| `/', which might or might not be a comment. In any case, copy | |
|
|
| what we saw. | |
|
|
`------------------------------------------------------------------*/ |
|
|
|
|
|
static inline void |
|
|
copy_comment (FILE *fin, struct obstack *oout) |
|
|
{ |
|
|
int cplus_comment; |
|
|
int ended; |
|
|
int c; |
|
|
|
|
|
/* We read a `/', output it. */ |
|
|
obstack_1grow (oout, '/'); |
|
|
|
|
|
switch ((c = getc (fin))) |
|
|
{ |
|
|
case '/': |
|
|
cplus_comment = 1; |
|
|
break; |
|
|
case '*': |
|
|
cplus_comment = 0; |
|
|
break; |
|
|
default: |
|
|
ungetc (c, fin); |
|
|
return; |
|
|
} |
|
|
|
|
|
obstack_1grow (oout, c); |
|
|
c = getc (fin); |
|
|
|
|
|
ended = 0; |
|
|
while (!ended) |
|
|
{ |
|
|
if (!cplus_comment && c == '*') |
|
|
{ |
|
|
while (c == '*') |
|
|
{ |
|
|
obstack_1grow (oout, c); |
|
|
c = getc (fin); |
|
|
} |
|
|
|
|
|
if (c == '/') |
|
|
{ |
|
|
obstack_1grow (oout, c); |
|
|
ended = 1; |
|
|
} |
|
|
} |
|
|
else if (c == '\n') |
|
|
{ |
|
|
++lineno; |
|
|
obstack_1grow (oout, c); |
|
|
if (cplus_comment) |
|
|
ended = 1; |
|
|
else |
|
|
c = getc (fin); |
|
|
} |
|
|
else if (c == EOF) |
|
|
fatal (_("unterminated comment")); |
|
|
else |
|
|
{ |
|
|
copy_character (oout, c); |
|
|
c = getc (fin); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------. |
|
|
| FIN is pointing to a location (i.e., a `@'). Output to OOUT a | |
|
|
| reference to this location. RULE_LENGTH is the number of values in | |
|
|
| the current rule so far, which says where to find `$0' with | |
|
|
| respect to the top of the stack. | |
|
|
`-------------------------------------------------------------------*/ |
|
|
|
|
|
static inline void |
|
|
copy_at (FILE *fin, struct obstack *oout, int rule_length) |
|
|
{ |
|
|
int c = getc (fin); |
|
|
locations_flag = 1; |
|
|
|
|
|
if (c == '$') |
|
|
{ |
|
|
obstack_sgrow (oout, "]b4_lhs_location["); |
|
|
} |
|
|
else if (isdigit (c) || c == '-') |
|
|
{ |
|
|
int n; |
|
|
|
|
|
ungetc (c, fin); |
|
|
n = read_signed_integer (fin); |
|
|
if (n > rule_length) |
|
|
complain (_("invalid value: %s%d"), "@", n); |
|
|
else |
|
|
obstack_fgrow2 (oout, "]b4_rhs_location([%d], [%d])[", |
|
|
rule_length, n); |
|
|
} |
|
|
else |
|
|
{ |
|
|
char buf[] = "@c"; |
|
|
buf[1] = c; |
|
|
complain (_("%s is invalid"), quote (buf)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------. |
|
|
| FIN is pointing to a wannabee semantic value (i.e., a `$'). | |
|
|
| | |
|
|
| Possible inputs: $[<TYPENAME>]($|integer) | |
|
|
| | |
|
|
| Output to OOUT a reference to this semantic value. RULE_LENGTH is | |
|
|
| the number of values in the current rule so far, which says where | |
|
|
| to find `$0' with respect to the top of the stack. | |
|
|
`------------------------------------------------------------------*/ |
|
|
|
|
|
static inline void |
|
|
copy_dollar (FILE *fin, struct obstack *oout, |
|
|
symbol_list *rule, int rule_length) |
|
|
{ |
|
|
int c = getc (fin); |
|
|
const char *type_name = NULL; |
|
|
|
|
|
/* Get the type name if explicit. */ |
|
|
if (c == '<') |
|
|
{ |
|
|
read_type_name (fin); |
|
|
type_name = token_buffer; |
|
|
c = getc (fin); |
|
|
} |
|
|
|
|
|
if (c == '$') |
|
|
{ |
|
|
if (!type_name) |
|
|
type_name = get_type_name (0, rule); |
|
|
if (!type_name && typed) |
|
|
complain (_("$$ of `%s' has no declared type"), |
|
|
rule->sym->tag); |
|
|
if (!type_name) |
|
|
type_name = ""; |
|
|
obstack_fgrow1 (oout, |
|
|
"]b4_lhs_value([%s])[", type_name); |
|
|
} |
|
|
else if (isdigit (c) || c == '-') |
|
|
{ |
|
|
int n; |
|
|
ungetc (c, fin); |
|
|
n = read_signed_integer (fin); |
|
|
|
|
|
if (n > rule_length) |
|
|
complain (_("invalid value: %s%d"), "$", n); |
|
|
else |
|
|
{ |
|
|
if (!type_name && n > 0) |
|
|
type_name = get_type_name (n, rule); |
|
|
if (!type_name && typed) |
|
|
complain (_("$%d of `%s' has no declared type"), |
|
|
n, rule->sym->tag); |
|
|
if (!type_name) |
|
|
type_name = ""; |
|
|
obstack_fgrow3 (oout, "]b4_rhs_value([%d], [%d], [%s])[", |
|
|
rule_length, n, type_name); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
char buf[] = "$c"; |
|
|
buf[1] = c; |
|
|
complain (_("%s is invalid"), quote (buf)); |
|
|
} |
|
|
} |
|
|
|
|
|
/*-------------------------------------------------------------------. |
|
|
| Copy the contents of a `%{ ... %}' into the definitions file. The | |
|
|
| `%{' has already been read. Return after reading the `%}'. | |
|
|
`-------------------------------------------------------------------*/ |
|
|
|
|
|
static void |
|
|
copy_definition (struct obstack *oout) |
|
|
{ |
|
|
int c; |
|
|
/* -1 while reading a character if prev char was %. */ |
|
|
int after_percent; |
|
|
|
|
|
if (!no_lines_flag) |
|
|
{ |
|
|
obstack_fgrow2 (oout, muscle_find ("linef"), |
|
|
lineno, quotearg_style (c_quoting_style, |
|
|
muscle_find ("filename"))); |
|
|
} |
|
91 |
|
|
|
after_percent = 0; |
|
|
|
|
|
c = getc (finput); |
|
|
|
|
|
for (;;) |
|
|
{ |
|
|
switch (c) |
|
|
{ |
|
|
case '\n': |
|
|
obstack_1grow (oout, c); |
|
|
++lineno; |
|
|
break; |
|
|
|
|
|
case '%': |
|
|
after_percent = -1; |
|
|
break; |
|
|
|
|
|
case '\'': |
|
|
case '"': |
|
|
copy_string (finput, oout, c); |
|
|
break; |
|
|
|
|
|
case '/': |
|
|
copy_comment (finput, oout); |
|
|
break; |
|
|
|
|
|
case EOF: |
|
|
fatal ("%s", _("unterminated `%{' definition")); |
|
|
|
|
|
default: |
|
|
copy_character (oout, c); |
|
|
} |
|
92 |
|
|
93 |
c = getc (finput); |
/*-----------------------. |
94 |
|
| Set the start symbol. | |
95 |
|
`-----------------------*/ |
96 |
|
|
97 |
if (after_percent) |
void |
98 |
{ |
grammar_start_symbol_set (symbol_t *s) |
|
if (c == '}') |
|
|
return; |
|
|
obstack_1grow (oout, '%'); |
|
|
} |
|
|
after_percent = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/*------------------------------------------. |
|
|
| Parse what comes after %token or %nterm. | |
|
|
`------------------------------------------*/ |
|
|
|
|
|
static void |
|
|
parse_token_decl (symbol_class class) |
|
|
{ |
|
|
token_t token = tok_undef; |
|
|
char *typename = NULL; |
|
|
|
|
|
/* The symbol being defined. */ |
|
|
symbol_t *symbol = NULL; |
|
|
|
|
|
/* After `%token' and `%nterm', any number of symbols maybe be |
|
|
defined. */ |
|
|
for (;;) |
|
|
{ |
|
|
int tmp_char = ungetc (skip_white_space (), finput); |
|
|
|
|
|
/* `%' (for instance from `%token', or from `%%' etc.) is the |
|
|
only valid means to end this declaration. */ |
|
|
if (tmp_char == '%') |
|
|
return; |
|
|
if (tmp_char == EOF) |
|
|
fatal (_("Premature EOF after %s"), token_buffer); |
|
|
|
|
|
token = lex (); |
|
|
switch (token) |
|
|
{ |
|
|
case tok_comma: |
|
|
symbol = NULL; |
|
|
break; |
|
|
|
|
|
case tok_typename: |
|
|
typename = xstrdup (token_buffer); |
|
|
symbol = NULL; |
|
|
break; |
|
|
|
|
|
case tok_identifier: |
|
|
if (*symval->tag == '\"' && symbol) |
|
|
{ |
|
|
symbol_make_alias (symbol, symval, typename); |
|
|
symbol = NULL; |
|
|
} |
|
|
else |
|
|
{ |
|
|
symbol = symval; |
|
|
symbol_class_set (symbol, class); |
|
|
if (typename) |
|
|
symbol_type_set (symbol, typename); |
|
|
} |
|
|
break; |
|
|
|
|
|
case tok_number: |
|
|
symbol_user_token_number_set (symbol, numval); |
|
|
break; |
|
|
|
|
|
default: |
|
|
complain (_("`%s' is invalid in %s"), |
|
|
token_buffer, |
|
|
(class == token_sym) ? "%token" : "%nterm"); |
|
|
skip_to_char ('%'); |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/*------------------------------. |
|
|
| Parse what comes after %start | |
|
|
`------------------------------*/ |
|
|
|
|
|
static void |
|
|
parse_start_decl (void) |
|
99 |
{ |
{ |
100 |
if (start_flag) |
if (start_flag) |
101 |
complain (_("multiple %s declarations"), "%start"); |
complain (_("multiple %s declarations"), "%start"); |
|
if (lex () != tok_identifier) |
|
|
complain (_("invalid %s declaration"), "%start"); |
|
102 |
else |
else |
103 |
{ |
{ |
104 |
start_flag = 1; |
start_flag = 1; |
105 |
startsymbol = symval; |
startsymbol = s; |
106 |
} |
} |
107 |
} |
} |
108 |
|
|
|
/*-----------------------------------------------------------. |
|
|
| read in a %type declaration and record its information for | |
|
|
| get_type_name to access | |
|
|
`-----------------------------------------------------------*/ |
|
|
|
|
|
static void |
|
|
parse_type_decl (void) |
|
|
{ |
|
|
char *name; |
|
|
|
|
|
if (lex () != tok_typename) |
|
|
{ |
|
|
complain ("%s", _("%type declaration has no <typename>")); |
|
|
skip_to_char ('%'); |
|
|
return; |
|
|
} |
|
|
|
|
|
name = xstrdup (token_buffer); |
|
|
|
|
|
for (;;) |
|
|
{ |
|
|
token_t t; |
|
|
int tmp_char = ungetc (skip_white_space (), finput); |
|
|
|
|
|
if (tmp_char == '%') |
|
|
return; |
|
|
if (tmp_char == EOF) |
|
|
fatal (_("Premature EOF after %s"), token_buffer); |
|
|
|
|
|
t = lex (); |
|
|
|
|
|
switch (t) |
|
|
{ |
|
|
case tok_comma: |
|
|
case tok_semicolon: |
|
|
break; |
|
|
|
|
|
case tok_identifier: |
|
|
symbol_type_set (symval, name); |
|
|
break; |
|
|
|
|
|
default: |
|
|
complain (_("invalid %%type declaration due to item: %s"), |
|
|
token_buffer); |
|
|
skip_to_char ('%'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
109 |
|
|
110 |
/*----------------------------------------------------------------. |
/*----------------------------------------------------------------. |
111 |
| Read in a %left, %right or %nonassoc declaration and record its | |
| There are two prologues: one before %union, one after. Augment | |
112 |
| information. | |
| the current one. | |
113 |
`----------------------------------------------------------------*/ |
`----------------------------------------------------------------*/ |
114 |
|
|
115 |
static void |
void |
116 |
parse_assoc_decl (associativity assoc) |
prologue_augment (const char *prologue, int location) |
|
{ |
|
|
char *name = NULL; |
|
|
int prev = 0; |
|
|
|
|
|
/* Assign a new precedence level, never 0. */ |
|
|
++lastprec; |
|
|
|
|
|
for (;;) |
|
|
{ |
|
|
token_t t; |
|
|
int tmp_char = ungetc (skip_white_space (), finput); |
|
|
|
|
|
if (tmp_char == '%') |
|
|
return; |
|
|
if (tmp_char == EOF) |
|
|
fatal (_("Premature EOF after %s"), token_buffer); |
|
|
|
|
|
t = lex (); |
|
|
|
|
|
switch (t) |
|
|
{ |
|
|
case tok_typename: |
|
|
name = xstrdup (token_buffer); |
|
|
break; |
|
|
|
|
|
case tok_comma: |
|
|
break; |
|
|
|
|
|
case tok_identifier: |
|
|
symbol_class_set (symval, token_sym); |
|
|
symbol_precedence_set (symval, lastprec, assoc); |
|
|
if (name) |
|
|
symbol_type_set (symval, name); |
|
|
break; |
|
|
|
|
|
case tok_number: |
|
|
if (prev == tok_identifier) |
|
|
{ |
|
|
symbol_user_token_number_set (symval, numval); |
|
|
} |
|
|
else |
|
|
{ |
|
|
complain |
|
|
(_("invalid text (%s) - number should be after identifier"), |
|
|
token_buffer); |
|
|
skip_to_char ('%'); |
|
|
} |
|
|
break; |
|
|
|
|
|
case tok_semicolon: |
|
|
return; |
|
|
|
|
|
default: |
|
|
complain (_("unexpected item: %s"), token_buffer); |
|
|
skip_to_char ('%'); |
|
|
} |
|
|
|
|
|
prev = t; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------. |
|
|
| Copy the union declaration into the stype muscle | |
|
|
| (and fdefines), where it is made into the definition of | |
|
|
| YYSTYPE, the type of elements of the parser value stack. | |
|
|
`--------------------------------------------------------------*/ |
|
|
|
|
|
static void |
|
|
parse_union_decl (void) |
|
|
{ |
|
|
int c; |
|
|
int count = 0; |
|
|
bool done = FALSE; |
|
|
struct obstack union_obstack; |
|
|
if (typed) |
|
|
complain (_("multiple %s declarations"), "%union"); |
|
|
|
|
|
typed = 1; |
|
|
|
|
|
MUSCLE_INSERT_INT ("stype_line", lineno); |
|
|
obstack_init (&union_obstack); |
|
|
obstack_sgrow (&union_obstack, "union"); |
|
|
|
|
|
while (!done) |
|
|
{ |
|
|
c = xgetc (finput); |
|
|
|
|
|
/* If C contains '/', it is output by copy_comment (). */ |
|
|
if (c != '/') |
|
|
obstack_1grow (&union_obstack, c); |
|
|
|
|
|
switch (c) |
|
|
{ |
|
|
case '\n': |
|
|
++lineno; |
|
|
break; |
|
|
|
|
|
case '/': |
|
|
copy_comment (finput, &union_obstack); |
|
|
break; |
|
|
|
|
|
case '{': |
|
|
++count; |
|
|
break; |
|
|
|
|
|
case '}': |
|
|
/* FIXME: Errr. How could this happen???. --akim */ |
|
|
if (count == 0) |
|
|
complain (_("unmatched %s"), "`}'"); |
|
|
count--; |
|
|
if (!count) |
|
|
done = TRUE; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
/* JF don't choke on trailing semi */ |
|
|
c = skip_white_space (); |
|
|
if (c != ';') |
|
|
ungetc (c, finput); |
|
|
obstack_1grow (&union_obstack, 0); |
|
|
muscle_insert ("stype", obstack_finish (&union_obstack)); |
|
|
} |
|
|
|
|
|
|
|
|
/*-------------------------------------------------------. |
|
|
| Parse the declaration %expect N which says to expect N | |
|
|
| shift-reduce conflicts. | |
|
|
`-------------------------------------------------------*/ |
|
|
|
|
|
static void |
|
|
parse_expect_decl (void) |
|
|
{ |
|
|
int c = skip_white_space (); |
|
|
ungetc (c, finput); |
|
|
|
|
|
if (!isdigit (c)) |
|
|
complain (_("argument of %%expect is not an integer")); |
|
|
else |
|
|
expected_conflicts = read_signed_integer (finput); |
|
|
} |
|
|
|
|
|
|
|
|
static void |
|
|
parse_muscle_decl (void) |
|
117 |
{ |
{ |
118 |
int ch = ungetc (skip_white_space (), finput); |
struct obstack *oout = |
119 |
char *muscle_key; |
!typed ? &pre_prologue_obstack : &post_prologue_obstack; |
|
char *muscle_value; |
|
120 |
|
|
121 |
/* Read key. */ |
if (!no_lines_flag) |
|
if (!isalpha (ch) && ch != '_') |
|
|
{ |
|
|
complain (_("invalid %s declaration"), "%define"); |
|
|
skip_to_char ('%'); |
|
|
return; |
|
|
} |
|
|
copy_identifier (finput, &muscle_obstack); |
|
|
obstack_1grow (&muscle_obstack, 0); |
|
|
muscle_key = obstack_finish (&muscle_obstack); |
|
|
|
|
|
/* Read value. */ |
|
|
ch = skip_white_space (); |
|
|
if (ch != '"') |
|
122 |
{ |
{ |
123 |
ungetc (ch, finput); |
obstack_fgrow2 (oout, muscle_find ("linef"), |
124 |
if (ch != EOF) |
location, quotearg_style (c_quoting_style, |
125 |
{ |
muscle_find ("filename"))); |
|
complain (_("invalid %s declaration"), "%define"); |
|
|
skip_to_char ('%'); |
|
|
return; |
|
|
} |
|
|
else |
|
|
fatal (_("Premature EOF after %s"), "\""); |
|
126 |
} |
} |
127 |
copy_string2 (finput, &muscle_obstack, '"', 0); |
obstack_sgrow (oout, prologue); |
|
obstack_1grow (&muscle_obstack, 0); |
|
|
muscle_value = obstack_finish (&muscle_obstack); |
|
|
|
|
|
/* Store the (key, value) pair in the environment. */ |
|
|
muscle_insert (muscle_key, muscle_value); |
|
128 |
} |
} |
129 |
|
|
130 |
|
|
131 |
|
|
|
/*---------------------------------. |
|
|
| Parse a double quoted parameter. | |
|
|
`---------------------------------*/ |
|
|
|
|
|
static const char * |
|
|
parse_dquoted_param (const char *from) |
|
|
{ |
|
|
struct obstack param_obstack; |
|
|
const char *param = NULL; |
|
|
int c; |
|
|
|
|
|
obstack_init (¶m_obstack); |
|
|
c = skip_white_space (); |
|
|
|
|
|
if (c != '"') |
|
|
{ |
|
|
complain (_("invalid %s declaration"), from); |
|
|
ungetc (c, finput); |
|
|
skip_to_char ('%'); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
while ((c = literalchar ()) != '"') |
|
|
obstack_1grow (¶m_obstack, c); |
|
|
|
|
|
obstack_1grow (¶m_obstack, '\0'); |
|
|
param = obstack_finish (¶m_obstack); |
|
|
|
|
|
if (c != '"' || strlen (param) == 0) |
|
|
{ |
|
|
complain (_("invalid %s declaration"), from); |
|
|
if (c != '"') |
|
|
ungetc (c, finput); |
|
|
skip_to_char ('%'); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
return param; |
|
|
} |
|
132 |
|
|
133 |
/*----------------------------------. |
/*----------------------. |
134 |
| Parse what comes after %skeleton. | |
| Handle the epilogue. | |
135 |
`----------------------------------*/ |
`----------------------*/ |
136 |
|
|
137 |
static void |
void |
138 |
parse_skel_decl (void) |
epilogue_set (const char *epilogue, int location) |
139 |
{ |
{ |
140 |
skeleton = parse_dquoted_param ("%skeleton"); |
struct obstack el_obstack; |
141 |
} |
obstack_init (&el_obstack); |
|
|
|
|
/*----------------------------------------------------------------. |
|
|
| Read from finput until `%%' is seen. Discard the `%%'. Handle | |
|
|
| any `%' declarations, and copy the contents of any `%{ ... %}' | |
|
|
| groups to PRE_PROLOGUE_OBSTACK or POST_PROLOGUE_OBSTACK. | |
|
|
`----------------------------------------------------------------*/ |
|
142 |
|
|
143 |
static void |
if (!no_lines_flag) |
|
read_declarations (void) |
|
|
{ |
|
|
for (;;) |
|
144 |
{ |
{ |
145 |
int c = skip_white_space (); |
obstack_fgrow2 (&el_obstack, muscle_find ("linef"), |
146 |
|
location, quotearg_style (c_quoting_style, |
147 |
if (c == '%') |
muscle_find ("filename"))); |
|
{ |
|
|
token_t tok = parse_percent_token (); |
|
|
|
|
|
switch (tok) |
|
|
{ |
|
|
case tok_two_percents: |
|
|
return; |
|
|
|
|
|
case tok_percent_left_curly: |
|
|
if (!typed) |
|
|
copy_definition (&pre_prologue_obstack); |
|
|
else |
|
|
copy_definition (&post_prologue_obstack); |
|
|
break; |
|
|
|
|
|
case tok_token: |
|
|
parse_token_decl (token_sym); |
|
|
break; |
|
|
|
|
|
case tok_nterm: |
|
|
parse_token_decl (nterm_sym); |
|
|
break; |
|
|
|
|
|
case tok_type: |
|
|
parse_type_decl (); |
|
|
break; |
|
|
|
|
|
case tok_start: |
|
|
parse_start_decl (); |
|
|
break; |
|
|
|
|
|
case tok_union: |
|
|
parse_union_decl (); |
|
|
break; |
|
|
|
|
|
case tok_expect: |
|
|
parse_expect_decl (); |
|
|
break; |
|
|
|
|
|
case tok_left: |
|
|
parse_assoc_decl (left_assoc); |
|
|
break; |
|
|
|
|
|
case tok_right: |
|
|
parse_assoc_decl (right_assoc); |
|
|
break; |
|
|
|
|
|
case tok_nonassoc: |
|
|
parse_assoc_decl (non_assoc); |
|
|
break; |
|
|
|
|
|
case tok_define: |
|
|
parse_muscle_decl (); |
|
|
break; |
|
|
|
|
|
case tok_skel: |
|
|
parse_skel_decl (); |
|
|
break; |
|
|
|
|
|
case tok_noop: |
|
|
break; |
|
|
|
|
|
case tok_stropt: |
|
|
case tok_intopt: |
|
|
case tok_obsolete: |
|
|
assert (0); |
|
|
break; |
|
|
|
|
|
case tok_illegal: |
|
|
default: |
|
|
complain (_("unrecognized: %s"), token_buffer); |
|
|
skip_to_char ('%'); |
|
|
} |
|
|
} |
|
|
else if (c == EOF) |
|
|
fatal (_("no input grammar")); |
|
|
else |
|
|
{ |
|
|
char buf[] = "c"; |
|
|
buf[0] = c; |
|
|
complain (_("unknown character: %s"), quote (buf)); |
|
|
skip_to_char ('%'); |
|
|
} |
|
148 |
} |
} |
149 |
|
obstack_sgrow (&el_obstack, epilogue); |
150 |
|
obstack_1grow (&el_obstack, 0); |
151 |
|
muscle_insert ("epilogue", obstack_finish (&el_obstack)); |
152 |
} |
} |
|
|
|
|
/*------------------------------------------------------------------. |
|
|
| Assuming that a `{' has just been seen, copy everything up to the | |
|
|
| matching `}' into ACTION_OBSTACK, and return a pointer to this | |
|
|
| action. | |
|
|
`------------------------------------------------------------------*/ |
|
153 |
|
|
|
static char * |
|
|
parse_action (symbol_list *rule) |
|
|
{ |
|
|
int count = 1; |
|
|
|
|
|
/* RULE_LENGTH is the number of values in the current rule so far, |
|
|
which says where to find `$0' with respect to the top of the |
|
|
stack. It is not the same as the rule->length in the case of mid |
|
|
rule actions. */ |
|
|
int rule_length = 0; |
|
|
symbol_list *rhs; |
|
|
for (rhs = rule->next; rhs; rhs = rhs->next) |
|
|
++rule_length; |
|
|
|
|
|
while (count > 0) |
|
|
{ |
|
|
int c; |
|
|
while ((c = getc (finput)) != '}') |
|
|
switch (c) |
|
|
{ |
|
|
case '\n': |
|
|
copy_character (&action_obstack, c); |
|
|
++lineno; |
|
|
break; |
|
|
|
|
|
case '{': |
|
|
copy_character (&action_obstack, c); |
|
|
++count; |
|
|
break; |
|
|
|
|
|
case '\'': |
|
|
case '"': |
|
|
copy_string (finput, &action_obstack, c); |
|
|
break; |
|
|
|
|
|
case '/': |
|
|
copy_comment (finput, &action_obstack); |
|
|
break; |
|
|
|
|
|
case '$': |
|
|
copy_dollar (finput, &action_obstack, rule, rule_length); |
|
|
break; |
|
|
|
|
|
case '@': |
|
|
copy_at (finput, &action_obstack, rule_length); |
|
|
break; |
|
|
|
|
|
case EOF: |
|
|
fatal (_("unmatched %s"), "`{'"); |
|
|
|
|
|
default: |
|
|
copy_character (&action_obstack, c); |
|
|
} |
|
|
|
|
|
/* Above loop exits when C is '}'. */ |
|
|
if (--count) |
|
|
copy_character (&action_obstack, c); |
|
|
} |
|
|
|
|
|
obstack_1grow (&action_obstack, '\0'); |
|
|
return obstack_finish (&action_obstack); |
|
|
} |
|
154 |
|
|
155 |
|
|
156 |
|
|
169 |
symbol_t *sym; |
symbol_t *sym; |
170 |
|
|
171 |
sprintf (buf, "@%d", ++gensym_count); |
sprintf (buf, "@%d", ++gensym_count); |
172 |
token_buffer = buf; |
sym = getsym (buf); |
|
sym = getsym (token_buffer); |
|
173 |
sym->class = nterm_sym; |
sym->class = nterm_sym; |
174 |
sym->number = nvars++; |
sym->number = nvars++; |
175 |
return sym; |
return sym; |
197 |
symbol_list *grammar_end = NULL; |
symbol_list *grammar_end = NULL; |
198 |
|
|
199 |
/* Append S to the GRAMMAR. */ |
/* Append S to the GRAMMAR. */ |
200 |
static void |
void |
201 |
grammar_symbol_append (symbol_t *s) |
grammar_symbol_append (symbol_t *s) |
202 |
{ |
{ |
203 |
symbol_list *p = symbol_list_new (s); |
symbol_list *p = symbol_list_new (s); |
218 |
|
|
219 |
/* Create a new rule for LHS in to the GRAMMAR. */ |
/* Create a new rule for LHS in to the GRAMMAR. */ |
220 |
|
|
221 |
static void |
void |
222 |
grammar_rule_begin (symbol_t *lhs) |
grammar_rule_begin (symbol_t *lhs) |
223 |
{ |
{ |
224 |
if (!start_flag) |
if (!start_flag) |
247 |
complain (_("rule given for %s, which is a token"), lhs->tag); |
complain (_("rule given for %s, which is a token"), lhs->tag); |
248 |
} |
} |
249 |
|
|
250 |
|
/* Check that the last rule (CURRENT_RULE) is properly defined. For |
251 |
|
instance, there should be no type clash on the default action. */ |
252 |
|
|
253 |
|
static void |
254 |
|
grammar_current_rule_check (void) |
255 |
|
{ |
256 |
|
symbol_t *lhs = current_rule->sym; |
257 |
|
symbol_t *first_rhs = current_rule->next->sym; |
258 |
|
|
259 |
|
/* If there is an action, then there is nothing we can do: the user |
260 |
|
is allowed to shoot in her foot. */ |
261 |
|
if (current_rule->action) |
262 |
|
return; |
263 |
|
|
264 |
|
/* If $$ is being set in default way, report if any type mismatch. |
265 |
|
*/ |
266 |
|
if (first_rhs) |
267 |
|
{ |
268 |
|
const char *lhs_type = lhs->type_name ? lhs->type_name : ""; |
269 |
|
const char *rhs_type = first_rhs->type_name ? first_rhs->type_name : ""; |
270 |
|
if (strcmp (lhs_type, rhs_type)) |
271 |
|
complain (_("type clash (`%s' `%s') on default action"), |
272 |
|
lhs_type, rhs_type); |
273 |
|
} |
274 |
|
/* Warn if there is no default for $$ but we need one. */ |
275 |
|
else |
276 |
|
{ |
277 |
|
if (lhs->type_name) |
278 |
|
complain (_("empty rule for typed nonterminal, and no action")); |
279 |
|
} |
280 |
|
} |
281 |
|
|
282 |
|
|
283 |
|
/* End the currently being grown rule. */ |
284 |
|
|
285 |
|
void |
286 |
|
grammar_rule_end (void) |
287 |
|
{ |
288 |
|
/* Put an empty link in the list to mark the end of this rule */ |
289 |
|
grammar_symbol_append (NULL); |
290 |
|
grammar_current_rule_check (); |
291 |
|
} |
292 |
|
|
293 |
|
|
294 |
/* The previous action turns out the be a mid-rule action. Attach it |
/* The previous action turns out the be a mid-rule action. Attach it |
295 |
to the current rule, i.e., create a dummy symbol, attach it this |
to the current rule, i.e., create a dummy symbol, attach it this |
296 |
mid-rule action, and append this dummy nonterminal to the current |
mid-rule action, and append this dummy nonterminal to the current |
297 |
rule. */ |
rule. */ |
298 |
|
|
299 |
static void |
void |
300 |
grammar_midrule_action (void) |
grammar_midrule_action (void) |
301 |
{ |
{ |
302 |
/* Since the action was written out with this rule's number, we must |
/* Since the action was written out with this rule's number, we must |
336 |
|
|
337 |
/* Set the precedence symbol of the current rule to PRECSYM. */ |
/* Set the precedence symbol of the current rule to PRECSYM. */ |
338 |
|
|
339 |
static void |
void |
340 |
grammar_current_rule_prec_set (symbol_t *precsym) |
grammar_current_rule_prec_set (symbol_t *precsym) |
341 |
{ |
{ |
342 |
if (current_rule->ruleprec) |
if (current_rule->ruleprec) |
344 |
current_rule->ruleprec = precsym; |
current_rule->ruleprec = precsym; |
345 |
} |
} |
346 |
|
|
|
/* Check that the last rule (CURRENT_RULE) is properly defined. For |
|
|
instance, there should be no type clash on the default action. */ |
|
|
|
|
|
static void |
|
|
grammar_current_rule_check (void) |
|
|
{ |
|
|
symbol_t *lhs = current_rule->sym; |
|
|
symbol_t *first_rhs = current_rule->next->sym; |
|
|
|
|
|
/* If there is an action, then there is nothing we can do: the user |
|
|
is allowed to shoot in her foot. */ |
|
|
if (current_rule->action) |
|
|
return; |
|
|
|
|
|
/* If $$ is being set in default way, report if any type mismatch. |
|
|
*/ |
|
|
if (first_rhs) |
|
|
{ |
|
|
const char *lhs_type = lhs->type_name ? lhs->type_name : ""; |
|
|
const char *rhs_type = first_rhs->type_name ? first_rhs->type_name : ""; |
|
|
if (strcmp (lhs_type, rhs_type)) |
|
|
complain (_("type clash (`%s' `%s') on default action"), |
|
|
lhs_type, rhs_type); |
|
|
} |
|
|
/* Warn if there is no default for $$ but we need one. */ |
|
|
else |
|
|
{ |
|
|
if (lhs->type_name) |
|
|
complain (_("empty rule for typed nonterminal, and no action")); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
347 |
/* Attach a SYMBOL to the current rule. If needed, move the previous |
/* Attach a SYMBOL to the current rule. If needed, move the previous |
348 |
action as a mid-rule action. */ |
action as a mid-rule action. */ |
349 |
|
|
350 |
static void |
void |
351 |
grammar_current_rule_symbol_append (symbol_t *symbol) |
grammar_current_rule_symbol_append (symbol_t *symbol) |
352 |
{ |
{ |
353 |
if (current_rule->action) |
if (current_rule->action) |
360 |
/* Attach an ACTION to the current rule. If needed, move the previous |
/* Attach an ACTION to the current rule. If needed, move the previous |
361 |
action as a mid-rule action. */ |
action as a mid-rule action. */ |
362 |
|
|
363 |
static void |
void |
364 |
grammar_current_rule_action_append (char *action, int action_line) |
grammar_current_rule_action_append (const char *action, int action_line) |
365 |
{ |
{ |
366 |
if (current_rule->action) |
if (current_rule->action) |
367 |
grammar_midrule_action (); |
grammar_midrule_action (); |
369 |
current_rule->action_line = action_line; |
current_rule->action_line = action_line; |
370 |
} |
} |
371 |
|
|
|
|
|
|
|
|
|
static void |
|
|
readgram (void) |
|
|
{ |
|
|
token_t t; |
|
|
symbol_t *lhs = NULL; |
|
|
|
|
|
t = lex (); |
|
|
|
|
|
while (t != tok_two_percents && t != tok_eof) |
|
|
if (t == tok_identifier || t == tok_bar) |
|
|
{ |
|
|
if (t == tok_identifier) |
|
|
{ |
|
|
lhs = symval; |
|
|
|
|
|
t = lex (); |
|
|
if (t != tok_colon) |
|
|
{ |
|
|
complain (_("ill-formed rule: initial symbol not followed by colon")); |
|
|
unlex (t); |
|
|
} |
|
|
} |
|
|
if (nrules == 0 && t == tok_bar) |
|
|
{ |
|
|
complain (_("grammar starts with vertical bar")); |
|
|
lhs = symval; /* BOGUS: use a random symval */ |
|
|
} |
|
|
|
|
|
grammar_rule_begin (lhs); |
|
|
/* read the rhs of the rule. */ |
|
|
|
|
|
for (;;) |
|
|
{ |
|
|
t = lex (); |
|
|
if (t == tok_prec) |
|
|
{ |
|
|
t = lex (); |
|
|
grammar_current_rule_prec_set (symval); |
|
|
t = lex (); |
|
|
} |
|
|
|
|
|
if (!(t == tok_identifier || t == tok_left_curly)) |
|
|
break; |
|
|
|
|
|
/* If next token is an identifier, see if a colon follows it. |
|
|
If one does, exit this rule now. */ |
|
|
if (t == tok_identifier) |
|
|
{ |
|
|
symbol_t *ssave; |
|
|
token_t t1; |
|
|
|
|
|
ssave = symval; |
|
|
t1 = lex (); |
|
|
unlex (t1); |
|
|
symval = ssave; |
|
|
if (t1 == tok_colon) |
|
|
{ |
|
|
warn (_("previous rule lacks an ending `;'")); |
|
|
break; |
|
|
} |
|
|
/* Not followed by colon => process as part of this |
|
|
rule's rhs. */ |
|
|
} |
|
|
|
|
|
if (t == tok_identifier) |
|
|
{ |
|
|
grammar_current_rule_symbol_append (symval); |
|
|
} |
|
|
else /* handle an action. */ |
|
|
{ |
|
|
int action_line = lineno; |
|
|
char *action = parse_action (current_rule); |
|
|
grammar_current_rule_action_append (action, action_line); |
|
|
} |
|
|
} /* end of read rhs of rule */ |
|
|
|
|
|
/* Put an empty link in the list to mark the end of this rule */ |
|
|
grammar_symbol_append (NULL); |
|
|
|
|
|
if (t == tok_prec) |
|
|
{ |
|
|
t = lex (); |
|
|
grammar_current_rule_prec_set (symval); |
|
|
t = lex (); |
|
|
} |
|
|
|
|
|
if (t == tok_left_curly) |
|
|
{ |
|
|
int action_line = lineno; |
|
|
char *action = parse_action (current_rule); |
|
|
grammar_current_rule_action_append (action, action_line); |
|
|
t = lex (); |
|
|
} |
|
|
|
|
|
grammar_current_rule_check (); |
|
|
|
|
|
if (t == tok_two_percents || t == tok_eof) |
|
|
warn (_("previous rule lacks an ending `;'")); |
|
|
if (t == tok_semicolon) |
|
|
t = lex (); |
|
|
} |
|
|
else |
|
|
{ |
|
|
complain (_("invalid input: %s"), quote (token_buffer)); |
|
|
t = lex (); |
|
|
} |
|
|
|
|
|
/* grammar has been read. Do some checking */ |
|
|
|
|
|
if (nrules == 0) |
|
|
fatal (_("no rules in the input grammar")); |
|
|
|
|
|
/* Report any undefined symbols and consider them nonterminals. */ |
|
|
symbols_check_defined (); |
|
|
|
|
|
/* Insert the initial rule, which line is that of the first rule |
|
|
(not that of the start symbol): |
|
|
|
|
|
axiom: %start EOF. */ |
|
|
{ |
|
|
symbol_list *p = symbol_list_new (axiom); |
|
|
p->line = grammar->line; |
|
|
p->next = symbol_list_new (startsymbol); |
|
|
p->next->next = symbol_list_new (eoftoken); |
|
|
p->next->next->next = symbol_list_new (NULL); |
|
|
p->next->next->next->next = grammar; |
|
|
nrules += 1; |
|
|
nritems += 3; |
|
|
grammar = p; |
|
|
} |
|
|
|
|
|
if (nsyms > SHRT_MAX) |
|
|
fatal (_("too many symbols (tokens plus nonterminals); maximum %d"), |
|
|
SHRT_MAX); |
|
|
|
|
|
assert (nsyms == ntokens + nvars); |
|
|
} |
|
|
|
|
|
/* At the end of the grammar file, some C source code must |
|
|
be stored. It is going to be associated to the epilogue |
|
|
directive. */ |
|
|
static void |
|
|
read_additionnal_code (void) |
|
|
{ |
|
|
int c; |
|
|
struct obstack el_obstack; |
|
|
|
|
|
obstack_init (&el_obstack); |
|
|
|
|
|
if (!no_lines_flag) |
|
|
{ |
|
|
obstack_fgrow2 (&el_obstack, muscle_find ("linef"), |
|
|
lineno, quotearg_style (c_quoting_style, |
|
|
muscle_find ("filename"))); |
|
|
} |
|
|
|
|
|
while ((c = getc (finput)) != EOF) |
|
|
copy_character (&el_obstack, c); |
|
|
|
|
|
obstack_1grow (&el_obstack, 0); |
|
|
muscle_insert ("epilogue", obstack_finish (&el_obstack)); |
|
|
} |
|
|
|
|
372 |
|
|
373 |
/*---------------------------------------------------------------. |
/*---------------------------------------------------------------. |
374 |
| Convert the rules into the representation using RRHS, RLHS and | |
| Convert the rules into the representation using RRHS, RLHS and | |
445 |
void |
void |
446 |
reader (void) |
reader (void) |
447 |
{ |
{ |
448 |
lex_init (); |
gram_control_t gram_control; |
449 |
lineno = 1; |
lineno = 1; |
450 |
|
|
451 |
/* Initialize the muscle obstack. */ |
/* Initialize the muscle obstack. */ |
477 |
obstack_init (&post_prologue_obstack); |
obstack_init (&post_prologue_obstack); |
478 |
|
|
479 |
finput = xfopen (infile, "r"); |
finput = xfopen (infile, "r"); |
480 |
|
gram_in = finput; |
481 |
|
|
482 |
|
gram_debug = !!getenv ("parse"); |
483 |
|
gram__flex_debug = !!getenv ("scan"); |
484 |
|
gram_parse (&gram_control); |
485 |
|
|
486 |
/* Read the declaration section. Copy %{ ... %} groups to |
/* Grammar has been read. Do some checking */ |
487 |
TABLE_OBSTACK and FDEFINES file. Also notice any %token, %left, |
if (nrules == 0) |
488 |
etc. found there. */ |
fatal (_("no rules in the input grammar")); |
489 |
read_declarations (); |
|
490 |
|
/* Report any undefined symbols and consider them nonterminals. */ |
491 |
|
symbols_check_defined (); |
492 |
|
|
493 |
/* If the user did not define her EOFTOKEN, do it now. */ |
/* If the user did not define her EOFTOKEN, do it now. */ |
494 |
if (!eoftoken) |
if (!eoftoken) |
500 |
eoftoken->user_token_number = 0; |
eoftoken->user_token_number = 0; |
501 |
} |
} |
502 |
|
|
503 |
/* Read in the grammar, build grammar in list form. Write out |
/* Insert the initial rule, which line is that of the first rule |
504 |
actions. */ |
(not that of the start symbol): |
505 |
readgram (); |
|
506 |
/* Some C code is given at the end of the grammar file. */ |
axiom: %start EOF. */ |
507 |
read_additionnal_code (); |
{ |
508 |
|
symbol_list *p = symbol_list_new (axiom); |
509 |
|
p->line = grammar->line; |
510 |
|
p->next = symbol_list_new (startsymbol); |
511 |
|
p->next->next = symbol_list_new (eoftoken); |
512 |
|
p->next->next->next = symbol_list_new (NULL); |
513 |
|
p->next->next->next->next = grammar; |
514 |
|
nrules += 1; |
515 |
|
nritems += 3; |
516 |
|
grammar = p; |
517 |
|
} |
518 |
|
|
519 |
|
if (nsyms > SHRT_MAX) |
520 |
|
fatal (_("too many symbols (tokens plus nonterminals); maximum %d"), |
521 |
|
SHRT_MAX); |
522 |
|
|
523 |
|
assert (nsyms == ntokens + nvars); |
524 |
|
|
|
lex_free (); |
|
525 |
xfclose (finput); |
xfclose (finput); |
526 |
|
|
527 |
/* Assign the symbols their symbol numbers. Write #defines for the |
/* Assign the symbols their symbol numbers. Write #defines for the |