summaryrefslogtreecommitdiff
path: root/semantic.c
diff options
context:
space:
mode:
authorbehdad <behdad>2004-05-03 05:17:48 +0000
committerbehdad <behdad>2004-05-03 05:17:48 +0000
commit577ed4095383ef5284225d45709e6b5f0598a064 (patch)
tree6c7d0ce55124a688b4d7050e684d9d7a1e3aa71d /semantic.c
Initial revisionHEADoriginmaster
Diffstat (limited to 'semantic.c')
-rw-r--r--semantic.c732
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 ;
+}
+