ALLOC(ptr, sizeof (type)); \
*(ptr) = (type) { __VA_ARGS__ }; \
} while (0)
+
+/*
+ * With the postprocessing performed by fix-yytname.awk, all the symbol
+ * name strings can be used directly in error messages and there is no
+ * need for any string processing.
+ */
+#define yytnamerr(a, b) cdecl__strlcpy(a, b, (a) ? INT_MAX : 0)
%}
%code requires {
/* Magic tokens */
%token T_LEX_ERROR
-%token T_ENGLISH
%token <strval> T_IDENT "identifier"
%token <uintval> T_UINT "integer constant"
%token T_VLA "variable-length"
%type <strval> vla_ident
+%type <uintval> array_length
%type <boolval> varargs
%type <spectype> declspec_simple qualifier_simple
%type <spectype> typespec_simple typespec_tagged
%type <declarator> direct_declarator declarator pointer array parens postfix
%type <declarator> direct_declarator_ish declarator_ish parameter_type_list
%type <decl> declaration declarators declarator_wrap
-%type <decl> parameter parameters
+%type <decl> parameter
%type <strval> english_vla
%type <declspec> storage_func_specs post_specs
%type <declspec> type_qual_spec type_qual_specs typedef_name_qual
%type <declarator> english_declarator english_array english_function
%type <declarator> english_parameter_list null_decl
-%type <decl> english_parameter english_parameters
%type <decl> english english_declaration
+%type <decl> english_parameter
-/*
- * Harmless shift/reduce conflicts in english_parameter. See comments below
- * for more details.
- */
-%expect 2
+/* Precedence declaration to avoid conflict in english_parameter; see below. */
+%right T_TYPE
+%right T_IDENT
%%
-input: T_ENGLISH english {
- *out = $2;
+input: english {
+ *out = $1;
} | declaration {
*out = $1;
};
| T_COMPLEX
| T_IMAGINARY
-typespec_tagged: T_STRUCT | T_UNION | T_ENUM
+typespec_tagged: T_STRUCT | T_UNION | T_ENUM | { $$ = CDECL_TYPE_IDENT; }
qualifier_simple: T_CONST
| T_RESTRICT
ALLOC_STRUCT($$, struct cdecl_declspec,
.type = $1,
.ident = $2);
-} | T_IDENT {
- ALLOC_STRUCT($$, struct cdecl_declspec,
- .type = CDECL_TYPE_IDENT,
- .ident = $1);
}
declspec_noid: declspec_notype | typespec_noid
vla_ident: T_IDENT | T_ASTERISK {
ALLOC($$, sizeof "");
- strcpy($$, "");
+ *$$ = 0;
}
-array: T_LBRACKET T_UINT T_RBRACKET {
- if ($2 == 0)
- FAIL(_("array length must be positive"));
-
+array: T_LBRACKET array_length T_RBRACKET {
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
.u.array.length = $2);
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
.u.array.vla = $2);
-} | T_LBRACKET T_RBRACKET {
- ALLOC_STRUCT($$, struct cdecl_declarator,
- .type = CDECL_DECL_ARRAY);
}
parameter: declspecs declarator {
.declarators = $2);
}
-parameters: parameter | parameters T_COMMA parameter {
- $$ = $3;
- $$->next = $1;
-}
-
varargs: { $$ = false; } | T_COMMA T_ELLIPSIS { $$ = true; }
-parameter_type_list: parameters varargs {
- struct cdecl *p, *c, *n;
-
- /* Parameters were accumulated in reverse order. */
- for (p = NULL, c = $1; c; p = c, c = n) {
- n = c->next;
- c->next = p;
- }
-
+parameter_type_list: parameter varargs {
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_FUNCTION,
- .u.function.parameters = p,
+ .u.function.parameters = $1,
.u.function.variadic = $2);
+} | parameter T_COMMA parameter_type_list {
+ $$ = $3;
+ $1->next = $$->u.function.parameters;
+ $$->u.function.parameters = $1;
}
parens: T_LPAREN parameter_type_list T_RPAREN {
$$ = $2;
}
-storage_func_specs: { $$ = NULL; } | declspec_simple storage_func_specs {
+/*
+ * We use a precedence declaration to prefer shifting an identifier
+ * over reducing this empty rule; see below.
+ */
+storage_func_specs: %prec T_TYPE { $$ = NULL; }
+storage_func_specs: declspec_simple storage_func_specs {
ALLOC_STRUCT($$, struct cdecl_declspec,
.type = $1,
.next = $2);
$$ = $3;
}
-english_parameter_list: english_parameters varargs {
- struct cdecl *p, *c, *n;
-
- /* Parameters were accumulated in reverse order. */
- for (p = NULL, c = $1; c; p = c, c = n) {
- n = c->next;
- c->next = p;
- }
-
+english_parameter_list: english_parameter varargs {
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_FUNCTION,
- .u.function.parameters = p,
+ .u.function.parameters = $1,
.u.function.variadic = $2);
-}
-
-english_parameters: english_parameters T_COMMA english_parameter {
+} | english_parameter T_COMMA english_parameter_list {
$$ = $3;
- $$->next = $1;
-} | english_parameter
+ $1->next = $$->u.function.parameters;
+ $$->u.function.parameters = $1;
+}
typedef_name_qual: T_IDENT qualifiers {
ALLOC_STRUCT($$, struct cdecl_declspec,
}
/*
- * There is a small shift/reduce conflict here. An unadorned identifier
- * as the first thing in the parameter might be a typedef name deep in the
- * first english_declaration (thus empty storage_func_specs and empty
- * english_declarator need to be reduced) or it might be the identifier
- * before the "as" (thus the identifier should be shifted).
+ * There is a shift/reduce conflict here when an identifier appears as the
+ * first token. The conflict is between shifting T_IDENT, or reducing the
+ * empty production for storage_func_specs (cf. english_declaration).
+ *
+ * - In either case, if we reduce, we won't match T_IDENT T_AS since the
+ * stack now has the extra storage_func_specs nonterminal symbol.
+ * - And if we shift, we won't match english_declaration since it is
+ * too late to add storage_func_specs to the stack.
*
- * The typedef name conflict is the only issue, so treating it as a special
- * case makes the shift harmless.
+ * The only valid input affected by the conflict is a simple type names,
+ * possibly followed by qualifiers. So the conflict is adequately resolved
+ * by shifting, so long as we have a special-case reduction to handle this.
*/
english_parameter: english_declaration | typedef_name_qual null_decl {
ALLOC_STRUCT($$, struct cdecl,
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
.u.array.vla = $3);
-} | T_ARRAY T_UINT T_OF {
- if ($2 == 0)
- FAIL(_("array length must be positive"));
-
+} | T_ARRAY array_length T_OF {
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
.u.array.length = $2);
-} | T_ARRAY T_OF {
- ALLOC_STRUCT($$, struct cdecl_declarator,
- .type = CDECL_DECL_ARRAY,
- .u.array.length = 0);
+}
+
+array_length: { $$ = 0; }
+array_length: T_UINT {
+ if (!($$ = $1))
+ FAIL(_("array length must be positive"));
}
english_vla: T_IDENT | {
ALLOC($$, sizeof "");
- strcpy($$, "");
+ *$$ = 0;
}
%%