This should resolve a rather nasty ambiguity in the C grammar:
[declaration] -> [declaration-specifiers] ;
-> [type-specifier] [declaration-specifiers] ;
-> int [declaration-specifiers] ;
-> int [type-specifier] ;
-> int [typedef-name] ;
-> int [identifier] ;
-> int x ;
versus
[declaration] -> [declaration-specifiers] [init-declarator-list] ;
-> [type-specifier] [init-declarator-list] ;
-> int [init-declarator-list] ;
-> int [init-declarator] ;
-> int [declarator] ;
-> int [direct-declarator] ;
-> int [identifier] ;
-> int x ;
The standard resolves this ambiguity by having a constraint that is not
part of the context-free grammar. This ambiguity gets really nasty with
things like int f (x); -- is it a declaration of x with two type
specifiers or a declaration of f (a function with one parameter) with
with one type specifier?
A side-effect of this change is that the parser will now reject
declarations without any type specifiers at all, which probably makes
the error messages a bit less informative.
%token T_UNION "union"
%token T_ENUM "enum"
%token T_UNION "union"
%token T_ENUM "enum"
-%type <uintval> declspec_simple
-%type <declspec> declspec declspecs
+%type <uintval> declspec_simple typespec_simple
+%type <declspec> declspec_notype declspec_noid typespec_noid typespec
+%type <declspec> declspecs declspecs_noid
%type <declarator> declarator declarators
%type <decl> declaration
%type <declarator> declarator declarators
%type <decl> declaration
-declspecs: { $$ = NULL; } | declspecs declspec {
- $$ = $2;
- $$->next = $1;
+declspecs: declspec_notype declspecs {
+ $$ = $1;
+ $$->next = $2;
+} | typespec declspecs_noid {
+ $$ = $1;
+ $$->next = $2;
+}
+
+declspecs_noid: { $$ = NULL; } | declspec_noid declspecs_noid {
+ $$ = $1;
+ $$->next = $2;
}
declarators: declarator | declarator T_COMMA declarators {
}
declarators: declarator | declarator T_COMMA declarators {
-declspec_simple: T_VOID { $$ = CDECL_TYPE_VOID; }
+declspec_simple: T_AUTO { $$ = CDECL_STOR_AUTO; }
+ | T_TYPEDEF { $$ = CDECL_STOR_TYPEDEF; }
+ | T_EXTERN { $$ = CDECL_STOR_EXTERN; }
+ | T_STATIC { $$ = CDECL_STOR_STATIC; }
+ | T_REGISTER { $$ = CDECL_STOR_REGISTER; }
+ | T_RESTRICT { $$ = CDECL_QUAL_RESTRICT; }
+ | T_VOLATILE { $$ = CDECL_QUAL_VOLATILE; }
+ | T_CONST { $$ = CDECL_QUAL_CONST; }
+ | T_INLINE { $$ = CDECL_FUNC_INLINE; }
+
+typespec_simple: T_VOID { $$ = CDECL_TYPE_VOID; }
| T_CHAR { $$ = CDECL_TYPE_CHAR; }
| T_SHORT { $$ = CDECL_TYPE_SHORT; }
| T_INT { $$ = CDECL_TYPE_INT; }
| T_CHAR { $$ = CDECL_TYPE_CHAR; }
| T_SHORT { $$ = CDECL_TYPE_SHORT; }
| T_INT { $$ = CDECL_TYPE_INT; }
| T_UNSIGNED { $$ = CDECL_TYPE_UNSIGNED; }
| T_BOOL { $$ = CDECL_TYPE_BOOL; }
| T_COMPLEX { $$ = CDECL_TYPE_COMPLEX; }
| T_UNSIGNED { $$ = CDECL_TYPE_UNSIGNED; }
| T_BOOL { $$ = CDECL_TYPE_BOOL; }
| T_COMPLEX { $$ = CDECL_TYPE_COMPLEX; }
- | T_TYPEDEF { $$ = CDECL_STOR_TYPEDEF; }
- | T_EXTERN { $$ = CDECL_STOR_EXTERN; }
- | T_STATIC { $$ = CDECL_STOR_STATIC; }
- | T_AUTO { $$ = CDECL_STOR_AUTO; }
- | T_REGISTER { $$ = CDECL_STOR_REGISTER; }
- | T_RESTRICT { $$ = CDECL_QUAL_RESTRICT; }
- | T_VOLATILE { $$ = CDECL_QUAL_VOLATILE; }
- | T_CONST { $$ = CDECL_QUAL_CONST; }
- | T_INLINE { $$ = CDECL_FUNC_INLINE; }
- ;
-declspec: declspec_simple {
+declspec_notype: declspec_simple {
ALLOC_STRUCT($$, struct cdecl_declspec, .type = $1);
ALLOC_STRUCT($$, struct cdecl_declspec, .type = $1);
+}
+
+typespec_noid: typespec_simple {
+ ALLOC_STRUCT($$, struct cdecl_declspec, .type = $1);
+}
+
+typespec: typespec_noid | T_STRUCT T_IDENT {
ALLOC_STRUCT($$, struct cdecl_declspec,
.type = CDECL_TYPE_STRUCT,
.ident = $2);
ALLOC_STRUCT($$, struct cdecl_declspec,
.type = CDECL_TYPE_STRUCT,
.ident = $2);
ALLOC_STRUCT($$, struct cdecl_declspec,
.type = CDECL_TYPE_IDENT,
.ident = $1);
ALLOC_STRUCT($$, struct cdecl_declspec,
.type = CDECL_TYPE_IDENT,
.ident = $1);
+}
+
+declspec_noid: declspec_notype | typespec_noid
declarator: T_IDENT {
ALLOC_STRUCT($$, struct cdecl_declarator,
declarator: T_IDENT {
ALLOC_STRUCT($$, struct cdecl_declarator,