diff options
Diffstat (limited to 'semantic.c')
-rw-r--r-- | semantic.c | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/semantic.c b/semantic.c new file mode 100644 index 0000000..1dac9b5 --- /dev/null +++ b/semantic.c @@ -0,0 +1,732 @@ +/* $Id: semantic.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * C manual page generator + * These routines implement the semantic actions executed by the yacc parser. + */ +#include "c2man.h" + +#include <ctype.h> +#include <errno.h> +#include "semantic.h" +#include "enum.h" +#include "manpage.h" +#include "strconcat.h" +#include "output.h" + +/* Return TRUE if the given identifier is really a typedef name. + * Search the symbol table for the identifier. + */ +boolean +is_typedef_name (name) +char *name; +{ + return (boolean)(find_symbol(typedef_names, name) != NULL); +} + +/* Initialize a new declaration specifier part. + */ +void +new_decl_spec (decl_spec, text, flags) +DeclSpec *decl_spec; +char *text; +int flags; +{ + decl_spec->text = text ? strduplicate(text) : NULL; + decl_spec->flags = flags; + decl_spec->enum_list = NULL; +} + +/* Free storage used by a declaration specifier part. + */ +void +free_decl_spec (decl_spec) +DeclSpec *decl_spec; +{ + safe_free(decl_spec->text); /* could be an ellipsis you know */ +} + +/* Initialize a new declaration specifier part, including an enum part. + */ +void +new_enum_decl_spec (decl_spec, text, flags, enum_list) +DeclSpec *decl_spec; +char *text; +int flags; +EnumeratorList *enum_list; +{ + decl_spec->text = text; + decl_spec->flags = flags; + decl_spec->enum_list = enum_list; +} + +/* Initialize a new declaration specifier part, but don't strdup the text. */ +void +dyn_decl_spec (decl_spec, text, flags) +DeclSpec *decl_spec; +char *text; +unsigned int flags; +{ + decl_spec->text = text; + decl_spec->flags = flags; + decl_spec->enum_list = NULL; +} + +/* Append two declaration specifier parts together. + */ +void +join_decl_specs (result, a, b) +DeclSpec *result, *a, *b; +{ + if (a->text) + { + if (b->text) + { + result->text = strconcat(a->text, " ", b->text, NULLCP); + free(a->text); + free(b->text); + } + else + result->text = a->text; + } + else + result->text = b->text; + + result->flags = a->flags | b->flags; + + /* only one of the decl specs should have an enum list! */ + result->enum_list = a->enum_list ? a->enum_list : b->enum_list; +} + +/* Allocate and initialize a declarator. */ +Declarator * +new_declarator (text, name) +char *name, *text; +{ + Declarator *d; + + d = (Declarator *)safe_malloc(sizeof(Declarator)); + d->text = text; + d->name = name; + d->type = DECL_SIMPLE; + d->comment = NULL; + d->retcomment = NULL; + new_ident_list(&d->params); + d->head = d; + d->func_stack = NULL; + return d; +} + +/* Free storage used by a declarator. + */ +void +free_declarator (d) +Declarator *d; +{ +#ifdef DEBUG + fprintf(stderr,"free_declarator: decl = %lx, name = %s, text = %s\n", + (long)d, d->name?d->name:"NULL", d->text?d->text:"NULL"); +#endif + safe_free(d->name); /* could be an ellipsis (ie: no name) */ + safe_free(d->text); /* ellipsis is marked by no text too */ + safe_free(d->comment); + safe_free(d->retcomment); + free_param_list(&(d->params)); + if (d->func_stack != NULL) + free_declarator(d->func_stack); + free(d); +} + +/* add a comment to the last declarator in the list */ +int +comment_last_decl(list, comment) +DeclaratorList *list; +char *comment; +{ + if (list->last->comment) + { + declarator_error(list->last); + free(comment); + return 0; + } + else + list->last->comment = comment; + return 1; +} + +/* Initialize a declarator list and add the given declarator to it. + */ +void +new_decl_list (decl_list, declarator) +DeclaratorList *decl_list; +Declarator *declarator; +{ + decl_list->first = decl_list->last = declarator; + declarator->next = NULL; +} + +/* Free storage used by the declarators in the declarator list. + */ +void +free_decl_list (decl_list) +DeclaratorList *decl_list; +{ + Declarator *d, *next; +#ifdef DEBUG + fprintf(stderr,"free_decl_list: decl_list = %lx, first = %lx\n", + (long)decl_list, (long)decl_list->first); +#endif + d = decl_list->first; + while (d != NULL) { + next = d->next; + free_declarator(d); + d = next; + } +} + +/* Add the declarator to the declarator list. + */ +void +add_decl_list (to, from, declarator) +DeclaratorList *to, *from; +Declarator *declarator; +{ + to->first = from->first; + from->last->next = declarator; + to->last = declarator; + to->last->next = NULL; +} + +/* Initialize the parameter structure. + */ +void +new_parameter (param, decl_spec, declarator, comment_before, comment_after) +Parameter *param; /* pointer to structure to be initialized */ +DeclSpec *decl_spec; /* declaration specifier structure */ +Declarator *declarator; /* declarator structure */ +char *comment_before; /* comment before the param */ +char *comment_after; /* comment after the param */ +{ + + if (decl_spec == NULL) { + new_decl_spec(&(param->decl_spec), NULLCP, DS_JUNK); + } else { + param->decl_spec = *decl_spec; + } + + if (declarator == NULL) { + declarator = new_declarator(NULLCP, NULLCP); + } + param->declarator = declarator; + + if (comment_before && comment_after) + { + parameter_error(param); + free(comment_after); /* comment_before will go in Parameter */ + } + + param->declarator->comment = + comment_before ? comment_before : comment_after; + param->suppress = FALSE; + param->duplicate = FALSE; +} + +/* Free the storage used by the parameter. + */ +void +free_parameter (param) +Parameter *param; +{ + free_decl_spec(&(param->decl_spec)); + free_declarator(param->declarator); +} + +/* add a comment to the last parameter in the list */ +int +comment_last_parameter(list, comment) +ParameterList *list; +char *comment; +{ + if (list->last == NULL) + { + yyerror("comment '%s' applies to non-existent parameter", comment); + free(comment); + return 0; + } + + if (list->last->declarator->comment) + { + parameter_error(list->last); + free(comment); + return 0; + } + else + list->last->declarator->comment = comment; + return 1; +} + +/* Initialize a list of function parameters. + */ +void +new_param_list (param_list, param) +ParameterList *param_list; +Parameter *param; +{ + Parameter *p; + + p = (Parameter *)safe_malloc((unsigned)sizeof(Parameter)); + *p = *param; + + param_list->first = param_list->last = p; + p->next = NULL; +} + +/* Free storage used by the elements in the function parameter list. + */ +void +free_param_list (param_list) +ParameterList *param_list; +{ + Parameter *p, *next; + + p = param_list->first; + while (p != NULL) { + next = p->next; + free_parameter(p); + free(p); + p = next; + } +} + +/* Add the function parameter declaration to the list. + */ +void +add_param_list (to, from, param) +ParameterList *to, *from; +Parameter *param; +{ + Parameter *p; + + p = (Parameter *)safe_malloc((unsigned)sizeof(Parameter)); + *p = *param; + + to->first = from->first; + from->last->next = p; + to->last = p; + p->next = NULL; +} + +/* Initialize an empty list of function parameter names. + */ +void +new_ident_list (param_list) +ParameterList *param_list; +{ + param_list->first = param_list->last = NULL; +} + +/* Add an item to the list of function parameter declarations but set only + * the parameter name field and the comments. + */ +void +add_ident_list (to, from, ident) +ParameterList *to, *from; +Identifier *ident; +{ + Parameter *p; + Declarator *declarator; + + p = (Parameter *)safe_malloc(sizeof(Parameter)); + declarator = new_declarator(ident->name, strduplicate(ident->name)); + new_parameter(p, (DeclSpec *)NULL, declarator, ident->comment_before, + ident->comment_after); + + to->first = from->first; + if (to->first == NULL) { + to->first = p; + } else { + from->last->next = p; + } + to->last = p; + p->next = NULL; +} + +/* Search the list of parameters for a matching parameter name. + * Return a pointer to the matching parameter or NULL if not found. + */ +static Parameter * +search_parameter_list (params, name) +ParameterList *params; +char *name; +{ + Parameter *p; + + for (p = params->first; p != NULL; p = p->next) { + if (p->declarator->name && strcmp(p->declarator->name, name) == 0) + return p; + } + return (Parameter *)NULL; +} + +/* This routine is called to generate function prototypes from traditional + * style function definitions. For each parameter name in the declarator + * list, find the matching parameter name in the parameter list and set + * that parameter's declaration specifier. + * This is also where we promote formal parameters. Parameters of type + * "char", "unsigned char", "short", or "unsigned short" get promoted to + * "int". Parameters of type "float" are promoted to "double". + */ +void +set_param_types (params, decl_spec, declarators, comment, eolcomment) +ParameterList *params; +DeclSpec *decl_spec; +DeclaratorList *declarators; +char *comment; +char *eolcomment; +{ + Declarator *d; + Parameter *p; + + if (comment && eolcomment) + { + yyerror("parameter declaration has multiple comments"); + return; + } + + if (!comment) comment = eolcomment; + + for (d = declarators->first; d != NULL; d = d->next) { + /* Search the parameter list for a matching name. */ + if ((p = search_parameter_list(params, d->name)) == NULL) { + output_error(); + fprintf(stderr, "declared argument \"%s\" is missing\n", d->name); + } else { + char *decl_spec_text = decl_spec->text; + if (promote_param && strcmp(d->text, d->name) == 0) { + if (decl_spec->flags & (DS_CHAR | DS_SHORT)) + decl_spec_text = "int"; + else if (decl_spec->flags & DS_FLOAT) + decl_spec_text = "double"; + } + safe_free(p->decl_spec.text); /* there shouldn't be one, but...*/ + p->decl_spec.text = strduplicate(decl_spec_text); + if (p->decl_spec.flags != decl_spec->flags) + { + if (p->decl_spec.flags & DS_JUNK) + p->decl_spec.flags = decl_spec->flags; + else + parameter_error(p); + } + if (p->decl_spec.enum_list != decl_spec->enum_list) + { + if (p->decl_spec.enum_list == NULL) + p->decl_spec.enum_list = decl_spec->enum_list; + else + parameter_error(p); + } + + free_declarator(p->declarator); + p->declarator = d; + + if (comment) + { + if (p->declarator->comment) + parameter_error(p); + else + p->declarator->comment = strduplicate(comment); + } + } + } + + free_decl_spec(decl_spec); + safe_free(comment); +} + +/* Output a declaration specifier for an external declaration. + */ +void +output_decl_spec (decl_spec) +DeclSpec *decl_spec; +{ + output->text(decl_spec->text); +} + +static void +output_parameters _((Declarator *d, boolean format)); + +/* does a function have any parameters? + * This accounts for both fn() and fn(void) + */ +boolean has_parameters(d) +const Declarator *d; +{ + Parameter *first = d->head->params.first; + + return (first != NULL && + (first->declarator->text != NULL || + strcmp(first->decl_spec.text, "void"))); +} + +/* output a declarator name, stripping leading underscores if necessary */ +void output_decl_text(text, keep_underscores) +char *text; +boolean keep_underscores; +{ + if (!keep_underscores) + { + /* skip leading stuff before the actual name */ + while (*text && *text != '_' && !isalnum(*text)) + output->character(*text++); + while (text[0] == '_' && text[1]) text++; + } + output->text(text); +} + +/* Output a function declarator. + */ +static void +output_func_declarator (declarator, format) +Declarator *declarator; +boolean format; +{ + char *s, *t, *decl_text; + + /* Output declarator text before function declarator place holder. */ + if ((s = strstr(declarator->text, "%s")) == NULL) + return; + *s = '\0'; + output->text(declarator->text); + + /* Substitute place holder with function declarator. */ + if (!is_function_declarator(declarator->func_stack)) { + + decl_text = declarator->func_stack->text; + if (declarator->name == NULL || declarator->name[0] == '\0') { + output->text(decl_text); + } else { + + /* Output the declarator text before the declarator name. */ + if ((t = strstr(decl_text, declarator->name)) == NULL) + return; + *t = '\0'; + output->text(decl_text); + *t = declarator->name[0]; + + if (format && strcmp(declarator_prefix, " ") != 0) + output_format_string(declarator_prefix); + + /* Output the declarator name. */ + output_decl_text(declarator->name, format); + + /* Output the remaining declarator text. */ + output->text(t + strlen(declarator->name)); + + /* Output the declarator suffix. */ + if (format) output_format_string(declarator_suffix); + } + } else { + output_func_declarator(declarator->func_stack,format); + } + *s = '%'; + s += 2; + + /* Output declarator text up to but before parameters place holder. */ + if ((t = strstr(s, "()")) == NULL) + return; + *t = '\0'; + output->text(s); + + /* Substitute place holder with function parameters. */ + output->character(*t++ = '('); + output_parameters(declarator, format); + output->text(t); +} + +/* Output a declarator. + */ +void +output_declarator (d, format) +Declarator *d; +boolean format; +{ + if (d->func_stack) { + output_func_declarator(d, format); + } else { + output_decl_text(d->text, format); + } +} + +/* Output a function parameter. + */ +void +output_parameter (p) +Parameter *p; +{ + if (p->decl_spec.text) + output->text(p->decl_spec.text); + else + /* Check for parameter names with no declaration specifiers. This + * happens when a parameter name appears in the identifier list of a + * function definition but does not appear in the parameter declaration + * part. The default type in this cause is "int". + */ + if (p->declarator->text && strcmp(p->declarator->text, "...") != 0) + output->text("int "); + + /* not all parameters must have declarators: might be a prototype */ + if (p->declarator->text) { + if (p->decl_spec.text) + output->character(' '); + /* don't format parameters; keep it all on one line */ + output_declarator(p->declarator, FALSE); + } +} + +/* Output the list of function parameters. + */ +static void +output_parameters (d, format) +Declarator *d; +boolean format; +{ + if (has_parameters(d)) { + Parameter *p = d->params.first; + if (format) output_format_string(first_param_prefix); + output_parameter(p); + p = p->next; + while (p != NULL) { + output->character(','); + if (format) output_format_string(middle_param_prefix); + output_parameter(p); + p = p->next; + } + if (format) output_format_string(last_param_suffix); + } + else + output->text("void"); +} + +/* remember variable and function declarations. */ +int +remember_declarations (comment, decl_spec, decl_list, eolcomment) +char *comment; /* comment before */ +DeclSpec *decl_spec; /* declaration specifier */ +DeclaratorList *decl_list; /* list of declared variables */ +char *eolcomment; /* eol comment after */ +{ + Declarator *d, *next; + int ret = 1; + + /* attach EOL comment to last one in list */ + if (eolcomment) + { + Declarator *attach; + + /* if it's a function, attach it to the last parameter */ + if (is_function_declarator(decl_list->last) && + decl_list->last->head->params.last) + attach = decl_list->last->head->params.last->declarator; + else + attach = decl_list->last; + + if (attach->comment) + { + declarator_error(attach); + free(eolcomment); + ret = 0; + } + else + attach->comment = eolcomment; + } + + /* special case of a single declarator handled efficiently */ + if (decl_list->first && decl_list->first->next == NULL) + { + d = decl_list->first; + /* free the declarator comment if it isn't going to get used */ + if (comment) + safe_free(d->comment); + else + comment = d->comment; + + /* and nuke it from the declarator so free_declarator won't free it + * (since safe_free will do that) if new_manual_page decides to throw + * it away. + */ + d->comment = NULL; + + new_manual_page(comment, decl_spec, d); + } + else + { + for (d = decl_list->first; d != NULL; d = next) + { + DeclSpec spec_copy; + char *comment_copy; + + next = d->next; +#ifdef DEBUG + fprintf(stderr, + "remember_declarations: text=%s name=%s\ncomment: %s\n", + d->text,d->name, comment ? comment : "NULL"); +#endif + spec_copy = *decl_spec; + spec_copy.text = strduplicate(decl_spec->text); + comment_copy = d->comment ? d->comment : + (comment ? strduplicate(comment) : NULL); + d->comment = NULL; + new_manual_page(comment_copy, &spec_copy,d); + } + + /* free 'em up */ + free_decl_spec(decl_spec); + safe_free(comment); + } + + return ret; +} + +void parameter_error(param) +Parameter *param; +{ + yyerror("parameter '%s' has multiple comments", param->declarator->name); +} + +void declarator_error(decl) +Declarator *decl; +{ + yyerror("declarator '%s' has multiple comments", decl->name); +} + +/* is a declarator for a function? (as opposed to a variable) */ +boolean is_function_declarator(decl) +const Declarator *decl; +{ + return decl->type == DECL_FUNCTION || decl->type == DECL_FUNCDEF; +} + +/* is a comment a start of a numbered list item */ +boolean is_numbered(text) +const char *text; +{ + char *next = NULL; + + if (*text == '(') { + ++text; + errno = 0; + strtol(text, &next, 0); + if (errno) + return FALSE; + if (*next == ')') + return TRUE; + } + else { + errno = 0; + strtol(text, &next, 0); + if (errno) + return FALSE; + if (*next == '.' || *next == ')') + return TRUE; + } + return FALSE ; +} + |