/* -*- Mode: C -*- * * CForall Grammar Version 1.0, Copyright (C) Peter A. Buhr 2001 -- Permission is granted to copy this * grammar and to use it within software systems. THIS GRAMMAR IS PROVIDED "AS IS" AND WITHOUT * ANY EXPRESS OR IMPLIED WARRANTIES. * * cfa.y -- * * Author : Peter A. Buhr * Created On : Sat Sep 1 20:22:55 2001 * Last Modified By : Peter A. Buhr * Last Modified On : Sat Jan 17 09:23:45 2015 * Update Count : 908 */ /* This grammar is based on the ANSI99/11 C grammar, specifically parts of EXPRESSION and STATEMENTS, and on the C grammar by James A. Roskind, specifically parts of DECLARATIONS and EXTERNAL DEFINITIONS. While parts have been copied, important changes have been made in all sections; these changes are sufficient to constitute a new grammar. In particular, this grammar attempts to be more syntactically precise, i.e., it parses less incorrect language syntax that must be subsequently rejected by semantic checks. Nevertheless, there are still several semantic checks required and many are noted in the grammar. Finally, the grammar is extended with GCC and CFA language extensions. */ /* Acknowledgments to Richard Bilson, Glen Ditchfield, and Rodolfo Gabriel Esteves who all helped when I got stuck with the grammar. */ /* The root language for this grammar is ANSI99/11 C. All of ANSI99/11 is parsed, except for: 1. designation with '=' (use ':' instead) Most of the syntactic extensions from ANSI90 to ANSI11 C are marked with the comment "C99/C11". This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions, except for: 1. nested functions 2. generalized lvalues 3. designation with and without '=' (use ':' instead) 4. attributes not allowed in parenthesis of declarator All of the syntactic extensions for GCC C are marked with the comment "GCC". The second extensions are for Cforall (CFA), which fixes several of C's outstanding problems and extends C with many modern language concepts. All of the syntactic extensions for CFA C are marked with the comment "CFA". As noted above, there is one unreconcileable parsing problem between C99 and CFA with respect to designators; this is discussed in detail before the "designation" grammar rule. */ %{ #define YYDEBUG_LEXER_TEXT (yylval) /* lexer loads this up each time */ #define YYDEBUG 1 /* get the pretty debugging code to compile*/ #undef __GNUC_MINOR__ #include #include #include "TypedefTable.h" #include "lex.h" #include "ParseNode.h" #include "LinkageSpec.h" DeclarationNode *theTree = 0; /* the resulting parse tree */ LinkageSpec::Type linkage = LinkageSpec::Cforall; std::stack< LinkageSpec::Type > linkageStack; TypedefTable typedefTable; %} /************************* TERMINAL TOKENS ********************************/ /* keywords */ %token TYPEDEF %token AUTO EXTERN REGISTER STATIC %token INLINE /* C99 */ %token FORTRAN /* C99, extension ISO/IEC 9899:1999 Section J.5.9(1) */ %token CONST VOLATILE %token RESTRICT /* C99 */ %token FORALL LVALUE /* CFA */ %token VOID CHAR SHORT INT LONG FLOAT DOUBLE SIGNED UNSIGNED %token BOOL COMPLEX IMAGINARY /* C99 */ %token TYPEOF LABEL /* GCC */ %token ENUM STRUCT UNION %token TYPE FTYPE DTYPE CONTEXT /* CFA */ %token SIZEOF %token ATTRIBUTE EXTENSION /* GCC */ %token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR BREAK CONTINUE GOTO RETURN %token CHOOSE FALLTHRU TRY CATCH FINALLY THROW /* CFA */ %token ASM /* C99, extension ISO/IEC 9899:1999 Section J.5.10(1) */ %token ALIGNAS ALIGNOF ATOMIC GENERIC NORETURN STATICASSERT THREADLOCAL /* C11 */ /* names and constants: lexer differentiates between identifier and typedef names */ %token IDENTIFIER QUOTED_IDENTIFIER TYPEDEFname TYPEGENname %token ATTR_IDENTIFIER ATTR_TYPEDEFname ATTR_TYPEGENname %token INTEGERconstant FLOATINGconstant CHARACTERconstant STRINGliteral %token ZERO ONE /* CFA */ /* multi-character operators */ %token ARROW /* -> */ %token ICR DECR /* ++ -- */ %token LS RS /* << >> */ %token LE GE EQ NE /* <= >= == != */ %token ANDAND OROR /* && || */ %token ELLIPSIS /* ... */ %token MULTassign DIVassign MODassign /* *= /= %= */ %token PLUSassign MINUSassign /* += -= */ %token LSassign RSassign /* <<= >>= */ %token ANDassign ERassign ORassign /* &= ^= |= */ /* Types declaration */ %union { Token tok; ParseNode *pn; ExpressionNode *en; DeclarationNode *decl; DeclarationNode::TyCon aggKey; DeclarationNode::TypeClass tclass; StatementNode *sn; ConstantNode *constant; InitializerNode *in; } %type zero_one identifier no_attr_identifier no_01_identifier %type identifier_or_typedef_name no_attr_identifier_or_typedef_name no_01_identifier_or_typedef_name %type string_literal_list /* expressions */ %type constant %type tuple tuple_expression_list %type unary_operator assignment_operator %type primary_expression postfix_expression unary_expression %type cast_expression multiplicative_expression additive_expression shift_expression %type relational_expression equality_expression AND_expression exclusive_OR_expression %type inclusive_OR_expression logical_AND_expression logical_OR_expression conditional_expression %type constant_expression assignment_expression assignment_expression_opt %type comma_expression comma_expression_opt %type argument_expression_list argument_expression for_control_expression assignment_opt %type subrange /* statements */ %type labeled_statement compound_statement expression_statement selection_statement %type iteration_statement jump_statement exception_statement asm_statement %type fall_through_opt fall_through %type statement statement_list %type block_item_list block_item %type case_clause %type case_value case_value_list %type case_label case_label_list %type switch_clause_list_opt switch_clause_list choose_clause_list_opt choose_clause_list %type handler_list handler_clause finally_clause /* declarations */ %type abstract_array abstract_declarator abstract_function abstract_parameter_array %type abstract_parameter_declaration abstract_parameter_declarator abstract_parameter_function %type abstract_parameter_ptr abstract_ptr %type aggregate_key %type aggregate_name %type array_dimension array_parameter_1st_dimension array_parameter_dimension multi_array_dimension %type assertion assertion_list_opt %type bit_subrange_size_opt bit_subrange_size %type basic_declaration_specifier basic_type_name basic_type_specifier direct_type_name indirect_type_name %type context_declaration context_declaration_list context_declaring_list context_specifier %type declaration declaration_list declaration_list_opt declaration_qualifier_list %type declaration_specifier declarator declaring_list %type elaborated_type_name %type enumerator_list enum_name %type enumerator_value_opt %type exception_declaration external_definition external_definition_list external_definition_list_opt %type field_declaration field_declaration_list field_declarator field_declaring_list %type field field_list %type function_array function_declarator function_definition function_no_ptr function_ptr %type identifier_parameter_array identifier_parameter_declarator identifier_parameter_function %type identifier_parameter_ptr identifier_list %type new_abstract_array new_abstract_declarator_no_tuple new_abstract_declarator_tuple %type new_abstract_function new_abstract_parameter_declaration new_abstract_parameter_list %type new_abstract_ptr new_abstract_tuple %type new_array_parameter_1st_dimension %type new_context_declaring_list new_declaration new_field_declaring_list %type new_function_declaration new_function_return new_function_specifier %type new_identifier_parameter_array new_identifier_parameter_declarator_no_tuple %type new_identifier_parameter_declarator_tuple new_identifier_parameter_ptr %type new_parameter_declaration new_parameter_list new_parameter_type_list new_parameter_type_list_opt %type new_typedef_declaration new_variable_declaration new_variable_specifier %type old_declaration old_declaration_list old_declaration_list_opt old_function_array %type old_function_declarator old_function_no_ptr old_function_ptr %type parameter_declaration parameter_list parameter_type_list %type parameter_type_list_opt %type paren_identifier paren_typedef %type storage_class storage_class_name storage_class_list %type sue_declaration_specifier sue_type_specifier %type type_class %type type_declarator type_declarator_name type_declaring_list %type typedef typedef_array typedef_declaration typedef_declaration_specifier typedef_expression %type typedef_function typedef_parameter_array typedef_parameter_function typedef_parameter_ptr %type typedef_parameter_redeclarator typedef_ptr typedef_redeclarator typedef_type_specifier %type typegen_declaration_specifier typegen_type_specifier %type type_name type_name_no_function %type type_parameter type_parameter_list %type type_name_list %type type_qualifier type_qualifier_name type_qualifier_list type_qualifier_list_opt type_specifier %type variable_abstract_array variable_abstract_declarator variable_abstract_function %type variable_abstract_ptr variable_array variable_declarator variable_function variable_ptr /* initializers */ %type initializer initializer_list initializer_opt /* designators */ %type designator designator_list designation /* Handle single shift/reduce conflict for dangling else by shifting the ELSE token. For example, this string is ambiguous: .---------. matches IF '(' comma_expression ')' statement if ( C ) S1 else S2 `-----------------' matches IF '(' comma_expression ')' statement ELSE statement */ %nonassoc THEN /* rule precedence for IF '(' comma_expression ')' statement */ %nonassoc ELSE /* token precedence for start of else clause in IF statement */ %start translation_unit /* parse-tree root */ %% /************************* Namespace Management ********************************/ /* The grammar in the ANSI C standard is not strictly context-free, since it relies upon the distinct terminal symbols "identifier" and "TYPEDEFname" that are lexically identical. While it is possible to write a purely context-free grammar, such a grammar would obscure the relationship between syntactic and semantic constructs. Hence, this grammar uses the ANSI style. Cforall compounds this problem by introducing type names local to the scope of a declaration (for instance, those introduced through "forall" qualifiers), and by introducing "type generators" -- parametrized types. This latter type name creates a third class of identifiers that must be distinguished by the scanner. Since the scanner cannot distinguish among the different classes of identifiers without some context information, it accesses a data structure (the TypedefTable) to allow classification of an identifier that it has just read. Semantic actions during the parser update this data structure when the class of identifiers change. Because the Cforall language is block-scoped, there is the possibility that an identifier can change its class in a local scope; it must revert to its original class at the end of the block. Since type names can be local to a particular declaration, each declaration is itself a scope. This requires distinguishing between type names that are local to the current declaration scope and those that persist past the end of the declaration (i.e., names defined in "typedef" or "type" declarations). The non-terminals "push" and "pop" derive the empty string; their only use is to denote the opening and closing of scopes. Every push must have a matching pop, although it is regrettable the matching pairs do not always occur within the same rule. These non-terminals may appear in more contexts than strictly necessary from a semantic point of view. Unfortunately, these extra rules are necessary to prevent parsing conflicts -- the parser may not have enough context and look-ahead information to decide whether a new scope is necessary, so the effect of these extra rules is to open a new scope unconditionally. As the grammar evolves, it may be neccesary to add or move around "push" and "pop" nonterminals to resolve conflicts of this sort. */ push: { typedefTable.enterScope(); } ; pop: { typedefTable.leaveScope(); } ; /************************* CONSTANTS ********************************/ constant: /* ENUMERATIONconstant is not included here; it is treated as a variable with type "enumeration constant". */ INTEGERconstant { $$ = new ConstantNode(ConstantNode::Integer, $1); } | FLOATINGconstant { $$ = new ConstantNode(ConstantNode::Float, $1); } | CHARACTERconstant { $$ = new ConstantNode(ConstantNode::Character, $1); } ; identifier: IDENTIFIER | ATTR_IDENTIFIER /* CFA */ | zero_one /* CFA */ ; no_01_identifier: IDENTIFIER | ATTR_IDENTIFIER /* CFA */ ; no_attr_identifier: IDENTIFIER ; zero_one: /* CFA */ ZERO | ONE ; string_literal_list: /* juxtaposed strings are concatenated */ STRINGliteral { $$ = new ConstantNode(ConstantNode::String, $1); } | string_literal_list STRINGliteral { $$ = $1->append( $2 ); } ; /************************* EXPRESSIONS ********************************/ primary_expression: IDENTIFIER /* typedef name cannot be used as a variable name */ { $$ = new VarRefNode($1); } | zero_one { $$ = new VarRefNode($1); } | constant { $$ = $1; } | string_literal_list { $$ = $1; } | '(' comma_expression ')' { $$ = $2; } | '(' compound_statement ')' /* GCC, lambda expression */ { $$ = new ValofExprNode($2); } ; postfix_expression: primary_expression | postfix_expression '[' push assignment_expression pop ']' /* CFA, comma_expression disallowed in the context because it results in a commom user error: subscripting a matrix with x[i,j] instead of x[i][j]. While this change is not backwards compatible, there seems to be little advantage to this feature and many disadvantages. It is possible to write x[(i,j)] in CFA, which is equivalent to the old x[i,j]. */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Index), $1, $4); } | postfix_expression '(' argument_expression_list ')' { $$ = new CompositeExprNode($1, $3); } | postfix_expression '.' no_attr_identifier { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::FieldSel), $1, new VarRefNode($3)); } | postfix_expression '.' '[' push field_list pop ']' /* CFA, tuple field selector */ | postfix_expression ARROW no_attr_identifier { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::PFieldSel), $1, new VarRefNode($3)); } | postfix_expression ARROW '[' push field_list pop ']' /* CFA, tuple field selector */ | postfix_expression ICR { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::IncrPost), $1); } | postfix_expression DECR { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::DecrPost), $1); } /* GCC has priority: cast_expression */ | '(' type_name_no_function ')' '{' initializer_list comma_opt '}' /* C99 */ { $$ = 0; } ; argument_expression_list: argument_expression | argument_expression_list ',' argument_expression { $$ = (ExpressionNode *)($1->set_link($3)); } ; argument_expression: /* empty */ /* use default argument */ { $$ = 0; } | assignment_expression | no_attr_identifier ':' assignment_expression { $$ = $3->set_asArgName($1); } /* Only a list of no_attr_identifier_or_typedef_name is allowed in this context. However, there is insufficient look ahead to distinguish between this list of parameter names and a tuple, so the tuple form must be used with an appropriate semantic check. */ | '[' push assignment_expression pop ']' ':' assignment_expression { $$ = $7->set_asArgName($3); } | '[' push assignment_expression ',' tuple_expression_list pop ']' ':' assignment_expression { $$ = $9->set_asArgName(new CompositeExprNode( new OperatorNode( OperatorNode::TupleC ), (ExpressionNode *)$3->set_link( flattenCommas( $5 )))); } ; field_list: /* CFA, tuple field selector */ field | field_list ',' field { $$ = (ExpressionNode *)$1->set_link( $3 ); } ; field: /* CFA, tuple field selector */ no_attr_identifier { $$ = new VarRefNode( $1 ); } | no_attr_identifier '.' field { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::FieldSel), new VarRefNode( $1 ), $3); } | no_attr_identifier '.' '[' push field_list pop ']' { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::FieldSel), new VarRefNode( $1 ), $5); } | no_attr_identifier ARROW field { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::PFieldSel), new VarRefNode( $1 ), $3); } | no_attr_identifier ARROW '[' push field_list pop ']' { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::PFieldSel), new VarRefNode( $1 ), $5); } ; unary_expression: postfix_expression | ICR unary_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Incr), $2); } | DECR unary_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Decr), $2); } | EXTENSION cast_expression /* GCC */ { $$ = $2; } | unary_operator cast_expression { $$ = new CompositeExprNode($1, $2); } | '!' cast_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Neg), $2); } | '*' cast_expression /* CFA */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::PointTo), $2); } /* '*' is is separated from unary_operator because of shift/reduce conflict in: { * X; } // dereference X { * int X; } // CFA declaration of pointer to int '&' must be moved here if C++ reference variables are supported. */ | SIZEOF unary_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::SizeOf), $2); } | SIZEOF '(' type_name_no_function ')' { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::SizeOf), new TypeValueNode($3)); } | ATTR_IDENTIFIER { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Attr), new VarRefNode($1)); } | ATTR_IDENTIFIER '(' type_name ')' { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Attr), new VarRefNode($1), new TypeValueNode($3)); } | ATTR_IDENTIFIER '(' argument_expression ')' { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Attr), new VarRefNode($1), $3); } | ALIGNOF unary_expression /* GCC, variable alignment */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::AlignOf), $2); } | ALIGNOF '(' type_name_no_function ')' /* GCC, type alignment */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::AlignOf), new TypeValueNode($3)); } | ANDAND no_attr_identifier /* GCC, address of label */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::LabelAddress), new VarRefNode($2, true)); } ; unary_operator: '&' { $$ = new OperatorNode(OperatorNode::AddressOf); } | '+' { $$ = new OperatorNode(OperatorNode::UnPlus); } | '-' { $$ = new OperatorNode(OperatorNode::UnMinus); } | '~' { $$ = new OperatorNode(OperatorNode::BitNeg); } ; cast_expression: unary_expression | '(' type_name_no_function ')' cast_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Cast), new TypeValueNode($2), $4); } | '(' type_name_no_function ')' tuple { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Cast), new TypeValueNode($2), $4); } ; multiplicative_expression: cast_expression | multiplicative_expression '*' cast_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Mul),$1,$3); } | multiplicative_expression '/' cast_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Div),$1,$3); } | multiplicative_expression '%' cast_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Mod),$1,$3); } ; additive_expression: multiplicative_expression | additive_expression '+' multiplicative_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Plus),$1,$3); } | additive_expression '-' multiplicative_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Minus),$1,$3); } ; shift_expression: additive_expression | shift_expression LS additive_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::LShift),$1,$3); } | shift_expression RS additive_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::RShift),$1,$3); } ; relational_expression: shift_expression | relational_expression '<' shift_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::LThan),$1,$3); } | relational_expression '>' shift_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::GThan),$1,$3); } | relational_expression LE shift_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::LEThan),$1,$3); } | relational_expression GE shift_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::GEThan),$1,$3); } ; equality_expression: relational_expression | equality_expression EQ relational_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Eq), $1, $3); } | equality_expression NE relational_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Neq), $1, $3); } ; AND_expression: equality_expression | AND_expression '&' equality_expression { $$ =new CompositeExprNode(new OperatorNode(OperatorNode::BitAnd), $1, $3); } ; exclusive_OR_expression: AND_expression | exclusive_OR_expression '^' AND_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Xor), $1, $3); } ; inclusive_OR_expression: exclusive_OR_expression | inclusive_OR_expression '|' exclusive_OR_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::BitOr), $1, $3); } ; logical_AND_expression: inclusive_OR_expression | logical_AND_expression ANDAND inclusive_OR_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::And), $1, $3); } ; logical_OR_expression: logical_AND_expression | logical_OR_expression OROR logical_AND_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Or), $1, $3); } ; conditional_expression: logical_OR_expression | logical_OR_expression '?' comma_expression ':' conditional_expression { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Cond), (ExpressionNode *)mkList((*$1,*$3,*$5))); } | logical_OR_expression '?' /* empty */ ':' conditional_expression /* GCC, omitted first operand */ { $$=new CompositeExprNode(new OperatorNode(OperatorNode::NCond),$1,$4); } | logical_OR_expression '?' comma_expression ':' tuple /* CFA, tuple expression */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Cond), (ExpressionNode *)mkList(( *$1, *$3, *$5 ))); } ; constant_expression: conditional_expression ; assignment_expression: /* CFA, assignment is separated from assignment_operator to ensure no assignment operations for tuples */ conditional_expression | unary_expression '=' assignment_expression { $$ =new CompositeExprNode(new OperatorNode(OperatorNode::Assign), $1, $3); } | unary_expression assignment_operator assignment_expression { $$ =new CompositeExprNode($2, $1, $3); } | tuple assignment_opt /* CFA, tuple expression */ { if ( $2 == 0 ) { $$ = $1; } else { $$ = new CompositeExprNode( new OperatorNode( OperatorNode::Assign ), $1, $2 ); } } ; assignment_expression_opt: /* empty */ { $$ = new NullExprNode; } | assignment_expression ; tuple: /* CFA, tuple */ /* CFA, one assignment_expression is factored out of comma_expression to eliminate a shift/reduce conflict with comma_expression in new_identifier_parameter_array and new_abstract_array */ '[' push pop ']' { $$ = new CompositeExprNode( new OperatorNode( OperatorNode::TupleC ) ); } | '[' push assignment_expression pop ']' { $$ = new CompositeExprNode( new OperatorNode( OperatorNode::TupleC ), $3 ); } | '[' push ',' tuple_expression_list pop ']' { $$ = new CompositeExprNode( new OperatorNode( OperatorNode::TupleC ), (ExpressionNode *)(new NullExprNode)->set_link( $4 ) ); } | '[' push assignment_expression ',' tuple_expression_list pop ']' { $$ = new CompositeExprNode( new OperatorNode( OperatorNode::TupleC ), (ExpressionNode *)$3->set_link( flattenCommas( $5 ) ) ); } ; tuple_expression_list: assignment_expression_opt | tuple_expression_list ',' assignment_expression_opt { $$ = (ExpressionNode *)$1->set_link( $3 ); } ; assignment_operator: MULTassign { $$ = new OperatorNode(OperatorNode::MulAssn); } | DIVassign { $$ = new OperatorNode(OperatorNode::DivAssn); } | MODassign { $$ = new OperatorNode(OperatorNode::ModAssn); } | PLUSassign { $$ = new OperatorNode(OperatorNode::PlusAssn); } | MINUSassign { $$ = new OperatorNode(OperatorNode::MinusAssn); } | LSassign { $$ = new OperatorNode(OperatorNode::LSAssn); } | RSassign { $$ = new OperatorNode(OperatorNode::RSAssn); } | ANDassign { $$ = new OperatorNode(OperatorNode::AndAssn); } | ERassign { $$ = new OperatorNode(OperatorNode::ERAssn); } | ORassign { $$ = new OperatorNode(OperatorNode::OrAssn); } ; comma_expression: assignment_expression | comma_expression ',' assignment_expression /* { $$ = (ExpressionNode *)$1->add_to_list($3); } */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Comma),$1,$3); } ; comma_expression_opt: /* empty */ { $$ = 0; } | comma_expression ; /*************************** STATEMENTS *******************************/ statement: labeled_statement | compound_statement | expression_statement { $$ = $1; } | selection_statement | iteration_statement | jump_statement | exception_statement | asm_statement ; labeled_statement: no_attr_identifier ':' attribute_list_opt statement { $$ = $4->add_label($1);} ; compound_statement: '{' '}' { $$ = new CompoundStmtNode( (StatementNode *)0 ); } | '{' /* Two scopes are necessary because the block itself has a scope, but every declaration within the block also requires its own scope */ push push label_declaration_opt /* GCC, local labels */ block_item_list pop '}' /* C99, intermix declarations and statements */ { $$ = new CompoundStmtNode( $5 ); } ; block_item_list: /* C99 */ block_item | block_item_list push block_item { if ($1 != 0) { $1->set_link($3); $$ = $1; } } ; block_item: declaration /* CFA, new & old style declarations */ { $$ = new StatementNode( $1 ); } | EXTENSION declaration /* GCC */ { $$ = new StatementNode( $2 ); } | statement pop ; statement_list: statement | statement_list statement { if ($1 != 0) { $1->set_link($2); $$ = $1; } } ; expression_statement: comma_expression_opt ';' { $$ = new StatementNode(StatementNode::Exp, $1, 0); } ; selection_statement: IF '(' comma_expression ')' statement %prec THEN /* explicitly deal with the shift/reduce conflict on if/else */ { $$ = new StatementNode(StatementNode::If, $3, $5); } | IF '(' comma_expression ')' statement ELSE statement { $$ = new StatementNode(StatementNode::If, $3, (StatementNode *)mkList((*$5, *$7)) ); } | SWITCH '(' comma_expression ')' case_clause /* CFA */ { $$ = new StatementNode(StatementNode::Switch, $3, $5); } | SWITCH '(' comma_expression ')' '{' push declaration_list_opt switch_clause_list_opt '}' /* CFA */ { $$ = new StatementNode(StatementNode::Switch, $3, $8); /* xxx */ } /* The semantics of the declaration list is changed to include any associated initialization, which is performed *before* the transfer to the appropriate case clause. Statements after the initial declaration list can never be executed, and therefore, are removed from the grammar even though C allows it. */ | CHOOSE '(' comma_expression ')' case_clause /* CFA */ { $$ = new StatementNode(StatementNode::Choose, $3, $5); } | CHOOSE '(' comma_expression ')' '{' push declaration_list_opt choose_clause_list_opt '}' /* CFA */ { $$ = new StatementNode(StatementNode::Choose, $3, $8); } ; /* CASE and DEFAULT clauses are only allowed in the SWITCH statement, precluding Duff's device. In addition, a case clause allows a list of values and subranges. */ case_value: /* CFA */ constant_expression { $$ = $1; } | constant_expression ELLIPSIS constant_expression /* GCC, subrange */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Range),$1,$3); } | subrange /* CFA, subrange */ ; case_value_list: /* CFA */ case_value | case_value_list ',' case_value { $$ = new CompositeExprNode(new OperatorNode( OperatorNode::TupleC ), (ExpressionNode *)(tupleContents($1))->set_link($3) ); } ; case_label: /* CFA */ CASE case_value_list ':' { $$ = new StatementNode(StatementNode::Case, $2, 0); } | DEFAULT ':' { $$ = new StatementNode(StatementNode::Default); } /* A semantic check is required to ensure only one default clause per switch/choose statement. */ ; case_label_list: /* CFA */ case_label | case_label_list case_label { $$ = (StatementNode *)($1->set_link($2)); } ; case_clause: /* CFA */ case_label_list statement { $$ = $1->append_last_case($2); } ; switch_clause_list_opt: /* CFA */ /* empty */ { $$ = 0; } | switch_clause_list ; switch_clause_list: /* CFA */ case_label_list statement_list { $$ = $1->append_last_case($2); } | switch_clause_list case_label_list statement_list { $$ = (StatementNode *)($1->set_link($2->append_last_case($3))); } ; choose_clause_list_opt: /* CFA */ /* empty */ { $$ = 0; } | choose_clause_list ; choose_clause_list: /* CFA */ case_label_list fall_through { $$ = $1->append_last_case($2); } | case_label_list statement_list fall_through_opt { $$ = $1->append_last_case((StatementNode *)mkList((*$2,*$3))); } | choose_clause_list case_label_list fall_through { $$ = (StatementNode *)($1->set_link($2->append_last_case($3))); } | choose_clause_list case_label_list statement_list fall_through_opt { $$ = (StatementNode *)($1->set_link($2->append_last_case((StatementNode *)mkList((*$3,*$4))))); } ; fall_through_opt: /* CFA */ /* empty */ { $$ = 0; } | fall_through ; fall_through: /* CFA */ FALLTHRU { $$ = new StatementNode(StatementNode::Fallthru, 0, 0); } | FALLTHRU ';' { $$ = new StatementNode(StatementNode::Fallthru, 0, 0); } ; iteration_statement: WHILE '(' comma_expression ')' statement { $$ = new StatementNode(StatementNode::While, $3, $5); } | DO statement WHILE '(' comma_expression ')' ';' { $$ = new StatementNode(StatementNode::Do, $5, $2); } | FOR '(' push for_control_expression ')' statement { $$ = new StatementNode(StatementNode::For, $4, $6); } ; for_control_expression: comma_expression_opt pop ';' comma_expression_opt ';' comma_expression_opt { $$ = new ForCtlExprNode($1, $4, $6); } | declaration comma_expression_opt ';' comma_expression_opt /* C99 */ /* Like C++, the loop index can be declared local to the loop. */ { $$ = new ForCtlExprNode($1, $2, $4); } ; jump_statement: GOTO no_attr_identifier ';' { $$ = new StatementNode(StatementNode::Goto, $2); } | GOTO '*' comma_expression ';' /* GCC, computed goto */ /* The syntax for the GCC computed goto violates normal expression precedence, e.g., goto *i+3; => goto *(i+3); whereas normal operator precedence yields goto (*i)+3; */ { $$ = new StatementNode(StatementNode::Goto, $3); } | CONTINUE ';' /* A semantic check is required to ensure this statement appears only in the body of an iteration statement. */ { $$ = new StatementNode(StatementNode::Continue, 0, 0); } | CONTINUE no_attr_identifier ';' /* CFA, multi-level continue */ /* A semantic check is required to ensure this statement appears only in the body of an iteration statement, and the target of the transfer appears only at the start of an iteration statement. */ { $$ = new StatementNode(StatementNode::Continue, $2); } | BREAK ';' /* A semantic check is required to ensure this statement appears only in the body of an iteration statement. */ { $$ = new StatementNode(StatementNode::Break, 0, 0); } | BREAK no_attr_identifier ';' /* CFA, multi-level exit */ /* A semantic check is required to ensure this statement appears only in the body of an iteration statement, and the target of the transfer appears only at the start of an iteration statement. */ { $$ = new StatementNode(StatementNode::Break, $2 ); } | RETURN comma_expression_opt ';' { $$ = new StatementNode(StatementNode::Return, $2, 0); } | THROW assignment_expression ';' { $$ = new StatementNode(StatementNode::Throw, $2, 0); } | THROW ';' { $$ = new StatementNode(StatementNode::Throw, 0, 0); } ; exception_statement: TRY compound_statement handler_list { $$ = new StatementNode(StatementNode::Try, 0,(StatementNode *)(mkList((*$2,*$3)))); } | TRY compound_statement finally_clause { $$ = new StatementNode(StatementNode::Try, 0,(StatementNode *)(mkList((*$2,*$3)))); } | TRY compound_statement handler_list finally_clause { $3->set_link($4); $$ = new StatementNode(StatementNode::Try, 0,(StatementNode *)(mkList((*$2,*$3)))); } ; handler_list: /* There must be at least one catch clause */ handler_clause /* ISO/IEC 9899:1999 Section 15.3(6) If present, a "..." handler shall be the last handler for its try block. */ | CATCH '(' ELLIPSIS ')' compound_statement { $$ = StatementNode::newCatchStmt( 0, $5, true ); } | handler_clause CATCH '(' ELLIPSIS ')' compound_statement { $$ = $1->set_link( StatementNode::newCatchStmt( 0, $6, true ) ); } ; handler_clause: CATCH '(' push push exception_declaration pop ')' compound_statement pop { $$ = StatementNode::newCatchStmt($5, $8); } | handler_clause CATCH '(' push push exception_declaration pop ')' compound_statement pop { $$ = $1->set_link( StatementNode::newCatchStmt($6, $9) ); } ; finally_clause: FINALLY compound_statement { $$ = new StatementNode(StatementNode::Finally, 0, $2); std::cout << "Just created a finally node" << std::endl; } ; exception_declaration: /* A semantic check is required to ensure type_specifier does not create a new type, e.g.: catch ( struct { int i; } x ) ... This new type cannot catch any thrown type because of name equivalence among types. */ type_specifier | type_specifier declarator { typedefTable.addToEnclosingScope( TypedefTable::ID ); $$ = $2->addType( $1 ); } | type_specifier variable_abstract_declarator { $$ = $2->addType( $1 ); } | new_abstract_declarator_tuple no_attr_identifier /* CFA */ { typedefTable.addToEnclosingScope( TypedefTable::ID ); $$ = $1->addName( $2 ); } | new_abstract_declarator_tuple /* CFA */ ; asm_statement: ASM type_qualifier_list_opt '(' constant_expression ')' ';' { $$ = new StatementNode(StatementNode::Asm, 0, 0); } | ASM type_qualifier_list_opt '(' constant_expression ':' asm_operands_opt ')' ';' /* remaining GCC */ { $$ = new StatementNode(StatementNode::Asm, 0, 0); } | ASM type_qualifier_list_opt '(' constant_expression ':' asm_operands_opt ':' asm_operands_opt ')' ';' { $$ = new StatementNode(StatementNode::Asm, 0, 0); } | ASM type_qualifier_list_opt '(' constant_expression ':' asm_operands_opt ':' asm_operands_opt ':' asm_clobbers_list ')' ';' { $$ = new StatementNode(StatementNode::Asm, 0, 0); } ; asm_operands_opt: /* GCC */ /* empty */ | asm_operands_list ; asm_operands_list: /* GCC */ asm_operand | asm_operands_list ',' asm_operand ; asm_operand: /* GCC */ STRINGliteral '(' constant_expression ')' {} ; asm_clobbers_list: /* GCC */ STRINGliteral {} | asm_clobbers_list ',' STRINGliteral ; /******************************* DECLARATIONS *********************************/ declaration_list_opt: /* used at beginning of switch statement */ pop { $$ = 0; } | declaration_list ; declaration_list: declaration | declaration_list push declaration { $$ = $1->appendList( $3 ); } ; old_declaration_list_opt: /* used to declare parameter types in K&R style functions */ pop { $$ = 0; } | old_declaration_list ; old_declaration_list: old_declaration | old_declaration_list push old_declaration { $$ = $1->appendList( $3 ); } ; label_declaration_opt: /* GCC, local label */ /* empty */ | label_declaration_list ; label_declaration_list: /* GCC, local label */ LABEL label_list ';' | label_declaration_list LABEL label_list ';' ; label_list: /* GCC, local label */ no_attr_identifier_or_typedef_name {} | label_list ',' no_attr_identifier_or_typedef_name {} ; declaration: /* CFA, new & old style declarations */ new_declaration | old_declaration ; /* C declaration syntax is notoriously confusing and error prone. Cforall provides its own type, variable and function declarations. CFA declarations use the same declaration tokens as in C; however, CFA places declaration modifiers to the left of the base type, while C declarations place modifiers to the right of the base type. CFA declaration modifiers are interpreted from left to right and the entire type specification is distributed across all variables in the declaration list (as in Pascal). ANSI C and the new CFA declarations may appear together in the same program block, but cannot be mixed within a specific declaration. CFA C [10] int x; int x[10]; // array of 10 integers [10] * char y; char *y[10]; // array of 10 pointers to char */ new_declaration: /* CFA */ new_variable_declaration pop ';' | new_typedef_declaration pop ';' | new_function_declaration pop ';' | type_declaring_list pop ';' | context_specifier pop ';' ; new_variable_declaration: /* CFA */ new_variable_specifier initializer_opt { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = $1; } | declaration_qualifier_list new_variable_specifier initializer_opt /* declaration_qualifier_list also includes type_qualifier_list, so a semantic check is necessary to preclude them as a type_qualifier cannot appear in that context. */ { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = $2->addQualifiers( $1 ); } | new_variable_declaration pop ',' push identifier_or_typedef_name initializer_opt { typedefTable.addToEnclosingScope( *$5, TypedefTable::ID); $$ = $1->appendList( $1->cloneType( $5 ) ); } ; new_variable_specifier: /* CFA */ /* A semantic check is required to ensure asm_name only appears on declarations with implicit or explicit static storage-class */ new_abstract_declarator_no_tuple identifier_or_typedef_name asm_name_opt { typedefTable.setNextIdentifier( *$2 ); $$ = $1->addName( $2 ); } | new_abstract_tuple identifier_or_typedef_name asm_name_opt { typedefTable.setNextIdentifier( *$2 ); $$ = $1->addName( $2 ); } | type_qualifier_list new_abstract_tuple identifier_or_typedef_name asm_name_opt { typedefTable.setNextIdentifier( *$3 ); $$ = $2->addQualifiers( $1 )->addName( $3 ); } ; new_function_declaration: /* CFA */ new_function_specifier { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = $1; } | declaration_qualifier_list new_function_specifier /* declaration_qualifier_list also includes type_qualifier_list, so a semantic check is necessary to preclude them as a type_qualifier cannot appear in this context. */ { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = $2->addQualifiers( $1 ); } | new_function_declaration pop ',' push identifier_or_typedef_name { typedefTable.addToEnclosingScope( *$5, TypedefTable::ID); $$ = $1->appendList( $1->cloneType( $5 ) ); } ; new_function_specifier: /* CFA */ '[' push pop ']' identifier '(' push new_parameter_type_list_opt pop ')' { typedefTable.setNextIdentifier( *($5) ); $$ = DeclarationNode::newFunction( $5, DeclarationNode::newTuple( 0 ), $8, 0, true ); } | '[' push pop ']' TYPEDEFname '(' push new_parameter_type_list_opt pop ')' { typedefTable.setNextIdentifier( *($5) ); $$ = DeclarationNode::newFunction( $5, DeclarationNode::newTuple( 0 ), $8, 0, true ); } /* identifier_or_typedef_name must be broken apart because of the sequence: '[' ']' identifier_or_typedef_name '(' new_parameter_type_list_opt ')' '[' ']' type_specifier type_specifier can resolve to just TYPEDEFname (e.g. typedef int T; int f( T );). Therefore this must be flattened to allow lookahead to the '(' without having to reduce identifier_or_typedef_name. */ | new_abstract_tuple identifier_or_typedef_name '(' push new_parameter_type_list_opt pop ')' /* To obtain LR(1), this rule must be factored out from function return type (see new_abstract_declarator). */ { $$ = DeclarationNode::newFunction( $2, $1, $5, 0, true ); } | new_function_return identifier_or_typedef_name '(' push new_parameter_type_list_opt pop ')' { $$ = DeclarationNode::newFunction( $2, $1, $5, 0, true ); } ; new_function_return: /* CFA */ '[' push new_parameter_list pop ']' { $$ = DeclarationNode::newTuple( $3 ); } | '[' push new_parameter_list pop ',' push new_abstract_parameter_list pop ']' /* To obtain LR(1), the last new_abstract_parameter_list is added into this flattened rule to lookahead to the ']'. */ { $$ = DeclarationNode::newTuple( $3->appendList( $7 ) ); } ; new_typedef_declaration: /* CFA */ TYPEDEF new_variable_specifier { typedefTable.addToEnclosingScope( TypedefTable::TD); $$ = $2->addTypedef(); } | TYPEDEF new_function_specifier { typedefTable.addToEnclosingScope( TypedefTable::TD); $$ = $2->addTypedef(); } | new_typedef_declaration pop ',' push no_attr_identifier { typedefTable.addToEnclosingScope( *$5, TypedefTable::TD); $$ = $1->appendList( $1->cloneType( $5 ) ); } ; /* Traditionally typedef is part of storage-class specifier for syntactic convenience only. Here, it is factored out as a separate form of declaration, which syntactically precludes storage-class specifiers and initialization. */ typedef_declaration: TYPEDEF type_specifier declarator { typedefTable.addToEnclosingScope( TypedefTable::TD); $$ = $3->addType( $2 )->addTypedef(); } | typedef_declaration pop ',' push declarator { typedefTable.addToEnclosingScope( TypedefTable::TD); $$ = $1->appendList( $1->cloneBaseType( $5 )->addTypedef() ); } | type_qualifier_list TYPEDEF type_specifier declarator /* remaining OBSOLESCENT (see 2) */ { typedefTable.addToEnclosingScope( TypedefTable::TD); $$ = $4->addType( $3 )->addQualifiers( $1 )->addTypedef(); } | type_specifier TYPEDEF declarator { typedefTable.addToEnclosingScope( TypedefTable::TD); $$ = $3->addType( $1 )->addTypedef(); } | type_specifier TYPEDEF type_qualifier_list declarator { typedefTable.addToEnclosingScope( TypedefTable::TD); $$ = $4->addQualifiers($1)->addTypedef()->addType($1); } ; typedef_expression: /* GCC, naming expression type */ TYPEDEF no_attr_identifier '=' assignment_expression { typedefTable.addToEnclosingScope(*($2), TypedefTable::TD); $$ = DeclarationNode::newName( 0 ); // XXX } | typedef_expression pop ',' push no_attr_identifier '=' assignment_expression { typedefTable.addToEnclosingScope(*($5), TypedefTable::TD); $$ = DeclarationNode::newName( 0 ); // XXX } ; old_declaration: declaring_list pop ';' | typedef_declaration pop ';' | typedef_expression pop ';' /* GCC, naming expression type */ | sue_declaration_specifier pop ';' ; declaring_list: /* A semantic check is required to ensure asm_name only appears on declarations with implicit or explicit static storage-class */ declaration_specifier declarator asm_name_opt initializer_opt { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = ($2->addType( $1 ))->addInitializer($4); } | declaring_list ',' attribute_list_opt declarator asm_name_opt initializer_opt { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = $1->appendList( $1->cloneBaseType( $4->addInitializer($6) ) ); } ; declaration_specifier: /* type specifier + storage class */ basic_declaration_specifier | sue_declaration_specifier | typedef_declaration_specifier | typegen_declaration_specifier ; type_specifier: /* declaration specifier - storage class */ basic_type_specifier | sue_type_specifier | typedef_type_specifier | typegen_type_specifier ; type_qualifier_list_opt: /* GCC, used in asm_statement */ /* empty */ { $$ = 0; } | type_qualifier_list ; type_qualifier_list: /* A semantic check is necessary to ensure a type qualifier is appropriate for the kind of declaration. ISO/IEC 9899:1999 Section 6.7.3(4) : If the same qualifier appears more than once in the same specifier-qualifier-list, either directly or via one or more typedefs, the behavior is the same as if it appeared only once. */ type_qualifier | type_qualifier_list type_qualifier { $$ = $1->addQualifiers( $2 ); } ; type_qualifier: type_qualifier_name | attribute { $$ = DeclarationNode::newQualifier( DeclarationNode::Const ); } ; type_qualifier_name: CONST { $$ = DeclarationNode::newQualifier( DeclarationNode::Const ); } | RESTRICT { $$ = DeclarationNode::newQualifier( DeclarationNode::Restrict ); } | VOLATILE { $$ = DeclarationNode::newQualifier( DeclarationNode::Volatile ); } | LVALUE /* CFA */ { $$ = DeclarationNode::newQualifier( DeclarationNode::Lvalue ); } | FORALL '(' { typedefTable.enterScope(); } type_parameter_list ')' /* CFA */ { typedefTable.leaveScope(); $$ = DeclarationNode::newForall( $4 ); } ; declaration_qualifier_list: storage_class_list | type_qualifier_list storage_class_list /* remaining OBSOLESCENT (see 2) */ { $$ = $1->addQualifiers( $2 ); } | declaration_qualifier_list type_qualifier_list storage_class_list { $$ = $1->addQualifiers( $2 )->addQualifiers( $3 ); } ; storage_class_list: /* A semantic check is necessary to ensure a storage class is appropriate for the kind of declaration and that only one of each is specified, except for inline, which can appear with the others. ISO/IEC 9899:1999 Section 6.7.1(2) : At most, one storage-class specifier may be given in the declaration specifiers in a declaration. */ storage_class | storage_class_list storage_class { $$ = $1->addQualifiers( $2 ); } ; storage_class: storage_class_name ; storage_class_name: AUTO { $$ = DeclarationNode::newStorageClass( DeclarationNode::Auto ); } | EXTERN { $$ = DeclarationNode::newStorageClass( DeclarationNode::Extern ); } | REGISTER { $$ = DeclarationNode::newStorageClass( DeclarationNode::Register ); } | STATIC { $$ = DeclarationNode::newStorageClass( DeclarationNode::Static ); } | INLINE /* C99 */ /* INLINE is essentially a storage class specifier for functions, and hence, belongs here. */ { $$ = DeclarationNode::newStorageClass( DeclarationNode::Inline ); } | FORTRAN /* C99 */ { $$ = DeclarationNode::newStorageClass( DeclarationNode::Fortran ); } ; basic_type_name: CHAR { $$ = DeclarationNode::newBasicType( DeclarationNode::Char ); } | DOUBLE { $$ = DeclarationNode::newBasicType( DeclarationNode::Double ); } | FLOAT { $$ = DeclarationNode::newBasicType( DeclarationNode::Float ); } | INT { $$ = DeclarationNode::newBasicType( DeclarationNode::Int ); } | LONG { $$ = DeclarationNode::newModifier( DeclarationNode::Long ); } | SHORT { $$ = DeclarationNode::newModifier( DeclarationNode::Short ); } | SIGNED { $$ = DeclarationNode::newModifier( DeclarationNode::Signed ); } | UNSIGNED { $$ = DeclarationNode::newModifier( DeclarationNode::Unsigned ); } | VOID { $$ = DeclarationNode::newBasicType( DeclarationNode::Void ); } | BOOL /* C99 */ { $$ = DeclarationNode::newBasicType( DeclarationNode::Bool ); } | COMPLEX /* C99 */ { $$ = DeclarationNode::newBasicType( DeclarationNode::Complex ); } | IMAGINARY /* C99 */ { $$ = DeclarationNode::newBasicType( DeclarationNode::Imaginary ); } ; basic_declaration_specifier: /* A semantic check is necessary for conflicting storage classes. */ basic_type_specifier | declaration_qualifier_list basic_type_specifier { $$ = $2->addQualifiers( $1 ); } | basic_declaration_specifier storage_class /* remaining OBSOLESCENT (see 2) */ { $$ = $1->addQualifiers( $2 ); } | basic_declaration_specifier storage_class type_qualifier_list { $$ = $1->addQualifiers( $2 )->addQualifiers( $3 ); } | basic_declaration_specifier storage_class basic_type_specifier { $$ = $3->addQualifiers( $2 )->addType( $1 ); } ; basic_type_specifier: direct_type_name | type_qualifier_list_opt indirect_type_name type_qualifier_list_opt { $$ = $2->addQualifiers( $1 )->addQualifiers( $3 ); } ; direct_type_name: /* A semantic check is necessary for conflicting type qualifiers. */ basic_type_name | type_qualifier_list basic_type_name { $$ = $2->addQualifiers( $1 ); } | direct_type_name type_qualifier { $$ = $1->addQualifiers( $2 ); } | direct_type_name basic_type_name { $$ = $1->addType( $2 ); } ; indirect_type_name: TYPEOF '(' type_name ')' /* GCC: typeof(x) y; */ { $$ = $3; } | TYPEOF '(' comma_expression ')' /* GCC: typeof(a+b) y; */ { $$ = DeclarationNode::newTypeof( $3 ); } | ATTR_TYPEGENname '(' type_name ')' /* CFA: e.g., @type(x) y; */ { $$ = DeclarationNode::newAttr( $1, $3 ); } | ATTR_TYPEGENname '(' comma_expression ')' /* CFA: e.g., @type(a+b) y; */ { $$ = DeclarationNode::newAttr( $1, $3 ); } ; sue_declaration_specifier: sue_type_specifier | declaration_qualifier_list sue_type_specifier { $$ = $2->addQualifiers( $1 ); } | sue_declaration_specifier storage_class /* remaining OBSOLESCENT (see 2) */ { $$ = $1->addQualifiers( $2 ); } | sue_declaration_specifier storage_class type_qualifier_list { $$ = $1->addQualifiers( $2 )->addQualifiers( $3 ); } ; sue_type_specifier: elaborated_type_name /* struct, union, enum */ | type_qualifier_list elaborated_type_name { $$ = $2->addQualifiers( $1 ); } | sue_type_specifier type_qualifier { $$ = $1->addQualifiers( $2 ); } ; typedef_declaration_specifier: typedef_type_specifier | declaration_qualifier_list typedef_type_specifier { $$ = $2->addQualifiers( $1 ); } | typedef_declaration_specifier storage_class /* remaining OBSOLESCENT (see 2) */ { $$ = $1->addQualifiers( $2 ); } | typedef_declaration_specifier storage_class type_qualifier_list { $$ = $1->addQualifiers( $2 )->addQualifiers( $3 ); } ; typedef_type_specifier: /* typedef types */ TYPEDEFname { $$ = DeclarationNode::newFromTypedef( $1 ); } | type_qualifier_list TYPEDEFname { $$ = DeclarationNode::newFromTypedef( $2 )->addQualifiers( $1 ); } | typedef_type_specifier type_qualifier { $$ = $1->addQualifiers( $2 ); } ; elaborated_type_name: aggregate_name | enum_name ; aggregate_name: aggregate_key '{' field_declaration_list '}' { $$ = DeclarationNode::newAggregate( $1, 0, 0, 0, $3 ); } | aggregate_key no_attr_identifier_or_typedef_name { $$ = DeclarationNode::newAggregate( $1, $2, 0, 0, 0 ); } | aggregate_key no_attr_identifier_or_typedef_name '{' field_declaration_list '}' { $$ = DeclarationNode::newAggregate( $1, $2, 0, 0, $4 ); } | aggregate_key '(' push type_parameter_list pop ')' '{' field_declaration_list '}' /* CFA */ { $$ = DeclarationNode::newAggregate( $1, 0, $4, 0, $8 ); } | aggregate_key '(' push type_parameter_list pop ')' no_attr_identifier_or_typedef_name /* CFA */ { $$ = DeclarationNode::newAggregate( $1, $7, $4, 0, 0 ); } | aggregate_key '(' push type_parameter_list pop ')' no_attr_identifier_or_typedef_name '{' field_declaration_list '}' /* CFA */ { $$ = DeclarationNode::newAggregate( $1, $7, $4, 0, $9 ); } | aggregate_key '(' push type_parameter_list pop ')' '(' type_name_list ')' '{' field_declaration_list '}' /* CFA */ { $$ = DeclarationNode::newAggregate( $1, 0, $4, $8, $11 ); } | aggregate_key '(' push type_name_list pop ')' no_attr_identifier_or_typedef_name /* CFA */ /* push and pop are only to prevent S/R conflicts */ { $$ = DeclarationNode::newAggregate( $1, $7, 0, $4, 0 ); } | aggregate_key '(' push type_parameter_list pop ')' '(' type_name_list ')' no_attr_identifier_or_typedef_name '{' field_declaration_list '}' /* CFA */ { $$ = DeclarationNode::newAggregate( $1, $10, $4, $8, $12 ); } ; aggregate_key: STRUCT attribute_list_opt { $$ = DeclarationNode::Struct; } | UNION attribute_list_opt { $$ = DeclarationNode::Union; } ; field_declaration_list: field_declaration { $$ = $1; } | field_declaration_list field_declaration { $$ = $1->appendList( $2 ); } ; field_declaration: new_field_declaring_list ';' /* CFA, new style field declaration */ | EXTENSION new_field_declaring_list ';' /* GCC */ { $$ = $2; } | field_declaring_list ';' | EXTENSION field_declaring_list ';' /* GCC */ { $$ = $2; } ; new_field_declaring_list: /* CFA, new style field declaration */ new_abstract_declarator_tuple /* CFA, no field name */ | new_abstract_declarator_tuple no_attr_identifier_or_typedef_name { $$ = $1->addName( $2 ); } | new_field_declaring_list ',' no_attr_identifier_or_typedef_name { $$ = $1->appendList( $1->cloneType( $3 ) ); } | new_field_declaring_list ',' /* CFA, no field name */ { $$ = $1->appendList( $1->cloneType( 0 ) ); } ; field_declaring_list: type_specifier field_declarator { $$ = $2->addType( $1 ); } | field_declaring_list ',' attribute_list_opt field_declarator { $$ = $1->appendList( $1->cloneBaseType( $4 ) ); } ; field_declarator: /* empty */ /* CFA, no field name */ { $$ = DeclarationNode::newName( 0 ); /* XXX */ } | bit_subrange_size /* no field name */ { $$ = DeclarationNode::newBitfield( $1 ); } | variable_declarator bit_subrange_size_opt /* A semantic check is required to ensure bit_subrange only appears on base type int. */ { $$ = $1->addBitfield( $2 ); } | typedef_redeclarator bit_subrange_size_opt /* A semantic check is required to ensure bit_subrange only appears on base type int. */ { $$ = $1->addBitfield( $2 ); } | variable_abstract_declarator /* CFA, no field name */ ; bit_subrange_size_opt: /* empty */ { $$ = 0; } | bit_subrange_size { $$ = $1; } ; bit_subrange_size: ':' constant_expression { $$ = $2; } ; enum_key: ENUM attribute_list_opt ; enum_name: enum_key '{' enumerator_list comma_opt '}' { $$ = DeclarationNode::newEnum( 0, $3 ); } | enum_key no_attr_identifier_or_typedef_name '{' enumerator_list comma_opt '}' { $$ = DeclarationNode::newEnum( $2, $4 ); } | enum_key no_attr_identifier_or_typedef_name { $$ = DeclarationNode::newEnum( $2, 0 ); } ; enumerator_list: no_attr_identifier_or_typedef_name enumerator_value_opt { $$ = DeclarationNode::newEnumConstant( $1, $2 ); } | enumerator_list ',' no_attr_identifier_or_typedef_name enumerator_value_opt { $$ = $1->appendList( DeclarationNode::newEnumConstant( $3, $4 ) ); } ; enumerator_value_opt: /* empty */ { $$ = 0; } | '=' constant_expression { $$ = $2; } ; /* Minimum of one parameter after which ellipsis is allowed only at the end. */ new_parameter_type_list_opt: /* CFA */ /* empty */ { $$ = 0; } | new_parameter_type_list ; new_parameter_type_list: /* CFA, abstract + real */ new_abstract_parameter_list | new_parameter_list | new_parameter_list pop ',' push new_abstract_parameter_list { $$ = $1->appendList( $5 ); } | new_abstract_parameter_list pop ',' push ELLIPSIS { $$ = $1->addVarArgs(); } | new_parameter_list pop ',' push ELLIPSIS { $$ = $1->addVarArgs(); } ; new_parameter_list: /* CFA */ /* To obtain LR(1) between new_parameter_list and new_abstract_tuple, the last new_abstract_parameter_list is factored out from new_parameter_list, flattening the rules to get lookahead to the ']'. */ new_parameter_declaration | new_abstract_parameter_list pop ',' push new_parameter_declaration { $$ = $1->appendList( $5 ); } | new_parameter_list pop ',' push new_parameter_declaration { $$ = $1->appendList( $5 ); } | new_parameter_list pop ',' push new_abstract_parameter_list pop ',' push new_parameter_declaration { $$ = $1->appendList( $5 )->appendList( $9 ); } ; new_abstract_parameter_list: /* CFA, new & old style abstract */ new_abstract_parameter_declaration | new_abstract_parameter_list pop ',' push new_abstract_parameter_declaration { $$ = $1->appendList( $5 ); } ; parameter_type_list_opt: /* empty */ { $$ = 0; } | parameter_type_list ; parameter_type_list: parameter_list | parameter_list pop ',' push ELLIPSIS { $$ = $1->addVarArgs(); } ; parameter_list: /* abstract + real */ abstract_parameter_declaration | parameter_declaration | parameter_list pop ',' push abstract_parameter_declaration { $$ = $1->appendList( $5 ); } | parameter_list pop ',' push parameter_declaration { $$ = $1->appendList( $5 ); } ; /* Provides optional identifier names (abstract_declarator/variable_declarator), no initialization, different semantics for typedef name by using typedef_parameter_redeclarator instead of typedef_redeclarator, and function prototypes. */ new_parameter_declaration: /* CFA, new & old style parameter declaration */ parameter_declaration | new_identifier_parameter_declarator_no_tuple identifier_or_typedef_name assignment_opt { $$ = $1->addName( $2 ); } | new_abstract_tuple identifier_or_typedef_name assignment_opt /* To obtain LR(1), these rules must be duplicated here (see new_abstract_declarator). */ { $$ = $1->addName( $2 ); } | type_qualifier_list new_abstract_tuple identifier_or_typedef_name assignment_opt { $$ = $2->addName( $3 )->addQualifiers( $1 ); } | new_function_specifier ; new_abstract_parameter_declaration: /* CFA, new & old style parameter declaration */ abstract_parameter_declaration | new_identifier_parameter_declarator_no_tuple | new_abstract_tuple /* To obtain LR(1), these rules must be duplicated here (see new_abstract_declarator). */ | type_qualifier_list new_abstract_tuple { $$ = $2->addQualifiers( $1 ); } | new_abstract_function ; parameter_declaration: declaration_specifier identifier_parameter_declarator assignment_opt { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = $2->addType( $1 )->addInitializer( new InitializerNode($3) ); } | declaration_specifier typedef_parameter_redeclarator assignment_opt { typedefTable.addToEnclosingScope( TypedefTable::ID); $$ = $2->addType( $1 )->addInitializer( new InitializerNode($3) ); } ; abstract_parameter_declaration: declaration_specifier | declaration_specifier abstract_parameter_declarator { $$ = $2->addType( $1 ); } ; /* ISO/IEC 9899:1999 Section 6.9.1(6) : "An identifier declared as a typedef name shall not be redeclared as a parameter." Because the scope of the K&R-style parameter-list sees the typedef first, the following is based only on identifiers. The ANSI-style parameter-list can redefine a typedef name. */ identifier_list: /* K&R-style parameter list => no types */ no_attr_identifier { $$ = DeclarationNode::newName( $1 ); } | identifier_list ',' no_attr_identifier { $$ = $1->appendList( DeclarationNode::newName( $3 ) ); } ; identifier_or_typedef_name: identifier | TYPEDEFname | TYPEGENname ; no_01_identifier_or_typedef_name: no_01_identifier | TYPEDEFname | TYPEGENname ; no_attr_identifier_or_typedef_name: no_attr_identifier | TYPEDEFname | TYPEGENname ; type_name_no_function: /* sizeof, alignof, cast (constructor) */ new_abstract_declarator_tuple /* CFA */ | type_specifier | type_specifier variable_abstract_declarator { $$ = $2->addType( $1 ); } ; type_name: /* typeof, assertion */ new_abstract_declarator_tuple /* CFA */ | new_abstract_function /* CFA */ | type_specifier | type_specifier abstract_declarator { $$ = $2->addType( $1 ); } ; initializer_opt: /* empty */ { $$ = 0; } | '=' initializer { $$ = $2; } ; initializer: assignment_expression { $$ = new InitializerNode($1); } | '{' initializer_list comma_opt '}' { $$ = new InitializerNode($2, true); } ; initializer_list: initializer | designation initializer { $$ = $2->set_designators( $1 ); } | initializer_list ',' initializer { $$ = (InitializerNode *)( $1->set_link($3) ); } | initializer_list ',' designation initializer { $$ = (InitializerNode *)( $1->set_link( $4->set_designators($3) ) ); } ; /* There is an unreconcileable parsing problem between C99 and CFA with respect to designators. The problem is use of '=' to separator the designator from the initializer value, as in: int x[10] = { [1] = 3 }; The string "[1] = 3" can be parsed as a designator assignment or a tuple assignment. To disambiguate this case, CFA changes the syntax from "=" to ":" as the separator between the designator and initializer. GCC does uses ":" for field selection. The optional use of the "=" in GCC, or in this case ":", cannot be supported either due to shift/reduce conflicts */ designation: designator_list ':' /* C99, CFA uses ":" instead of "=" */ | no_attr_identifier_or_typedef_name ':' /* GCC, field name */ { $$ = new VarRefNode( $1 ); } ; designator_list: /* C99 */ designator | designator_list designator { $$ = (ExpressionNode *)($1->set_link( $2 )); } ; designator: '.' no_attr_identifier_or_typedef_name /* C99, field name */ { $$ = new VarRefNode( $2 ); } | '[' push assignment_expression pop ']' /* C99, single array element */ /* assignment_expression used instead of constant_expression because of shift/reduce conflicts with tuple. */ { $$ = $3; } | '[' push subrange pop ']' /* CFA, multiple array elements */ { $$ = $3; } | '[' push constant_expression ELLIPSIS constant_expression pop ']' /* GCC, multiple array elements */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Range), $3, $5); } | '.' '[' push field_list pop ']' /* CFA, tuple field selector */ { $$ = $4; } ; /* The CFA type system is based on parametric polymorphism, the ability to declare functions with type parameters, rather than an object-oriented type system. This required four groups of extensions: Overloading: function, data, and operator identifiers may be overloaded. Type declarations: "type" is used to generate new types for declaring objects. Similarly, "dtype" is used for object and incomplete types, and "ftype" is used for function types. Type declarations with initializers provide definitions of new types. Type declarations with storage class "extern" provide opaque types. Polymorphic functions: A forall clause declares a type parameter. The corresponding argument is inferred at the call site. A polymorphic function is not a template; it is a function, with an address and a type. Specifications and Assertions: Specifications are collections of declarations parameterized by one or more types. They serve many of the purposes of abstract classes, and specification hierarchies resemble subclass hierarchies. Unlike classes, they can define relationships between types. Assertions declare that a type or types provide the operations declared by a specification. Assertions are normally used to declare requirements on type arguments of polymorphic functions. */ typegen_declaration_specifier: /* CFA */ typegen_type_specifier | declaration_qualifier_list typegen_type_specifier { $$ = $2->addQualifiers( $1 ); } | typegen_declaration_specifier storage_class /* remaining OBSOLESCENT (see 2) */ { $$ = $1->addQualifiers( $2 ); } | typegen_declaration_specifier storage_class type_qualifier_list { $$ = $1->addQualifiers( $2 )->addQualifiers( $3 ); } ; typegen_type_specifier: /* CFA */ TYPEGENname '(' type_name_list ')' { $$ = DeclarationNode::newFromTypeGen( $1, $3 ); } | type_qualifier_list TYPEGENname '(' type_name_list ')' { $$ = DeclarationNode::newFromTypeGen( $2, $4 )->addQualifiers( $1 ); } | typegen_type_specifier type_qualifier { $$ = $1->addQualifiers( $2 ); } ; type_parameter_list: /* CFA */ type_parameter assignment_opt | type_parameter_list ',' type_parameter assignment_opt { $$ = $1->appendList( $3 ); } ; type_parameter: /* CFA */ type_class no_attr_identifier_or_typedef_name { typedefTable.addToEnclosingScope(*($2), TypedefTable::TD); } assertion_list_opt { $$ = DeclarationNode::newTypeParam( $1, $2 )->addAssertions( $4 ); } | type_specifier identifier_parameter_declarator ; type_class: /* CFA */ TYPE { $$ = DeclarationNode::Type; } | DTYPE { $$ = DeclarationNode::Ftype; } | FTYPE { $$ = DeclarationNode::Dtype; } ; assertion_list_opt: /* CFA */ /* empty */ { $$ = 0; } | assertion_list_opt assertion { $$ = $1 == 0 ? $2 : $1->appendList( $2 ); } ; assertion: /* CFA */ '|' no_attr_identifier_or_typedef_name '(' type_name_list ')' { typedefTable.openContext( *($2) ); $$ = DeclarationNode::newContextUse( $2, $4 ); } | '|' '{' push context_declaration_list '}' { $$ = $4; } | '|' '(' push type_parameter_list pop ')' '{' push context_declaration_list '}' '(' type_name_list ')' { $$ = 0; } ; type_name_list: /* CFA */ type_name { $$ = new TypeValueNode( $1 ); } | assignment_expression | type_name_list ',' type_name { $$ = (ExpressionNode *)($1->set_link(new TypeValueNode( $3 ))); } | type_name_list ',' assignment_expression { $$ = (ExpressionNode *)($1->set_link($3)); } ; type_declaring_list: /* CFA */ TYPE type_declarator { $$ = $2; } | storage_class_list TYPE type_declarator { $$ = $3->addQualifiers( $1 ); } | type_declaring_list ',' type_declarator { $$ = $1->appendList( $3->copyStorageClasses( $1 ) ); } ; type_declarator: /* CFA */ type_declarator_name assertion_list_opt { $$ = $1->addAssertions( $2 ); } | type_declarator_name assertion_list_opt '=' type_name { $$ = $1->addAssertions( $2 )->addType( $4 ); } ; type_declarator_name: /* CFA */ no_attr_identifier_or_typedef_name { typedefTable.addToEnclosingScope(*($1), TypedefTable::TD); $$ = DeclarationNode::newTypeDecl( $1, 0 ); } | no_01_identifier_or_typedef_name '(' push type_parameter_list pop ')' { typedefTable.addToEnclosingScope(*($1), TypedefTable::TG); $$ = DeclarationNode::newTypeDecl( $1, $4 ); } ; context_specifier: /* CFA */ CONTEXT no_attr_identifier_or_typedef_name '(' push type_parameter_list pop ')' '{' '}' { typedefTable.addToEnclosingScope(*($2), TypedefTable::ID); $$ = DeclarationNode::newContext( $2, $5, 0 ); } | CONTEXT no_attr_identifier_or_typedef_name '(' push type_parameter_list pop ')' '{' { typedefTable.enterContext( *($2) ); typedefTable.enterScope(); } context_declaration_list '}' { typedefTable.leaveContext(); typedefTable.addToEnclosingScope(*($2), TypedefTable::ID); $$ = DeclarationNode::newContext( $2, $5, $10 ); } ; context_declaration_list: /* CFA */ context_declaration | context_declaration_list push context_declaration { $$ = $1->appendList( $3 ); } ; context_declaration: /* CFA */ new_context_declaring_list pop ';' | context_declaring_list pop ';' ; new_context_declaring_list: /* CFA */ new_variable_specifier { typedefTable.addToEnclosingScope2( TypedefTable::ID ); $$ = $1; } | new_function_specifier { typedefTable.addToEnclosingScope2( TypedefTable::ID ); $$ = $1; } | new_context_declaring_list pop ',' push identifier_or_typedef_name { typedefTable.addToEnclosingScope2( *($5), TypedefTable::ID ); $$ = $1->appendList( $1->cloneType( $5 ) ); } ; context_declaring_list: /* CFA */ type_specifier declarator { typedefTable.addToEnclosingScope2( TypedefTable::ID); $$ = $2->addType( $1 ); } | context_declaring_list pop ',' push declarator { typedefTable.addToEnclosingScope2( TypedefTable::ID); $$ = $1->appendList( $1->cloneBaseType( $5 ) ); } ; /***************************** EXTERNAL DEFINITIONS *****************************/ translation_unit: /* empty */ /* empty input file */ {} | external_definition_list { if ( theTree ) { theTree->appendList( $1 ); } else { theTree = $1; } } ; external_definition_list: external_definition | external_definition_list push external_definition { if ( $1 ) { $$ = $1->appendList( $3 ); } else { $$ = $3; } } ; external_definition_list_opt: /* empty */ { $$ = 0; } | external_definition_list ; external_definition: declaration | function_definition | asm_statement /* GCC, global assembler statement */ {} | EXTERN STRINGliteral { linkageStack.push( linkage ); linkage = LinkageSpec::fromString( *$2 ); } '{' external_definition_list_opt '}' /* C++-style linkage specifier */ { linkage = linkageStack.top(); linkageStack.pop(); $$ = $5; } | EXTENSION external_definition { $$ = $2; } ; function_definition: new_function_specifier compound_statement /* CFA */ { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $1->addFunctionBody( $2 ); } | declaration_qualifier_list new_function_specifier compound_statement /* CFA */ /* declaration_qualifier_list also includes type_qualifier_list, so a semantic check is necessary to preclude them as a type_qualifier cannot appear in this context. */ { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $2->addFunctionBody( $3 )->addQualifiers( $1 ); } | declaration_specifier function_declarator compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $2->addFunctionBody( $3 )->addType( $1 ); } /* These rules are a concession to the "implicit int" type_specifier because there is a significant amount of code with functions missing a type-specifier on the return type. Parsing is possible because function_definition does not appear in the context of an expression (nested functions would preclude this concession). A function prototype declaration must still have a type_specifier. OBSOLESCENT (see 1) */ | function_declarator compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $1->addFunctionBody( $2 ); } | type_qualifier_list function_declarator compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $2->addFunctionBody( $3 )->addQualifiers( $1 ); } | declaration_qualifier_list function_declarator compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $2->addFunctionBody( $3 )->addQualifiers( $1 ); } | declaration_qualifier_list type_qualifier_list function_declarator compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $3->addFunctionBody( $4 )->addQualifiers( $2 )->addQualifiers( $1 ); } /* Old-style K&R function definition, OBSOLESCENT (see 4) */ | declaration_specifier old_function_declarator push old_declaration_list_opt compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $2->addOldDeclList( $4 )->addFunctionBody( $5 )->addType( $1 ); } | old_function_declarator push old_declaration_list_opt compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $1->addOldDeclList( $3 )->addFunctionBody( $4 ); } | type_qualifier_list old_function_declarator push old_declaration_list_opt compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $2->addOldDeclList( $4 )->addFunctionBody( $5 )->addQualifiers( $1 ); } /* Old-style K&R function definition with "implicit int" type_specifier, OBSOLESCENT (see 4) */ | declaration_qualifier_list old_function_declarator push old_declaration_list_opt compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $2->addOldDeclList( $4 )->addFunctionBody( $5 )->addQualifiers( $1 ); } | declaration_qualifier_list type_qualifier_list old_function_declarator push old_declaration_list_opt compound_statement { typedefTable.addToEnclosingScope( TypedefTable::ID ); typedefTable.leaveScope(); $$ = $3->addOldDeclList( $5 )->addFunctionBody( $6 )->addQualifiers( $2 )->addQualifiers( $1 ); } ; declarator: variable_declarator | function_declarator | typedef_redeclarator ; subrange: constant_expression '~' constant_expression /* CFA, integer subrange */ { $$ = new CompositeExprNode(new OperatorNode(OperatorNode::Range), $1, $3); } ; asm_name_opt: /* GCC */ /* empty */ | ASM '(' string_literal_list ')' attribute_list_opt ; attribute_list_opt: /* GCC */ /* empty */ | attribute_list ; attribute_list: /* GCC */ attribute | attribute_list attribute ; attribute: /* GCC */ ATTRIBUTE '(' '(' attribute_parameter_list ')' ')' ; attribute_parameter_list: /* GCC */ attrib | attribute_parameter_list ',' attrib ; attrib: /* GCC */ /* empty */ | any_word | any_word '(' comma_expression_opt ')' ; any_word: /* GCC */ identifier_or_typedef_name {} | storage_class_name {} | basic_type_name {} | type_qualifier {} ; /* ============================================================================ The following sections are a series of grammar patterns used to parse declarators. Multiple patterns are necessary because the type of an identifier in wrapped around the identifier in the same form as its usage in an expression, as in: int (*f())[10] { ... }; ... (*f())[3] += 1; // definition mimics usage Because these patterns are highly recursive, changes at a lower level in the recursion require copying some or all of the pattern. Each of these patterns has some subtle variation to ensure correct syntax in a particular context. ============================================================================ */ /* ---------------------------------------------------------------------------- The set of valid declarators before a compound statement for defining a function is less than the set of declarators to define a variable or function prototype, e.g.: valid declaration invalid definition ----------------- ------------------ int f; int f {} int *f; int *f {} int f[10]; int f[10] {} int (*f)(int); int (*f)(int) {} To preclude this syntactic anomaly requires separating the grammar rules for variable and function declarators, hence variable_declarator and function_declarator. ---------------------------------------------------------------------------- */ /* This pattern parses a declaration of a variable that is not redefining a typedef name. The pattern precludes declaring an array of functions versus a pointer to an array of functions. */ variable_declarator: paren_identifier attribute_list_opt | variable_ptr | variable_array attribute_list_opt | variable_function attribute_list_opt ; paren_identifier: identifier { typedefTable.setNextIdentifier( *($1) ); $$ = DeclarationNode::newName( $1 ); } | '(' paren_identifier ')' /* redundant parenthesis */ { $$ = $2; } ; variable_ptr: '*' variable_declarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list variable_declarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' variable_ptr ')' { $$ = $2; } ; variable_array: paren_identifier array_dimension { $$ = $1->addArray( $2 ); } | '(' variable_ptr ')' array_dimension { $$ = $2->addArray( $4 ); } | '(' variable_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' variable_array ')' /* redundant parenthesis */ { $$ = $2; } ; variable_function: '(' variable_ptr ')' '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $2->addParamList( $6 ); } | '(' variable_function ')' /* redundant parenthesis */ { $$ = $2; } ; /* This pattern parses a function declarator that is not redefining a typedef name. Because functions cannot be nested, there is no context where a function definition can redefine a typedef name. To allow nested functions requires further separation of variable and function declarators in typedef_redeclarator. The pattern precludes returning arrays and functions versus pointers to arrays and functions. */ function_declarator: function_no_ptr attribute_list_opt | function_ptr | function_array attribute_list_opt ; function_no_ptr: paren_identifier '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $1->addParamList( $4 ); } | '(' function_ptr ')' '(' push parameter_type_list_opt pop ')' { $$ = $2->addParamList( $6 ); } | '(' function_no_ptr ')' /* redundant parenthesis */ { $$ = $2; } ; function_ptr: '*' function_declarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list function_declarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' function_ptr ')' { $$ = $2; } ; function_array: '(' function_ptr ')' array_dimension { $$ = $2->addArray( $4 ); } | '(' function_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' function_array ')' /* redundant parenthesis */ { $$ = $2; } ; /* This pattern parses an old-style K&R function declarator (OBSOLESCENT, see 4) that is not redefining a typedef name (see function_declarator for additional comments). The pattern precludes returning arrays and functions versus pointers to arrays and functions. */ old_function_declarator: old_function_no_ptr | old_function_ptr | old_function_array ; old_function_no_ptr: paren_identifier '(' identifier_list ')' /* function_declarator handles empty parameter */ { $$ = $1->addIdList( $3 ); } | '(' old_function_ptr ')' '(' identifier_list ')' { $$ = $2->addIdList( $5 ); } | '(' old_function_no_ptr ')' /* redundant parenthesis */ { $$ = $2; } ; old_function_ptr: '*' old_function_declarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list old_function_declarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' old_function_ptr ')' { $$ = $2; } ; old_function_array: '(' old_function_ptr ')' array_dimension { $$ = $2->addArray( $4 ); } | '(' old_function_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' old_function_array ')' /* redundant parenthesis */ { $$ = $2; } ; /* This pattern parses a declaration for a variable or function prototype that redefines a typedef name, e.g.: typedef int foo; { int foo; // redefine typedef name in new scope } The pattern precludes declaring an array of functions versus a pointer to an array of functions, and returning arrays and functions versus pointers to arrays and functions. */ typedef_redeclarator: paren_typedef attribute_list_opt | typedef_ptr | typedef_array attribute_list_opt | typedef_function attribute_list_opt ; paren_typedef: TYPEDEFname { typedefTable.setNextIdentifier( *($1) ); $$ = DeclarationNode::newName( $1 ); } | '(' paren_typedef ')' { $$ = $2; } ; typedef_ptr: '*' typedef_redeclarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list typedef_redeclarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' typedef_ptr ')' { $$ = $2; } ; typedef_array: paren_typedef array_dimension { $$ = $1->addArray( $2 ); } | '(' typedef_ptr ')' array_dimension { $$ = $2->addArray( $4 ); } | '(' typedef_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' typedef_array ')' /* redundant parenthesis */ { $$ = $2; } ; typedef_function: paren_typedef '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $1->addParamList( $4 ); } | '(' typedef_ptr ')' '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $2->addParamList( $6 ); } | '(' typedef_function ')' /* redundant parenthesis */ { $$ = $2; } ; /* This pattern parses a declaration for a parameter variable or function prototype that is not redefining a typedef name and allows the C99 array options, which can only appear in a parameter list. The pattern precludes declaring an array of functions versus a pointer to an array of functions, and returning arrays and functions versus pointers to arrays and functions. */ identifier_parameter_declarator: paren_identifier attribute_list_opt | identifier_parameter_ptr | identifier_parameter_array attribute_list_opt | identifier_parameter_function attribute_list_opt ; identifier_parameter_ptr: '*' identifier_parameter_declarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list identifier_parameter_declarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' identifier_parameter_ptr ')' { $$ = $2; } ; identifier_parameter_array: paren_identifier array_parameter_dimension { $$ = $1->addArray( $2 ); } | '(' identifier_parameter_ptr ')' array_dimension { $$ = $2->addArray( $4 ); } | '(' identifier_parameter_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' identifier_parameter_array ')' /* redundant parenthesis */ { $$ = $2; } ; identifier_parameter_function: paren_identifier '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $1->addParamList( $4 ); } | '(' identifier_parameter_ptr ')' '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $2->addParamList( $6 ); } | '(' identifier_parameter_function ')' /* redundant parenthesis */ { $$ = $2; } ; /* This pattern parses a declaration for a parameter variable or function prototype that is redefining a typedef name, e.g.: typedef int foo; int f( int foo ); // redefine typedef name in new scope and allows the C99 array options, which can only appear in a parameter list. In addition, the pattern handles the special meaning of parenthesis around a typedef name: ISO/IEC 9899:1999 Section 6.7.5.3(11) : "In a parameter declaration, a single typedef name in parentheses is taken to be an abstract declarator that specifies a function with a single parameter, not as redundant parentheses around the identifier." which precludes the following cases: typedef float T; int f( int ( T [5] ) ); // see abstract_parameter_declarator int g( int ( T ( int ) ) ); // see abstract_parameter_declarator int f( int f1( T a[5] ) ); // see identifier_parameter_declarator int g( int g1( T g2( int p ) ) ); // see identifier_parameter_declarator In essence, a '(' immediately to the left of typedef name, T, is interpreted as starting a parameter type list, and not as redundant parentheses around a redeclaration of T. Finally, the pattern also precludes declaring an array of functions versus a pointer to an array of functions, and returning arrays and functions versus pointers to arrays and functions. */ typedef_parameter_redeclarator: typedef attribute_list_opt | typedef_parameter_ptr | typedef_parameter_array attribute_list_opt | typedef_parameter_function attribute_list_opt ; typedef: TYPEDEFname { typedefTable.setNextIdentifier( *($1) ); $$ = DeclarationNode::newName( $1 ); } ; typedef_parameter_ptr: '*' typedef_parameter_redeclarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list typedef_parameter_redeclarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' typedef_parameter_ptr ')' { $$ = $2; } ; typedef_parameter_array: typedef array_parameter_dimension { $$ = $1->addArray( $2 ); } | '(' typedef_parameter_ptr ')' array_parameter_dimension { $$ = $2->addArray( $4 ); } ; typedef_parameter_function: typedef '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $1->addParamList( $4 ); } | '(' typedef_parameter_ptr ')' '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $2->addParamList( $6 ); } ; /* This pattern parses a declaration of an abstract variable or function prototype, i.e., there is no identifier to which the type applies, e.g.: sizeof( int ); sizeof( int [10] ); The pattern precludes declaring an array of functions versus a pointer to an array of functions, and returning arrays and functions versus pointers to arrays and functions. */ abstract_declarator: abstract_ptr | abstract_array attribute_list_opt | abstract_function attribute_list_opt ; abstract_ptr: '*' { $$ = DeclarationNode::newPointer( 0 ); } | '*' type_qualifier_list { $$ = DeclarationNode::newPointer( $2 ); } | '*' abstract_declarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list abstract_declarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' abstract_ptr ')' { $$ = $2; } ; abstract_array: array_dimension | '(' abstract_ptr ')' array_dimension { $$ = $2->addArray( $4 ); } | '(' abstract_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' abstract_array ')' /* redundant parenthesis */ { $$ = $2; } ; abstract_function: '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = DeclarationNode::newFunction( 0, 0, $3, 0 ); } | '(' abstract_ptr ')' '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $2->addParamList( $6 ); } | '(' abstract_function ')' /* redundant parenthesis */ { $$ = $2; } ; array_dimension: /* Only the first dimension can be empty. */ '[' push pop ']' { $$ = DeclarationNode::newArray( 0, 0, false ); } | '[' push pop ']' multi_array_dimension { $$ = DeclarationNode::newArray( 0, 0, false )->addArray( $5 ); } | multi_array_dimension ; multi_array_dimension: '[' push assignment_expression pop ']' { $$ = DeclarationNode::newArray( $3, 0, false ); } | '[' push '*' pop ']' /* C99 */ { $$ = DeclarationNode::newVarArray( 0 ); } | multi_array_dimension '[' push assignment_expression pop ']' { $$ = $1->addArray( DeclarationNode::newArray( $4, 0, false ) ); } | multi_array_dimension '[' push '*' pop ']' /* C99 */ { $$ = $1->addArray( DeclarationNode::newVarArray( 0 ) ); } ; /* This pattern parses a declaration of a parameter abstract variable or function prototype, i.e., there is no identifier to which the type applies, e.g.: int f( int ); // abstract variable parameter; no parameter name specified int f( int (int) ); // abstract function-prototype parameter; no parameter name specified The pattern precludes declaring an array of functions versus a pointer to an array of functions, and returning arrays and functions versus pointers to arrays and functions. */ abstract_parameter_declarator: abstract_parameter_ptr | abstract_parameter_array attribute_list_opt | abstract_parameter_function attribute_list_opt ; abstract_parameter_ptr: '*' { $$ = DeclarationNode::newPointer( 0 ); } | '*' type_qualifier_list { $$ = DeclarationNode::newPointer( $2 ); } | '*' abstract_parameter_declarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list abstract_parameter_declarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' abstract_parameter_ptr ')' { $$ = $2; } ; abstract_parameter_array: array_parameter_dimension | '(' abstract_parameter_ptr ')' array_parameter_dimension { $$ = $2->addArray( $4 ); } | '(' abstract_parameter_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' abstract_parameter_array ')' /* redundant parenthesis */ { $$ = $2; } ; abstract_parameter_function: '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = DeclarationNode::newFunction( 0, 0, $3, 0 ); } | '(' abstract_parameter_ptr ')' '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $2->addParamList( $6 ); } | '(' abstract_parameter_function ')' /* redundant parenthesis */ { $$ = $2; } ; array_parameter_dimension: /* Only the first dimension can be empty or have qualifiers. */ array_parameter_1st_dimension | array_parameter_1st_dimension multi_array_dimension { $$ = $1->addArray( $2 ); } | multi_array_dimension ; /* The declaration of an array parameter has additional syntax over arrays in normal variable declarations: ISO/IEC 9899:1999 Section 6.7.5.2(1) : "The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array type derivation." */ array_parameter_1st_dimension: '[' push pop ']' { $$ = DeclarationNode::newArray( 0, 0, false ); } // multi_array_dimension handles the '[' '*' ']' case | '[' push type_qualifier_list '*' pop ']' /* remaining C99 */ { $$ = DeclarationNode::newVarArray( $3 ); } | '[' push type_qualifier_list pop ']' { $$ = DeclarationNode::newArray( 0, $3, false ); } // multi_array_dimension handles the '[' assignment_expression ']' case | '[' push type_qualifier_list assignment_expression pop ']' { $$ = DeclarationNode::newArray( $4, $3, false ); } | '[' push STATIC type_qualifier_list_opt assignment_expression pop ']' { $$ = DeclarationNode::newArray( $5, $4, true ); } | '[' push type_qualifier_list STATIC assignment_expression pop ']' { $$ = DeclarationNode::newArray( $5, $3, true ); } ; /* This pattern parses a declaration of an abstract variable, i.e., there is no identifier to which the type applies, e.g.: sizeof( int ); // abstract variable; no identifier name specified The pattern precludes declaring an array of functions versus a pointer to an array of functions, and returning arrays and functions versus pointers to arrays and functions. */ variable_abstract_declarator: variable_abstract_ptr | variable_abstract_array attribute_list_opt | variable_abstract_function attribute_list_opt ; variable_abstract_ptr: '*' { $$ = DeclarationNode::newPointer( 0 ); } | '*' type_qualifier_list { $$ = DeclarationNode::newPointer( $2 ); } | '*' variable_abstract_declarator { $$ = $2->addPointer( DeclarationNode::newPointer( 0 ) ); } | '*' type_qualifier_list variable_abstract_declarator { $$ = $3->addPointer( DeclarationNode::newPointer( $2 ) ); } | '(' variable_abstract_ptr ')' { $$ = $2; } ; variable_abstract_array: array_dimension | '(' variable_abstract_ptr ')' array_dimension { $$ = $2->addArray( $4 ); } | '(' variable_abstract_array ')' multi_array_dimension /* redundant parenthesis */ { $$ = $2->addArray( $4 ); } | '(' variable_abstract_array ')' /* redundant parenthesis */ { $$ = $2; } ; variable_abstract_function: '(' variable_abstract_ptr ')' '(' push parameter_type_list_opt pop ')' /* empty parameter list OBSOLESCENT (see 3) */ { $$ = $2->addParamList( $6 ); } | '(' variable_abstract_function ')' /* redundant parenthesis */ { $$ = $2; } ; /* This pattern parses a new-style declaration for a parameter variable or function prototype that is either an identifier or typedef name and allows the C99 array options, which can only appear in a parameter list. */ new_identifier_parameter_declarator_tuple: /* CFA */ new_identifier_parameter_declarator_no_tuple | new_abstract_tuple | type_qualifier_list new_abstract_tuple { $$ = $2->addQualifiers( $1 ); } ; new_identifier_parameter_declarator_no_tuple: /* CFA */ new_identifier_parameter_ptr | new_identifier_parameter_array ; new_identifier_parameter_ptr: /* CFA */ '*' type_specifier { $$ = $2->addNewPointer( DeclarationNode::newPointer( 0 ) ); } | type_qualifier_list '*' type_specifier { $$ = $3->addNewPointer( DeclarationNode::newPointer( $1 ) ); } | '*' new_abstract_function { $$ = $2->addNewPointer( DeclarationNode::newPointer( 0 ) ); } | type_qualifier_list '*' new_abstract_function { $$ = $3->addNewPointer( DeclarationNode::newPointer( $1 ) ); } | '*' new_identifier_parameter_declarator_tuple { $$ = $2->addNewPointer( DeclarationNode::newPointer( 0 ) ); } | type_qualifier_list '*' new_identifier_parameter_declarator_tuple { $$ = $3->addNewPointer( DeclarationNode::newPointer( $1 ) ); } ; new_identifier_parameter_array: /* CFA */ /* Only the first dimension can be empty or have qualifiers. Empty dimension must be factored out due to shift/reduce conflict with new-style empty (void) function return type. */ '[' push pop ']' type_specifier { $$ = $5->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | new_array_parameter_1st_dimension type_specifier { $$ = $2->addNewArray( $1 ); } | '[' push pop ']' multi_array_dimension type_specifier { $$ = $6->addNewArray( $5 )->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | new_array_parameter_1st_dimension multi_array_dimension type_specifier { $$ = $3->addNewArray( $2 )->addNewArray( $1 ); } | multi_array_dimension type_specifier { $$ = $2->addNewArray( $1 ); } | '[' push pop ']' new_identifier_parameter_ptr { $$ = $5->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | new_array_parameter_1st_dimension new_identifier_parameter_ptr { $$ = $2->addNewArray( $1 ); } | '[' push pop ']' multi_array_dimension new_identifier_parameter_ptr { $$ = $6->addNewArray( $5 )->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | new_array_parameter_1st_dimension multi_array_dimension new_identifier_parameter_ptr { $$ = $3->addNewArray( $2 )->addNewArray( $1 ); } | multi_array_dimension new_identifier_parameter_ptr { $$ = $2->addNewArray( $1 ); } ; new_array_parameter_1st_dimension: '[' push type_qualifier_list '*' pop ']' /* remaining C99 */ { $$ = DeclarationNode::newVarArray( $3 ); } | '[' push type_qualifier_list assignment_expression pop ']' { $$ = DeclarationNode::newArray( $4, $3, false ); } | '[' push declaration_qualifier_list assignment_expression pop ']' /* declaration_qualifier_list must be used because of shift/reduce conflict with assignment_expression, so a semantic check is necessary to preclude them as a type_qualifier cannot appear in this context. */ { $$ = DeclarationNode::newArray( $4, $3, true ); } | '[' push declaration_qualifier_list type_qualifier_list assignment_expression pop ']' { $$ = DeclarationNode::newArray( $5, $4->addQualifiers( $3 ), true ); } ; /* This pattern parses a new-style declaration of an abstract variable or function prototype, i.e., there is no identifier to which the type applies, e.g.: [int] f( int ); // abstract variable parameter; no parameter name specified [int] f( [int] (int) ); // abstract function-prototype parameter; no parameter name specified These rules need LR(3): new_abstract_tuple identifier_or_typedef_name '[' new_parameter_list ']' identifier_or_typedef_name '(' new_parameter_type_list_opt ')' since a function return type can be syntactically identical to a tuple type: [int, int] t; [int, int] f( int ); Therefore, it is necessary to look at the token after identifier_or_typedef_name to know when to reduce new_abstract_tuple. To make this LR(1), several rules have to be flattened (lengthened) to allow the necessary lookahead. To accomplish this, new_abstract_declarator has an entry point without tuple, and tuple declarations are duplicated when appearing with new_function_specifier. */ new_abstract_declarator_tuple: /* CFA */ new_abstract_tuple | type_qualifier_list new_abstract_tuple { $$ = $2->addQualifiers( $1 ); } | new_abstract_declarator_no_tuple ; new_abstract_declarator_no_tuple: /* CFA */ new_abstract_ptr | new_abstract_array ; new_abstract_ptr: /* CFA */ '*' type_specifier { $$ = $2->addNewPointer( DeclarationNode::newPointer( 0 ) ); } | type_qualifier_list '*' type_specifier { $$ = $3->addNewPointer( DeclarationNode::newPointer( $1 ) ); } | '*' new_abstract_function { $$ = $2->addNewPointer( DeclarationNode::newPointer( 0 ) ); } | type_qualifier_list '*' new_abstract_function { $$ = $3->addNewPointer( DeclarationNode::newPointer( $1 ) ); } | '*' new_abstract_declarator_tuple { $$ = $2->addNewPointer( DeclarationNode::newPointer( 0 ) ); } | type_qualifier_list '*' new_abstract_declarator_tuple { $$ = $3->addNewPointer( DeclarationNode::newPointer( $1 ) ); } ; new_abstract_array: /* CFA */ /* Only the first dimension can be empty. Empty dimension must be factored out due to shift/reduce conflict with empty (void) function return type. */ '[' push pop ']' type_specifier { $$ = $5->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | '[' push pop ']' multi_array_dimension type_specifier { $$ = $6->addNewArray( $5 )->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | multi_array_dimension type_specifier { $$ = $2->addNewArray( $1 ); } | '[' push pop ']' new_abstract_ptr { $$ = $5->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | '[' push pop ']' multi_array_dimension new_abstract_ptr { $$ = $6->addNewArray( $5 )->addNewArray( DeclarationNode::newArray( 0, 0, false ) ); } | multi_array_dimension new_abstract_ptr { $$ = $2->addNewArray( $1 ); } ; new_abstract_tuple: /* CFA */ '[' push new_abstract_parameter_list pop ']' { $$ = DeclarationNode::newTuple( $3 ); } ; new_abstract_function: /* CFA */ '[' push pop ']' '(' new_parameter_type_list_opt ')' { $$ = DeclarationNode::newFunction( 0, DeclarationNode::newTuple( 0 ), $6, 0 ); } | new_abstract_tuple '(' push new_parameter_type_list_opt pop ')' { $$ = DeclarationNode::newFunction( 0, $1, $4, 0 ); } | new_function_return '(' push new_parameter_type_list_opt pop ')' { $$ = DeclarationNode::newFunction( 0, $1, $4, 0 ); } ; /* 1) ISO/IEC 9899:1999 Section 6.7.2(2) : "At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each structure declaration and type name." 2) ISO/IEC 9899:1999 Section 6.11.5(1) : "The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature." 3) ISO/IEC 9899:1999 Section 6.11.6(1) : "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature." 4) ISO/IEC 9899:1999 Section 6.11.7(1) : "The use of function definitions with separate parameter identifier and declaration lists (not prototype-format parameter type and identifier declarators) is an obsolescent feature." */ /************************* MISCELLANEOUS ********************************/ comma_opt: /* redundant comma */ /* empty */ | ',' ; assignment_opt: /* empty */ { $$ = 0; } | '=' assignment_expression { $$ = $2; } ; %% /* ----end of grammar----*/ void yyerror( char *string ) { using std::cout; using std::endl; cout << "Error "; if ( yyfilename ) { cout << "in file " << yyfilename << " "; } cout << "at line " << yylineno << " reading token \"" << *(yylval.tok.str) << "\"" << endl; } /* Local Variables: */ /* fill-column: 110 */ /* compile-command: "make install" */ /* End: */