474 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			474 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| /* C global declaration parser for genksyms.
 | |
|    Copyright 1996, 1997 Linux International.
 | |
| 
 | |
|    New implementation contributed by Richard Henderson <rth@tamu.edu>
 | |
|    Based on original work by Bjorn Ekwall <bj0rn@blox.se>
 | |
| 
 | |
|    This file is part of the Linux modutils.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify it
 | |
|    under the terms of the GNU General Public License as published by the
 | |
|    Free Software Foundation; either version 2 of the License, or (at your
 | |
|    option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful, but
 | |
|    WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software Foundation,
 | |
|    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 | |
| 
 | |
| 
 | |
| %{
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <malloc.h>
 | |
| #include "genksyms.h"
 | |
| 
 | |
| static int is_typedef;
 | |
| static int is_extern;
 | |
| static char *current_name;
 | |
| static struct string_list *decl_spec;
 | |
| 
 | |
| static void yyerror(const char *);
 | |
| 
 | |
| static inline void
 | |
| remove_node(struct string_list **p)
 | |
| {
 | |
|   struct string_list *node = *p;
 | |
|   *p = node->next;
 | |
|   free_node(node);
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| remove_list(struct string_list **pb, struct string_list **pe)
 | |
| {
 | |
|   struct string_list *b = *pb, *e = *pe;
 | |
|   *pb = e;
 | |
|   free_list(b, e);
 | |
| }
 | |
| 
 | |
| %}
 | |
| 
 | |
| %token ASM_KEYW
 | |
| %token ATTRIBUTE_KEYW
 | |
| %token AUTO_KEYW
 | |
| %token BOOL_KEYW
 | |
| %token CHAR_KEYW
 | |
| %token CONST_KEYW
 | |
| %token DOUBLE_KEYW
 | |
| %token ENUM_KEYW
 | |
| %token EXTERN_KEYW
 | |
| %token EXTENSION_KEYW
 | |
| %token FLOAT_KEYW
 | |
| %token INLINE_KEYW
 | |
| %token INT_KEYW
 | |
| %token LONG_KEYW
 | |
| %token REGISTER_KEYW
 | |
| %token RESTRICT_KEYW
 | |
| %token SHORT_KEYW
 | |
| %token SIGNED_KEYW
 | |
| %token STATIC_KEYW
 | |
| %token STRUCT_KEYW
 | |
| %token TYPEDEF_KEYW
 | |
| %token UNION_KEYW
 | |
| %token UNSIGNED_KEYW
 | |
| %token VOID_KEYW
 | |
| %token VOLATILE_KEYW
 | |
| %token TYPEOF_KEYW
 | |
| 
 | |
| %token EXPORT_SYMBOL_KEYW
 | |
| 
 | |
| %token ASM_PHRASE
 | |
| %token ATTRIBUTE_PHRASE
 | |
| %token BRACE_PHRASE
 | |
| %token BRACKET_PHRASE
 | |
| %token EXPRESSION_PHRASE
 | |
| 
 | |
| %token CHAR
 | |
| %token DOTS
 | |
| %token IDENT
 | |
| %token INT
 | |
| %token REAL
 | |
| %token STRING
 | |
| %token TYPE
 | |
| %token OTHER
 | |
| %token FILENAME
 | |
| 
 | |
| %%
 | |
| 
 | |
| declaration_seq:
 | |
| 	declaration
 | |
| 	| declaration_seq declaration
 | |
| 	;
 | |
| 
 | |
| declaration:
 | |
| 	{ is_typedef = 0; is_extern = 0; current_name = NULL; decl_spec = NULL; }
 | |
| 	declaration1
 | |
| 	{ free_list(*$2, NULL); *$2 = NULL; }
 | |
| 	;
 | |
| 
 | |
| declaration1:
 | |
| 	EXTENSION_KEYW TYPEDEF_KEYW { is_typedef = 1; } simple_declaration
 | |
| 		{ $$ = $4; }
 | |
| 	| TYPEDEF_KEYW { is_typedef = 1; } simple_declaration
 | |
| 		{ $$ = $3; }
 | |
| 	| simple_declaration
 | |
| 	| function_definition
 | |
| 	| asm_definition
 | |
| 	| export_definition
 | |
| 	| error ';'				{ $$ = $2; }
 | |
| 	| error '}'				{ $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| simple_declaration:
 | |
| 	decl_specifier_seq_opt init_declarator_list_opt ';'
 | |
| 		{ if (current_name) {
 | |
| 		    struct string_list *decl = (*$3)->next;
 | |
| 		    (*$3)->next = NULL;
 | |
| 		    add_symbol(current_name,
 | |
| 			       is_typedef ? SYM_TYPEDEF : SYM_NORMAL,
 | |
| 			       decl, is_extern);
 | |
| 		    current_name = NULL;
 | |
| 		  }
 | |
| 		  $$ = $3;
 | |
| 		}
 | |
| 	;
 | |
| 
 | |
| init_declarator_list_opt:
 | |
| 	/* empty */				{ $$ = NULL; }
 | |
| 	| init_declarator_list
 | |
| 	;
 | |
| 
 | |
| init_declarator_list:
 | |
| 	init_declarator
 | |
| 		{ struct string_list *decl = *$1;
 | |
| 		  *$1 = NULL;
 | |
| 		  add_symbol(current_name,
 | |
| 			     is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
 | |
| 		  current_name = NULL;
 | |
| 		  $$ = $1;
 | |
| 		}
 | |
| 	| init_declarator_list ',' init_declarator
 | |
| 		{ struct string_list *decl = *$3;
 | |
| 		  *$3 = NULL;
 | |
| 		  free_list(*$2, NULL);
 | |
| 		  *$2 = decl_spec;
 | |
| 		  add_symbol(current_name,
 | |
| 			     is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
 | |
| 		  current_name = NULL;
 | |
| 		  $$ = $3;
 | |
| 		}
 | |
| 	;
 | |
| 
 | |
| init_declarator:
 | |
| 	declarator asm_phrase_opt attribute_opt initializer_opt
 | |
| 		{ $$ = $4 ? $4 : $3 ? $3 : $2 ? $2 : $1; }
 | |
| 	;
 | |
| 
 | |
| /* Hang on to the specifiers so that we can reuse them.  */
 | |
| decl_specifier_seq_opt:
 | |
| 	/* empty */				{ decl_spec = NULL; }
 | |
| 	| decl_specifier_seq
 | |
| 	;
 | |
| 
 | |
| decl_specifier_seq:
 | |
| 	decl_specifier				{ decl_spec = *$1; }
 | |
| 	| decl_specifier_seq decl_specifier	{ decl_spec = *$2; }
 | |
| 	;
 | |
| 
 | |
| decl_specifier:
 | |
| 	storage_class_specifier
 | |
| 		{ /* Version 2 checksumming ignores storage class, as that
 | |
| 		     is really irrelevant to the linkage.  */
 | |
| 		  remove_node($1);
 | |
| 		  $$ = $1;
 | |
| 		}
 | |
| 	| type_specifier
 | |
| 	;
 | |
| 
 | |
| storage_class_specifier:
 | |
| 	AUTO_KEYW
 | |
| 	| REGISTER_KEYW
 | |
| 	| STATIC_KEYW
 | |
| 	| EXTERN_KEYW	{ is_extern = 1; $$ = $1; }
 | |
| 	| INLINE_KEYW	{ is_extern = 0; $$ = $1; }
 | |
| 	;
 | |
| 
 | |
| type_specifier:
 | |
| 	simple_type_specifier
 | |
| 	| cvar_qualifier
 | |
| 	| TYPEOF_KEYW '(' decl_specifier_seq '*' ')'
 | |
| 	| TYPEOF_KEYW '(' decl_specifier_seq ')'
 | |
| 
 | |
| 	/* References to s/u/e's defined elsewhere.  Rearrange things
 | |
| 	   so that it is easier to expand the definition fully later.  */
 | |
| 	| STRUCT_KEYW IDENT
 | |
| 		{ remove_node($1); (*$2)->tag = SYM_STRUCT; $$ = $2; }
 | |
| 	| UNION_KEYW IDENT
 | |
| 		{ remove_node($1); (*$2)->tag = SYM_UNION; $$ = $2; }
 | |
| 	| ENUM_KEYW IDENT
 | |
| 		{ remove_node($1); (*$2)->tag = SYM_ENUM; $$ = $2; }
 | |
| 
 | |
| 	/* Full definitions of an s/u/e.  Record it.  */
 | |
| 	| STRUCT_KEYW IDENT class_body
 | |
| 		{ struct string_list *s = *$3, *i = *$2, *r;
 | |
| 		  r = copy_node(i); r->tag = SYM_STRUCT;
 | |
| 		  r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL;
 | |
| 		  add_symbol(i->string, SYM_STRUCT, s, is_extern);
 | |
| 		  $$ = $3;
 | |
| 		}
 | |
| 	| UNION_KEYW IDENT class_body
 | |
| 		{ struct string_list *s = *$3, *i = *$2, *r;
 | |
| 		  r = copy_node(i); r->tag = SYM_UNION;
 | |
| 		  r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL;
 | |
| 		  add_symbol(i->string, SYM_UNION, s, is_extern);
 | |
| 		  $$ = $3;
 | |
| 		}
 | |
| 	| ENUM_KEYW IDENT BRACE_PHRASE
 | |
| 		{ struct string_list *s = *$3, *i = *$2, *r;
 | |
| 		  r = copy_node(i); r->tag = SYM_ENUM;
 | |
| 		  r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL;
 | |
| 		  add_symbol(i->string, SYM_ENUM, s, is_extern);
 | |
| 		  $$ = $3;
 | |
| 		}
 | |
| 
 | |
| 	/* Anonymous s/u/e definitions.  Nothing needs doing.  */
 | |
| 	| ENUM_KEYW BRACE_PHRASE			{ $$ = $2; }
 | |
| 	| STRUCT_KEYW class_body			{ $$ = $2; }
 | |
| 	| UNION_KEYW class_body				{ $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| simple_type_specifier:
 | |
| 	CHAR_KEYW
 | |
| 	| SHORT_KEYW
 | |
| 	| INT_KEYW
 | |
| 	| LONG_KEYW
 | |
| 	| SIGNED_KEYW
 | |
| 	| UNSIGNED_KEYW
 | |
| 	| FLOAT_KEYW
 | |
| 	| DOUBLE_KEYW
 | |
| 	| VOID_KEYW
 | |
| 	| BOOL_KEYW
 | |
| 	| TYPE			{ (*$1)->tag = SYM_TYPEDEF; $$ = $1; }
 | |
| 	;
 | |
| 
 | |
| ptr_operator:
 | |
| 	'*' cvar_qualifier_seq_opt
 | |
| 		{ $$ = $2 ? $2 : $1; }
 | |
| 	;
 | |
| 
 | |
| cvar_qualifier_seq_opt:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| cvar_qualifier_seq
 | |
| 	;
 | |
| 
 | |
| cvar_qualifier_seq:
 | |
| 	cvar_qualifier
 | |
| 	| cvar_qualifier_seq cvar_qualifier		{ $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| cvar_qualifier:
 | |
| 	CONST_KEYW | VOLATILE_KEYW | ATTRIBUTE_PHRASE
 | |
| 	| RESTRICT_KEYW
 | |
| 		{ /* restrict has no effect in prototypes so ignore it */
 | |
| 		  remove_node($1);
 | |
| 		  $$ = $1;
 | |
| 		}
 | |
| 	;
 | |
| 
 | |
| declarator:
 | |
| 	ptr_operator declarator			{ $$ = $2; }
 | |
| 	| direct_declarator
 | |
| 	;
 | |
| 
 | |
| direct_declarator:
 | |
| 	IDENT
 | |
| 		{ if (current_name != NULL) {
 | |
| 		    error_with_pos("unexpected second declaration name");
 | |
| 		    YYERROR;
 | |
| 		  } else {
 | |
| 		    current_name = (*$1)->string;
 | |
| 		    $$ = $1;
 | |
| 		  }
 | |
| 		}
 | |
| 	| direct_declarator '(' parameter_declaration_clause ')'
 | |
| 		{ $$ = $4; }
 | |
| 	| direct_declarator '(' error ')'
 | |
| 		{ $$ = $4; }
 | |
| 	| direct_declarator BRACKET_PHRASE
 | |
| 		{ $$ = $2; }
 | |
| 	| '(' declarator ')'
 | |
| 		{ $$ = $3; }
 | |
| 	| '(' error ')'
 | |
| 		{ $$ = $3; }
 | |
| 	;
 | |
| 
 | |
| /* Nested declarators differ from regular declarators in that they do
 | |
|    not record the symbols they find in the global symbol table.  */
 | |
| nested_declarator:
 | |
| 	ptr_operator nested_declarator		{ $$ = $2; }
 | |
| 	| direct_nested_declarator
 | |
| 	;
 | |
| 
 | |
| direct_nested_declarator:
 | |
| 	IDENT
 | |
| 	| TYPE
 | |
| 	| direct_nested_declarator '(' parameter_declaration_clause ')'
 | |
| 		{ $$ = $4; }
 | |
| 	| direct_nested_declarator '(' error ')'
 | |
| 		{ $$ = $4; }
 | |
| 	| direct_nested_declarator BRACKET_PHRASE
 | |
| 		{ $$ = $2; }
 | |
| 	| '(' nested_declarator ')'
 | |
| 		{ $$ = $3; }
 | |
| 	| '(' error ')'
 | |
| 		{ $$ = $3; }
 | |
| 	;
 | |
| 
 | |
| parameter_declaration_clause:
 | |
| 	parameter_declaration_list_opt DOTS		{ $$ = $2; }
 | |
| 	| parameter_declaration_list_opt
 | |
| 	| parameter_declaration_list ',' DOTS		{ $$ = $3; }
 | |
| 	;
 | |
| 
 | |
| parameter_declaration_list_opt:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| parameter_declaration_list
 | |
| 	;
 | |
| 
 | |
| parameter_declaration_list:
 | |
| 	parameter_declaration
 | |
| 	| parameter_declaration_list ',' parameter_declaration
 | |
| 		{ $$ = $3; }
 | |
| 	;
 | |
| 
 | |
| parameter_declaration:
 | |
| 	decl_specifier_seq m_abstract_declarator
 | |
| 		{ $$ = $2 ? $2 : $1; }
 | |
| 	;
 | |
| 
 | |
| m_abstract_declarator:
 | |
| 	ptr_operator m_abstract_declarator
 | |
| 		{ $$ = $2 ? $2 : $1; }
 | |
| 	| direct_m_abstract_declarator
 | |
| 	;
 | |
| 
 | |
| direct_m_abstract_declarator:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| IDENT
 | |
| 		{ /* For version 2 checksums, we don't want to remember
 | |
| 		     private parameter names.  */
 | |
| 		  remove_node($1);
 | |
| 		  $$ = $1;
 | |
| 		}
 | |
| 	/* This wasn't really a typedef name but an identifier that
 | |
| 	   shadows one.  */
 | |
| 	| TYPE
 | |
| 		{ remove_node($1);
 | |
| 		  $$ = $1;
 | |
| 		}
 | |
| 	| direct_m_abstract_declarator '(' parameter_declaration_clause ')'
 | |
| 		{ $$ = $4; }
 | |
| 	| direct_m_abstract_declarator '(' error ')'
 | |
| 		{ $$ = $4; }
 | |
| 	| direct_m_abstract_declarator BRACKET_PHRASE
 | |
| 		{ $$ = $2; }
 | |
| 	| '(' m_abstract_declarator ')'
 | |
| 		{ $$ = $3; }
 | |
| 	| '(' error ')'
 | |
| 		{ $$ = $3; }
 | |
| 	;
 | |
| 
 | |
| function_definition:
 | |
| 	decl_specifier_seq_opt declarator BRACE_PHRASE
 | |
| 		{ struct string_list *decl = *$2;
 | |
| 		  *$2 = NULL;
 | |
| 		  add_symbol(current_name, SYM_NORMAL, decl, is_extern);
 | |
| 		  $$ = $3;
 | |
| 		}
 | |
| 	;
 | |
| 
 | |
| initializer_opt:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| initializer
 | |
| 	;
 | |
| 
 | |
| /* We never care about the contents of an initializer.  */
 | |
| initializer:
 | |
| 	'=' EXPRESSION_PHRASE
 | |
| 		{ remove_list($2, &(*$1)->next); $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| class_body:
 | |
| 	'{' member_specification_opt '}'		{ $$ = $3; }
 | |
| 	| '{' error '}'					{ $$ = $3; }
 | |
| 	;
 | |
| 
 | |
| member_specification_opt:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| member_specification
 | |
| 	;
 | |
| 
 | |
| member_specification:
 | |
| 	member_declaration
 | |
| 	| member_specification member_declaration	{ $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| member_declaration:
 | |
| 	decl_specifier_seq_opt member_declarator_list_opt ';'
 | |
| 		{ $$ = $3; }
 | |
| 	| error ';'
 | |
| 		{ $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| member_declarator_list_opt:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| member_declarator_list
 | |
| 	;
 | |
| 
 | |
| member_declarator_list:
 | |
| 	member_declarator
 | |
| 	| member_declarator_list ',' member_declarator	{ $$ = $3; }
 | |
| 	;
 | |
| 
 | |
| member_declarator:
 | |
| 	nested_declarator attribute_opt			{ $$ = $2 ? $2 : $1; }
 | |
| 	| IDENT member_bitfield_declarator		{ $$ = $2; }
 | |
| 	| member_bitfield_declarator
 | |
| 	;
 | |
| 
 | |
| member_bitfield_declarator:
 | |
| 	':' EXPRESSION_PHRASE				{ $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| attribute_opt:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| attribute_opt ATTRIBUTE_PHRASE
 | |
| 	;
 | |
| 
 | |
| asm_definition:
 | |
| 	ASM_PHRASE ';'					{ $$ = $2; }
 | |
| 	;
 | |
| 
 | |
| asm_phrase_opt:
 | |
| 	/* empty */					{ $$ = NULL; }
 | |
| 	| ASM_PHRASE
 | |
| 	;
 | |
| 
 | |
| export_definition:
 | |
| 	EXPORT_SYMBOL_KEYW '(' IDENT ')' ';'
 | |
| 		{ export_symbol((*$3)->string); $$ = $5; }
 | |
| 	;
 | |
| 
 | |
| 
 | |
| %%
 | |
| 
 | |
| static void
 | |
| yyerror(const char *e)
 | |
| {
 | |
|   error_with_pos("%s", e);
 | |
| }
 |