1987-03-10 17:51:10 +00:00
|
|
|
/*
|
|
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
|
|
*/
|
1986-03-10 13:07:55 +00:00
|
|
|
/* $Header$ */
|
|
|
|
/* DECLARATION SYNTAX PARSER */
|
|
|
|
|
|
|
|
{
|
1987-01-24 00:25:56 +00:00
|
|
|
#include <alloc.h>
|
1986-03-10 13:07:55 +00:00
|
|
|
#include "nobitfield.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "arith.h"
|
|
|
|
#include "LLlex.h"
|
1988-08-19 13:55:22 +00:00
|
|
|
#include "label.h"
|
|
|
|
#include "code.h"
|
1986-03-10 13:07:55 +00:00
|
|
|
#include "idf.h"
|
|
|
|
#include "type.h"
|
|
|
|
#include "struct.h"
|
|
|
|
#include "field.h"
|
|
|
|
#include "decspecs.h"
|
|
|
|
#include "def.h"
|
1986-03-10 15:10:56 +00:00
|
|
|
#include "declar.h"
|
1986-03-10 13:07:55 +00:00
|
|
|
#include "label.h"
|
|
|
|
#include "expr.h"
|
|
|
|
#include "sizes.h"
|
1987-03-25 23:14:43 +00:00
|
|
|
#include "level.h"
|
1986-03-10 13:07:55 +00:00
|
|
|
|
|
|
|
extern char options[];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 8 */
|
|
|
|
declaration
|
|
|
|
{struct decspecs Ds;}
|
|
|
|
:
|
|
|
|
{Ds = null_decspecs;}
|
|
|
|
decl_specifiers(&Ds)
|
|
|
|
init_declarator_list(&Ds)?
|
|
|
|
';'
|
|
|
|
;
|
|
|
|
|
|
|
|
/* A `decl_specifiers' describes a sequence of a storage_class_specifier,
|
|
|
|
an unsigned_specifier, a size_specifier and a simple type_specifier,
|
|
|
|
which may occur in arbitrary order and each of which may be absent;
|
|
|
|
at least one of them must be present, however, since the totally
|
|
|
|
empty case has already be dealt with in `external_definition'.
|
|
|
|
This means that something like:
|
|
|
|
unsigned extern int short xx;
|
|
|
|
is perfectly good C.
|
|
|
|
|
|
|
|
On top of that, multiple occurrences of storage_class_specifiers,
|
|
|
|
unsigned_specifiers and size_specifiers are errors, but a second
|
|
|
|
type_specifier should end the decl_specifiers and be treated as
|
|
|
|
the name to be declared (see the thin ice in RM11.1).
|
|
|
|
Such a language is not easily expressed in a grammar; enumeration
|
|
|
|
of the permutations is unattractive. We solve the problem by
|
|
|
|
having a regular grammar for the "soft" items, handling the single
|
|
|
|
occurrence of the type_specifier in the grammar (we have no choice),
|
|
|
|
collecting all data in a `struct decspecs' and turning that data
|
|
|
|
structure into what we want.
|
|
|
|
|
|
|
|
The existence of declarations like
|
|
|
|
short typedef yepp;
|
|
|
|
makes all hope of writing a specific grammar for typedefs illusory.
|
|
|
|
*/
|
|
|
|
|
1987-02-09 23:19:42 +00:00
|
|
|
decl_specifiers /* non-empty */ (register struct decspecs *ds;)
|
1986-03-10 13:07:55 +00:00
|
|
|
/* Reads a non-empty decl_specifiers and fills the struct
|
|
|
|
decspecs *ds.
|
|
|
|
*/
|
|
|
|
:
|
|
|
|
[
|
|
|
|
other_specifier(ds)+
|
1987-08-18 10:05:18 +00:00
|
|
|
[%if (DOT != IDENTIFIER || AHEAD == IDENTIFIER)
|
|
|
|
/* the thin ice in R.M. 11.1 */
|
1986-03-10 13:07:55 +00:00
|
|
|
single_type_specifier(ds) other_specifier(ds)*
|
|
|
|
|
|
|
|
|
empty
|
|
|
|
]
|
|
|
|
|
|
|
|
|
single_type_specifier(ds) other_specifier(ds)*
|
|
|
|
]
|
|
|
|
{do_decspecs(ds);}
|
|
|
|
;
|
|
|
|
|
|
|
|
/* 8.1 */
|
1987-02-09 23:19:42 +00:00
|
|
|
other_specifier(register struct decspecs *ds;):
|
1986-03-10 13:07:55 +00:00
|
|
|
[ AUTO | STATIC | EXTERN | TYPEDEF | REGISTER ]
|
|
|
|
{ if (ds->ds_sc_given)
|
|
|
|
error("repeated storage class specifier");
|
1987-03-25 23:14:43 +00:00
|
|
|
ds->ds_sc_given = 1;
|
|
|
|
ds->ds_sc = DOT;
|
1986-03-10 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
[ SHORT | LONG ]
|
|
|
|
{ if (ds->ds_size)
|
|
|
|
error("repeated size specifier");
|
1987-03-25 23:14:43 +00:00
|
|
|
ds->ds_size = DOT;
|
1986-03-10 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
UNSIGNED
|
|
|
|
{ if (ds->ds_unsigned)
|
|
|
|
error("unsigned specified twice");
|
1987-03-25 23:14:43 +00:00
|
|
|
ds->ds_unsigned = 1;
|
1986-03-10 13:07:55 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
|
|
|
/* 8.2 */
|
|
|
|
type_specifier(struct type **tpp;)
|
|
|
|
/* Used in struct/union declarations and in casts; only the
|
|
|
|
type is relevant.
|
|
|
|
*/
|
|
|
|
{struct decspecs Ds; Ds = null_decspecs;}
|
|
|
|
:
|
|
|
|
decl_specifiers(&Ds)
|
|
|
|
{
|
|
|
|
if (Ds.ds_sc_given)
|
|
|
|
error("storage class ignored");
|
|
|
|
if (Ds.ds_sc == REGISTER)
|
|
|
|
error("register ignored");
|
|
|
|
}
|
|
|
|
{*tpp = Ds.ds_type;}
|
|
|
|
;
|
|
|
|
|
1987-02-09 23:19:42 +00:00
|
|
|
single_type_specifier(register struct decspecs *ds;):
|
1987-10-05 10:17:44 +00:00
|
|
|
%default TYPE_IDENTIFIER /* this includes INT, CHAR, etc. */
|
1986-03-10 13:07:55 +00:00
|
|
|
{idf2type(dot.tk_idf, &ds->ds_type);}
|
1987-08-18 10:05:18 +00:00
|
|
|
|
|
|
|
|
IDENTIFIER
|
1987-10-05 10:17:44 +00:00
|
|
|
{
|
|
|
|
error("%s is not a type identifier", dot.tk_idf->id_text);
|
1988-08-19 13:55:22 +00:00
|
|
|
ds->ds_type = error_type;
|
1987-10-05 10:17:44 +00:00
|
|
|
if (dot.tk_idf->id_def) {
|
1988-08-19 13:55:22 +00:00
|
|
|
dot.tk_idf->id_def->df_type = error_type;
|
|
|
|
dot.tk_idf->id_def->df_sc = TYPEDEF;
|
1987-10-05 10:17:44 +00:00
|
|
|
}
|
1987-08-18 10:05:18 +00:00
|
|
|
}
|
1986-03-10 13:07:55 +00:00
|
|
|
|
|
|
|
|
struct_or_union_specifier(&ds->ds_type)
|
|
|
|
|
|
|
|
|
enum_specifier(&ds->ds_type)
|
|
|
|
;
|
|
|
|
|
|
|
|
/* 8.3 */
|
|
|
|
init_declarator_list(struct decspecs *ds;):
|
|
|
|
init_declarator(ds)
|
|
|
|
[ ',' init_declarator(ds) ]*
|
|
|
|
;
|
|
|
|
|
1987-02-09 23:19:42 +00:00
|
|
|
init_declarator(register struct decspecs *ds;)
|
1986-03-10 13:07:55 +00:00
|
|
|
{
|
|
|
|
struct declarator Dc;
|
|
|
|
}
|
|
|
|
:
|
|
|
|
{
|
|
|
|
Dc = null_declarator;
|
|
|
|
}
|
|
|
|
[
|
|
|
|
declarator(&Dc)
|
|
|
|
{
|
|
|
|
reject_params(&Dc);
|
|
|
|
declare_idf(ds, &Dc, level);
|
|
|
|
}
|
1987-03-25 23:14:43 +00:00
|
|
|
[
|
|
|
|
initializer(Dc.dc_idf, ds->ds_sc)
|
|
|
|
|
|
|
|
|
{ code_declaration(Dc.dc_idf, (struct expr *) 0, level, ds->ds_sc); }
|
|
|
|
]
|
1986-03-10 13:07:55 +00:00
|
|
|
]
|
1988-08-19 13:55:22 +00:00
|
|
|
{
|
|
|
|
remove_declarator(&Dc);
|
|
|
|
}
|
1986-03-10 13:07:55 +00:00
|
|
|
;
|
|
|
|
|
1987-03-25 23:14:43 +00:00
|
|
|
/* 8.6: initializer */
|
|
|
|
initializer(struct idf *idf; int sc;)
|
|
|
|
{
|
|
|
|
struct expr *expr = (struct expr *) 0;
|
|
|
|
int globalflag = level == L_GLOBAL ||
|
1987-08-28 09:56:00 +00:00
|
|
|
(level >= L_LOCAL && sc == STATIC);
|
1987-03-25 23:14:43 +00:00
|
|
|
}
|
|
|
|
:
|
|
|
|
{ if (idf->id_def->df_type->tp_fund == FUNCTION) {
|
|
|
|
error("illegal initialization of function");
|
1988-01-11 14:06:20 +00:00
|
|
|
idf->id_def->df_type->tp_fund = ERRONEOUS;
|
1987-03-25 23:14:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
[
|
|
|
|
'='
|
|
|
|
|
|
|
|
|
empty
|
|
|
|
{warning("old-fashioned initialization, insert =");}
|
|
|
|
/* This causes trouble at declarator and at
|
|
|
|
external_definition, q.v.
|
|
|
|
*/
|
|
|
|
]
|
1988-08-19 13:55:22 +00:00
|
|
|
{
|
|
|
|
if (globalflag) {
|
1987-03-25 23:14:43 +00:00
|
|
|
struct expr ex;
|
|
|
|
code_declaration(idf, &ex, level, sc);
|
1988-08-19 13:55:22 +00:00
|
|
|
}
|
1987-03-25 23:14:43 +00:00
|
|
|
}
|
|
|
|
initial_value(globalflag ? &(idf->id_def->df_type) : (struct type **)0,
|
|
|
|
&expr)
|
|
|
|
{ if (! globalflag) {
|
|
|
|
if (idf->id_def->df_type->tp_fund == FUNCTION) {
|
|
|
|
free_expression(expr);
|
|
|
|
expr = 0;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
print_expr("initializer-expression", expr);
|
|
|
|
#endif DEBUG
|
1987-11-04 16:30:02 +00:00
|
|
|
code_declaration(idf, expr, level, sc);
|
1988-08-19 13:55:22 +00:00
|
|
|
}
|
|
|
|
init_idf(idf);
|
1987-03-25 23:14:43 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
1986-03-10 13:07:55 +00:00
|
|
|
/*
|
|
|
|
Functions yielding pointers to functions must be declared as, e.g.,
|
|
|
|
int (*hehe(par1, par2))() char *par1, *par2; {}
|
|
|
|
Since the function heading is read as a normal declarator,
|
|
|
|
we just include the (formal) parameter list in the declarator
|
|
|
|
description list dc.
|
|
|
|
*/
|
1987-02-09 23:19:42 +00:00
|
|
|
declarator(register struct declarator *dc;)
|
1986-03-10 13:07:55 +00:00
|
|
|
{
|
|
|
|
arith count;
|
1986-07-18 21:10:42 +00:00
|
|
|
struct formal *fm = 0;
|
1986-03-10 13:07:55 +00:00
|
|
|
}
|
|
|
|
:
|
|
|
|
primary_declarator(dc)
|
|
|
|
[%while(1) /* int i (M + 2) / 4;
|
|
|
|
is a function, not an
|
|
|
|
old-fashioned initialization.
|
|
|
|
*/
|
|
|
|
'('
|
1986-07-18 21:10:42 +00:00
|
|
|
formal_list(&fm) ? /* semantic check later... */
|
1986-03-10 13:07:55 +00:00
|
|
|
')'
|
|
|
|
{
|
1986-07-18 21:10:42 +00:00
|
|
|
add_decl_unary(dc, FUNCTION, (arith)0, fm);
|
|
|
|
fm = 0;
|
1986-03-10 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
arrayer(&count)
|
|
|
|
{add_decl_unary(dc, ARRAY, count, NO_PARAMS);}
|
|
|
|
]*
|
|
|
|
|
|
|
|
|
'*' declarator(dc)
|
|
|
|
{add_decl_unary(dc, POINTER, (arith)0, NO_PARAMS);}
|
|
|
|
;
|
|
|
|
|
1987-02-09 23:19:42 +00:00
|
|
|
primary_declarator(register struct declarator *dc;) :
|
1986-03-10 13:07:55 +00:00
|
|
|
identifier(&dc->dc_idf)
|
|
|
|
|
|
|
|
|
'(' declarator(dc) ')'
|
|
|
|
;
|
|
|
|
|
|
|
|
arrayer(arith *sizep;)
|
|
|
|
{ struct expr *expr; }
|
|
|
|
:
|
|
|
|
'['
|
|
|
|
[
|
|
|
|
constant_expression(&expr)
|
|
|
|
{
|
1986-09-28 20:33:15 +00:00
|
|
|
check_array_subscript(expr);
|
1986-03-10 13:07:55 +00:00
|
|
|
*sizep = expr->VL_VALUE;
|
|
|
|
free_expression(expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
empty
|
|
|
|
{ *sizep = (arith)-1; }
|
|
|
|
]
|
|
|
|
']'
|
|
|
|
;
|
|
|
|
|
1986-07-18 21:10:42 +00:00
|
|
|
formal_list (struct formal **fmp;)
|
1986-03-10 13:07:55 +00:00
|
|
|
:
|
1986-07-18 21:10:42 +00:00
|
|
|
formal(fmp) [ ',' formal(fmp) ]*
|
1986-03-10 13:07:55 +00:00
|
|
|
;
|
|
|
|
|
1986-07-18 21:10:42 +00:00
|
|
|
formal(struct formal **fmp;)
|
1986-03-10 13:07:55 +00:00
|
|
|
{struct idf *idf; }
|
|
|
|
:
|
|
|
|
identifier(&idf)
|
|
|
|
{
|
1987-02-09 23:19:42 +00:00
|
|
|
register struct formal *new = new_formal();
|
1986-03-10 13:07:55 +00:00
|
|
|
|
1986-07-18 21:10:42 +00:00
|
|
|
new->fm_idf = idf;
|
|
|
|
new->next = *fmp;
|
|
|
|
*fmp = new;
|
1986-03-10 13:07:55 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
|
|
|
/* Change 2 */
|
1987-02-09 23:19:42 +00:00
|
|
|
enum_specifier(register struct type **tpp;)
|
1986-03-10 13:07:55 +00:00
|
|
|
{
|
|
|
|
struct idf *idf;
|
|
|
|
arith l = (arith)0;
|
|
|
|
}
|
|
|
|
:
|
|
|
|
ENUM
|
|
|
|
[
|
|
|
|
{declare_struct(ENUM, (struct idf *) 0, tpp);}
|
|
|
|
enumerator_pack(*tpp, &l)
|
|
|
|
|
|
|
|
|
identifier(&idf)
|
|
|
|
[
|
|
|
|
{declare_struct(ENUM, idf, tpp);}
|
|
|
|
enumerator_pack(*tpp, &l)
|
|
|
|
|
|
|
|
|
{apply_struct(ENUM, idf, tpp);}
|
|
|
|
empty
|
|
|
|
]
|
|
|
|
]
|
|
|
|
;
|
|
|
|
|
1987-02-09 23:19:42 +00:00
|
|
|
enumerator_pack(register struct type *tp; arith *lp;) :
|
1986-03-10 13:07:55 +00:00
|
|
|
'{'
|
|
|
|
enumerator(tp, lp)
|
|
|
|
[%while(AHEAD != '}') /* >>> conflict on ',' */
|
|
|
|
','
|
|
|
|
enumerator(tp, lp)
|
|
|
|
]*
|
|
|
|
','? /* optional trailing comma */
|
|
|
|
'}'
|
|
|
|
{tp->tp_size = int_size;}
|
|
|
|
/* fancy implementations that put small enums in 1 byte
|
|
|
|
or so should start here.
|
|
|
|
*/
|
|
|
|
;
|
|
|
|
|
|
|
|
enumerator(struct type *tp; arith *lp;)
|
|
|
|
{
|
|
|
|
struct idf *idf;
|
|
|
|
struct expr *expr;
|
|
|
|
}
|
|
|
|
:
|
|
|
|
identifier(&idf)
|
|
|
|
[
|
|
|
|
'='
|
|
|
|
constant_expression(&expr)
|
|
|
|
{
|
|
|
|
*lp = expr->VL_VALUE;
|
|
|
|
free_expression(expr);
|
|
|
|
}
|
|
|
|
]?
|
|
|
|
{declare_enum(tp, idf, (*lp)++);}
|
|
|
|
;
|
|
|
|
|
|
|
|
/* 8.5 */
|
1987-02-09 23:19:42 +00:00
|
|
|
struct_or_union_specifier(register struct type **tpp;)
|
1986-03-10 13:07:55 +00:00
|
|
|
{
|
|
|
|
int fund;
|
1987-02-09 23:19:42 +00:00
|
|
|
struct idf *idfX;
|
|
|
|
register struct idf *idf;
|
1986-03-10 13:07:55 +00:00
|
|
|
}
|
|
|
|
:
|
|
|
|
[ STRUCT | UNION ]
|
|
|
|
{fund = DOT;}
|
|
|
|
[
|
|
|
|
{
|
|
|
|
declare_struct(fund, (struct idf *)0, tpp);
|
|
|
|
}
|
|
|
|
struct_declaration_pack(*tpp)
|
|
|
|
|
|
1987-02-09 23:19:42 +00:00
|
|
|
identifier(&idfX) { idf = idfX; }
|
1986-03-10 13:07:55 +00:00
|
|
|
[
|
|
|
|
{
|
|
|
|
declare_struct(fund, idf, tpp);
|
|
|
|
(idf->id_struct->tg_busy)++;
|
|
|
|
}
|
|
|
|
struct_declaration_pack(*tpp)
|
|
|
|
{
|
|
|
|
(idf->id_struct->tg_busy)--;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
{apply_struct(fund, idf, tpp);}
|
|
|
|
empty
|
|
|
|
]
|
|
|
|
]
|
|
|
|
;
|
|
|
|
|
1987-02-09 23:19:42 +00:00
|
|
|
struct_declaration_pack(register struct type *stp;)
|
1986-03-10 13:07:55 +00:00
|
|
|
{
|
|
|
|
struct sdef **sdefp = &stp->tp_sdef;
|
|
|
|
arith size = (arith)0;
|
|
|
|
}
|
|
|
|
:
|
|
|
|
/* The size is only filled in after the whole struct has
|
|
|
|
been read, to prevent recursive definitions.
|
|
|
|
*/
|
|
|
|
'{'
|
|
|
|
struct_declaration(stp, &sdefp, &size)+
|
|
|
|
'}'
|
|
|
|
{stp->tp_size = align(size, stp->tp_align);}
|
|
|
|
;
|
|
|
|
|
|
|
|
struct_declaration(struct type *stp; struct sdef ***sdefpp; arith *szp;)
|
|
|
|
{struct type *tp;}
|
|
|
|
:
|
|
|
|
type_specifier(&tp)
|
|
|
|
struct_declarator_list(tp, stp, sdefpp, szp)
|
|
|
|
[ /* in some standard UNIX compilers the semicolon
|
|
|
|
is optional, would you believe!
|
|
|
|
*/
|
|
|
|
';'
|
|
|
|
|
|
|
|
|
empty
|
|
|
|
{warning("no semicolon after declarator");}
|
|
|
|
]
|
|
|
|
;
|
|
|
|
|
|
|
|
struct_declarator_list(struct type *tp, *stp;
|
|
|
|
struct sdef ***sdefpp; arith *szp;)
|
|
|
|
:
|
|
|
|
struct_declarator(tp, stp, sdefpp, szp)
|
|
|
|
[ ',' struct_declarator(tp, stp, sdefpp, szp) ]*
|
|
|
|
;
|
|
|
|
|
|
|
|
struct_declarator(struct type *tp; struct type *stp;
|
|
|
|
struct sdef ***sdefpp; arith *szp;)
|
|
|
|
{
|
|
|
|
struct declarator Dc;
|
|
|
|
struct field *fd = 0;
|
|
|
|
}
|
|
|
|
:
|
|
|
|
{
|
|
|
|
Dc = null_declarator;
|
|
|
|
}
|
|
|
|
[
|
|
|
|
declarator(&Dc)
|
|
|
|
{reject_params(&Dc);}
|
|
|
|
bit_expression(&fd)?
|
|
|
|
|
|
|
|
|
{Dc.dc_idf = gen_idf();}
|
|
|
|
bit_expression(&fd)
|
|
|
|
]
|
|
|
|
{add_sel(stp, declare_type(tp, &Dc), Dc.dc_idf, sdefpp, szp, fd);}
|
|
|
|
{remove_declarator(&Dc);}
|
|
|
|
;
|
|
|
|
|
|
|
|
bit_expression(struct field **fd;)
|
|
|
|
{ struct expr *expr; }
|
|
|
|
:
|
|
|
|
{
|
|
|
|
*fd = new_field();
|
|
|
|
}
|
|
|
|
':'
|
|
|
|
constant_expression(&expr)
|
|
|
|
{
|
|
|
|
(*fd)->fd_width = expr->VL_VALUE;
|
|
|
|
free_expression(expr);
|
|
|
|
#ifdef NOBITFIELD
|
|
|
|
error("bitfields are not implemented");
|
|
|
|
#endif NOBITFIELD
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
|
|
|
/* 8.7 */
|
|
|
|
cast(struct type **tpp;) {struct declarator Dc;} :
|
|
|
|
{Dc = null_declarator;}
|
|
|
|
'('
|
|
|
|
type_specifier(tpp)
|
|
|
|
abstract_declarator(&Dc)
|
|
|
|
')'
|
|
|
|
{*tpp = declare_type(*tpp, &Dc);}
|
|
|
|
{remove_declarator(&Dc);}
|
|
|
|
;
|
|
|
|
|
|
|
|
/* This code is an abject copy of that of 'declarator', for lack of
|
|
|
|
a two-level grammar.
|
|
|
|
*/
|
1987-02-09 23:19:42 +00:00
|
|
|
abstract_declarator(register struct declarator *dc;)
|
1986-03-10 13:07:55 +00:00
|
|
|
{arith count;}
|
|
|
|
:
|
|
|
|
primary_abstract_declarator(dc)
|
|
|
|
[
|
|
|
|
'(' ')'
|
|
|
|
{add_decl_unary(dc, FUNCTION, (arith)0, NO_PARAMS);}
|
|
|
|
|
|
|
|
|
arrayer(&count)
|
|
|
|
{add_decl_unary(dc, ARRAY, count, NO_PARAMS);}
|
|
|
|
]*
|
|
|
|
|
|
|
|
|
'*' abstract_declarator(dc)
|
|
|
|
{add_decl_unary(dc, POINTER, (arith)0, NO_PARAMS);}
|
|
|
|
;
|
|
|
|
|
|
|
|
primary_abstract_declarator(struct declarator *dc;) :
|
|
|
|
[%if (AHEAD == ')')
|
|
|
|
empty
|
|
|
|
|
|
|
|
|
'(' abstract_declarator(dc) ')'
|
|
|
|
]
|
|
|
|
;
|
|
|
|
|
|
|
|
empty:
|
|
|
|
;
|
|
|
|
|
|
|
|
/* 8.8 */
|
|
|
|
/* included in the IDENTIFIER/TYPE_IDENTIFIER mechanism */
|