%{ /******************************************************************************* * * MODULE: parser.y * ******************************************************************************** * * DESCRIPTION: C parser * ******************************************************************************** * * $Project: /Convert-Binary-C $ * $Author: mhx $ * $Date: 2006/11/01 18:58:55 +0100 $ * $Revision: 86 $ * $Source: /ctlib/parser.y $ * ******************************************************************************** * * Copyright (c) 2002-2006 Marcus Holland-Moritz. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the same terms as Perl itself. * * Portions Copyright (c) 1989, 1990 James A. Roskind. * Also see the original copyright notice below. * *******************************************************************************/ /* Copyright (C) 1989,1990 James A. Roskind, All rights reserved. This grammar was developed and written by James A. Roskind. Copying of this grammar description, as a whole, is permitted providing this notice is intact and applicable in all complete copies. Translations as a whole to other parser generator input languages (or grammar description languages) is permitted provided that this notice is intact and applicable in all such copies, along with a disclaimer that the contents are a translation. The reproduction of derived text, such as modified versions of this grammar, or the output of parser generators, is permitted, provided the resulting work includes the copyright notice "Portions Copyright (c) 1989, 1990 James A. Roskind". Derived products, such as compilers, translators, browsers, etc., that use this grammar, must also provide the notice "Portions Copyright (c) 1989, 1990 James A. Roskind" in a manner appropriate to the utility, and in keeping with copyright law (e.g.: EITHER displayed when first invoked/executed; OR displayed continuously on display terminal; OR via placement in the object code in form readable in a printout, with or near the title of the work, or at the end of the file). No royalties, licenses or commissions of any kind are required to copy this grammar, its translations, or derivative products, when the copies are made in compliance with this notice. Persons or corporations that do make copies in compliance with this notice may charge whatever price is agreeable to a buyer, for such copies or derivative works. THIS GRAMMAR IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. James A. Roskind Independent Consultant 516 Latania Palm Drive Indialantic FL, 32903 (407)729-4348 jar@ileaf.com ACKNOWLEDGMENT: Without the effort expended by the ANSI C standardizing committee, I would have been lost. Although the ANSI C standard does not include a fully disambiguated syntax description, the committee has at least provided most of the disambiguating rules in narratives. Several reviewers have also recently critiqued this grammar, and/or assisted in discussions during it's preparation. These reviewers are certainly not responsible for the errors I have committed here, but they are responsible for allowing me to provide fewer errors. These colleagues include: Bruce Blodgett, and Mark Langley. */ /*===== GLOBAL INCLUDES ======================================================*/ #include #include #include /*===== LOCAL INCLUDES =======================================================*/ #include "ctdebug.h" #include "ctparse.h" #include "cterror.h" #include "fileinfo.h" #include "parser.h" #include "pragma.h" #include "util/ccattr.h" #include "util/list.h" #include "util/memalloc.h" #include "ucpp/cpp.h" #include "cppreent.h" /*===== DEFINES ==============================================================*/ /* ADDITIONAL BISON CONFIGURATION */ #define YYMAXDEPTH 10000 #define YYPARSE_PARAM pState #define YYLEX_PARAM pState /* * Bison version >= 1.31 is needed for YYFPRINTF */ #if YYDEBUG && defined CTLIB_DEBUGGING #define YYFPRINTF BisonDebugFunc #endif #define c_error(msg) parser_error(PSTATE, msg) #define c_parse CTlib_c_parse /* MACROS */ #define PSTATE ((ParserState *) pState) #define DELETE_NODE(node) \ do { \ if (node != NULL) \ HN_delete(node); \ } while (0) #define POSTFIX_DECL(decl, postfix) \ do { \ if (postfix) \ { \ if (decl->pointer_flag) \ LL_destroy(postfix, (LLDestroyFunc) value_delete); \ else \ { \ if (decl->array_flag) \ LL_delete(LL_splice(decl->ext.array, 0, 0, postfix)); \ else \ { \ decl->array_flag = 1; \ decl->ext.array = postfix; \ } \ } \ } \ } while (0) #define MAKE_TYPEDEF(list, decl) \ do { \ Typedef *pTypedef = typedef_new(&(list->type), decl); \ CT_DEBUG(PARSER, ("making new typedef => %s (list @ %p)", \ decl->identifier, list)); \ LL_push(list->typedefs, pTypedef); \ HT_store(PSTATE->pCPI->htTypedefs, decl->identifier, 0, 0, pTypedef);\ } while (0) #define UNDEF_VAL(x) do { x.iv = 0; x.flags = V_IS_UNDEF; } while (0) #define UNARY_OP(result, op, val) \ do { result.iv = op val.iv; result.flags = val.flags; } while (0) #define BINARY_OP(result, val1, op, val2) \ do { \ result.iv = val1.iv op val2.iv; \ result.flags = val1.flags | val2.flags; \ } while (0) #define LLC_OR(t1, t2) \ ( \ ((t1) & T_LONG) && ((t2) & T_LONG) \ ? (t1) | (t2) | T_LONGLONG : (t1) | (t2) \ ) #define F_LOCAL 0x00000001U #define BEGIN_LOCAL (PSTATE->flags |= F_LOCAL) #define END_LOCAL (PSTATE->flags &= ~F_LOCAL) #define IS_LOCAL (PSTATE->flags & F_LOCAL) /*===== TYPEDEFS =============================================================*/ struct _parserState { const CParseConfig *pCPC; CParseInfo *pCPI; PragmaState pragma; struct CPP *pp; struct lexer_state *pLexer; FileInfo *pFI; u_32 flags; }; /*===== STATIC VARIABLES =====================================================*/ /* TOKEN MAPPING TABLE */ static const int tokentab[] = { 0, /* NONE, */ /* whitespace */ 0, /* NEWLINE, */ /* newline */ 0, /* COMMENT, */ /* comment */ 0, /* NUMBER, */ /* number constant */ 0, /* NAME, */ /* identifier */ 0, /* BUNCH, */ /* non-C characters */ 0, /* PRAGMA, */ /* a #pragma directive */ 0, /* CONTEXT, */ /* new file or #line */ 0, /* STRING, */ /* constant "xxx" */ CONSTANT, /* CHAR, */ /* constant 'xxx' */ '/', /* SLASH, */ /* / */ DIV_ASSIGN, /* ASSLASH, */ /* /= */ '-', /* MINUS, */ /* - */ DEC_OP, /* MMINUS, */ /* -- */ SUB_ASSIGN, /* ASMINUS, */ /* -= */ PTR_OP, /* ARROW, */ /* -> */ '+', /* PLUS, */ /* + */ INC_OP, /* PPLUS, */ /* ++ */ ADD_ASSIGN, /* ASPLUS, */ /* += */ '<', /* LT, */ /* < */ LE_OP, /* LEQ, */ /* <= */ LEFT_OP, /* LSH, */ /* << */ LEFT_ASSIGN, /* ASLSH, */ /* <<= */ '>', /* GT, */ /* > */ GE_OP, /* GEQ, */ /* >= */ RIGHT_OP, /* RSH, */ /* >> */ RIGHT_ASSIGN, /* ASRSH, */ /* >>= */ '=', /* ASGN, */ /* = */ EQ_OP, /* SAME, */ /* == */ #ifdef CAST_OP 0, /* CAST, */ /* => */ #endif '~', /* NOT, */ /* ~ */ NE_OP, /* NEQ, */ /* != */ '&', /* AND, */ /* & */ AND_OP, /* LAND, */ /* && */ AND_ASSIGN, /* ASAND, */ /* &= */ '|', /* OR, */ /* | */ OR_OP, /* LOR, */ /* || */ OR_ASSIGN, /* ASOR, */ /* |= */ '%', /* PCT, */ /* % */ MOD_ASSIGN, /* ASPCT, */ /* %= */ '*', /* STAR, */ /* * */ MUL_ASSIGN, /* ASSTAR, */ /* *= */ '^', /* CIRC, */ /* ^ */ XOR_ASSIGN, /* ASCIRC, */ /* ^= */ '!', /* LNOT, */ /* ! */ '{', /* LBRA, */ /* { */ '}', /* RBRA, */ /* } */ '[', /* LBRK, */ /* [ */ ']', /* RBRK, */ /* ] */ '(', /* LPAR, */ /* ( */ ')', /* RPAR, */ /* ) */ ',', /* COMMA, */ /* , */ '?', /* QUEST, */ /* ? */ ';', /* SEMIC, */ /* ; */ ':', /* COLON, */ /* : */ '.', /* DOT, */ /* . */ ELLIPSIS, /* MDOTS, */ /* ... */ 0, /* SHARP, */ /* # */ 0, /* DSHARP, */ /* ## */ 0, /* OPT_NONE, */ /* optional space to separate tokens in text output */ 0, /* DIGRAPH_TOKENS, */ /* there begin digraph tokens */ /* for DIG_*, do not change order, unless checking undig() in cpp.c */ '[', /* DIG_LBRK, */ /* <: */ ']', /* DIG_RBRK, */ /* :> */ '{', /* DIG_LBRA, */ /* <% */ '}', /* DIG_RBRA, */ /* %> */ 0, /* DIG_SHARP, */ /* %: */ 0, /* DIG_DSHARP, */ /* %:%: */ 0, /* DIGRAPH_TOKENS_END, */ /* digraph tokens end here */ 0, /* LAST_MEANINGFUL_TOKEN, */ /* reserved words will go there */ 0, /* MACROARG, */ /* special token for representing macro arguments */ 0, /* UPLUS = CPPERR, */ /* unary + */ 0, /* UMINUS */ /* unary - */ }; %} /*===== YACC PARSER DEFINITION ================================================*/ /* This refined grammar resolves several typedef ambiguities in the draft proposed ANSI C standard syntax down to 1 shift/reduce conflict, as reported by a YACC process. Note that the one shift reduce conflicts is the traditional if-if-else conflict that is not resolved by the grammar. This ambiguity can be removed using the method described in the Dragon Book (2nd edition), but this does not appear worth the effort. There was quite a bit of effort made to reduce the conflicts to this level, and an additional effort was made to make the grammar quite similar to the C++ grammar being developed in parallel. Note that this grammar resolves the following ANSI C ambiguity as follows: ANSI C section 3.5.6, "If the [typedef name] is redeclared at an inner scope, the type specifiers shall not be omitted in the inner declaration". Supplying type specifiers prevents consideration of T as a typedef name in this grammar. Failure to supply type specifiers forced the use of the TYPEDEFname as a type specifier. ANSI C section 3.5.4.3, "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". This is extended to cover the following cases: typedef float T; int noo(const (T[5])); int moo(const (T(int))); ... Where again the '(' immediately to the left of 'T' is interpreted as being the start of a parameter type list, and not as a redundant paren around a redeclaration of T. Hence an equivalent code fragment is: typedef float T; int noo(const int identifier1 (T identifier2 [5])); int moo(const int identifier1 (T identifier2 (int identifier3))); ... */ %union { HashNode identifier; Declarator *pDecl; AbstractDeclarator absDecl; StructDeclaration *pStructDecl; TypedefList *pTypedefList; LinkedList list; Enumerator *pEnum; Typedef *pTypedef; TypeSpec tspec; Value value; struct { u_32 uval; ContextInfo ctx; } context; u_32 uval; char oper; } %{ /*===== STATIC FUNCTION PROTOTYPES ===========================================*/ static inline int c_lex(YYSTYPE *plval, ParserState *pState); static inline int get_char_value(const char *s); static inline int string_size(const char *s); static inline int check_type(YYSTYPE *plval, ParserState *pState, const char *s); static void parser_error(ParserState *pState, const char *msg); %} /* Define terminal tokens */ /* keywords */ %token AUTO_TOK DOUBLE_TOK INT_TOK STRUCT_TOK %token BREAK_TOK ELSE_TOK LONG_TOK SWITCH_TOK %token CASE_TOK ENUM_TOK REGISTER_TOK TYPEDEF_TOK %token CHAR_TOK EXTERN_TOK RETURN_TOK UNION_TOK %token CONST_TOK FLOAT_TOK SHORT_TOK UNSIGNED_TOK %token CONTINUE_TOK FOR_TOK SIGNED_TOK VOID_TOK %token DEFAULT_TOK GOTO_TOK SIZEOF_TOK VOLATILE_TOK %token DO_TOK IF_TOK STATIC_TOK WHILE_TOK /* keywords new in ANSI-C99 */ %token INLINE_TOK RESTRICT_TOK /* special tokens */ %token ASM_TOK %token SKIP_TOK /* Multi-Character operators */ %token PTR_OP /* -> */ %token INC_OP DEC_OP /* ++ -- */ %token LEFT_OP RIGHT_OP /* << >> */ %token LE_OP GE_OP EQ_OP NE_OP /* <= >= == != */ %token AND_OP OR_OP /* && || */ %token ELLIPSIS /* ... */ /* modifying assignment operators */ %token MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN /* *= /= %= */ %token ADD_ASSIGN SUB_ASSIGN /* += -= */ %token LEFT_ASSIGN RIGHT_ASSIGN /* <<= >>= */ %token AND_ASSIGN XOR_ASSIGN OR_ASSIGN /* &= ^= |= */ /* ANSI Grammar suggestions */ %token STRING_LITERAL %token CONSTANT /* New Lexical element, whereas ANSI suggested non-terminal */ %token TYPE_NAME /* Lexer will tell the difference between this and an identifier! An identifier that is CURRENTLY in scope as a typedef name is provided to the parser as a TYPE_NAME.*/ %token IDENTIFIER %type identifier_or_typedef_name %destructor { if ($$) { CT_DEBUG(PARSER, ("deleting node @ %p", $$)); HN_delete($$); } } IDENTIFIER identifier_or_typedef_name %printer { if ($$) fprintf(yyoutput, "'%s' len=%d, hash=0x%lx", $$->key, $$->keylen, (unsigned long)$$->hash); else fprintf(yyoutput, "NULL"); } IDENTIFIER identifier_or_typedef_name %type unary_operator %type enumerator %type abstract_declarator unary_abstract_declarator postfix_abstract_declarator %type declaring_list default_declaring_list %type declaration_specifier sue_declaration_specifier typedef_declaration_specifier elaborated_type_name su_type_specifier sut_type_specifier sue_type_specifier enum_type_specifier typedef_type_specifier aggregate_name enum_name type_specifier %type member_declaration member_declaring_list unnamed_su_declaration %destructor { if ($$) { CT_DEBUG(PARSER, ("deleting struct declaration @ %p", $$)); structdecl_delete($$); } } member_declaration member_declaring_list unnamed_su_declaration %type identifier_declarator declarator member_declarator parameter_typedef_declarator typedef_declarator paren_typedef_declarator clean_typedef_declarator clean_postfix_typedef_declarator paren_postfix_typedef_declarator simple_paren_typedef_declarator unary_identifier_declarator paren_identifier_declarator postfix_identifier_declarator %destructor { if ($$) { CT_DEBUG(PARSER, ("deleting declarator @ %p", $$)); decl_delete($$); } } identifier_declarator declarator member_declarator parameter_typedef_declarator typedef_declarator paren_typedef_declarator clean_typedef_declarator clean_postfix_typedef_declarator paren_postfix_typedef_declarator simple_paren_typedef_declarator unary_identifier_declarator paren_identifier_declarator postfix_identifier_declarator %printer { if ($$) { if ($$->bitfield_flag) fprintf(yyoutput, "%s:%d", $$->identifier, $$->ext.bitfield.bits); else { fprintf(yyoutput, "%s%s", $$->pointer_flag ? "*" : "", $$->identifier); if ($$->array_flag) { ListIterator ai; Value *pValue; LL_foreach(pValue, ai, $$->ext.array) fprintf(yyoutput, "[%ld]", pValue->iv); } } } else fprintf(yyoutput, "NULL"); } identifier_declarator declarator member_declarator parameter_typedef_declarator typedef_declarator paren_typedef_declarator clean_typedef_declarator clean_postfix_typedef_declarator paren_postfix_typedef_declarator simple_paren_typedef_declarator unary_identifier_declarator paren_identifier_declarator postfix_identifier_declarator %type enumerator_list postfixing_abstract_declarator array_abstract_declarator member_declaration_list member_declaration_list_opt %destructor { if ($$) { CT_DEBUG(PARSER, ("deleting enumerator list @ %p", $$)); LL_destroy($$, (LLDestroyFunc) enum_delete); } } enumerator_list %destructor { if ($$) { CT_DEBUG(PARSER, ("deleting array list @ %p", $$)); LL_destroy($$, (LLDestroyFunc) value_delete); } } array_abstract_declarator postfixing_abstract_declarator %destructor { if ($$) { CT_DEBUG(PARSER, ("deleting struct declaration list @ %p", $$)); LL_destroy($$, (LLDestroyFunc) structdecl_delete); } } member_declaration_list member_declaration_list_opt %type basic_declaration_specifier declaration_qualifier_list basic_type_specifier storage_class basic_type_name declaration_qualifier aggregate_key %type aggregate_key_context enum_key_context %type string_literal_list primary_expression postfix_expression unary_expression cast_expression multiplicative_expression additive_expression shift_expression relational_expression equality_expression AND_expression exclusive_OR_expression inclusive_OR_expression logical_AND_expression logical_OR_expression conditional_expression assignment_expression assignment_expression_opt comma_expression constant_expression type_name bit_field_size bit_field_size_opt /*************************************************************************/ %expect 1 %pure-parser %error-verbose %start source_file /*************************************************************************/ %% string_literal_list : STRING_LITERAL | string_literal_list STRING_LITERAL { BINARY_OP($$, $1, +, $2); } ; /********************* ASSEMBLER DIRECTIVES ***************************/ asm_string : ASM_TOK '(' string_literal_list ')' ; asm_string_opt : /* nothing */ | asm_string ; asm_expr : ASM_TOK '(' comma_expression ')' ';' ; asm_statement : ASM_TOK type_qualifier_list_opt '(' comma_expression ')' ';' | ASM_TOK type_qualifier_list_opt '(' comma_expression ':' asm_operands_opt ')' ';' | ASM_TOK type_qualifier_list_opt '(' comma_expression ':' asm_operands_opt ':' asm_operands_opt ')' ';' | ASM_TOK type_qualifier_list_opt '(' comma_expression ':' asm_operands_opt ':' asm_operands_opt ':' asm_clobbers ')' ';' ; asm_operands_opt : /* nothing */ | asm_operands ; asm_operands : asm_operand | asm_operands ',' asm_operand ; asm_operand : STRING_LITERAL '(' comma_expression ')' | '[' IDENTIFIER ']' STRING_LITERAL '(' comma_expression ')' { if ($2) HN_delete($2); } ; asm_clobbers : string_literal_list | asm_clobbers ',' string_literal_list ; /************************* EXPRESSIONS ********************************/ primary_expression : IDENTIFIER /* We cannot use a typedef name as a variable */ { UNDEF_VAL($$); if ($1) { Enumerator *pEnum = HT_get(PSTATE->pCPI->htEnumerators, $1->key, $1->keylen, $1->hash); if (pEnum) { CT_DEBUG(CLEXER, ("enum found!")); $$ = pEnum->value; } HN_delete($1); } } | CONSTANT | string_literal_list { $$ = $1; $$.iv++; } | '(' comma_expression ')' { $$ = $2; } ; /* * We don't have to deal with postfix expressions currently, since a primary * expression (which the postfix expression is based on) cannot be a type, * but only a variable. And we don't support sizeof(variable) at the moment, * since all variables are discarded. */ postfix_expression : primary_expression | postfix_expression '[' comma_expression ']' { UNDEF_VAL($$); } | postfix_expression '(' ')' { UNDEF_VAL($$); } | postfix_expression '(' argument_expression_list ')' { UNDEF_VAL($$); } | postfix_expression {} '.' member_name { UNDEF_VAL($$); } | postfix_expression {} PTR_OP member_name { UNDEF_VAL($$); } | postfix_expression INC_OP { UNDEF_VAL($$); } | postfix_expression DEC_OP { UNDEF_VAL($$); } | '(' type_name ')' '{' initializer_list comma_opt '}' { UNDEF_VAL($$); } /* ANSI-C99 addition */ ; member_name : IDENTIFIER { if($1) HN_delete($1); } | TYPE_NAME {} ; argument_expression_list : assignment_expression {} | argument_expression_list ',' assignment_expression {} ; unary_expression : postfix_expression | INC_OP unary_expression { UNDEF_VAL($$); } | DEC_OP unary_expression { UNDEF_VAL($$); } | unary_operator cast_expression { switch( $1 ) { case '-' : UNARY_OP($$, -, $2); break; case '~' : UNARY_OP($$, ~, $2); break; case '!' : UNARY_OP($$, !, $2); break; case '+' : $$ = $2; break; case '*' : case '&' : $$ = $2; $$.flags |= V_IS_UNSAFE_PTROP; break; default: UNDEF_VAL($$); break; } } | SIZEOF_TOK unary_expression { $$ = $2; } | SIZEOF_TOK '(' type_name ')' { $$ = $3; } ; unary_operator : '&' { $$ = '&'; } | '*' { $$ = '*'; } | '+' { $$ = '+'; } | '-' { $$ = '-'; } | '~' { $$ = '~'; } | '!' { $$ = '!'; } ; cast_expression : unary_expression | '(' type_name ')' cast_expression { $$ = $4; $$.flags |= V_IS_UNSAFE_CAST; } ; multiplicative_expression : cast_expression | multiplicative_expression '*' cast_expression { BINARY_OP( $$, $1, *, $3 ); } | multiplicative_expression '/' cast_expression { if ($3.iv == 0) UNDEF_VAL($$); else BINARY_OP($$, $1, /, $3); } | multiplicative_expression '%' cast_expression { if ($3.iv == 0) UNDEF_VAL($$); else BINARY_OP($$, $1, %, $3); } ; additive_expression : multiplicative_expression | additive_expression '+' multiplicative_expression { BINARY_OP($$, $1, +, $3); } | additive_expression '-' multiplicative_expression { BINARY_OP($$, $1, -, $3); } ; shift_expression : additive_expression | shift_expression LEFT_OP additive_expression { BINARY_OP($$, $1, <<, $3); } | shift_expression RIGHT_OP additive_expression { BINARY_OP($$, $1, >>, $3); } ; relational_expression : shift_expression | relational_expression '<' shift_expression { BINARY_OP($$, $1, <, $3); } | relational_expression '>' shift_expression { BINARY_OP($$, $1, >, $3); } | relational_expression LE_OP shift_expression { BINARY_OP($$, $1, <=, $3); } | relational_expression GE_OP shift_expression { BINARY_OP($$, $1, >=, $3); } ; equality_expression : relational_expression | equality_expression EQ_OP relational_expression { BINARY_OP($$, $1, ==, $3); } | equality_expression NE_OP relational_expression { BINARY_OP($$, $1, !=, $3); } ; AND_expression : equality_expression | AND_expression '&' equality_expression { BINARY_OP($$, $1, &, $3); } ; exclusive_OR_expression : AND_expression | exclusive_OR_expression '^' AND_expression { BINARY_OP($$, $1, ^, $3); } ; inclusive_OR_expression : exclusive_OR_expression | inclusive_OR_expression '|' exclusive_OR_expression { BINARY_OP($$, $1, |, $3); } ; logical_AND_expression : inclusive_OR_expression | logical_AND_expression AND_OP inclusive_OR_expression { BINARY_OP($$, $1, &&, $3); } ; logical_OR_expression : logical_AND_expression | logical_OR_expression OR_OP logical_AND_expression { BINARY_OP($$, $1, ||, $3); } ; conditional_expression : logical_OR_expression | logical_OR_expression '?' comma_expression ':' conditional_expression { $$ = $1.iv ? $3 : $5; $$.flags |= $1.flags; } ; assignment_expression : conditional_expression | unary_expression assignment_operator assignment_expression { UNDEF_VAL($$); } ; assignment_operator : '=' {} | MUL_ASSIGN {} | DIV_ASSIGN {} | MOD_ASSIGN {} | ADD_ASSIGN {} | SUB_ASSIGN {} | LEFT_ASSIGN {} | RIGHT_ASSIGN {} | AND_ASSIGN {} | XOR_ASSIGN {} | OR_ASSIGN {} ; assignment_expression_opt : /* nothing */ { UNDEF_VAL($$); } | assignment_expression comma_expression : assignment_expression | comma_expression ',' assignment_expression { $$ = $3; } ; constant_expression : conditional_expression ; /* The following was used for clarity */ comma_expression_opt : /* Nothing */ | comma_expression {} ; /******************************* DECLARATIONS *********************************/ /* The following is different from the ANSI C specified grammar. The changes were made to disambiguate typedef's presence in declaration_specifiers (vs. in the declarator for redefinition); to allow struct/union/enum tag declarations without declarators, and to better reflect the parsing of declarations (declarators must be combined with declaration_specifiers ASAP so that they are visible in scope). Example of typedef use as either a declaration_specifier or a declarator: typedef int T; struct S { T T;}; / * redefinition of T as member name * / Example of legal and illegal statements detected by this grammar: int; / * syntax error: vacuous declaration * / struct S; / * no error: tag is defined or elaborated * / Example of result of proper declaration binding: int a=sizeof(a); / * note that "a" is declared with a type in the name space BEFORE parsing the initializer * / int b, c[sizeof(b)]; / * Note that the first declarator "b" is declared with a type BEFORE the second declarator is parsed * / */ declaration : sue_declaration_specifier ';' {} | sue_type_specifier ';' {} | declaring_list ';' {} | default_declaring_list ';' {} ; /* Note that if a typedef were redeclared, then a declaration specifier must be supplied */ default_declaring_list /* Can't redeclare typedef names */ : declaration_qualifier_list identifier_declarator asm_string_opt initializer_opt { if (IS_LOCAL) $$ = NULL; else { if ($1 & T_TYPEDEF) { TypeSpec ts; ts.tflags = $1; ts.ptr = NULL; if ((ts.tflags & ANY_TYPE_NAME) == 0) ts.tflags |= T_INT; $$ = typedef_list_new(ts, LL_new()); LL_push(PSTATE->pCPI->typedef_lists, $$); MAKE_TYPEDEF($$, $2); } else { $$ = NULL; decl_delete($2); } } } | type_qualifier_list identifier_declarator asm_string_opt initializer_opt { $$ = NULL; if ($2) decl_delete($2); } | default_declaring_list ',' identifier_declarator asm_string_opt initializer_opt { $$ = $1; if ($$) MAKE_TYPEDEF($$, $3); else if($3) decl_delete($3); } ; declaring_list : declaration_specifier declarator asm_string_opt initializer_opt { if (IS_LOCAL) $$ = NULL; else { if ($1.tflags & T_TYPEDEF) { if (($1.tflags & ANY_TYPE_NAME) == 0) $1.tflags |= T_INT; ctt_refcount_inc($1.ptr); $$ = typedef_list_new($1, LL_new()); LL_push(PSTATE->pCPI->typedef_lists, $$); MAKE_TYPEDEF($$, $2); } else { $$ = NULL; decl_delete($2); } } } | type_specifier declarator asm_string_opt initializer_opt { $$ = NULL; if ($2) decl_delete($2); } | declaring_list ',' declarator asm_string_opt initializer_opt { $$ = $1; if ($$) MAKE_TYPEDEF($$, $3); else if ($3) decl_delete($3); } ; /* those are all potential typedefs */ declaration_specifier : basic_declaration_specifier /* Arithmetic or void */ { $$.ptr = NULL; $$.tflags = $1; } | sue_declaration_specifier /* struct/union/enum */ | typedef_declaration_specifier /* typedef*/ ; /* those can't be typedefs */ type_specifier : basic_type_specifier /* Arithmetic or void */ { $$.ptr = NULL; $$.tflags = $1; } | sue_type_specifier /* Struct/Union/Enum */ | typedef_type_specifier /* Typedef */ ; /* those are all potential typedefs */ declaration_qualifier_list /* const/volatile, AND storage class */ : storage_class | type_qualifier_list storage_class { $$ = $2; } | declaration_qualifier_list declaration_qualifier { $$ = $1 | $2; } ; type_qualifier_list : type_qualifier | type_qualifier_list type_qualifier ; type_qualifier_list_opt : /* nothing */ | type_qualifier_list ; declaration_qualifier : storage_class | type_qualifier { $$ = 0; } /* const or volatile */ ; type_qualifier : CONST_TOK | RESTRICT_TOK | VOLATILE_TOK ; basic_declaration_specifier /* Storage Class+Arithmetic or void */ : declaration_qualifier_list basic_type_name { $$ = LLC_OR($1, $2); } | basic_type_specifier storage_class { $$ = LLC_OR($1, $2); } | basic_declaration_specifier declaration_qualifier { $$ = LLC_OR($1, $2); } | basic_declaration_specifier basic_type_name { $$ = LLC_OR($1, $2); } ; basic_type_specifier /* Arithmetic or void */ : basic_type_name | type_qualifier_list basic_type_name { $$ = $2; } | basic_type_specifier type_qualifier { $$ = $1; } | basic_type_specifier basic_type_name { $$ = LLC_OR($1, $2); } ; sue_declaration_specifier /* Storage Class + struct/union/enum */ : declaration_qualifier_list elaborated_type_name { $$.ptr = $2.ptr; $$.tflags = $2.tflags | $1; } | sue_type_specifier storage_class { $$.ptr = $1.ptr; $$.tflags = $1.tflags | $2; } | sue_declaration_specifier declaration_qualifier { $$.ptr = $1.ptr; $$.tflags = $1.tflags | $2; } ; sue_type_specifier /* struct/union/enum */ : su_type_specifier | enum_type_specifier ; enum_type_specifier /* enum */ : enum_name | type_qualifier_list enum_name { $$ = $2; } /* we don't care about */ | enum_type_specifier type_qualifier { $$ = $1; } /* type qualifiers */ ; su_type_specifier /* struct/union */ : aggregate_name | type_qualifier_list aggregate_name { $$ = $2; } /* we don't care about */ | su_type_specifier type_qualifier { $$ = $1; } /* type qualifiers */ ; sut_type_specifier /* struct/union/typedef */ : su_type_specifier | typedef_type_specifier typedef_declaration_specifier /* Storage Class + typedef types */ : typedef_type_specifier storage_class { $$.ptr = $1.ptr; $$.tflags = $1.tflags | $2; } | declaration_qualifier_list TYPE_NAME { $$.ptr = $2; $$.tflags = T_TYPE | $1; } | typedef_declaration_specifier declaration_qualifier { $$.ptr = $1.ptr; $$.tflags = $1.tflags | $2; } ; typedef_type_specifier /* typedef types */ : TYPE_NAME { $$.ptr = $1; $$.tflags = T_TYPE; } | type_qualifier_list TYPE_NAME { $$.ptr = $2; $$.tflags = T_TYPE; } /* we don't care about */ | typedef_type_specifier type_qualifier { $$ = $1; } /* type qualifiers */ ; storage_class : TYPEDEF_TOK { $$ = T_TYPEDEF; } | EXTERN_TOK { $$ = 0; } | STATIC_TOK { $$ = 0; } | AUTO_TOK { $$ = 0; } /* don't care about anything but typedefs */ | REGISTER_TOK { $$ = 0; } | INLINE_TOK { $$ = 0; } /* ANSI-C99 */ ; basic_type_name : INT_TOK { $$ = T_INT; } | CHAR_TOK { $$ = T_CHAR; } | SHORT_TOK { $$ = T_SHORT; } | LONG_TOK { $$ = T_LONG; } | FLOAT_TOK { $$ = T_FLOAT; } | DOUBLE_TOK { $$ = T_DOUBLE; } | SIGNED_TOK { $$ = T_SIGNED; } | UNSIGNED_TOK { $$ = T_UNSIGNED; } | VOID_TOK { $$ = T_VOID; } ; elaborated_type_name : aggregate_name | enum_name ; aggregate_name : aggregate_key_context '{' member_declaration_list_opt '}' { if (IS_LOCAL) { $$.tflags = 0; $$.ptr = NULL; } else { Struct *pStruct; pStruct = struct_new(NULL, 0, $1.uval, PSTATE->pragma.pack.current, $3); pStruct->context = $1.ctx; LL_push(PSTATE->pCPI->structs, pStruct); $$.tflags = $1.uval; $$.ptr = pStruct; } } | aggregate_key_context identifier_or_typedef_name '{' member_declaration_list_opt '}' { if (IS_LOCAL) { $$.tflags = 0; $$.ptr = NULL; /* identifier_or_typedef_name is NULL */ } else { Struct *pStruct = HT_get(PSTATE->pCPI->htStructs, $2->key, $2->keylen, $2->hash); if (pStruct == NULL) { pStruct = struct_new($2->key, $2->keylen, $1.uval, PSTATE->pragma.pack.current, $4); pStruct->context = $1.ctx; LL_push(PSTATE->pCPI->structs, pStruct); HT_storenode(PSTATE->pCPI->htStructs, $2, pStruct); } else { DELETE_NODE($2); if (pStruct->declarations == NULL) { pStruct->context = $1.ctx; pStruct->declarations = $4; pStruct->pack = PSTATE->pragma.pack.current; } else LL_destroy($4, (LLDestroyFunc) structdecl_delete); } $$.tflags = $1.uval; $$.ptr = pStruct; } } | aggregate_key_context identifier_or_typedef_name { if (IS_LOCAL) { $$.tflags = 0; $$.ptr = NULL; /* identifier_or_typedef_name is NULL */ } else { Struct *pStruct = HT_get(PSTATE->pCPI->htStructs, $2->key, $2->keylen, $2->hash); if (pStruct == NULL) { pStruct = struct_new($2->key, $2->keylen, $1.uval, 0, NULL); pStruct->context = $1.ctx; LL_push(PSTATE->pCPI->structs, pStruct); HT_storenode(PSTATE->pCPI->htStructs, $2, pStruct); } else DELETE_NODE($2); $$.tflags = $1.uval; $$.ptr = pStruct; } } ; aggregate_key_context : aggregate_key { $$.uval = $1; $$.ctx.pFI = PSTATE->pFI; $$.ctx.line = PSTATE->pLexer->ctok->line; } ; aggregate_key : STRUCT_TOK { $$ = T_STRUCT; } | UNION_TOK { $$ = T_UNION; } ; member_declaration_list_opt : /* nothing */ { $$ = IS_LOCAL ? NULL : LL_new(); } | member_declaration_list ; member_declaration_list : member_declaration { if (IS_LOCAL) $$ = NULL; else { ctt_refcount_inc($1->type.ptr); $$ = LL_new(); LL_push($$, $1); } } | member_declaration_list member_declaration { if (IS_LOCAL) $$ = NULL; else { ctt_refcount_inc($2->type.ptr); $$ = $1; LL_push($$, $2); } } ; member_declaration : member_declaring_list ';' | unnamed_su_declaration ';' ; unnamed_su_declaration : sut_type_specifier { $$ = IS_LOCAL ? NULL : structdecl_new($1, NULL); } ; member_declaring_list : type_specifier member_declarator { if (IS_LOCAL) $$ = NULL; else { if (($1.tflags & ANY_TYPE_NAME) == 0) $1.tflags |= T_INT; $$ = structdecl_new($1, LL_new()); if ($2) LL_push($$->declarators, $2); } } | member_declaring_list ',' member_declarator { if (IS_LOCAL) $$ = NULL; else { $$ = $1; if ($3) LL_push($$->declarators, $3); } } ; member_declarator : declarator bit_field_size_opt { if (IS_LOCAL) $$ = NULL; else { $$ = $1; if (($2.flags & V_IS_UNDEF) == 0) { if ($2.iv <= 0) { char *msg; AllocF(char *, msg, 80 + CTT_IDLEN($1)); sprintf(msg, "%s width for bit-field '%s'", $2.iv < 0 ? "negative" : "zero", $1->identifier); decl_delete($1); yyerror(msg); Free(msg); YYERROR; } $$->bitfield_flag = 1; $$->ext.bitfield.bits = (unsigned char) $2.iv; } } } | bit_field_size { if (IS_LOCAL) $$ = NULL; else { if ($1.iv < 0) { yyerror("negative width for bit-field"); YYERROR; } $$ = decl_new("", 0); $$->bitfield_flag = 1; $$->ext.bitfield.bits = (unsigned char) $1.iv; } } ; bit_field_size_opt : /* nothing */ { UNDEF_VAL($$); } | bit_field_size ; bit_field_size : ':' constant_expression { $$ = $2; } ; enum_name : enum_key_context '{' enumerator_list comma_opt '}' { if (IS_LOCAL) { $$.tflags = 0; $$.ptr = NULL; LL_destroy($3, (LLDestroyFunc) enum_delete); } else { EnumSpecifier *pEnum = enumspec_new(NULL, 0, $3); pEnum->context = $1.ctx; LL_push(PSTATE->pCPI->enums, pEnum); $$.tflags = T_ENUM; $$.ptr = pEnum; } } | enum_key_context identifier_or_typedef_name '{' enumerator_list comma_opt '}' { if (IS_LOCAL) { $$.tflags = 0; $$.ptr = NULL; /* identifier_or_typedef_name is NULL */ } else { EnumSpecifier *pEnum = HT_get(PSTATE->pCPI->htEnums, $2->key, $2->keylen, $2->hash); if (pEnum == NULL) { pEnum = enumspec_new($2->key, $2->keylen, $4); pEnum->context = $1.ctx; LL_push(PSTATE->pCPI->enums, pEnum); HT_storenode(PSTATE->pCPI->htEnums, $2, pEnum); } else { DELETE_NODE($2); if (pEnum->enumerators == NULL) { enumspec_update(pEnum, $4); pEnum->context = $1.ctx; } else LL_destroy($4, (LLDestroyFunc) enum_delete); } $$.tflags = T_ENUM; $$.ptr = pEnum; } } | enum_key_context identifier_or_typedef_name { if (IS_LOCAL) { $$.tflags = 0; $$.ptr = NULL; /* identifier_or_typedef_name is NULL */ } else { EnumSpecifier *pEnum = HT_get(PSTATE->pCPI->htEnums, $2->key, $2->keylen, $2->hash); if (pEnum == NULL) { pEnum = enumspec_new($2->key, $2->keylen, NULL); pEnum->context = $1.ctx; LL_push(PSTATE->pCPI->enums, pEnum); HT_storenode(PSTATE->pCPI->htEnums, $2, pEnum); } else { DELETE_NODE($2); } $$.tflags = T_ENUM; $$.ptr = pEnum; } } ; enum_key_context : ENUM_TOK { $$.ctx.pFI = PSTATE->pFI; $$.ctx.line = PSTATE->pLexer->ctok->line; } ; enumerator_list : enumerator { if (IS_LOCAL) $$ = NULL; else { $$ = LL_new(); if ($1->value.flags & V_IS_UNDEF) { $1->value.flags &= ~V_IS_UNDEF; $1->value.iv = 0; } LL_push($$, $1); } } | enumerator_list ',' enumerator { if (IS_LOCAL) $$ = NULL; else { if ($3->value.flags & V_IS_UNDEF) { Enumerator *pEnum = LL_get($1, -1); $3->value.flags = pEnum->value.flags; $3->value.iv = pEnum->value.iv + 1; } LL_push($1, $3); $$ = $1; } } ; enumerator : identifier_or_typedef_name { if (IS_LOCAL) { $$ = NULL; /* identifier_or_typedef_name is NULL */ } else { $$ = enum_new($1->key, $1->keylen, NULL); HT_storenode(PSTATE->pCPI->htEnumerators, $1, $$); } } | identifier_or_typedef_name '=' constant_expression { if (IS_LOCAL) { $$ = NULL; /* identifier_or_typedef_name is NULL */ } else { $$ = enum_new($1->key, $1->keylen, &$3); HT_storenode(PSTATE->pCPI->htEnumerators, $1, $$); } } ; parameter_type_list : parameter_list | parameter_list ',' ELLIPSIS ; parameter_list : parameter_declaration | parameter_list ',' parameter_declaration ; parameter_declaration : declaration_specifier {} | declaration_specifier abstract_declarator {} | declaration_specifier identifier_declarator { if ($2) decl_delete($2); } | declaration_specifier parameter_typedef_declarator { if ($2) decl_delete($2); } | declaration_qualifier_list {} | declaration_qualifier_list abstract_declarator {} | declaration_qualifier_list identifier_declarator { if ($2) decl_delete($2); } | type_specifier {} | type_specifier abstract_declarator {} | type_specifier identifier_declarator { if ($2) decl_delete($2); } | type_specifier parameter_typedef_declarator { if ($2) decl_delete($2); } | type_qualifier_list {} | type_qualifier_list abstract_declarator {} | type_qualifier_list identifier_declarator { if ($2) decl_delete($2); } ; /* ANSI C section 3.7.1 states "An identifier declared as a typedef name shall not be redeclared as a parameter". Hence the following is based only on IDENTIFIERs */ identifier_list : IDENTIFIER { if ($1) HN_delete($1); } | identifier_list ',' IDENTIFIER { if ($3) HN_delete($3); } ; identifier_or_typedef_name : IDENTIFIER | TYPE_NAME { $$ = IS_LOCAL ? NULL : HN_new($1->pDecl->identifier, CTT_IDLEN($1->pDecl), 0); } ; type_name : type_specifier { if (!IS_LOCAL) { unsigned size; u_32 flags; (void) PSTATE->pCPC->get_type_info(&PSTATE->pCPC->layout, &$1, NULL, "sf", &size, &flags); $$.iv = size; $$.flags = 0; if (flags & T_UNSAFE_VAL) $$.flags |= V_IS_UNSAFE; } } | type_specifier abstract_declarator { if (!IS_LOCAL) { if ($2.pointer_flag) { $$.iv = PSTATE->pCPC->layout.ptr_size * $2.multiplicator; $$.flags = 0; } else { unsigned size; u_32 flags; (void) PSTATE->pCPC->get_type_info(&PSTATE->pCPC->layout, &$1, NULL, "sf", &size, &flags); $$.iv = size * $2.multiplicator; $$.flags = 0; if (flags & T_UNSAFE_VAL) $$.flags |= V_IS_UNSAFE; } } } | type_qualifier_list { if (!IS_LOCAL) { $$.iv = PSTATE->pCPC->layout.int_size; $$.flags = 0; } } | type_qualifier_list abstract_declarator { if (!IS_LOCAL) { $$.iv = $2.multiplicator * ($2.pointer_flag ? PSTATE->pCPC->layout.ptr_size : PSTATE->pCPC->layout.int_size); $$.flags = 0; } } ; initializer_opt : /* nothing */ | '=' initializer ; initializer : '{' '}' | '{' initializer_list comma_opt '}' | assignment_expression {} ; initializer_list : designation_opt initializer | initializer_list ',' designation_opt initializer ; designation_opt : /* nothing */ | designator_list '=' ; designator_list : designator | designator_list designator ; designator : '[' constant_expression ']' | '.' identifier_or_typedef_name { DELETE_NODE($2); } ; comma_opt : /* nothing */ | ',' ; /*************************** STATEMENTS *******************************/ statement : labeled_statement | compound_statement | expression_statement | selection_statement | iteration_statement | jump_statement | asm_statement ; labeled_statement : identifier_or_typedef_name ':' statement { DELETE_NODE($1); } | CASE_TOK constant_expression ':' statement | DEFAULT_TOK ':' statement ; compound_statement : '{' '}' | '{' declaration_list '}' | '{' statement_list '}' | '{' declaration_list statement_list '}' ; declaration_list : declaration | declaration_list declaration ; statement_list : statement | statement_list statement ; expression_statement : comma_expression_opt ';' ; selection_statement : IF_TOK '(' comma_expression ')' statement | IF_TOK '(' comma_expression ')' statement ELSE_TOK statement | SWITCH_TOK '(' comma_expression ')' statement ; iteration_statement : WHILE_TOK '(' comma_expression ')' statement | DO_TOK statement WHILE_TOK '(' comma_expression ')' ';' | FOR_TOK '(' comma_expression_opt ';' comma_expression_opt ';' comma_expression_opt ')' statement ; jump_statement : GOTO_TOK identifier_or_typedef_name ';' { DELETE_NODE($2); } | CONTINUE_TOK ';' | BREAK_TOK ';' | RETURN_TOK comma_expression_opt ';' ; /***************************** EXTERNAL DEFINITIONS *****************************/ source_file : /* empty file */ | translation_unit ; translation_unit : external_definition | translation_unit external_definition ; external_definition : function_definition | declaration | asm_expr ; function_definition : identifier_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; decl_delete($1); } | declaration_specifier identifier_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; decl_delete($2); } | type_specifier identifier_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; decl_delete($2); } | declaration_qualifier_list identifier_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; decl_delete($2); } | type_qualifier_list identifier_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; decl_delete($2); } | old_function_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | declaration_specifier old_function_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | type_specifier old_function_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | declaration_qualifier_list old_function_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | type_qualifier_list old_function_declarator { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | old_function_declarator declaration_list { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | declaration_specifier old_function_declarator declaration_list { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | type_specifier old_function_declarator declaration_list { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | declaration_qualifier_list old_function_declarator declaration_list { BEGIN_LOCAL; } compound_statement { END_LOCAL; } | type_qualifier_list old_function_declarator declaration_list { BEGIN_LOCAL; } compound_statement { END_LOCAL; } ; declarator : identifier_declarator | typedef_declarator ; typedef_declarator : paren_typedef_declarator /* would be ambiguous as parameter*/ | parameter_typedef_declarator /* not ambiguous as param*/ ; parameter_typedef_declarator : TYPE_NAME { $$ = IS_LOCAL ? NULL : decl_new($1->pDecl->identifier, CTT_IDLEN($1->pDecl)); } | TYPE_NAME postfixing_abstract_declarator { if (IS_LOCAL) $$ = NULL; else { $$ = decl_new($1->pDecl->identifier, CTT_IDLEN($1->pDecl)); if ($2) { $$->array_flag = 1; $$->ext.array = $2; } } } | clean_typedef_declarator ; /* The following have at least one '*'. There is no (redundant) '(' between the '*' and the TYPE_NAME. */ clean_typedef_declarator : clean_postfix_typedef_declarator | '*' parameter_typedef_declarator { if ($2) $2->pointer_flag = 1; $$ = $2; } | '*' type_qualifier_list parameter_typedef_declarator { if ($3) $3->pointer_flag = 1; $$ = $3; } ; clean_postfix_typedef_declarator : '(' clean_typedef_declarator ')' { $$ = $2; } | '(' clean_typedef_declarator ')' postfixing_abstract_declarator { POSTFIX_DECL($2, $4); $$ = $2; } ; /* The following have a redundant '(' placed immediately to the left of the TYPE_NAME */ paren_typedef_declarator : paren_postfix_typedef_declarator | '*' '(' simple_paren_typedef_declarator ')' { if ($3) $3->pointer_flag = 1; $$ = $3; } | '*' type_qualifier_list '(' simple_paren_typedef_declarator ')' { if ($4) $4->pointer_flag = 1; $$ = $4; } | '*' paren_typedef_declarator { if ($2) $2->pointer_flag = 1; $$ = $2; } | '*' type_qualifier_list paren_typedef_declarator { if ($3) $3->pointer_flag = 1; $$ = $3; } ; paren_postfix_typedef_declarator : '(' paren_typedef_declarator ')' { $$ = $2; } | '(' simple_paren_typedef_declarator postfixing_abstract_declarator ')' { POSTFIX_DECL($2, $3); $$ = $2; } | '(' paren_typedef_declarator ')' postfixing_abstract_declarator { POSTFIX_DECL($2, $4); $$ = $2; } ; simple_paren_typedef_declarator : TYPE_NAME { $$ = IS_LOCAL ? NULL : decl_new($1->pDecl->identifier, CTT_IDLEN($1->pDecl)); } | '(' simple_paren_typedef_declarator ')' { $$ = $2; } ; identifier_declarator : unary_identifier_declarator | paren_identifier_declarator ; unary_identifier_declarator : postfix_identifier_declarator | '*' identifier_declarator { if ($2) $2->pointer_flag = 1; $$ = $2; } | '*' type_qualifier_list identifier_declarator { if ($3) $3->pointer_flag = 1; $$ = $3; } ; postfix_identifier_declarator : paren_identifier_declarator postfixing_abstract_declarator { POSTFIX_DECL($1, $2); $$ = $1; } | '(' unary_identifier_declarator ')' { $$ = $2; } | '(' unary_identifier_declarator ')' postfixing_abstract_declarator { POSTFIX_DECL($2, $4); $$ = $2; } ; paren_identifier_declarator : IDENTIFIER { if ($1) { $$ = decl_new($1->key, $1->keylen); HN_delete($1); } else { $$ = NULL; } } | '(' paren_identifier_declarator ')' { $$ = $2; } ; old_function_declarator : postfix_old_function_declarator {} | '*' old_function_declarator {} | '*' type_qualifier_list old_function_declarator {} ; postfix_old_function_declarator : paren_identifier_declarator '(' identifier_list ')' { if ($1) decl_delete($1); } | '(' old_function_declarator ')' {} | '(' old_function_declarator ')' postfixing_abstract_declarator { if ($4) LL_destroy($4, (LLDestroyFunc) value_delete); } ; abstract_declarator : unary_abstract_declarator | postfix_abstract_declarator | postfixing_abstract_declarator { $$.pointer_flag = 0; $$.multiplicator = 1; if ($1) { ListIterator ai; Value *pValue; LL_foreach(pValue, ai, $1) $$.multiplicator *= pValue->iv; LL_destroy($1, (LLDestroyFunc) value_delete); } } ; postfixing_abstract_declarator : array_abstract_declarator | '(' ')' { $$ = NULL; } | '(' parameter_type_list ')' { $$ = NULL; } ; array_abstract_declarator : '[' type_qualifier_list_opt assignment_expression_opt ']' { if (IS_LOCAL) $$ = NULL; else { $$ = LL_new(); LL_push($$, value_new($3.iv, $3.flags)); CT_DEBUG(PARSER, ("array dimension => %ld", $3.iv)); } } | '[' STATIC_TOK type_qualifier_list_opt assignment_expression ']' { if (IS_LOCAL) $$ = NULL; else { $$ = LL_new(); LL_push($$, value_new($4.iv, $4.flags)); CT_DEBUG(PARSER, ("array dimension => %ld", $4.iv)); } } | '[' type_qualifier_list STATIC_TOK assignment_expression ']' { if (IS_LOCAL) $$ = NULL; else { $$ = LL_new(); LL_push($$, value_new($4.iv, $4.flags)); CT_DEBUG(PARSER, ("array dimension => %ld", $4.iv)); } } | '[' type_qualifier_list_opt '*' ']' { $$ = NULL; } | array_abstract_declarator '[' assignment_expression ']' { if (IS_LOCAL) $$ = NULL; else { $$ = $1 ? $1 : LL_new(); LL_push($$, value_new($3.iv, $3.flags)); CT_DEBUG(PARSER, ("array dimension => %ld", $3.iv)); } } | array_abstract_declarator '[' '*' ']' { if (IS_LOCAL) $$ = NULL; else { $$ = $1 ? $1 : LL_new(); LL_push($$, value_new(0, 0)); CT_DEBUG(PARSER, ("array dimension => *" )); } } ; unary_abstract_declarator : '*' { $$.pointer_flag = 1; $$.multiplicator = 1; } | '*' type_qualifier_list { $$.pointer_flag = 1; $$.multiplicator = 1; } | '*' abstract_declarator { $2.pointer_flag = 1; $$ = $2; } | '*' type_qualifier_list abstract_declarator { $3.pointer_flag = 1; $$ = $3; } ; postfix_abstract_declarator : '(' unary_abstract_declarator ')' { $$ = $2; } | '(' postfix_abstract_declarator ')' { $$ = $2; } | '(' postfixing_abstract_declarator ')' { $$.pointer_flag = 0; $$.multiplicator = 1; if ($2) { ListIterator ai; Value *pValue; LL_foreach(pValue, ai, $2) $$.multiplicator *= pValue->iv; LL_destroy($2, (LLDestroyFunc) value_delete); } } | '(' unary_abstract_declarator ')' postfixing_abstract_declarator { $$ = $2; if ($4) LL_destroy($4, (LLDestroyFunc) value_delete); } ; %% /*===== STATIC FUNCTIONS =====================================================*/ /******************************************************************************* * * ROUTINE: c_lex * * WRITTEN BY: Marcus Holland-Moritz ON: Jan 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: C lexer. * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ static inline int c_lex(YYSTYPE *plval, ParserState *pState) { int rval, token; struct lexer_state *pLexer = pState->pLexer; dUCPP(pState->pp); CT_DEBUG(CLEXER, ("parser.y::c_lex()")); while ((rval = lex(aUCPP_ pLexer)) < CPPERR_EOF) { if (rval) { CT_DEBUG(CLEXER, ("lex() returned %d", rval)); continue; } token = pLexer->ctok->type; switch (token) { case NONE: CT_DEBUG(CLEXER, ("token-type => NONE")); break; case COMMENT: CT_DEBUG(CLEXER, ("token-type => COMMENT => [%s]", pLexer->ctok->name)); break; case NEWLINE: CT_DEBUG(CLEXER, ("token-type => NEWLINE")); break; case BUNCH: CT_DEBUG(CLEXER, ("token-type => BUNCH => [%s]", pLexer->ctok->name)); break; case CONTEXT: CT_DEBUG(CLEXER, ("token-type => CONTEXT => [%s]", pLexer->ctok->name)); { FileInfo *pFI; size_t len = strlen(pLexer->ctok->name); CT_DEBUG(CLEXER, ("new context: file '%s', line %ld", pLexer->ctok->name, pLexer->ctok->line)); pFI = HT_get(pState->pCPI->htFiles, pLexer->ctok->name, len, 0); if (pFI == NULL) { pFI = fileinfo_new(pLexer->input, pLexer->ctok->name, len); HT_store(pState->pCPI->htFiles, pLexer->ctok->name, len, 0, pFI); } pState->pFI = pFI; } break; case NUMBER: CT_DEBUG(CLEXER, ("token-type => NUMBER => [%s]", pLexer->ctok->name)); plval->value.iv = strtol(pLexer->ctok->name, NULL, 0); plval->value.flags = 0; CT_DEBUG(CLEXER, ("constant: %s -> %ld", pLexer->ctok->name, plval->value.iv)); return CONSTANT; case STRING: CT_DEBUG(CLEXER, ("token-type => STRING => [%s]", pLexer->ctok->name)); plval->value.iv = string_size(pLexer->ctok->name); plval->value.flags = 0; CT_DEBUG(CLEXER, ("string literal: %s -> %ld", pLexer->ctok->name, plval->value.iv)); return STRING_LITERAL; case CHAR: CT_DEBUG(CLEXER, ("token-type => CHAR => [%s]", pLexer->ctok->name)); plval->value.iv = get_char_value(pLexer->ctok->name); plval->value.flags = 0; CT_DEBUG(CLEXER, ("constant: %s -> %ld", pLexer->ctok->name, plval->value.iv)); return CONSTANT; case PRAGMA: CT_DEBUG(CLEXER, ("token-type => PRAGMA")); CT_DEBUG(CLEXER, ("line %ld: <#pragma>", pLexer->line)); pState->pragma.str = pLexer->ctok->name; pragma_parse( &pState->pragma ); CT_DEBUG(CLEXER, ("current packing: %d\n", pState->pragma.pack.current)); break; case NAME: CT_DEBUG(CLEXER, ("token-type => NAME => [%s]", pLexer->ctok->name)); { char *tokstr = pLexer->ctok->name; const CKeywordToken *ckt; #include "token/t_parser.c" unknown: if ((ckt = HT_get(pState->pCPC->keyword_map, tokstr, 0, 0)) != NULL) { if (ckt->token == SKIP_TOK) { CT_DEBUG(CLEXER, ("skipping token '%s' in line %ld", tokstr, pLexer->line)); break; } return ckt->token; } return check_type(plval, pState, tokstr); } default: CT_DEBUG(CLEXER, ("token-type => %d", token)); if ((rval = tokentab[token]) != 0) return rval; CT_DEBUG(CLEXER, ("unhandled token in line %ld: <%2d>", pLexer->line, token)); break; } } CT_DEBUG(CLEXER, ("EOF!")); return 0; } /******************************************************************************* * * ROUTINE: parser_error * * WRITTEN BY: Marcus Holland-Moritz ON: Jan 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ static void parser_error(ParserState *pState, const char *msg) { push_error(pState->pCPI, "%s, line %ld: %s", pState->pFI ? pState->pFI->name : "[unknown]", pState->pLexer->ctok->line, msg); } /******************************************************************************* * * ROUTINE: get_char_value * * WRITTEN BY: Marcus Holland-Moritz ON: Jan 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ static inline int get_char_value(const char *s) { while (*s && *s != '\'') s++; if (*++s != '\\') return (int) *s; switch (*++s) { case '0' : case '1' : case '2' : case '3' : return (int) strtol(s, NULL, 8); case 'a' : return (int) '\a'; case 'b' : return (int) '\b'; case 'f' : return (int) '\f'; case 'h' : return (int) strtol(++s, NULL, 16); case 'n' : return (int) '\n'; case 'r' : return (int) '\r'; case 't' : return (int) '\t'; case 'v' : return (int) '\v'; default: return (int) *s; } } /******************************************************************************* * * ROUTINE: string_size * * WRITTEN BY: Marcus Holland-Moritz ON: Jan 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ static inline int string_size(const char *s) { int size, count; while (*s && *s != '\"') s++; for (s++, size=0; *s; size++) { if (*s == '\"') break; if (*s++ != '\\') continue; if (*s == 'x') { count = 0; do s++; while (count++ < 2 && ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F'))); continue; } if (*s >= '0' && *s <= '7') { count = 0; do s++; while (count++ < 2 && *s >= '0' && *s <= '7'); } else s++; } return size; } /******************************************************************************* * * ROUTINE: check_type * * WRITTEN BY: Marcus Holland-Moritz ON: Jan 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ static inline int check_type(YYSTYPE *plval, ParserState *pState, const char *s) { Typedef *pTypedef; HashSum hash; int len; CT_DEBUG(CLEXER, ("check_type( \"%s\" )", s)); HASH_STR_LEN(hash, s, len); pTypedef = HT_get(pState->pCPI->htTypedefs, s, len, hash); if (pTypedef) { CT_DEBUG(CLEXER, ("typedef found!")); plval->pTypedef = pTypedef; return TYPE_NAME; } plval->identifier = pState->flags & F_LOCAL ? NULL : HN_new(s, len, hash); return IDENTIFIER; } /*===== FUNCTIONS ============================================================*/ /******************************************************************************* * * ROUTINE: get_c_keyword_token * * WRITTEN BY: Marcus Holland-Moritz ON: Dec 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: Create a new C parser. * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ const CKeywordToken *get_c_keyword_token(const char *name) { #include "token/t_ckeytok.c" unknown: return NULL; } /******************************************************************************* * * ROUTINE: get_skip_token * * WRITTEN BY: Marcus Holland-Moritz ON: Dec 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: Create a new C parser. * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ const CKeywordToken *get_skip_token(void) { static const CKeywordToken ckt = { SKIP_TOK, NULL }; return &ckt; } /******************************************************************************* * * ROUTINE: c_parser_new * * WRITTEN BY: Marcus Holland-Moritz ON: Dec 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: Create a new C parser. * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ ParserState *c_parser_new(const CParseConfig *pCPC, CParseInfo *pCPI, pUCPP_ struct lexer_state *pLexer) { ParserState *pState; #ifdef CTLIB_DEBUGGING #ifdef YYDEBUG extern int pragma_debug; c_debug = pragma_debug = DEBUG_FLAG(YACC) ? 1 : 0; #endif #endif if (pCPC == NULL || pCPI == NULL || pLexer == NULL) return NULL; AllocF(ParserState *, pState, sizeof(ParserState)); pState->pCPI = pCPI; pState->pCPC = pCPC; pState->pLexer = pLexer; pState->pp = aUCPP; pState->flags = 0; pState->pFI = NULL; pragma_init(&pState->pragma); return pState; } /******************************************************************************* * * ROUTINE: c_parser_run * * WRITTEN BY: Marcus Holland-Moritz ON: Dec 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: Run the C parser. * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ int c_parser_run(ParserState *pState) { return c_parse((void *) pState); } /******************************************************************************* * * ROUTINE: c_parser_delete * * WRITTEN BY: Marcus Holland-Moritz ON: Dec 2002 * CHANGED BY: ON: * ******************************************************************************** * * DESCRIPTION: Delete a C parser object. * * ARGUMENTS: * * RETURNS: * *******************************************************************************/ void c_parser_delete(ParserState *pState) { if (pState == NULL) return; pragma_free(&pState->pragma); Free(pState); }