442 lines
10 KiB
C
442 lines
10 KiB
C
/*
|
|
* FILE: table.c
|
|
* AUTH: Michael John Radwin <mjr@acm.org>
|
|
*
|
|
* DESC: stubgen symbol table goodies
|
|
*
|
|
* DATE: 1996/08/14 22:04:47
|
|
* $Id: table.c,v 1.1 2003-09-21 22:46:55 ocoursiere Exp $
|
|
*
|
|
* Copyright (c) 1996-1998 Michael John Radwin
|
|
*
|
|
* 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 "table.h"
|
|
#include "util.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
|
|
static const char rcsid[] = "$Id: table.c,v 1.1 2003-09-21 22:46:55 ocoursiere Exp $";
|
|
|
|
/* global table */
|
|
static syntaxelem_t etable[NELEMS];
|
|
static int eindex;
|
|
|
|
/* initializes all of the tables to default values. */
|
|
void init_tables()
|
|
{
|
|
memset(etable, 0, sizeof(syntaxelem_t) * NELEMS);
|
|
eindex = 0;
|
|
|
|
#if 0
|
|
syntaxelem_t *elt;
|
|
|
|
for(elt = etable; elt < &etable[NELEMS]; elt++) {
|
|
elt->name = NULL;
|
|
elt->ret_type = NULL;
|
|
elt->args = NULL;
|
|
elt->templ = NULL;
|
|
elt->parent = NULL;
|
|
elt->children = NULL;
|
|
elt->throw_decl = NULL;
|
|
elt->const_flag = 0;
|
|
elt->kind = 73;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* recursively frees all syntaxelem_t's. */
|
|
void free_tables()
|
|
{
|
|
syntaxelem_t *elt;
|
|
|
|
for (elt = etable; elt < &etable[NELEMS]; elt++) {
|
|
if (elt->name) {
|
|
free(elt->name);
|
|
free(elt->ret_type);
|
|
free_args(elt->args);
|
|
|
|
if (elt->throw_decl) free(elt->throw_decl);
|
|
if (elt->templ) free(elt->templ);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* recursively frees the argument list and assoicated fields */
|
|
void free_args(arg_t *args)
|
|
{
|
|
arg_t *a;
|
|
|
|
for (a = args; a != NULL; ) {
|
|
arg_t *next_arg = a->next;
|
|
free(a->type);
|
|
if (a->name)
|
|
free(a->name);
|
|
if (a->array)
|
|
free(a->array);
|
|
free(a);
|
|
a = next_arg;
|
|
}
|
|
}
|
|
|
|
/* compares two arguments for equality. Returns 0 for equal,
|
|
non-zero if they differ. */
|
|
int arg_cmp(arg_t *a1, arg_t *a2)
|
|
{
|
|
if (strcmp(a1->type, a2->type) != 0)
|
|
return 1;
|
|
|
|
if (a1->array) {
|
|
if (a2->array) {
|
|
if (strcmp(a1->array, a2->array) != 0)
|
|
return 1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} else if (a2->array) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* recursively compares two argument lists for equality.
|
|
Returns 0 for equal, non-zero if they differ. */
|
|
int args_cmp(arg_t *a1, arg_t *a2)
|
|
{
|
|
while (a1 != NULL) {
|
|
if (a2 == NULL)
|
|
return 1;
|
|
|
|
if (arg_cmp(a1, a2) != 0)
|
|
return 1;
|
|
|
|
a1 = a1->next;
|
|
a2 = a2->next;
|
|
}
|
|
|
|
if (a2 != NULL)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* provides a string rep of this arglist. conses up new
|
|
memory, so client is responsible for freeing it. */
|
|
char * args_to_string(arg_t *args, int indent_length)
|
|
{
|
|
arg_t *a;
|
|
int len = 0;
|
|
char *str;
|
|
char *indent_str;
|
|
|
|
if (indent_length <= 0) {
|
|
indent_str = strdup(", ");
|
|
indent_length = 2;
|
|
|
|
} else {
|
|
indent_length += 2; /* room for ",\n" as well */
|
|
indent_str = (char *) malloc(indent_length + 1);
|
|
memset(indent_str, ' ', indent_length);
|
|
indent_str[0] = ',';
|
|
indent_str[1] = '\n';
|
|
indent_str[indent_length] = '\0';
|
|
}
|
|
|
|
for (a = args; a != NULL; a = a->next) {
|
|
len += strlen(a->type); /* we always have a type */
|
|
if (a->name)
|
|
len += (1 + strlen(a->name)); /* room for ' ' and name */
|
|
if (a->array)
|
|
len += strlen(a->array);
|
|
if (a != args)
|
|
len += indent_length; /* room for ", " */
|
|
}
|
|
|
|
str = (char *) malloc(len + 1);
|
|
str[0] = '\0';
|
|
|
|
for (a = args; a != NULL; a = a->next) {
|
|
if (a != args)
|
|
strcat(str, indent_str);
|
|
|
|
strcat(str, a->type);
|
|
if (a->name) {
|
|
if (((a->type)[strlen(a->type)-1] != '*') &&
|
|
((a->type)[strlen(a->type)-1] != '&'))
|
|
strcat(str, " ");
|
|
strcat(str, a->name);
|
|
if (a->array)
|
|
strcat(str, a->array); /* array hugs variable name */
|
|
} else {
|
|
if (a->array)
|
|
strcat(str, a->array); /* array hugs type name */
|
|
}
|
|
}
|
|
|
|
free(indent_str);
|
|
return str;
|
|
}
|
|
|
|
/* compares a skeleton element to a header element for equality of
|
|
signatures. this isn't a strict comparison, because the kind and
|
|
parent fields ought to be different. returns 0 if equal, non-zero if
|
|
different */
|
|
int skel_elemcmp(syntaxelem_t *skel_elem, syntaxelem_t *hdr_elem)
|
|
{
|
|
char *tmp_str;
|
|
int result;
|
|
|
|
assert(hdr_elem->kind == FUNC_KIND);
|
|
assert(skel_elem->kind == SKEL_KIND);
|
|
assert(hdr_elem->parent != NULL);
|
|
|
|
if (skel_elem->const_flag != hdr_elem->const_flag)
|
|
return 1;
|
|
|
|
/*
|
|
* templ and throw_decl are allowed to be NULL,
|
|
* so we must not pass them onto strcmp without
|
|
* a check first.
|
|
*/
|
|
if (skel_elem->templ != NULL) {
|
|
if (hdr_elem->templ == NULL)
|
|
return 1;
|
|
else if (strcmp(skel_elem->templ, hdr_elem->templ) != 0)
|
|
return 1;
|
|
} else if (hdr_elem->templ != NULL) {
|
|
return 1;
|
|
}
|
|
|
|
if (skel_elem->throw_decl != NULL) {
|
|
if (hdr_elem->throw_decl == NULL)
|
|
return 1;
|
|
else if (strcmp(skel_elem->throw_decl, hdr_elem->throw_decl) != 0)
|
|
return 1;
|
|
} else if (hdr_elem->throw_decl != NULL) {
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* these two won't differ across files, and they're
|
|
* guaranteed not to be NULL.
|
|
*/
|
|
if (strcmp(skel_elem->ret_type, hdr_elem->ret_type) != 0)
|
|
return 1;
|
|
|
|
/* now make sure the argument signatures match */
|
|
if (args_cmp(skel_elem->args, hdr_elem->args) != 0)
|
|
return 1;
|
|
|
|
/*
|
|
* the name, of course, is the hard part. we gotta
|
|
* look at the parent to make a scoped name.
|
|
*/
|
|
tmp_str = (char *) malloc(strlen(hdr_elem->parent->name) +
|
|
strlen(hdr_elem->name) + 3);
|
|
sprintf(tmp_str, "%s::%s", hdr_elem->parent->name, hdr_elem->name);
|
|
|
|
result = strcmp(skel_elem->name, tmp_str);
|
|
free(tmp_str);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* allocates a new element from the table, filling in the apropriate fields */
|
|
syntaxelem_t * new_elem(char *ret_type, char *name, arg_t *args, int kind)
|
|
{
|
|
syntaxelem_t *se;
|
|
|
|
if (eindex == NELEMS - 1) {
|
|
fatal(2, "Too many symbols. Please mail mjr@acm.org.");
|
|
return NULL;
|
|
}
|
|
|
|
se = &etable[eindex++];
|
|
se->ret_type = ret_type;
|
|
se->name = name;
|
|
se->args = args;
|
|
se->kind = kind;
|
|
|
|
return se;
|
|
}
|
|
|
|
|
|
/* given the head of the list, reverses it and returns the new head */
|
|
syntaxelem_t * reverse_list(syntaxelem_t *head)
|
|
{
|
|
syntaxelem_t *elt = head;
|
|
syntaxelem_t *prev = NULL;
|
|
syntaxelem_t *next;
|
|
|
|
while (elt != NULL) {
|
|
next = elt->next;
|
|
elt->next = prev;
|
|
prev = elt;
|
|
elt = next;
|
|
}
|
|
|
|
return prev;
|
|
}
|
|
|
|
arg_t * reverse_arg_list(arg_t *head)
|
|
{
|
|
arg_t *a = head;
|
|
arg_t *prev = NULL;
|
|
arg_t *next;
|
|
|
|
while (a != NULL) {
|
|
next = a->next;
|
|
a->next = prev;
|
|
prev = a;
|
|
a = next;
|
|
}
|
|
|
|
return prev;
|
|
}
|
|
|
|
const char * string_kind(int some_KIND)
|
|
{
|
|
switch(some_KIND) {
|
|
case IGNORE_KIND:
|
|
return "IGNORE_KIND";
|
|
case FUNC_KIND:
|
|
return "FUNC_KIND";
|
|
case CLASS_KIND:
|
|
return "CLASS_KIND";
|
|
case STRUCT_KIND:
|
|
return "STRUCT_KIND";
|
|
case INLINED_KIND:
|
|
return "INLINED_KIND";
|
|
case SKEL_KIND:
|
|
return "SKEL_KIND";
|
|
case DONE_FUNC_KIND:
|
|
return "DONE_FUNC_KIND";
|
|
case DONE_CLASS_KIND:
|
|
return "DONE_CLASS_KIND";
|
|
default:
|
|
return "BAD KIND!";
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef SGDEBUG
|
|
void print_se(syntaxelem_t *elt)
|
|
{
|
|
char *arg_str = args_to_string(elt->args, 0);
|
|
|
|
log_printf("\nSTUBELEM name: %s\n", elt->name);
|
|
log_printf(" ret_typ: %s\n", elt->ret_type);
|
|
log_printf(" args: %s\n", arg_str);
|
|
log_printf(" parent: %s\n", (elt->parent) ? elt->parent->name : "NULL");
|
|
log_printf(" next: %s\n", (elt->next) ? elt->next->name : "NULL");
|
|
log_printf(" const: %d\n", elt->const_flag);
|
|
log_printf(" kind: %s\n", string_kind(elt->kind));
|
|
log_printf(" throw: %s\n", (elt->throw_decl)? elt->throw_decl : "NULL");
|
|
log_printf(" templ: %s\n\n", (elt->templ)? elt->templ : "NULL");
|
|
free(arg_str);
|
|
}
|
|
#endif /* SGDEBUG */
|
|
|
|
/*
|
|
* we can't use the 'next' field of syntaxelem_t because it is used to
|
|
* chain together methods. Make a slightly bigger struct so we can
|
|
* queue these up.
|
|
*/
|
|
typedef struct _skelnode {
|
|
syntaxelem_t *elt;
|
|
struct _skelnode *next;
|
|
} skelnode_t;
|
|
|
|
/* the queue of elements found in the .H file, possibly to be expanded */
|
|
static skelnode_t *exp_head = NULL;
|
|
static skelnode_t *exp_tail = NULL;
|
|
|
|
void enqueue_class(syntaxelem_t *elt)
|
|
{
|
|
skelnode_t *new_class;
|
|
|
|
new_class = (skelnode_t *)malloc(sizeof(skelnode_t));
|
|
new_class->elt = elt;
|
|
new_class->next = NULL;
|
|
|
|
if (exp_head == NULL)
|
|
exp_tail = exp_head = new_class;
|
|
else {
|
|
exp_tail->next = new_class;
|
|
exp_tail = new_class;
|
|
}
|
|
}
|
|
|
|
syntaxelem_t * dequeue_class()
|
|
{
|
|
skelnode_t *old_head = exp_head;
|
|
syntaxelem_t *to_return = exp_head->elt;
|
|
|
|
assert(exp_head != NULL);
|
|
exp_head = exp_head->next;
|
|
free(old_head);
|
|
|
|
return to_return;
|
|
}
|
|
|
|
int class_queue_empty()
|
|
{
|
|
return exp_head == NULL;
|
|
}
|
|
|
|
/* the list of skeletons found in the .C file, already expanded */
|
|
static skelnode_t *skel_head = NULL;
|
|
|
|
void enqueue_skeleton(syntaxelem_t *elt)
|
|
{
|
|
skelnode_t *new_class;
|
|
|
|
new_class = (skelnode_t *)malloc(sizeof(skelnode_t));
|
|
new_class->elt = elt;
|
|
new_class->next = skel_head;
|
|
|
|
skel_head = new_class;
|
|
}
|
|
|
|
syntaxelem_t * find_skeleton(syntaxelem_t *elt)
|
|
{
|
|
skelnode_t *node;
|
|
|
|
/* the order of skel_elemcmp(node->elt, elt) is important. */
|
|
for (node = skel_head; node != NULL; node = node->next)
|
|
if (skel_elemcmp(node->elt, elt) == 0)
|
|
return node->elt;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void clear_skeleton_queue()
|
|
{
|
|
skelnode_t *node = skel_head;
|
|
|
|
while (node != NULL) {
|
|
skelnode_t *next_node = node->next;
|
|
free(node);
|
|
node = next_node;
|
|
}
|
|
|
|
skel_head = NULL;
|
|
}
|