+vla_ident: T_IDENT | T_ASTERISK {
+ ALLOC($$, sizeof "");
+ strcpy($$, "");
+}
+
+array: T_LBRACKET T_UINT T_RBRACKET {
+ if ($2 == 0)
+ FAIL(_("array length must be positive"));
+
+ ALLOC_STRUCT($$, struct cdecl_declarator,
+ .type = CDECL_DECL_ARRAY,
+ .u.array.length = $2);
+} | T_LBRACKET vla_ident T_RBRACKET {
+ 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 {
+ ALLOC_STRUCT($$, struct cdecl,
+ .specifiers = $1,
+ .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;
+ }
+
+ ALLOC_STRUCT($$, struct cdecl_declarator,
+ .type = CDECL_DECL_FUNCTION,
+ .u.function.parameters = p,
+ .u.function.variadic = $2);
+}
+
+parens: T_LPAREN parameter_type_list T_RPAREN {
+ $$ = $2;
+} | T_LPAREN declarator_ish T_RPAREN {
+ ALLOC_STRUCT($$, struct cdecl_declarator,
+ .type = CDECL_DECL_FUNCTION);
+ ALLOC_STRUCT($$->u.function.parameters, struct cdecl,
+ .declarators = $2);
+}
+
+pointer: T_ASTERISK qualifiers direct_declarator {
+ ALLOC_STRUCT($$, struct cdecl_declarator,
+ .type = CDECL_DECL_POINTER,
+ .u.pointer.qualifiers = $2,
+ .child = $3);
+} | T_ASTERISK qualifiers pointer {
+ ALLOC_STRUCT($$, struct cdecl_declarator,
+ .type = CDECL_DECL_POINTER,
+ .u.pointer.qualifiers = $2,
+ .child = $3);
+}
+
+declarator: direct_declarator | pointer
+declarator_ish: direct_declarator_ish | pointer
+postfix: array | parens
+
+direct_declarator_ish: {
+ ALLOC_STRUCT($$, struct cdecl_declarator,
+ .type = CDECL_DECL_NULL);
+} | direct_declarator_ish postfix {
+ $$ = $2;
+ $$->child = $1;
+}