diff options
author | behdad <behdad> | 2004-05-03 05:17:48 +0000 |
---|---|---|
committer | behdad <behdad> | 2004-05-03 05:17:48 +0000 |
commit | 577ed4095383ef5284225d45709e6b5f0598a064 (patch) | |
tree | 6c7d0ce55124a688b4d7050e684d9d7a1e3aa71d /grammar.y |
Diffstat (limited to 'grammar.y')
-rw-r--r-- | grammar.y | 947 |
1 files changed, 947 insertions, 0 deletions
diff --git a/grammar.y b/grammar.y new file mode 100644 index 0000000..9721dbe --- /dev/null +++ b/grammar.y @@ -0,0 +1,947 @@ +/* $Id: grammar.y,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * yacc grammar for C manual page generator + * This was derived from the grammar given in Appendix A of + * "The C Programming Language" by Kernighan and Ritchie. + */ + +/* identifiers that are not reserved words */ +%token T_IDENTIFIER T_TYPEDEF_NAME + +/* storage class */ +%token T_AUTO T_EXTERN T_REGISTER T_STATIC T_TYPEDEF +/* This keyword included for compatibility with C++. */ +%token T_INLINE + +/* type specifiers */ +%token T_CHAR T_DOUBLE T_FLOAT T_INT T_VOID +%token T_LONG T_SHORT T_SIGNED T_UNSIGNED +%token T_ENUM T_STRUCT T_UNION + +/* type qualifiers */ +%token T_CONST T_VOLATILE +/* These keywords included for compatibility with MSDOS C compilers. */ +%token T_CDECL T_FAR T_HUGE T_INTERRUPT T_NEAR T_PASCAL + +/* paired braces and everything between them: { ... } */ +%token T_BRACES + +/* paired square brackets and everything between them: [ ... ] */ +%token T_BRACKETS + +/* three periods */ +%token T_ELLIPSIS + +/* equal sign followed by constant expression or stuff between braces */ +%token T_INITIALIZER + +/* string literal */ +%token T_STRING_LITERAL + +/* text inside a regular comment, and one at the end of a non-empty line */ +%token T_COMMENT T_EOLCOMMENT + +%type <declaration> declaration +%type <parameter> function_definition +%type <decl_spec> declaration_specifiers declaration_specifier +%type <decl_spec> storage_class type_specifier type_qualifier +%type <decl_spec> struct_or_union_specifier enum_specifier +%type <decl_list> declarator_list init_declarator_list +%type <declarator> init_declarator declarator direct_declarator +%type <declarator> parameter_declarator abstract_parameter_declarator +%type <declarator> abstract_declarator direct_abstract_declarator +%type <param_list> parameter_type_list parameter_list +%type <parameter> parameter_declaration +%type <param_list> opt_identifier_list identifier_list +%type <enumerator> enumerator +%type <enum_list> enumerator_list +%type <identifier> identifier +%type <text> struct_or_union +%type <text> pointer type_qualifier_list +%type <text> opt_comment opt_eolcomment +%type <text> any_id T_IDENTIFIER T_TYPEDEF_NAME +%type <text> T_BRACKETS +%type <text> T_COMMENT T_EOLCOMMENT T_STRING_LITERAL + +%{ +#include "c2man.h" +#include "semantic.h" +#include "strconcat.h" +#include "strappend.h" +#include "manpage.h" +#include "enum.h" + +#ifdef I_STDARG +#include <stdarg.h> +#endif +#ifdef I_VARARGS +#include <varargs.h> +#endif + +int yylex(); + +#define YYMAXDEPTH 150 + +/* where are we up to scanning through an enum? */ +static enum { NOENUM, KEYWORD, BRACES } enum_state = NOENUM; + +/* Pointer to parameter list for the current function definition. */ +static ParameterList *func_params; + +/* Table of typedef names */ +SymbolTable *typedef_names; + +boolean first_comment; /* are we still looking for the first comment? */ +static char *body_comment = NULL;/* last comment found at start of func body */ +%} +%% + +program + : /* empty */ + | translation_unit + ; + +translation_unit + : external_declaration + | translation_unit external_declaration + ; + +external_declaration + : declaration opt_eolcomment + { + remember_declarations(NULL, &$1.decl_spec, &$1.decl_list, $2); + } + | T_COMMENT declaration opt_eolcomment + { + remember_declarations($1, &$2.decl_spec, &$2.decl_list, $3); + } + | function_definition opt_eolcomment + { + if (look_at_body_start && body_comment) { + /* Use the body comment */ + new_manual_page(body_comment,&$1.decl_spec,$1.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($1.declarator); + free_decl_spec(&$1.decl_spec); + } + } + | T_COMMENT function_definition opt_eolcomment + { + if (body_start_only) { + if (body_comment) { + new_manual_page(body_comment,&$2.decl_spec,$2.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($2.declarator); + free_decl_spec(&$2.decl_spec); + safe_free($1); + } + } else { + new_manual_page($1,&$2.decl_spec,$2.declarator); + } + safe_free($3); + } + | function_definition ';' opt_eolcomment + { + if (look_at_body_start && body_comment) { + new_manual_page(body_comment,&$1.decl_spec,$1.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($1.declarator); + free_decl_spec(&$1.decl_spec); + } + safe_free($3); + } + | T_COMMENT function_definition ';' opt_eolcomment + { + if (body_start_only) { + if (body_comment) { + new_manual_page(body_comment,&$2.decl_spec,$2.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($2.declarator); + free_decl_spec(&$2.decl_spec); + safe_free($1); + } + } else { + new_manual_page($1,&$2.decl_spec,$2.declarator); + } + safe_free($4); + } + | linkage_specification + | T_COMMENT T_EOLCOMMENT + { + free($1); + free($2); + } + | T_COMMENT + { + if (inbasefile && first_comment) + { + remember_terse($1); + first_comment = FALSE; + } + free($1); + } + | T_EOLCOMMENT + { + free($1); + } + | error ';' + { + yyerrok; + } + ; + +linkage_specification + : T_EXTERN T_STRING_LITERAL T_BRACES + { + /* Provide an empty action here so bison will not complain about + * incompatible types in the default action it normally would + * have generated. + */ + } + | T_EXTERN T_STRING_LITERAL declaration + { + /* empty */ + } + ; + +function_definition + : declaration_specifiers declarator opt_eolcomment + { + if ($2->type != DECL_FUNCTION) { + yyerror("syntax error"); + YYERROR; + } + func_params = &($2->head->params); + if ($3) comment_last_parameter(&$2->head->params, $3); + } + opt_declaration_list T_BRACES + { + func_params = NULL; + $2->type = DECL_FUNCDEF; + + $$.decl_spec = $1; + $$.declarator = $2; + } + | declarator opt_eolcomment + { + if ($1->type != DECL_FUNCTION) { + yyerror("syntax error"); + YYERROR; + } + func_params = &($1->head->params); + if ($2) comment_last_parameter(&$1->head->params, $2); + } + opt_declaration_list T_BRACES + { + DeclSpec decl_spec; + + func_params = NULL; + $1->type = DECL_FUNCDEF; + + new_decl_spec(&$$.decl_spec, "int", DS_NONE); + $$.declarator = $1; + } + ; + +declaration + : declaration_specifiers ';' + { + $$.decl_spec = $1; + $$.decl_list.first = NULL; + } + | declaration_specifiers init_declarator_list ';' + { + $$.decl_spec = $1; + $$.decl_list = $2; + } + | T_TYPEDEF declaration_specifiers declarator_list ';' + { + new_typedef_symbols(&$2,&$3); + $$.decl_spec = $2; + $$.decl_list = $3; + } + ; + +declarator_list + : declarator + { + new_decl_list(&$$, $1); + } + | declarator_list ',' opt_eolcomment declarator + { + if ($3) comment_last_decl(&$1, $3); + add_decl_list(&$$, &$1, $4); + } + | declarator_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_decl(&$1, $2); + } + ; + +opt_declaration_list + : /* empty */ + | declaration_list + | declaration_list T_ELLIPSIS + ; + +declaration_list + : opt_comment declaration opt_eolcomment + { + set_param_types(func_params, &$2.decl_spec, &$2.decl_list, $1, $3); + } + | declaration_list opt_comment declaration opt_eolcomment + { + set_param_types(func_params, &$3.decl_spec, &$3.decl_list, $2, $4); + } + ; + +declaration_specifiers + : declaration_specifier + | declaration_specifiers declaration_specifier + { + join_decl_specs(&$$, &$1, &$2); + } + ; + +declaration_specifier + : storage_class + | type_specifier + | type_qualifier + ; + +storage_class + : T_AUTO + { + new_decl_spec(&$$, "auto", DS_NONE); + } + | T_EXTERN + { + new_decl_spec(&$$, "extern", DS_EXTERN); + } + | T_REGISTER + { + new_decl_spec(&$$, "register", DS_NONE); + } + | T_STATIC + { + new_decl_spec(&$$, "static", DS_STATIC); + } + | T_INLINE + { + new_decl_spec(&$$, "inline", DS_INLINE); + } + ; + +type_specifier + : T_CHAR + { + new_decl_spec(&$$, "char", DS_CHAR); + } + | T_DOUBLE + { + new_decl_spec(&$$, "double", DS_NONE); + } + | T_FLOAT + { + new_decl_spec(&$$, "float", DS_FLOAT); + } + | T_INT + { + new_decl_spec(&$$, "int", DS_NONE); + } + | T_LONG + { + new_decl_spec(&$$, "long", DS_NONE); + } + | T_SHORT + { + new_decl_spec(&$$, "short", DS_SHORT); + } + | T_SIGNED + { + new_decl_spec(&$$, "signed", DS_NONE); + } + | T_UNSIGNED + { + new_decl_spec(&$$, "unsigned", DS_NONE); + } + | T_VOID + { + new_decl_spec(&$$, "void", DS_NONE); + } + | struct_or_union_specifier + | enum_specifier + | T_TYPEDEF_NAME + { + Symbol *s = find_symbol(typedef_names, $1); + + new_enum_decl_spec(&$$, $1, s->flags, + s->valtype == SYMVAL_ENUM ? s->value.enum_list + : (EnumeratorList *)NULL); + } + ; + +type_qualifier + : T_CONST + { + new_decl_spec(&$$, "const", DS_NONE); + } + | T_VOLATILE + { + new_decl_spec(&$$, "volatile", DS_NONE); + } + | T_CDECL + { + new_decl_spec(&$$, "cdecl", DS_NONE); + } + | T_INTERRUPT + { + new_decl_spec(&$$, "interrupt", DS_NONE); + } + | T_FAR + { + new_decl_spec(&$$, "far", DS_NONE); + } + | T_HUGE + { + new_decl_spec(&$$, "huge", DS_NONE); + } + | T_NEAR + { + new_decl_spec(&$$, "near", DS_NONE); + } + | T_PASCAL + { + new_decl_spec(&$$, "pascal", DS_NONE); + } + ; + +struct_or_union_specifier + : struct_or_union any_id T_BRACES + { + dyn_decl_spec(&$$, strconcat($1, " ",$2," {}",NULLCP), DS_NONE); + free($2); + } + | struct_or_union T_BRACES + { + dyn_decl_spec(&$$, strconcat($1," {}",NULLCP), DS_NONE); + } + | struct_or_union any_id + { + dyn_decl_spec(&$$, strconcat($1, " ",$2,NULLCP), DS_NONE); + free($2); + } + ; + +struct_or_union + : T_STRUCT + { + $$ = "struct"; + } + | T_UNION + { + $$ = "union"; + } + ; + +init_declarator_list + : init_declarator + { + new_decl_list(&$$, $1); + } + | init_declarator_list ',' opt_eolcomment init_declarator + { + if ($3) comment_last_decl(&$1, $3); + add_decl_list(&$$, &$1, $4); + } + | init_declarator_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_decl(&$1, $2); + } + ; + +init_declarator + : declarator + | declarator T_INITIALIZER + ; + +enum_specifier + : T_ENUM any_id '{' opt_eolcomment enumerator_list '}' + { + add_enum_symbol($2, $5); + new_enum_decl_spec(&$$, strconcat("enum ",$2," {}",NULLCP), + DS_NONE, $5); + free($2); + safe_free($4); + enum_state = NOENUM; + } + | T_ENUM '{' opt_eolcomment enumerator_list '}' + { + new_enum_decl_spec(&$$, strduplicate("enum {}"), DS_NONE, $4); + safe_free($3); + enum_state = NOENUM; + } + | T_ENUM any_id + { + new_enum_decl_spec(&$$, strconcat("enum ",$2,NULLCP), DS_NONE, + find_enum_symbol($2)); + free($2); + enum_state = NOENUM; + } + ; + +enumerator_list + : enumerator + { + $$ = new_enumerator_list(&$1); + } + | enumerator_list ',' opt_eolcomment enumerator + { + $$ = $1; + if ($3) comment_last_enumerator($$, $3); + add_enumerator_list($$, &$4); + } + | enumerator_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_enumerator($$, $2); + } + | enumerator_list ',' opt_eolcomment + { + $$ = $1; + if ($3) comment_last_enumerator($$, $3); + } + ; + +enumerator + : identifier + { + new_enumerator(&$$,$1.name,$1.comment_before,$1.comment_after); + } + ; + +any_id + : T_IDENTIFIER + | T_TYPEDEF_NAME + | any_id T_COMMENT + { + $$ = $1; + free($2); + } + | any_id T_EOLCOMMENT + { + $$ = $1; + free($2); + } + ; + +declarator + : pointer T_EOLCOMMENT direct_declarator + { + char *newtext = strappend($1,$3->text,NULLCP); + free($3->text); + $$ = $3; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + $$->retcomment = $2; + } + | pointer direct_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | T_EOLCOMMENT direct_declarator + { + $$ = $2; + $$->retcomment = $1; + } + | direct_declarator + { + $$ = $1; + } + ; + +parameter_declarator + : pointer direct_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | direct_declarator + { + $$ = $1; + } + ; + +direct_declarator + : T_IDENTIFIER + { + $$ = new_declarator($1, strduplicate($1)); + } + | '(' declarator ')' + { + char *newtext = strconcat("(",$2->text,")",NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + } + | direct_declarator T_BRACKETS + { + $$ = $1; + $$->text = strappend($1->text,$2,NULLCP); + free($2); + } + | direct_declarator '(' parameter_type_list ')' + { + $$ = new_declarator(strduplicate("%s()"), strduplicate($1->name)); + $$->params = $3; + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + | direct_declarator '(' opt_identifier_list ')' + { + $$ = new_declarator(strduplicate("%s()"), strduplicate($1->name)); + $$->params = $3; + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + ; + +pointer + : '*' type_qualifier_list + { + $$ = strconcat("*",$2, NULLCP); + safe_free($2); + } + | '*' type_qualifier_list pointer + { + $$ = $2 ? strconcat("*",$2, $3, NULLCP) + : strconcat("*", $3, NULLCP); + safe_free($2); + free($3); + } + ; + +type_qualifier_list + : /* empty */ + { + $$ = NULL; + } + | type_qualifier_list type_qualifier + { + $$ = $1 ? strconcat($1," ",$2.text," ",NULLCP) + : strconcat($2.text," ",NULLCP); + safe_free($1); + free_decl_spec(&$2); + } + ; + +parameter_type_list + : parameter_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_parameter(&$1, $2); + } + | parameter_list ',' opt_eolcomment + opt_comment T_ELLIPSIS opt_comment opt_eolcomment + { + Identifier ellipsis; + + if ($3) comment_last_parameter(&$1, $3); + ellipsis.name = strduplicate("..."); + + if ($4 && $6 && $7) + { + yyerror("ellipsis parameter has multiple comments"); + free($7); + free($6); + free($4); + ellipsis.comment_before = ellipsis.comment_after = NULL; + } + else + { + ellipsis.comment_before = $4; + ellipsis.comment_after = $6 ? $6 : $7; + } + + add_ident_list(&$$, &$1, &ellipsis); + } + ; + +parameter_list + : parameter_declaration + { + new_param_list(&$$, &$1); + } + | parameter_list ',' opt_eolcomment parameter_declaration + { + if ($3) comment_last_parameter(&$1, $3); + add_param_list(&$$, &$1, &$4); + } + ; + +parameter_declaration + : opt_comment declaration_specifiers parameter_declarator opt_comment + { + new_parameter(&$$, &$2, $3, $1, $4); + } + | opt_comment declaration_specifiers abstract_parameter_declarator opt_comment + { + new_parameter(&$$, &$2, $3, $1, $4); + } + | opt_comment declaration_specifiers opt_comment + { + new_parameter(&$$, &$2, (Declarator *)NULL, $1, $3); + } + ; + +opt_identifier_list + : /* empty */ + { + new_ident_list(&$$); + } + | identifier_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_parameter(&$1, $2); + } + ; + +identifier_list + : identifier + { + new_ident_list(&$$); + add_ident_list(&$$, &$$, &$1); + } + | identifier_list ',' opt_eolcomment identifier + { + if ($3) comment_last_parameter(&$1, $3); + add_ident_list(&$$, &$1, &$4); + } + ; + +identifier + : opt_comment T_IDENTIFIER opt_comment + { + $$.comment_before = $1; + $$.comment_after = $3; + $$.name = $2; + } + +abstract_declarator + : pointer + { + $$ = new_declarator($1, NULLCP); + } + | pointer T_EOLCOMMENT direct_abstract_declarator + { + char *newtext = strappend($1,$3->text,NULLCP); + free($3->text); + $$ = $3; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + $$->retcomment = $2; + } + | pointer direct_abstract_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | T_EOLCOMMENT direct_abstract_declarator + { + $$ = $2; + $$->retcomment = $1; + } + | direct_abstract_declarator + { + $$ = $1; + } + ; + +abstract_parameter_declarator + : pointer + { + $$ = new_declarator($1, NULLCP); + } + | pointer direct_abstract_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | direct_abstract_declarator + { + $$ = $1; + } + ; + +direct_abstract_declarator + : '(' abstract_declarator ')' + { + char *newtext = strconcat("(",$2->text,")",NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + } + | direct_abstract_declarator T_BRACKETS + { + $$ = $1; + $$->text = strappend($1->text,$2,NULLCP); + free($2); + } + | T_BRACKETS + { + $$ = new_declarator($1, NULLCP); + } + | direct_abstract_declarator '(' parameter_type_list ')' + { + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->params = $3; + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + | direct_abstract_declarator '(' ')' + { + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + | '(' parameter_type_list ')' + { + Declarator *d; + + d = new_declarator(NULL, NULL); + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->params = $2; + $$->func_stack = d; + $$->head = $$; + } + | '(' ')' + { + Declarator *d; + + d = new_declarator(NULL, NULL); + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->func_stack = d; + $$->head = $$; + } + ; + +opt_comment + : /* empty */ + { + $$ = NULL; + } + | T_COMMENT + ; + +opt_eolcomment + : /* empty */ + { + $$ = NULL; + } + | T_EOLCOMMENT + ; + +%% +#ifdef MSDOS +#include "lex_yy.c" +#else +#ifdef VMS +#include "lexyy.c" +#else +#include "lex.yy.c" +#endif /* !VMS */ +#endif /* !MSDOS */ + +#ifdef I_STDARG +void yyerror(const char *format, ...) +#else +void yyerror(va_alist) + va_dcl +#endif +{ +#ifndef I_STDARG + const char *format; +#endif + va_list args; + + output_error(); + +#ifdef I_STDARG + va_start(args, format); +#else + va_start(args); + format = va_arg(args, char *); +#endif + + vfprintf(stderr, format, args); + va_end(args); + putc('.',stderr); + putc('\n',stderr); +} + +void +parse_file (start_file) +const char *start_file; +{ + const char *s; +#ifdef FLEX_SCANNER + static boolean restart = FALSE; +#endif + + cur_file = start_file ? strduplicate(start_file) : NULL; + + if (basefile && strlen(basefile) > 2) { + s = basefile + strlen(basefile) - 2; + if (strcmp(s, ".l") == 0 || strcmp(s, ".y") == 0) + BEGIN LEXYACC; + } + + typedef_names = create_symbol_table(); + enum_table = create_symbol_table(); + + line_num = 1; + ly_count = 0; + first_comment = group_together && !terse_specified; + + /* flex needs a yyrestart before every file but the first */ +#ifdef FLEX_SCANNER + if (restart) yyrestart(yyin); + restart = TRUE; +#endif + + yyparse(); + + destroy_symbol_table(enum_table); + destroy_symbol_table(typedef_names); + + safe_free(cur_file); +} |