%{ /* enable chatty error messages */ #define YYERROR_VERBOSE 1 /* figure out the location of a non-terminal. We discard extent information * but this isn't necessarily disastrous for our purpose. */ #define YYLLOC_DEFAULT(Current, Rhs, N) do { \ Current = Rhs[1]; \ } while(0) #include "cparse.h" #include #include /* various bits of parsing infrastructure. increasingly i'm thinking some of * this belongs in its own file. still... */ /* hang on to both ends of a declarator while it is being parsed. This idiom * is used a lot with anonymous structures below, but we need an actual name * for the type for this particular case. */ struct parsing_declarator { struct declarator_type *first, **end; char *name; /* pick up name */ }; /* we need to know what declaration specifiers apply to the current * declarator. This means we need a stack of them to cope with nested * declarations. */ struct declaration_specifiers_stack { struct declaration_specifiers_stack *next; struct declaration_specifiers *ds; }; static struct declaration_specifiers_stack *declaration_specifiers_stack; static void push_declaration_specifiers(struct declaration_specifiers *ds) { struct declaration_specifiers_stack *n; NEW(n); n->next = declaration_specifiers_stack; n->ds = ds; declaration_specifiers_stack = n; } static void pop_declaration_specifiers(void) { /* can we have ->= and .= operators? */ declaration_specifiers_stack = declaration_specifiers_stack->next; } static struct declaration_specifiers *top_declaration_specifiers(void) { return declaration_specifiers_stack->ds; } static void check_top_declaration_specifier(struct declaration_specifiers *ds) { assert(declaration_specifiers_stack->ds == ds); } /* make up an actual declarator from a completed parse */ static struct declarator *get_declarator(struct parsing_declarator *pd, const struct location *where) { struct declarator *d; NEW(d); d->declarator_type = pd->first; d->declaration_specifiers = top_declaration_specifiers(); d->name = pd->name; d->where = *where; return d; } static struct expression expr_star; /* kludge */ static void warn_old_style_function(const struct location *where) { inputwarning(where, warn_obsolete, "old-style function declarator"); } void parser_init(FILE *fp) { yyin = fp; translation_unit = 0; scope_init(); gcc_extensions(); } %} %debug %verbose %expect 11 %defines %union { long i; unsigned long u; char *s; struct { char *name; struct declarator *declarator; } name; struct declaration_specifiers *declaration_specifiers; struct declarator *declarator; struct parsing_declarator *declarator_parse; struct declaration *declaration; struct identifier_list *identifier_list; struct expression *expression; struct expression_list *expression_list; struct initializer *initializer; struct designator *designator; struct external_declaration *external_declaration; struct function_definition *function_definition; struct enumerator *enumerator; struct statement *statement; /* transients for parsing lists */ struct { struct declarator *first, **end; } *declarator_list; struct { struct declaration *first, **end; } *declaration_list; struct { struct identifier_list *first, **end; } *identifier_list_parse; struct { struct expression_list *first, **end; } *parsing_expression_list; struct { struct initializer *first, **end; } *initializer_list; struct { struct designator *first, **end; } *designator_list; struct { struct external_declaration *first, **end; } *external_declaration_list; struct { struct enumerator *first, **end; } *enumerator_list; struct { struct statement *first, **end; } *statement_list; } %token MEMBER "->" %token INCR "++" %token DECR "--" %token SL "<<" %token SR ">>" %token LE "<=" %token GE ">=" %token EQ "==" %token NE "!=" %token AND "&&" %token OR "||" %token MULEQ "*=" %token DIVEQ "/=" %token MODEQ "%=" %token ADDEQ "+=" %token SUBEQ "-=" %token SLEQ "<<=" %token SREQ ">>=" %token ANDEQ "&=" %token XOREQ "^=" %token OREQ "|=" %token VARARG "..." %token '&' %token '*' %token '+' %token '-' %token '~' %token '!' %token '/' %token '%' %token '<' %token '>' %token '^' %token '|' %token ',' %token '=' %token '.' %token AUTO "auto" %token BREAK "break" %token CASE "case" %token CHAR "char" %token CONST "const" %token CONTINUE "continue" %token DEFAULT "default" %token DO "do" %token DOUBLE "double" %token ELSE "else" %token ENUM "enum" %token EXTERN "extern" %token FLOAT "float" %token FOR "for" %token GOTO "goto" %token IF "if" %token INLINE "inline" %token INT "int" %token LONG "long" %token REGISTER "register" %token RESTRICT "restrict" %token RETURN "return" %token SHORT "short" %token SIGNED "signed" %token SIZEOF "sizeof" %token STATIC "static" %token STRUCT "struct" %token SWITCH "switch" %token TYPEDEF "typedef" %token UNION "union" %token UNSIGNED "unsigned" %token VOID "void" %token VOLATILE "volatile" %token WHILE "while" %token BOOL "_Bool" %token COMPLEX "_Complex" %token IMAGINARY "_Imaginary" %token ATTRIBUTE "__attribute__" %token GCC_VA_LIST "__builtin_va_list" %token GCC_VA_ARG "__builtin_va_arg" %token GCC_EXPECT "__builtin_expect" %token TYPEDEF_NAME %token ID %token NUMBER %token STRINGLIT %token CHARLIT %token WSTRINGLIT %token WCHARLIT %type storage_class_specifier %type type_qualifier %type struct_or_union %type type_qualifier_list %type type_qualifier_list_opt %type function_specifier %type basic_type_specifier %type identifier %type identifier_opt %type type_specifier %type struct_or_union_specifier %type enum_specifier %type declaration_specifiers %type declaration_specifiers_definition %type declaration_specifiers_opt %type parameter_declaration_specifiers %type specifier_qualifier_list %type specifier_qualifier_list_definition %type specifier_qualifier_list_opt %type array_specifiers %type declarator_opt %type init_declarator_list_opt %type attributed_declarator %type attributed_init_declarator %type struct_declarator %type init_declarator_list %type struct_declarator_list %type declarator pointer %type direct_declarator %type abstract_declarator %type pointer_opt %type direct_abstract_declarator %type direct_abstract_declarator_opt %type abstract_declarator_opt %type declaration %type struct_declaration %type parameter_declaration %type type_name %type main %type translation_unit %type external_declaration %type struct_declaration_list %type parameter_list %type declaration_list_opt %type ID_list %type ID_list_opt %type expression %type expression_opt %type primary_expression %type postfix_expression %type unary_expression %type cast_expression %type multiplicative_expression %type additive_expression %type shift_expression %type relational_expression %type equality_expression %type AND_expression %type exclusive_OR_expression %type inclusive_OR_expression %type logical_AND_expression %type logical_OR_expression %type conditional_expression %type assignment_expression %type constant_expression %type array_size %type argument_expression_list_opt %type argument_expression_list %type initializer %type designated_initializer %type initializer_list %type designator_list %type designator %type variadic %type incrdecr %type member_operator %type unary_operator %type multiplicative_operator %type additive_operator %type shift_operator %type relational_operator %type equality_operator %type assignment_operator %type comma_opt %type function_definition %type enumerator %type registered_enumerator %type enumerator_list %type statement %type function_body %type block_item %type else_part_opt %type block_item_list_opt %% main: translation_unit { translation_unit = $1->first; } ; /* kludge */ identifier: ID | TYPEDEF_NAME ; identifier_opt: identifier | { $$.name = 0; $$.declarator = 0; } ; /* C99 6.5 expressions */ primary_expression: ID { struct declarator *d; $$ = expr(ex_id, &@1); $$->u.name = $1.name; d = $1.declarator; if(!d) { inputerror(&@1, "undeclared identifier '%s'", $1.name); } else if(d->declaration_specifiers && (d->declaration_specifiers->storage_class_specifiers & SCS_TYPEDEF)) inputerror(&@1, "typedef-name '%s' used in expression", $1); else $$->valuetype = resolve_typedefs(d); } | NUMBER { $$ = expr(ex_number, &@1); $$->u.constant = $1; $$->valuetype = numbertype($$); } | CHARLIT { $$ = stringexpr(ex_char, $1, &@1); } | STRINGLIT { $$ = stringexpr(ex_string, $1, &@1); } | WCHARLIT { $$ = stringexpr(ex_wchar, $1, &@1); } | WSTRINGLIT { $$ = stringexpr(ex_wstring, $1, &@1); } | '(' expression ')' { $$ = paren($2, &@1); } ; postfix_expression: primary_expression | postfix_expression '[' expression ']' { $$ = binary('[', $1, $3, &@2); } | postfix_expression '(' argument_expression_list_opt ')' { $$ = fncallexpr($1, $3, &@2); } | GCC_VA_ARG '(' assignment_expression ',' type_name ')' { $$ = expr(ex_gcc_va_arg, &@2); $$->u.gcc_va_arg.arg = $3; $$->u.gcc_va_arg.type = $5; $$->valuetype = resolve_typedefs($5->declarator_list); } | GCC_EXPECT '(' assignment_expression ',' assignment_expression ')' { /* we can't hack this in as a function as the return type is that of * the LH argument */ $$ = expr(ex_gcc_expect, &@1); /* XXX does the LH arg undergo any implicit conversions? */ $$->u.binary.l = $3; $$->u.binary.r = $5; $$->valuetype = $3->valuetype; } | postfix_expression member_operator identifier { struct expression *e = expr(ex_id, &@2); e->u.name = $3.name; $$ = binary($2, $1, e, &@2); } | postfix_expression incrdecr { $$ = postfix($2, $1, &@2); } | '(' type_name ')' '{' initializer_list comma_opt '}' { $$ = expr(ex_compound_literal, &@1); $$->u.compound_literal.type = $2; $$->u.compound_literal.value = $5->first; declaration_constraints($2, dc_compound_literal); $$->valuetype = resolve_typedefs($2->declarator_list); } ; member_operator: '.' | "->" ; incrdecr: "++" | "--" ; argument_expression_list: assignment_expression { NEW($$); NEW($$->first); $$->first->e = $1; $$->end = &$$->first->next; } | argument_expression_list ',' assignment_expression { struct expression_list *e; NEW(e); e->e = $3; *($$ = $1)->end = e; $$->end = &e->next; } ; argument_expression_list_opt: argument_expression_list { $$ = $1->first; } | { $$ = 0; } ; unary_expression: postfix_expression | unary_operator cast_expression { $$ = prefix($1, $2, &@1); } | "sizeof" unary_expression { $$ = prefix(SIZEOF, $2, &@1); } | "sizeof" '(' type_name ')' { $$ = expr(ex_sizeof_type, &@1); $$->u.type = $3; declaration_constraints($3, dc_sizeof); } ; /* putting sizeof in here would conflict with sizeof(type) */ unary_operator: '&' | '*' | '+' | '-' | '~' | '!' | incrdecr ; cast_expression: unary_expression | '(' type_name ')' cast_expression { $$ = expr(ex_cast, &@1); $$->valuetype = $2->declarator_list; $$->u.cast = $4; declaration_constraints($2, dc_cast); } ; multiplicative_expression: cast_expression | multiplicative_expression multiplicative_operator cast_expression { $$ = binary($2, $1, $3, &@2); } ; multiplicative_operator: '*' | '/' | '%' ; additive_expression: multiplicative_expression | additive_expression additive_operator multiplicative_expression { $$ = binary($2, $1, $3, &@2); } ; additive_operator: '+' | '-' ; shift_expression: additive_expression | shift_expression shift_operator additive_expression { $$ = binary($2, $1, $3, &@2); } ; shift_operator: "<<" | ">>" ; relational_expression: shift_expression | relational_expression relational_operator shift_expression { $$ = binary($2, $1, $3, &@2); } ; relational_operator: '<' | '>' | "<=" | ">=" ; equality_expression: relational_expression | equality_expression equality_operator relational_expression { $$ = binary($2, $1, $3, &@2); } ; equality_operator: "==" | "!=" ; AND_expression: equality_expression | AND_expression '&' equality_expression { $$ = binary($2, $1, $3, &@2); } ; exclusive_OR_expression: AND_expression | exclusive_OR_expression '^' AND_expression { $$ = binary($2, $1, $3, &@2); } ; inclusive_OR_expression: exclusive_OR_expression | inclusive_OR_expression '|' exclusive_OR_expression { $$ = binary($2, $1, $3, &@2); } ; logical_AND_expression: inclusive_OR_expression | logical_AND_expression "&&" inclusive_OR_expression { $$ = binary($2, $1, $3, &@2); } ; logical_OR_expression: logical_AND_expression | logical_OR_expression "||" logical_AND_expression { $$ = binary($2, $1, $3, &@2); } ; conditional_expression: logical_OR_expression | logical_OR_expression '?' expression ':' conditional_expression { $$ = binary('?', $1, binary(':', $3, $5, &@4), &@2); } ; assignment_expression: conditional_expression | unary_expression assignment_operator assignment_expression { $$ = binary($2, $1, $3, &@2); } ; assignment_operator: '=' | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" ; expression: assignment_expression | expression ',' assignment_expression { $$ = binary($2, $1, $3, &@2); } ; expression_opt: expression | { $$ = 0; } ; constant_expression: conditional_expression ; /* C99 6.7 declarations */ declaration: declaration_specifiers init_declarator_list_opt ';' { struct declaration_specifiers *ds = $1; struct declarator *decl = $2; check_top_declaration_specifier(ds); pop_declaration_specifiers(); /* will have already done add_declarator() */ NEW($$); $$->declaration_specifiers = ds; $$->declarator_list = decl; $$->where = @1; /* constraints checked by calling production */ } ; declaration_specifiers: declaration_specifiers_definition { push_declaration_specifiers($1); $$ = $1; } ; declaration_specifiers_definition: storage_class_specifier declaration_specifiers_opt { struct declaration_specifiers ds = { .type_specifiers = 0, .storage_class_specifiers = $1, .type_qualifiers = 0, .function_specifiers = 0, .name = 0, .structure = 0, .enumerators = 0, .type = 0, .enum_compat_type = 0 }; $$ = merge_declaration_specifiers($2, &ds); } | type_specifier declaration_specifiers_opt { $$ = merge_declaration_specifiers($2, $1); } | type_qualifier declaration_specifiers_opt { struct declaration_specifiers ds = { .type_specifiers = 0, .storage_class_specifiers = 0, .type_qualifiers = $1, .function_specifiers = 0, .name = 0, .structure = 0, .enumerators = 0, .type = 0, .enum_compat_type = 0 }; $$ = merge_declaration_specifiers($2, &ds); } | function_specifier declaration_specifiers_opt { struct declaration_specifiers ds = { .type_specifiers = 0, .storage_class_specifiers = 0, .type_qualifiers = 0, .function_specifiers = $1, .name = 0, .structure = 0, .enumerators = 0, .type = 0, .enum_compat_type = 0 }; $$ = merge_declaration_specifiers($2, &ds); } | attribute_specifier declaration_specifiers_opt { $$ = $2; } ; declaration_specifiers_opt: declaration_specifiers_definition | { NEW($$); } ; init_declarator_list_opt: init_declarator_list { $$ = $1->first; } | { $$ = 0; } ; init_declarator_list: attributed_init_declarator { NEW($$); $$->first = $1; $$->end = &$1->next; } | init_declarator_list ',' attribute_specifier_list attributed_init_declarator { $$ = $1; *$$->end = $4; $$->end = &$4->next; } ; attributed_declarator: declarator attribute_specifier_list { $$ = get_declarator($1, &@1); } ; attributed_init_declarator: attributed_declarator { add_declaration($$ = $1); } | attributed_declarator '=' { /* need to do this before we parse the initializer */ add_declaration($1); } initializer { $$ = $1; $$->initializer = $4; } ; /* C99 6.7.1 storage-class specifiers */ storage_class_specifier: "typedef" | "extern" | "static" | "auto" | "register" ; /* C99 6.7.2 type specifiers */ type_specifier: basic_type_specifier { NEW($$); $$->type_specifiers = $1; } | struct_or_union_specifier { $$ = $1; } | enum_specifier | TYPEDEF_NAME { NEW($$); $$->type_specifiers = TS_TYPEDEF; $$->name = $1.name; $$->type = $1.declarator; } ; basic_type_specifier: "void" | "char" | "short" | "int" | "long" | "float" | "double" | "signed" | "unsigned" | "_Bool" | "_Complex" | "_Imaginary" | GCC_VA_LIST ; /* C99 6.2.7.1 structure and union specifiers */ /* shift/reduce conflict between ID and ID '{' resolved by shifting (which is right) */ struct_or_union_specifier: struct_or_union identifier_opt '{' struct_declaration_list '}' attribute_specifier_list { NEW($$); $$->type_specifiers = $1 | TS_DEFINITION; $$->name = $2.name; $$->structure = $4->first; } | struct_or_union identifier { NEW($$); $$->type_specifiers = $1; $$->name = $2.name; } | struct_or_union { /* shift/reduce conflict */ inputerror(&@1, "structure must have a tag or a definition"); NEW($$); $$->type_specifiers = $1; } ; struct_or_union: "struct" attribute_specifier_list { $$ = $1; } | "union" attribute_specifier_list { $$ = $1; } ; struct_declaration_list: struct_declaration { NEW($$); $$->first = $1; $$->end = &$1->next; } | struct_declaration_list struct_declaration { $$ = $1; *$$->end = $2; $$->end = &$2->next; } ; struct_declaration: specifier_qualifier_list struct_declarator_list ';' { check_top_declaration_specifier($1); pop_declaration_specifiers(); NEW($$); $$->declaration_specifiers = $1; $$->declarator_list = $2->first; declaration_constraints($$, dc_struct_member); /* XXX perhaps add to a dictionary of struct/union declarations? */ $$->where = @1; } ; specifier_qualifier_list: specifier_qualifier_list_definition { push_declaration_specifiers($1); $$ = $1; } ; specifier_qualifier_list_definition: type_specifier specifier_qualifier_list_opt { $$ = merge_declaration_specifiers($2, $1); } | type_qualifier specifier_qualifier_list_opt { struct declaration_specifiers ds = { .type_specifiers = 0, .storage_class_specifiers = 0, .type_qualifiers = $1, .function_specifiers = 0, .name = 0, .structure = 0, .enumerators = 0, .type = 0, .enum_compat_type = 0 }; $$ = merge_declaration_specifiers($2, &ds); } | storage_class_specifier specifier_qualifier_list_opt { inputerror(&@1, "storage class specifiers not allowed here"); $$ = $2; } | function_specifier specifier_qualifier_list_opt { inputerror(&@1, "function specifiers not allowed here"); $$ = $2; } | attribute_specifier specifier_qualifier_list_opt { $$ = $2; } ; specifier_qualifier_list_opt: specifier_qualifier_list_definition | { NEW($$); } ; struct_declarator_list: struct_declarator attribute_specifier_list { NEW($$); $$->first = $1; $$->end = &$1->next; } | struct_declarator_list ',' attribute_specifier_list struct_declarator attribute_specifier_list { $$ = $1; *$$->end = $4; $$->end = &$4->next; } ; struct_declarator: attributed_declarator | declarator_opt ':' constant_expression { if(!($$ = $1)) { NEW($$); $$->declaration_specifiers = top_declaration_specifiers(); } $$->bits = $3; $$->where = @2; } ; /* C99 6.7.2.2 enumeration specifiers */ enum_specifier: enum identifier_opt '{' enumerator_list comma_opt '}' attribute_specifier_list { NEW($$); $$->type_specifiers = TS_ENUM | TS_DEFINITION; $$->name = $2.name; $$->enumerators = $4->first; $$->enum_compat_type = TS_INT; /* XXX */ if($5) inputwarning(&@5, warn_compat, "enumerator-list trailing comma not supported in C89 or C++"); /* XXX pick type and fix up the declarators of all the enumerators */ } | enum identifier { NEW($$); $$->type_specifiers = TS_ENUM; $$->name = $2.name; } ; enum: "enum" attribute_specifier_list ; enumerator_list: registered_enumerator { NEW($$); $$->first = $1; $$->end = &$1->next; } | enumerator_list ',' registered_enumerator { *($$ = $1)->end = $3; $$->end = &$3->next; } ; registered_enumerator: enumerator { struct declarator *d; NEW(d); d->name = $1->name; d->where = $$->where; NEW(d->declaration_specifiers); d->declaration_specifiers->type_specifiers = TS_INT; add_declaration(d); $$ = $1; $$->declarator = d; } ; enumerator: identifier { NEW($$); $$->name = $1.name; $$->where = @1; } | identifier '=' constant_expression { NEW($$); $$->name = $1.name; $$->value = $3; $$->where = @1; } ; /* C99 6.7.3 type qualifiers */ type_qualifier: "const" | "restrict" | "volatile" ; /* C99 6.7.4 function specifiers */ function_specifier: "inline" ; /* C99 6.7.5 declarators */ declarator_opt: declarator { $$ = get_declarator($1, &@1); } | { $$ = 0; } ; declarator: pointer direct_declarator { $$ = $2; *$$->end = $1->first; $$->end = $1->end; } | direct_declarator ; direct_declarator: ID { NEW($$); $$->end = &$$->first; $$->name = $1.name; /* XXX warn about redeclaration */ } | '(' attribute_specifier_list declarator ')' { $$ = $3; } | direct_declarator '[' array_specifiers array_size ']' { struct declarator_type *t; NEW(t); $$ = $1; t->type = dt_array; t->type_qualifiers = $3->type_qualifiers; t->storage_class_specifiers = $3->storage_class_specifiers; if($4 == &expr_star) { t->u.array.size = 0; t->u.array.flags = AF_STAR; } else t->u.array.size = $4; t->where = @2; *$$->end = t; $$->end = &t->next; } | direct_declarator '(' ID_list_opt ')' { struct declarator_type *t; NEW(t); $$ = $1; t->type = dt_old_function; t->u.old_function.args = $3; t->where = @2; *$$->end = t; $$->end = &t->next; warn_old_style_function(&@2); } | direct_declarator '(' parameter_list variadic ')' { struct declarator_type *t; NEW(t); $$ = $1; t->type = dt_function; t->u.function.args = $3->first; t->u.function.variadic = $4; t->where = @2; *$$->end = t; $$->end = &t->next; } ; array_size: assignment_expression | '*' { $$ = &expr_star; } | { $$ = 0; } ; array_specifiers: type_qualifier_list_opt { NEW($$); } | "static" type_qualifier_list_opt { NEW($$); $$->type_qualifiers = $2; $$->storage_class_specifiers = SCS_STATIC; } | type_qualifier_list "static" { NEW($$); $$->type_qualifiers = $1; $$->storage_class_specifiers = SCS_STATIC; } ; variadic: ',' "..." { $$ = 1; } | { $$ = 0; } ; pointer_opt: pointer | { NEW($$); $$->end = &$$->first; } ; pointer: '*' type_qualifier_list_opt { struct declarator_type *t; NEW(t); t->type = dt_pointer; t->type_qualifiers = $2; t->where = @1; NEW($$); $$->first = t; $$->end = &t->next; } | '*' type_qualifier_list_opt pointer { struct declarator_type *t; NEW(t); t->type = dt_pointer; t->type_qualifiers = $2; t->where = @1; $$ = $3; *$$->end = t; $$->end = &t->next; } ; type_qualifier_list: type_qualifier | type_qualifier_list type_qualifier { $$ = $1 | $2; } | type_qualifier_list attribute_specifier ; type_qualifier_list_opt: type_qualifier_list | { $$ = 0; } ; parameter_list: parameter_declaration { NEW($$); $$->first = $1; $$->end = &$1->next; } | parameter_list ',' parameter_declaration { *$$->end = $3; $$->end = &$3->next; } ; parameter_declaration_specifiers: declaration_specifiers { $$ = $1; } ; /* declaration_constraints will be checked recursively once we know what * context we're really in (i.e. function-definition or not) */ parameter_declaration: parameter_declaration_specifiers declarator { struct declarator *d = get_declarator($2, &@2); check_top_declaration_specifier($1); pop_declaration_specifiers(); NEW($$); $$->declaration_specifiers = $1; $$->declarator_list = d; $$->where = @1; } | parameter_declaration_specifiers abstract_declarator_opt { struct declarator *d = get_declarator($2, $2->first ? &@2 : &@1); check_top_declaration_specifier($1); pop_declaration_specifiers(); NEW($$); $$->declaration_specifiers = $1; $$->declarator_list = d; $$->where = @1; } ; /* an old-style function declaration where all the parameter names match * typedef names is ambiguous - it could be a parameter list where all the * declarators are abstract. We resolve the ambiguity by assumiong that it is * the latter, modern form. */ ID_list_opt: ID_list { $$ = $1->first; } | { $$ = 0; } ; ID_list: ID { struct identifier_list *i; NEW(i); i->id = $1.name; /* XXX warn about redeclaration */ NEW($$); $$->first = i; $$->end = &i->next; } | ID_list ',' ID { struct identifier_list *i; NEW(i); i->id = $3.name; /* XXX warn about redeclaration */ *($$ = $1)->end = i; $$->end = &i->next; } ; /* C99 6.7.6 type names */ type_name: specifier_qualifier_list abstract_declarator_opt { struct declarator *d = get_declarator($2, $2->first ? &@2 : &@1); check_top_declaration_specifier($1); pop_declaration_specifiers(); NEW($$); $$->declaration_specifiers = $1; $$->declarator_list = d; $$->where = @1; /* declaration_constraints checked by calling production */ } ; direct_abstract_declarator: '(' attribute_specifier_list abstract_declarator ')' { $$ = $3; } | direct_abstract_declarator_opt '[' array_specifiers array_size ']' { struct declarator_type *t; NEW(t); $$ = $1; t->type = dt_array; t->type_qualifiers = $3->type_qualifiers; t->storage_class_specifiers = $3->storage_class_specifiers; if($4 == &expr_star) { t->u.array.size = 0; t->u.array.flags = AF_STAR; } else t->u.array.size = $4; t->where = @2; *$$->end = t; $$->end = &t->next; } | direct_abstract_declarator_opt '(' ')' { struct declarator_type *t; NEW(t); $$ = $1; t->type = dt_old_function; t->u.old_function.args = 0; t->where = @2; *$$->end = t; $$->end = &t->next; warn_old_style_function(&@2); } | direct_abstract_declarator_opt '(' parameter_list variadic ')' { struct declarator_type *t; NEW(t); $$ = $1; t->type = dt_function; t->u.function.args = $3->first; t->u.function.variadic = $4; t->where = @2; *$$->end = t; $$->end = &t->next; } ; direct_abstract_declarator_opt: direct_abstract_declarator | { NEW($$); $$->end = &$$->first; @$ = (struct location){path, line}; } ; abstract_declarator_opt: abstract_declarator | { NEW($$); $$->end = &$$->first; @$ = (struct location){path, line}; } ; abstract_declarator: pointer | pointer_opt direct_abstract_declarator { $$ = $2; *$$->end = $1->first; $$->end = $1->end; } ; /* C99 6.7.8 initializers */ initializer: assignment_expression { NEW($$); $$->type = in_expr; $$->u.expr = $1; } | '{' initializer_list comma_opt '}' { NEW($$); $$->type = in_list; $$->u.list = $2->first; } ; comma_opt: ',' { $$ = 1; } | { $$ = 0; } ; initializer_list: designated_initializer { NEW($$); $$->first = $1; $$->end = &$1->next; } | initializer_list ',' designated_initializer { *($$ = $1)->end = $3; $$->end = &$3->next; } ; designated_initializer: designator_list '=' initializer { ($$ = $3)->designator = $1->first; $$->syntax = des_c99; } | designator initializer { ($$ = $2)->designator = $1; $$->syntax = des_gcc_raw; inputwarning(&@1, warn_compat, "non-portable GNU C initializer designator"); } | ID ':' initializer { struct designator *d; NEW(d); d->type = des_field; d->u.name = $1.name; d->where = @1; ($$ = $3)->designator = d; $$->syntax = des_gcc_colon; inputwarning(&@1, warn_compat, "non-portable GNU C initializer designator"); } | initializer ; designator_list: designator { NEW($$); $$->first = $1; $$->end = &$1->next; } | designator_list designator { *($$ = $1)->end = $2; $$->end = &$2->next; } ; designator: '[' constant_expression ']' { NEW($$); $$->type = des_expr; $$->u.expr = $2; $$->where = @1; } | '.' identifier { NEW($$); $$->type = des_field; $$->u.name = $2.name; $$->where = @1; } ; /* C99 6.8 statements and blocks */ statement: identifier ':' attribute_specifier_list statement { NEW($$); $$->type = st_label; $$->u.label.label = $1.name; /* XXX warn about redeclaration */ $$->u.label.body = $4; $$->where = @1; } | "case" constant_expression ':' statement { NEW($$); $$->type = st_case; $$->u.case_.value = $2; $$->u.case_.body = $4; $$->where = @1; } | "default" ':' statement { NEW($$); $$->type = st_default; $$->u.default_ = $3; $$->where = @1; } | "if" '(' expression ')' statement else_part_opt { NEW($$); $$->type = st_if; $$->u.if_.cond = $3; $$->u.if_.true = $5; $$->u.if_.false = $6; $$->where = @1; } | "switch" '(' expression ')' statement { NEW($$); $$->type = st_switch; $$->u.switch_.cond = $3; $$->u.switch_.body = $5; $$->where = @1; } | expression_opt ';' { NEW($$); $$->type = st_expression; $$->u.expression = $1; $$->where = $1 ? @1 : @2; } | "while" '(' expression ')' statement { NEW($$); $$->type = st_while; $$->u.while_.cond = $3; $$->u.while_.body = $5; $$->where = @1; } | "do" statement "while" '(' expression ')' ';' { NEW($$); $$->type = st_do; $$->u.while_.cond = $5; $$->u.while_.body = $2; $$->where = @1; } | "for" '(' expression_opt ';' expression_opt ';' expression_opt ')' statement { NEW($$); $$->type = st_for; $$->u.for_.init = $3; $$->u.for_.cond = $5; $$->u.for_.iter = $7; $$->u.for_.body = $9; $$->where = @1; } | "for" '(' { enter_scope(); } declaration expression_opt ';' expression_opt ')' statement { NEW($$); $$->type = st_for_declaration; $$->u.for_declaration.init = $4; $$->u.for_declaration.cond = $5; $$->u.for_declaration.iter = $7; $$->u.for_declaration.body = $9; $$->where = @1; exit_scope(); } | "goto" identifier ';' { NEW($$); $$->type = st_goto; $$->u.goto_ = $2.name; $$->where = @1; } | "continue" ';' { NEW($$); $$->type = st_continue; $$->where = @1; } | "break" ';' { NEW($$); $$->type = st_break; $$->where = @1; } | "return" expression_opt ';' { NEW($$); $$->type = st_return; $$->u.expression = $2; $$->where = @1; } | '{' { enter_scope(); } block_item_list_opt '}' { NEW($$); $$->type = st_compound; $$->u.compound.body = $3->first; $$->where = @1; $$->u.compound.endwhere = @4; /*$$->u.compound.scope = scope;*/ exit_scope(); } ; /* scope handling for function bodies is done by function_definition */ function_body: '{' block_item_list_opt '}' { NEW($$); $$->type = st_compound; $$->u.compound.body = $2->first; $$->where = @1; $$->u.compound.endwhere = @3; /*$$->u.compound.scope = scope;*/ } ; block_item_list_opt: block_item_list_opt block_item { *($$ = $1)->end = $2; $$->end = &$2->next; } | { NEW($$); $$->end = &$$->first; } ; block_item: declaration { NEW($$); $$->type = st_declaration; $$->u.declaration = $1; $$->where = @1; declaration_constraints($1, dc_block_scope); } | statement ; /* shift/reduce conflict here */ else_part_opt: "else" statement { $$ = $2; } | { $$ = 0; } ; /* C99 6.9 external definitions */ translation_unit: external_declaration { NEW($$); translation_unit = $$->first = $1; $$->end = &$1->next; } | translation_unit external_declaration { $$ = $1; *$$->end = $2; $$->end = &$2->next; } | translation_unit ';' { inputerror(&@2, "redundant semicolon at top level"); } ; external_declaration: declaration { NEW($$); $$->type = ed_declaration; $$->u.declaration = $1; declaration_constraints($1, dc_file_scope); } | function_definition { NEW($$); $$->type = ed_function_definition; $$->u.function_definition = $1; /* constraints checked in function_definition (i.e. before errors * from the function body) */ } ; /* C99 6.9.1 function definitions */ function_definition: declaration_specifiers attributed_declarator { check_top_declaration_specifier($1); pop_declaration_specifiers(); declarator_constraints($1, $2, dc_function_definition, 0); NEW($$); $$->declaration_specifiers = $1; $$->declarator_list = $2; $$->where = @1; add_declaration($2); enter_scope(); { struct declarator *d; NEW(d); NEW(d->declaration_specifiers); NEW(d->declarator_type); d->name = xstrdup("__PRETTY_FUNCTION__"); d->declaration_specifiers->type_qualifiers = TQ_CONST; d->declaration_specifiers->type_specifiers = TS_CHAR; d->declarator_type->type = dt_pointer; add_declaration(d); } /* we had better be in the right scope before parsing the declaration * list for old style functions */ } declaration_list_opt { struct declaration *decl; struct declarator *d = 0; /* quieten compiler */ /* inject function args into scope */ if($2->declarator_type) switch($2->declarator_type->type) { case dt_function: if(!is_void_args($2->declarator_type)) { for(decl = $2->declarator_type->u.function.args; decl; decl = decl->next) { decl->declarator_list->flags |= DF_PARAM; add_declaration(decl->declarator_list); } } if($4->first) inputerror(&@4, "declaration-list not required for new-style function definitions"); break; case dt_old_function: if($2->declarator_type->u.old_function.args) { struct identifier_list *i; for(i = $2->declarator_type->u.old_function.args; i; i = i->next) { for(decl = $4->first; decl; decl = decl->next) for(d = decl->declarator_list; d; d = d->next) if(!strcmp(d->name, i->id)) break; if(d) { d->flags |= DF_PARAM; add_declaration(d); } else { /* default to int */ NEW(d); d->name = i->id; NEW(d->declaration_specifiers); d->declaration_specifiers->type_specifiers = TS_INT; d->flags = DF_PARAM; add_declaration(d); } } } /* check that all the identifiers in the list are actually * parameters */ for(decl = $4->first; decl; decl = decl->next) for(d = decl->declarator_list; d; d = d->next) { struct identifier_list *i; for(i = $2->declarator_type->u.old_function.args; i; i = i->next) if(!strcmp(d->name, i->id)) break; if(!i) inputerror(&d->where, "'%s' is not a parameter", d->name); } break; default: /* quieten compiler */ break; } } function_body { NEW($$); $$->declaration = $3; $$->args = $4->first; $$->body = $6; exit_scope(); } ; declaration_list_opt: declaration_list_opt declaration { *($$ = $1)->end = $2; $$->end = &$2->next; } | { NEW($$); $$->end = &$$->first; } ; /* gcc.info attribute syntax */ attribute_specifier_list: attribute_specifier_list attribute_specifier | ; attribute_specifier: ATTRIBUTE '(' '(' attribute_list ')' ')' { static int attr_warning; if(!attr_warning++) inputwarning(&@1, warn_compat, "GNU C attributes are ignored"); } ; attribute_list: attribute_list attribute | ; attribute: CONST { } | identifier { } | identifier '(' { suppress_errors(); } argument_expression_list_opt { restore_errors(); } ')' { } ; %% /* Local Variables: c-basic-offset:2 comment-column:40 fill-column:79 End: */