/*
** Most of the semantic routines are found here.
*/

#include <sys/types.h>
#include <stdio.h>
#include "ansi.h"
#include "host.h"
#include "files.h"
#include "hash.h"
#include "il.h"
#include "type_util.h"
#include "nodeop.h"
#include "stab.h"
#include "allocate.h"
#include "gen.h"
#include "ada_name.h"
#ifndef CCPROTO
#include "anonymous.h"
#endif
#include "config.h"

/* Import from scanner */
extern file_pos_t yypos;
extern int auto_package;

#undef NULL
#define NULL			0L

#undef N_BUILTINS
#define N_BUILTINS		5

static symbol_t builtins[N_BUILTINS];
static typeinfo_t builtin_types[N_BUILTINS];

static void dump_type _ANSI_PROTO_((typeinfo_t*));
static void dump_pos _ANSI_PROTO_((void));


void
not_implemented(file, line)
	char *file;
	int line;
{
	fatal(file_name(yypos), line_number(yypos),
		  "This has not yet been implemented [%s:%d]", file, line);
}

/*
 * These are the builtin/intrinsic types
 */
void
type_init()
{
	int i;
	symbol_t *sym;
	typeinfo_t *typ;

	/* int must be 0 */
	sym = &builtins[0];
	sym->gened = 1;
	sym->intrinsic = 1;
	sym->sym_type = &builtin_types[0];
	sym->sym_kind = type_symbol;
	sym->sym_ident = new_node(_Ident, "int");
#ifndef CCPROTO
	sym->sym_ada_name = TYPE_NAMEOF_SIGNED_INT;
#endif
	sym->sym_type->type_kind = int_type;
	sym->sym_type->_sizeof = SIZEOF_INT;
	sym->sym_type->_alignof = ALIGNOF_INT;
	sym->sym_type->type_base = sym;
	/*store_sym(sym);*/
	/* void must be 1 */
	sym = &builtins[1];
	sym->gened = 1;
	sym->intrinsic = 1;
	sym->sym_type = &builtin_types[1];
	sym->sym_kind = type_symbol;
	sym->sym_ident = new_node(_Ident, "void");
#ifndef CCPROTO
	sym->sym_ada_name = TYPE_NAMEOF_VOID;
#endif
	sym->sym_type->type_kind = void_type;
	sym->sym_type->_sizeof = SIZEOF_INT;
	sym->sym_type->_alignof = ALIGNOF_INT;
	sym->sym_type->type_base = sym;
	/*store_sym(sym);*/
	/* char must be 2 */
	sym = &builtins[2];
	sym->gened = 1;
	sym->intrinsic = 1;
	sym->sym_type = &builtin_types[2];
	sym->sym_kind = type_symbol;
	sym->sym_ident = new_node(_Ident, "char");
	sym->sym_type->type_kind = int_type;
	sym->sym_type->_sizeof = SIZEOF_CHAR;
	sym->sym_type->_alignof = ALIGNOF_CHAR;
	sym->sym_type->type_base = sym;
#ifdef CHARS_ARE_UNSIGNED
# ifndef CCPROTO
	sym->sym_ada_name = TYPE_NAMEOF_UNSIGNED_CHAR;
# endif
	sym->sym_type->_unsigned = 1;
#else
	sym->sym_type->_signed = 1;
# ifndef CCPROTO
	sym->sym_ada_name = TYPE_NAMEOF_SIGNED_CHAR;
# endif
#endif
	/*store_sym(sym);*/
	/* float must be 3 */
	sym = &builtins[3];
	sym->intrinsic = 1;
	sym->gened = 1;
	sym->sym_type = &builtin_types[3];
	sym->sym_kind = type_symbol;
	sym->sym_ident = new_node(_Ident, "float");
#ifndef CCPROTO
	sym->sym_ada_name = TYPE_NAMEOF_FLOAT;
#endif
	sym->sym_type->type_kind = float_type;
	sym->sym_type->_sizeof = SIZEOF_FLOAT;
	sym->sym_type->_alignof = ALIGNOF_FLOAT;
	sym->sym_type->type_base = sym;
	/*store_sym(sym);*/
	/* double must be 4 */
	sym = &builtins[4];
	sym->intrinsic = 1;
	sym->gened = 1;
	sym->sym_type = &builtin_types[4];
	sym->sym_kind = type_symbol;
	sym->sym_ident = new_node(_Ident, "double");
#ifndef CCPROTO
	sym->sym_ada_name = TYPE_NAMEOF_DOUBLE;
#endif
	sym->sym_type->type_kind = float_type;
	sym->sym_type->_sizeof = SIZEOF_DOUBLE;
	sym->sym_type->_alignof = ALIGNOF_DOUBLE;
	sym->sym_type->type_base = sym;
	/*store_sym(sym);*/

	for (i = 0; i < N_BUILTINS; i++) {
		typ = &builtin_types[i];
		typ->_builtin = 1;
		set_hash_for_type(&builtin_types[i]);
	}

#ifndef CCPROTO
	init_anonymous_types();
#endif
}

symbol_t*
copy_sym(sym)
	symbol_t *sym;
{
	symbol_t *d;
	d = new_sym();
	d->sym_type = copy_type(sym->sym_type);
	return d;
}

typeinfo_t*
typeof_int(is_unsigned)
{
	typeinfo_t *typ;
	typ = copy_type(&builtin_types[0]);
	if (is_unsigned) typ->_unsigned = 1;
	return typ;
}

static symbol_t*
int_basetype()
{
	return &builtins[0];
}

typeinfo_t*
typeof_void()
{
	return copy_type(&builtin_types[1]);
}

typeinfo_t*
typeof_char()
{
	return copy_type(&builtin_types[2]);
}

typeinfo_t*
typeof_float()
{
	return copy_type(&builtin_types[3]);
}

typeinfo_t*
typeof_double()
{
	return copy_type(&builtin_types[4]);
}

static typeinfo_t*
typeof_enum()
{
	typeinfo_t *typ;

	typ = new_type(enum_type);
	typ->_sizeof = SIZEOF_ENUM;			/* See host.h */
	typ->_alignof = ALIGNOF_ENUM;
	set_hash_for_type(typ);

	return typ;
}

static typeinfo_t*
typeof_rec(is_union)
	int is_union;
{
	return new_type(is_union ? union_of : struct_of);
}

decl_class_t
decl_class(typ)
	typeinfo_t *typ;
{
	if (typ == NULL) {
		return int_decl;
	}

	switch (typ->type_kind) {
	  case field_type:
		return field_decl;
	  case pointer_to:
		return pointer_decl;
	  case array_of:
		return array_decl;
	  case struct_of:
	  case union_of:
		return struct_decl;
	  case int_type:
		return int_decl;
	  case float_type:
		return fp_decl;
	  case void_type:
		return int_decl;
	  case function_type:
		return func_decl;
	  case enum_type:
		return enum_decl;
	  default:
		fatal(__FILE__,__LINE__,"Unexpected type kind %d",
			  typ->type_kind);
		return int_decl;
	}
}

static symbol_t*
sym_decl(typ, n, uniq)
	typeinfo_t *typ;
	node_t *n;
	int uniq;					/* symbol name must be uniq */
{
	symbol_t *sym;

	assert(typ != NULL);
	assert(n != NULL);
	assert(n->node_kind == _Ident);
	assert(n->node.id.name != NULL);

	sym = new_sym();
	sym->sym_def = n->node_def;
	sym->sym_ident = n;
#ifndef CCPROTO
	sym->sym_ada_name
		= ada_name(sym->sym_ident->node.id.name,
				   uniq ? FILE_ORD(sym->sym_def) : -1);
#endif
	sym->sym_type = copy_type(typ);

	return sym;
}

symbol_t*
concat_symbols(s1,s2)
	symbol_t *s1, *s2;
{
	symbol_t *s;

	if (s1 == NULL)		return s2;
	if (s2 == NULL)		return s1;

	for (s = s1; s->sym_parse_list; s = s->sym_parse_list);

	s->sym_parse_list = s2;
	return s1;
}

#ifndef CCPROTO
/*
 * OK.  The following looks really crappy.  Since C lets you
 * generate array types and pointer types without any formal
 * declaration, we'll need to traverse all types seen in the
 * C source and generate similar Ada types.  For example, if
 * foo(int*) is seen we'll need to generate type a_int_t is
 * access c.signed_int automatically.  OK wise guy, do you
 * have a better idea?
 */

static void gen_tag_types _ANSI_PROTO_((symbol_t*));

static void
all_types_gened(typ)
	typeinfo_t *typ;
{
	symbol_t *basetype;

	if (typ) {
		all_types_gened(typ->type_next);

		if (! typ->_builtin) {
			switch (typ->type_kind) {
			  case pointer_to:
			  case array_of:
			  case struct_of:
			  case union_of:
			  case enum_type:
				break;
			  case field_type:
			  case void_type:
			  case int_type:
			  case float_type:
			  case function_type:
				return;
			  case typemodifier:
			  default:
				assert(0);
			}

			basetype = typ->type_base;

			if (basetype == NULL) {
				basetype = get_anonymous_type(typ);
				assert(basetype != NULL);
			}
			if (! basetype->gened) {
				basetype->gened = 1;
				gen_tag_types(basetype->sym_tags);
				gen_ada_type(basetype);
			}
		}
	}
}

static void
gen_tag_types(tags)
	symbol_t *tags;
{
	for (; tags; tags = tags->sym_parse_list) {
		all_types_gened(tags->sym_type);
	}
}
#endif

static typeinfo_t*
grok_array_type(typ, elem)
	typeinfo_t *typ;
	node_t *elem;
{
	host_int_t nelem = -1;
	typeinfo_t *array_type;
	unsigned long bsize;

	assert(typ != NULL);

	if (elem != NULL) {
		reduce_node(elem);
		if (elem->node_kind == _Int_Number) {
			nelem = elem->node.ival;
			if (nelem < 0) {
				warning(NODE_FNAME(elem), NODE_FLINE(elem),
						"Array length %d is not supported (see %s:%d)",
						nelem, __FILE__, __LINE__);
				nelem = -1;
			}
			free_node(elem);
		}
		else {
			nelem = -1;
			/*
			 * Hell!  Probably reduce_node() in nodeop.c isn't
			 * doing the right thing.  Or it could be in cpp_eval
			 */
			warning(NODE_FNAME(elem), NODE_FLINE(elem),
					"Failed reducing array index to a constant integer value (see %s:%d)",
					__FILE__, __LINE__);
		}
	}

	array_type = add_array_type(typ, nelem);
	all_types_gened(array_type);
	return array_type;
#if 0
	array_type = new_type(array_of);
	array_type->type_info.array_elements = nelem;
	array_type->type_next = typ;
	array_type->_typedef = typ->_typedef;

	switch (nelem) {
	  case -1:
		array_type->_sizeof = SIZEOF_ADDRESS;
		array_type->_alignof = ALIGNOF_ADDRESS;
		break;
	  case 0:
		array_type->_sizeof = 0;
		array_type->_alignof = 0;
		break;
	  default:
		array_type->_alignof = type_alignof(typ);
		bsize = alignto(type_sizeof(typ), array_type->_alignof);
		array_type->_sizeof = bsize * nelem;
		break;
	}

	set_hash_for_type(array_type);
	return array_type;
#endif
}

typeinfo_t*
typeof_typemod(adj)
	int adj;
{
	typeinfo_t *typ;

	typ = new_type(typemodifier);

	switch (adj) {
	  case TYPEMOD_SHORT:
		typ->_short = 1;
		break;
	  case TYPEMOD_LONG:
		typ->_long = 1;
		break;
	  case TYPEMOD_SIGNED:
		typ->_signed = 1;
		typ->_unsigned = 0;
		break;
	  case TYPEMOD_UNSIGNED:
		typ->_unsigned = 1;
		typ->_signed = 0;
		break;
	  case TYPEMOD_CONST:
		typ->_constant = 1;
		break;
	  case TYPEMOD_VOLATILE:
		typ->_volatile = 1;
		break;
	  case TYPEMOD_TYPEDEF:
		typ->_typedef = 1;
		break;
	  case TYPEMOD_EXTERN:
		typ->_extern = 1;
		break;
	  case TYPEMOD_STATIC:
		typ->_static = 1;
		break;
	  case TYPEMOD_AUTO:
		typ->_auto = 1;
		break;
	  case TYPEMOD_REGISTER:
		typ->_register = 1;
		break;
	  default:
		assert(0);
		break;
	}

	return typ;
}

static void
combine_typespec(tmod)
	typeinfo_t *tmod;
{
	typeinfo_t *typ;

	assert(tmod != NULL);
	assert(tmod->type_kind == typemodifier);
	assert(tmod->type_next != NULL);

	typ = tmod->type_next;

	if (typ->_volatile) tmod->_volatile = 1;
	if (typ->_constant) tmod->_constant = 1;
	if (typ->_extern) 	tmod->_extern = 1;
	if (typ->_static)	tmod->_static = 1;
	if (typ->_auto)		tmod->_auto = 1;
	if (typ->_register) tmod->_register = 1;
	if (typ->_typedef)	tmod->_typedef = 1;

	if (tmod->_signed) {
		tmod->_unsigned = 0;
	}
	else if (tmod->_unsigned) {
		tmod->_signed = 0;
	}
	else if (typ->_unsigned) {
		tmod->_unsigned = 1;
		tmod->_signed = 0;
	}
	else if (typ->_signed) {
		tmod->_unsigned = 0;
		tmod->_signed = 1;
	}

	if (tmod->_short == 0) {
		if (typ->_short)	tmod->_short = 1;
		if (typ->_long_long)tmod->_long_long = 1;
	
		if (typ->_long) {
			if (tmod->_long) {
				tmod->_long_long = 1;
			}
			else {
				tmod->_long = 1;
			}
		}
	}

	tmod->type_kind = typ->type_kind;
	tmod->type_info = typ->type_info;

	tmod->_sizeof = typ->_sizeof;
	tmod->_alignof = typ->_alignof;

	tmod->type_base = typ->type_base;
	tmod->type_next = typ->type_next;

	set_hash_for_type(tmod);

	deallocate(typ);
}

typeinfo_t*
typeof_typespec(tlist)
	typeinfo_t *tlist;
{
	typeinfo_t *prev = NULL;
	typeinfo_t *result = tlist;

  top:
	assert(tlist != NULL);

	switch (tlist->type_kind) {
	  case typemodifier:
		if (tlist->type_next == NULL) {
			/*
			 * If type is not specified it's assumed to be an int.
			 * ie.  short foo; == short int foo;
			 */
			result = tlist;
			result->type_kind = int_type;
			result->type_base = int_basetype();
			if (result->_short) {
				result->_sizeof = SIZEOF_SHORT;
				result->_alignof = ALIGNOF_SHORT;
			}
#ifdef SIZEOF_LONG_LONG
			else if (result->_long_long) {
				result->_sizeof = SIZEOF_LONG_LONG;
				result->_alignof = ALIGNOF_LONG_LONG;
				result->_short = 0;
				result->_long = 0;
				set_hash_for_type(result);
			}
#endif
			else if (result->_long) {
				result->_sizeof = SIZEOF_LONG;
				result->_alignof = ALIGNOF_LONG;
			}
			else {
				result->_sizeof = SIZEOF_INT;
				result->_alignof = ALIGNOF_INT;
			}
			set_hash_for_type(result);
		}
		else {
			combine_typespec(tlist);
			goto top;
		}
		break;
	  case void_type:
	  case int_type:
	  case float_type:
	  case struct_of:
	  case union_of:
	  case enum_type:
		assert(tlist->type_next == NULL);
		break;
	  case array_of:
	  case pointer_to:
	  case function_type:
		break;
	  default:
		assert(0);
		break;
	}

	return result;
}

typeinfo_t*
typeof_specifier(sym)
	symbol_t *sym;
{
	typeinfo_t *typ;

	assert(sym != NULL);
	assert(sym->sym_type != NULL);

	typ = copy_type(sym->sym_type);
	typ->type_base = sym;

	return typ;
}

static symbol_t*
KnR_formals(params)
	node_t *params;
{
	symbol_t *p1, *p2;

	if (params == NULL) return NULL;

	switch (params->node_kind) {
	  case _List:
		p1 = KnR_formals(params->node.binary.l);
		p2 = KnR_formals(params->node.binary.r);
		free_node(params);
		p1 = concat_symbols(p1, p2);
		break;
	  case _Ident:
		p1 = sym_decl(typeof_int(0), params, 0);
		break;
	  default:
		assert(0);
		break;
	}

	return p1;
}

static symbol_t*
grok_formals(params)
	node_t *params;
{
	symbol_t *p;

	if (params == NULL) return NULL;

	if (params->node_kind == _Sym) { /* Ansi function decl */
		p = params->node.sym;
		free_node(params);
		return p;
	}

	return KnR_formals(params);
}

static void
grok_subp(subp)
	symbol_t *subp;
{
	symbol_t *param, *prev;

	prev = NULL;
	param = subp->sym_tags;

	for (; param && param->sym_parse_list; param = param->sym_parse_list) {
		prev = param;
	}

	if (param != NULL && param->var_params) {
		subp->var_params = 1;
		if (prev) {
			prev->sym_parse_list = NULL;
		} else {
			subp->sym_tags = NULL;
		}
	}
}

static typeinfo_t*
add_field(typ, width)
	typeinfo_t *typ;
	node_t *width;
{
	typeinfo_t *ftype;

	assert(typ != NULL);
	assert(width != NULL);

	reduce_node(width);

	if (width->node_kind != _Int_Number || width->node.ival < 1 || width->node.ival > 31) {
		warning(NODE_FNAME(width), NODE_FLINE(width),
				"Bit filed width not handled properly (see %s:%d)",
				__FILE__, __LINE__);
		return typ;
	}

	ftype = new_type(field_type);
	ftype->type_next = typ;
	ftype->_sizeof = width->node.ival;

	free_node(width);

	set_hash_for_type(ftype);
	return ftype;
}

static symbol_t*
grok_decl_list(tspec, vlist, uniq)
	typeinfo_t *tspec;
	node_t *vlist;
	int uniq;					/* Generate uniq Ada idenifer name */
{
	symbol_t *d1, *d2;

	assert(tspec != NULL);
	assert(vlist != NULL);

	switch (vlist->node_kind) {
	  case _List:
		d1 = grok_decl_list(tspec, vlist->node.binary.l, uniq);
		d2 = grok_decl_list(tspec, vlist->node.binary.r, uniq);
		free_node(vlist);
		return concat_symbols(d1, d2);
	  case _Ident:
		assert(vlist->node.id.name != NULL);
		d1 = sym_decl(tspec, vlist, uniq);
		return d1;
	  case _Assign:
		d1 = grok_decl_list(tspec, vlist->node.binary.l, uniq);
		d1->sym_value.initializer = vlist->node.binary.r;
		d1->has_initializer = 1;
		free_node(vlist);
		return d1;
	  case _Bit_Field:
		tspec = add_field(tspec, vlist->node.binary.r);
		if (vlist->node.binary.l == NULL) {
			d1 = new_sym();
			d1->sym_type = copy_type(tspec);
		}
		else {
			d1 = grok_decl_list(tspec, vlist->node.binary.l, 0);
		}
		free_node(vlist);
		return d1;
	  case _Array_Index:
		if (vlist->node.binary.l == NULL) {
			d1 = new_sym();
			d1->sym_type = tspec;
		}
		else {
			d1 = grok_decl_list(tspec, vlist->node.binary.l, uniq);
		}
		d1->sym_type = grok_array_type(d1->sym_type, vlist->node.binary.r);
		return d1;
	  case _Indirect:
		tspec = add_pointer_type(tspec);
		if (vlist->node.unary == NULL) {
			d1 = new_sym();
			d1->sym_type = tspec;
			return d1;
		}
		return grok_decl_list(tspec, vlist->node.unary, uniq);
	  case _Func_Call:
		tspec = add_function_type(tspec);
		d1 = grok_decl_list(tspec, vlist->node.binary.l, uniq);
		if (d1->sym_tags == NULL) {
			d1->sym_tags = grok_formals(vlist->node.binary.r);
			grok_subp(d1);
		}
		tspec->type_info.subp_params = d1->sym_tags;
		return d1;
	  default:
		fatal(file_name(yypos), line_number(yypos),
			  "Unhandled node kind %d [%s:%d]",
			  vlist->node_kind, __FILE__, __LINE__);
		break;
	}

	return NULL;
}

static void
check_type_base(sym)
	symbol_t *sym;
{
	typeinfo_t *typ;

	assert(sym != NULL);

	typ = sym->sym_type;
	assert(typ != NULL);

	switch (decl_class(typ)) {
	  case pointer_decl:
	  case array_decl:
		typ->type_base = sym;
		break;
	  default:
		assert(typ->type_base != NULL);
		break;
	}
}

static symbol_t*
set_symbol_kind(vlist)
	symbol_t *vlist;
{
	symbol_t *s;
	typeinfo_t *typ;

	assert(vlist != NULL);

	for (s = vlist; s; s = s->sym_parse_list) {
		typ = s->sym_type;
		if (typ->_typedef) {
			s->sym_kind = type_symbol;
			if (is_array(typ)) {
				typ->type_base = s;
			}
		}
		else if (typ->type_kind == function_type) {
			s->sym_kind = func_symbol;
		}
		else {
			s->sym_kind = var_symbol;
		}
	}

	return vlist;
}

static void
dump_pos()
{
	fprintf(stderr, "%s:%d\n", file_name(yypos), line_number(yypos));
	fflush(stderr);
}

/*
 * Useful routine for debugging if your debugger lets
 * you invoke it directly.  If using verdix debugger
 * you can say:  p debug_type(typ)
 * buy vads :-)
 */
static void
dump_type(typ)
	typeinfo_t *typ;
{
	symbol_t *basetype;

	for (; typ; typ = typ->type_next) {
		if (typ->_static) fputs("static/", stderr);
		if (typ->_typedef) fputs("typedef/", stderr);
		if (typ->_short) fputs("short/", stderr);
		if (typ->_long) fputs("long/", stderr);
		if (typ->_long_long) fputs("long_long/", stderr);
		if (typ->_signed) fputs("signed/", stderr);
		if (typ->_unsigned) fputs("unsigned/", stderr);
		switch (typ->type_kind) {
		  case pointer_to:
			fputs("pointer_to", stderr);
			break;
		  case array_of:
			fputs("array_of", stderr);
			break;
		  case struct_of:
			fputs("struct_of", stderr);
			break;
		  case union_of:
			fputs("union_of", stderr);
			break;
		  case field_type:
			fputs("field_type", stderr);
			break;
		  case int_type:
			fputs("int_type", stderr);
			break;
		  case float_type:
			fputs("float_type", stderr);
			break;
		  case void_type:
			fputs("void_type", stderr);
			break;
		  case function_type:
			fputs("function_type", stderr);
			break;
		  case enum_type:
			fputs("enum_type", stderr);
			break;
		  case typemodifier:
			fputs("typemodifier", stderr);
			break;
		  default:
			fputs("default", stderr);
			break;
		}
		fputc('[',stderr);
		basetype = typ->type_base;
		if (basetype != NULL) {
			assert(basetype->sym_ident != NULL);
			assert(basetype->sym_ident->node_kind == _Ident);
			assert(basetype->sym_ident->node.id.name != NULL);
			fputs(basetype->sym_ident->node.id.name, stderr);
		}
		fputs("] ",stderr);
	}
	fputc('\n',stderr);
}

static int
decl_defined(sym)
	symbol_t *sym;
{
	symbol_t *dup;

	assert(sym->sym_ident != NULL);
	assert(sym->sym_ident->node_kind == _Ident);
	assert(sym->sym_ident->node.id.name != NULL);
	dup = find_sym(sym->sym_ident->node.id.name);

	if (dup == NULL) {
		store_sym(sym);
		return 0;
	}

	if (sym->sym_kind != dup->sym_kind) {
		return 0;
	}

	return equal_types(sym->sym_type, dup->sym_type);
}

static void find_units();

static void
get_basetype_unit_list(ord, sym)
	symbol_t *sym;
{
	symbol_t *tags;

	for (tags = sym->sym_tags; tags; tags = tags->sym_parse_list) {
		find_units(ord, tags->sym_type);
	}

	if (sym->sym_type->type_base != sym) {
		find_units(ord, sym->sym_type);
	}
}

static void
find_units(ord, typ)
	int ord;
	typeinfo_t *typ;
{
#ifndef CCPROTO
	typeinfo_t *t;
	symbol_t *basetype;
	int unit_ord;

	for (t = typ; t; t = t->type_next) {
		if (! t->_builtin) {
			basetype = t->type_base;
			if (basetype != NULL && !basetype->intrinsic && basetype->traversal_unit != ord) {
				basetype->traversal_unit = ord;
				get_basetype_unit_list(ord, basetype);
				unit_ord = FILE_ORD(basetype->sym_def);
				if (unit_ord != ord) {
					unit_dependency(ord, unit_ord);
				}
			}
		}
	}
#endif
}

/*
 * Determine which Ada package need to be with'd
 * for the types of this symbol
 */
static void
get_unit_list(sym)
	symbol_t *sym;
{
	symbol_t *tags;
	int unit_ord;

	if (!auto_package) return;

	for (tags = sym->sym_tags; tags; tags = tags->sym_parse_list) {
		unit_ord = FILE_ORD(tags->sym_def);
		find_units(unit_ord, tags->sym_type);
	}

	unit_ord = FILE_ORD(sym->sym_def);
	find_units(unit_ord, sym->sym_type);
}

static void
grok_decl(sym)
	symbol_t *sym;
{
	typeinfo_t *typ, *ntyp;

	assert(sym != NULL);

	typ = sym->sym_type;
	assert(typ != NULL);
	assert(sym->sym_parse_list == NULL);

	if (typ->_typedef) {
		if (typ->type_kind == union_of || typ->type_kind == struct_of) {
			assert(typ->type_base != NULL);
			if (typ->type_base != sym) {
				sym->sym_tags = typ->type_base->sym_tags;
			}
		}
		else if (is_access(typ)) {
			ntyp = typ->type_next;
			assert(ntyp != NULL);
			all_types_gened(ntyp);
			typ->type_base = sym;
		}
	}

#ifndef CCPROTO
	all_types_gened(typ);
	gen_tag_types(sym->sym_tags);

	get_unit_list(sym);
#endif

	switch (sym->sym_kind) {
	  case type_symbol:
		store_sym(sym);
		if (! sym->gened) {
			sym->gened = 1;
			gen_ada_type(sym);
		}
		break;
	  case func_symbol:
#ifndef CCPROTO
		if (! is_static_function(typ)) {
#endif
			if (! decl_defined(sym)) {
				if (! sym->gened) {
					sym->gened = 1;
					gen_ada_func(sym);
				}
			}
#ifndef CCPROTO
		}
#endif
		break;
	  case var_symbol:
		if (! typ->_static) {
			if (! decl_defined(sym)) {
				if (! sym->gened) {
					sym->gened = 1;
					gen_ada_var(sym);
				}
			}
		}
		break;
	  case enum_literal:
	  default:
		fatal(__FILE__,__LINE__,"Unhandled symbol kind %d",
			  sym->sym_kind);
		break;
	}
}

void
grok_declarations(list)
	symbol_t *list;
{
	symbol_t *next;

	for (; list; list = next) {
		next = list->sym_parse_list;
		list->sym_parse_list = NULL;
		grok_decl(list);
	}
}

static void
grok_enum_lits(tags, typ)
	symbol_t *tags;
	typeinfo_t *typ;
{
	symbol_t *sym;
	int ord = 0;

	for (sym = tags; sym; sym = sym->sym_parse_list) {
		assert(sym->sym_type == NULL);
		sym->sym_type = typ;
		if (sym->sym_value.intval == 0xBAD) {
			sym->sym_value.intval = ord++;
		}
		else {
			ord = sym->sym_value.intval + 1;
		}
	}
}

static symbol_t*
gen_enum_sym(id)
	node_t *id;
{
	symbol_t *sym;
	char enum_name[2048];

	assert(id != NULL);
	assert(id->node_kind == _Ident);

	enum_name[0] = ENUM_PREFIX;
	strcpy(&enum_name[1], id->node.id.name);
	id->node.id.name = new_string(enum_name);

	sym = new_sym();
	sym->sym_type = typeof_enum();
	sym->sym_kind = type_symbol;
	sym->sym_ident = id;
#ifndef CCPROTO
	sym->sym_ada_name = ada_name(enum_name, FILE_ORD(sym->sym_def));
#endif
	sym->sym_def = id->node_def;

	return sym;
}

static void
add_tags(decls, tags)
	symbol_t *decls, *tags;
{
	symbol_t *sym;

	for (sym = decls; sym; sym = sym->sym_parse_list) {
		assert(sym->sym_tags == NULL);
		sym->sym_tags = tags;
	}
}

static void
grok_sizeof_struct(styp, fields)
	typeinfo_t *styp;
	symbol_t *fields;
{
	symbol_t *sym;
	typeinfo_t *typ, *ftype;
	int min = 0;
	int aggsize = 0;
	int bitsize = 0;
	int width, tmp;

	for (sym = fields; sym; sym = sym->sym_parse_list) {
		typ = sym->sym_type;
		assert(typ != NULL);

		if (typ->type_kind == field_type) {
			width = typ->_sizeof;
			ftype = typ->type_next; assert(ftype != NULL);

			if (ftype->_sizeof > min) {
				min = ftype->_sizeof;
			}

		  align_field:
			tmp = alignto(aggsize, ftype->_alignof);
			if (tmp != aggsize) {
				aggsize = tmp;
				bitsize = 0;
			}

			tmp = bitsize + width;
			if (tmp > ftype->_sizeof * BITS_PER_BYTE) {
				aggsize += (bitsize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
				bitsize = 0;
				goto align_field;
			}

			sym->bitoffset = aggsize * BITS_PER_BYTE + bitsize;
			typ->_alignof = bitsize;

			bitsize += width;
		}
		else {
			if (bitsize != 0) {
				aggsize += (bitsize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
				bitsize = 0;
			}

			aggsize = alignto(aggsize, typ->_alignof);
			sym->bitoffset = aggsize * BITS_PER_BYTE;

			aggsize += typ->_sizeof;
		}
	}

	if (bitsize != 0) {
		aggsize += (bitsize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
	}

	if (aggsize < min) {
		aggsize = min;
	}

	styp->_sizeof = aggsize;
	set_hash_for_type(styp);
}

static void
grok_sizeof_union(utyp, fields)
	typeinfo_t *utyp;
	symbol_t *fields;
{
	symbol_t *sym;
	typeinfo_t *typ;
	int aggsize = 0;

	for (sym = fields; sym; sym = sym->sym_parse_list) {
		typ = sym->sym_type;
		assert(typ != NULL);
		assert(typ->type_kind != field_type);
		if (typ->_sizeof > aggsize) {
			aggsize = typ->_sizeof;
		}
	}

	utyp->_sizeof = aggsize;
	set_hash_for_type(utyp);
}

static void
grok_alignof_record(rtyp, fields)
	typeinfo_t *rtyp;
	symbol_t *fields;
{
	symbol_t *sym;
	typeinfo_t *typ;
	int max = 0;

	for (sym = fields; sym; sym = sym->sym_parse_list) {
		typ = sym->sym_type;
		assert(typ != NULL);
		if (typ->type_kind == field_type) {
			typ = typ->type_next;
			assert(typ != NULL);
		}
		if (typ->_alignof > max) {
			max = typ->_alignof;
		}
	}

	rtyp->_alignof = max;
	set_hash_for_type(rtyp);
}

static void
grok_type(typ)
	typeinfo_t *typ;
{
	if (typ == NULL) return;

	switch (decl_class(typ)) {
	  case pointer_decl:
		typ->_sizeof = SIZEOF_ADDRESS;
		typ->_alignof = ALIGNOF_ADDRESS;
		if (is_function_pointer(typ)) {
			grok_type(typ->type_next->type_next);
		} else {
			grok_type(typ->type_next);
		}
		set_hash_for_type(typ);
		break;
	  case int_decl:
#ifdef SIZEOF_LONG_LONG
		if (typ->_long_long) {
			typ->_sizeof = SIZEOF_LONG_LONG;
			typ->_alignof = ALIGNOF_LONG_LONG;
			typ->_short = 0;
			typ->_long = 0;
			set_hash_for_type(typ);
			return;
		}
#endif
		if (typ->_long) {
			typ->_sizeof = SIZEOF_LONG;
			typ->_alignof = ALIGNOF_LONG;
			typ->_short = 0;
			typ->_long_long = 0;
			set_hash_for_type(typ);
			return;
		}
		if (typ->_short) {
			typ->_sizeof = SIZEOF_SHORT;
			typ->_alignof = ALIGNOF_SHORT;
			typ->_long_long = 0;
			set_hash_for_type(typ);
			return;
		}
		break;
	  case fp_decl:
#ifdef SIZEOF_LONG_DOUBLE
		if (typ->_long) {
			if (typ->_sizeof == SIZEOF_DOUBLE&&typ->_alignof == ALIGNOF_DOUBLE){
				typ->_sizeof = SIZEOF_LONG_DOUBLE;
				typ->_alignof = ALIGNOF_LONG_DOUBLE;
				set_hash_for_type(typ);
				return;
			}
		}
#endif
		break;
	  case func_decl:
		assert(0);
		break;
	  case enum_decl:
		typ->_sizeof = SIZEOF_INT;
		typ->_alignof = ALIGNOF_INT;
		set_hash_for_type(typ);
		break;
	  case array_decl:
	  case struct_decl:
		set_hash_for_type(typ);
		break;
	  default:
		assert(0);
		break;
	}
}

static symbol_t*
gen_rec_sym(id, typ)
	node_t *id;
	typeinfo_t *typ;
{
	symbol_t *sym;
	char rec_name[2048];

	assert(id != NULL);
	assert(id->node_kind == _Ident);

	rec_name[0] = (typ->type_kind == struct_of) ? STRUCT_PREFIX : UNION_PREFIX;
	strcpy(&rec_name[1], id->node.id.name);
	id->node.id.name = new_string(rec_name);

	sym = new_sym();
	sym->sym_type = typ;
	sym->sym_kind = type_symbol;
	sym->sym_ident = id;
#ifndef CCPROTO
	sym->sym_ada_name = ada_name(rec_name, FILE_ORD(sym->sym_def));
#endif
	sym->sym_def = id->node_def;
	typ->type_base = sym;

	return sym;
}

symbol_t*
grok_enumerator(id, val)
	node_t *id, *val;
{
	symbol_t *sym;

	assert(id != NULL);
	assert(id->node_kind == _Ident);
	assert(id->node.id.name != NULL);

	sym = new_sym();
	sym->sym_ident = id;
#ifndef CCPROTO
	sym->sym_ada_name = ada_name(sym->sym_ident->node.id.name, FILE_ORD(sym->sym_def));
#endif

	if (val == NULL) {
		sym->sym_value.intval = 0xBAD;
	}
	else {
		reduce_node(val);
		if (val->node_kind == _Int_Number) {
			sym->sym_value.intval = val->node.ival;
		}
		else {
			warning(NODE_FNAME(val), NODE_FLINE(val),
					"Enum literal value ignored");
			sym->sym_value.intval = 0xBAD;
		}
	}

	store_sym(sym);
	return sym;
}

static char*
gen_type_name(prefix)
	int prefix;
{
	char name[256];

#ifdef CCPROTO
	name[0] = prefix;
	name[1] = 0;
#else
	sprintf(name, "%c%s%d%s", prefix, OTHER_TYPE_PREFIX,
			next_anonymous_ord(), TYPE_POSTFIX);
#endif
	return new_string(name);
}

static char*
add_prefix(name, prefix, buf, len)
	char *name, *buf;
	int prefix, len;
{
	char *result;
	int nlen;

	assert(name != NULL);

	nlen = strlen(name) + 2;

	if (buf == NULL || nlen > len) {
		result = (char*) malloc(strlen(name)+2);
	}
	else {
		result = buf;
	}
	assert(result != NULL);
	result[0] = prefix;
	strcpy(&result[1], name);
	return result;
}

symbol_t*
anonymous_enum(literals)
	symbol_t *literals;
{
	symbol_t *sym;

	assert(literals != NULL);

	sym = new_sym();
	sym->sym_type = typeof_enum();
	sym->sym_type->type_base = sym;
	sym->sym_kind = type_symbol;
	sym->sym_ident = new_node(_Ident, gen_type_name(ENUM_PREFIX));
#ifndef CCPROTO
	sym->sym_ada_name = ada_name(sym->sym_ident->node.id.name, FILE_ORD(sym->sym_def));
#endif
	sym->_created_name = 1;
	grok_enum_lits(literals, sym->sym_type);
	add_tags(sym, literals);
	return sym;
}

symbol_t*
named_enum(id, literals)
	node_t *id;
	symbol_t *literals;
{
	symbol_t *sym;
	char buf[1024];

	assert(id != NULL);
	assert(literals != NULL);
	assert(id->node_kind == _Ident);

	buf[0] = ENUM_PREFIX;
	strcpy(&buf[1], id->node.id.name);
	sym = find_sym(buf);

	if (sym == NULL) {
		sym = new_sym();
		sym->sym_type = typeof_enum();
		sym->sym_type->type_base = sym;
		sym->sym_ident = new_node(_Ident, new_string(buf));
#ifndef CCPROTO
		sym->sym_ada_name = ada_name(sym->sym_ident->node.id.name, FILE_ORD(sym->sym_def));
#endif
		sym->sym_kind = type_symbol;
		store_sym(sym);
	}
	else {
		sym->_created_by_reference = 0;
	}

	sym->sym_def = id->node_def;
	grok_enum_lits(literals, sym->sym_type);
	add_tags(sym, literals);
	return sym;
}

symbol_t*
enum_reference(id)
	node_t *id;
{
	char buf[256];
	char *name;
	symbol_t *sym;

	assert(id != NULL);
	assert(id->node_kind == _Ident);

	name = add_prefix(id->node.id.name, ENUM_PREFIX, buf, sizeof(buf));

	sym = find_sym(name);
	if (sym == NULL || !is_typedef(sym)) {
		sym = new_sym();
		sym->sym_type = typeof_enum();
		sym->sym_type->type_base = sym;
		sym->sym_kind = type_symbol;
		if (name != buf) {
			id->node.id.name = name;
		}
		else {
			id->node.id.name = new_string(name);
		}
		sym->sym_ident = id;
#ifndef CCPROTO
		sym->sym_ada_name = ada_name(sym->sym_ident->node.id.name, FILE_ORD(sym->sym_def));
#endif
		sym->_created_by_reference = 1;
		store_sym(sym);
	}
	else {
		if (name != buf) {
			free(name);
		}
	}

	return sym;
}

static char*
anonymous_rec_name(is_union)
	int is_union;
{
	return gen_type_name(is_union ? UNION_PREFIX : STRUCT_PREFIX);
}

static symbol_t*
delete_unamed_fields(tags)
	symbol_t *tags;
{
	symbol_t *head = NULL;
	symbol_t *last = NULL;

	if (tags == NULL) return NULL;

	for (; tags; tags = tags->sym_parse_list) {
		if (tags->sym_ident == NULL) {
			if (head != NULL) {
				last->sym_parse_list = tags->sym_parse_list;
			}
		}
		else {
			if (head == NULL) {
				head = tags;
			}
			else {
				last->sym_parse_list = tags;
			}
			last = tags;
		}
	}

	return head;
}

symbol_t*
anonymous_rec(is_union, tags)
	int is_union;
	symbol_t *tags;
{
	symbol_t *sym;
	typeinfo_t *rtyp;

	assert(tags != NULL);

	rtyp = typeof_rec(is_union);

	sym = new_sym();
	sym->sym_type = rtyp;
	sym->sym_kind = type_symbol;
	sym->sym_ident = new_node(_Ident, anonymous_rec_name(is_union));
#ifndef CCPROTO
	sym->sym_ada_name = ada_name(sym->sym_ident->node.id.name, FILE_ORD(sym->sym_def));
#endif
	sym->sym_tags = tags;

	rtyp->type_base = sym;

	if (is_union) {
		grok_sizeof_union(rtyp, tags);
	}
	else {
		grok_sizeof_struct(rtyp, tags);
		tags = delete_unamed_fields(tags);
		sym->sym_tags = tags;
	}

	grok_alignof_record(rtyp, tags);

	return sym;
}

symbol_t*
named_rec(is_union, id, tags)
	int is_union;
	node_t *id;
	symbol_t *tags;
{
	symbol_t *sym;
	typeinfo_t *rtyp;
	char buf[1024];

	assert(id != NULL);
	assert(id->node_kind == _Ident);
	assert(tags != NULL);

	buf[0] = is_union ? UNION_PREFIX : STRUCT_PREFIX; 
	strcpy(&buf[1], id->node.id.name);

	sym = find_sym(buf);
	if (sym == NULL) {
		rtyp = typeof_rec(is_union);
		sym = gen_rec_sym(id, rtyp);
		store_sym(sym);
	}
	else {
		sym->_created_by_reference = 0;
		rtyp = sym->sym_type;
	}

	sym->sym_tags = tags;

	if (is_union) {
		grok_sizeof_union(rtyp, tags);
	}
	else {
		grok_sizeof_struct(rtyp, tags);
		tags = delete_unamed_fields(tags);
		sym->sym_tags = tags;
	}

	grok_alignof_record(rtyp, tags);

	return sym;
}

symbol_t*
rec_reference(is_union, id)
	int is_union;
	node_t *id;
{
	symbol_t *sym;
	typeinfo_t *rtyp;
	char buf[1024];

	assert(id != NULL);
	assert(id->node_kind == _Ident);

	buf[0] = is_union ? UNION_PREFIX : STRUCT_PREFIX; 
	strcpy(&buf[1], id->node.id.name);

	sym = find_sym(buf);
	if (sym == NULL) {
		rtyp = typeof_rec(is_union);
		sym = gen_rec_sym(id, rtyp);
		sym->_created_by_reference = 1;
		store_sym(sym);
	}

	return sym;
}

static int
no_typemods(typ)
	typeinfo_t *typ;
{
	for (; typ; typ = typ->type_next) {
		if (typ->type_kind == typemodifier) {
			return 0;
		}
	}
	return 1;
}

symbol_t*
novar_declaration(tlist)
	typeinfo_t *tlist;
{
	symbol_t *result = NULL;

	assert(tlist != NULL);

	tlist = typeof_typespec(tlist);
	assert(no_typemods(tlist));
	
	switch (decl_class(tlist)) {
	  case int_decl:
		return NULL;
	  case enum_decl:
	  case struct_decl:
		assert(tlist->type_base != NULL);
		return tlist->type_base;
	  case pointer_decl:
	  case fp_decl:
	  case func_decl:
	  case array_decl:
		break;
	  default:
		assert(0);
		break;
	}
	assert(0);
	return result;
}

symbol_t*
var_declaration(tlist, vlist)
	typeinfo_t *tlist;
	node_t *vlist;
{
	symbol_t *decl_list;

	assert(tlist != NULL);
	assert(vlist != NULL);

	tlist = typeof_typespec(tlist);
	assert(no_typemods(tlist));
	grok_type(tlist);

	decl_list = grok_decl_list(tlist, vlist, 1);
	return set_symbol_kind(decl_list);
}

symbol_t*
function_spec(tlist, f)
	typeinfo_t *tlist;
	node_t *f;
{
	symbol_t *fdecl;

	assert(f != NULL);

	if (tlist == NULL) {
		tlist = typeof_int(0);
	}
	else {
		tlist = typeof_typespec(tlist);
	}

	assert(no_typemods(tlist));
	grok_type(tlist);

	fdecl = grok_decl_list(tlist, f, 1);
	assert(fdecl != NULL);

	fdecl->sym_kind = func_symbol;
	return fdecl;
}


static void
set_field_names(tags)
	symbol_t *tags;
{
#ifndef CCPROTO
	for (; tags; tags = tags->sym_parse_list) {
		if (tags->sym_ada_name == NULL) {
			if (tags->sym_ident != NULL) {
				tags->sym_ada_name = ada_name(tags->sym_ident->node.id.name, FILE_ORD(tags->sym_def));
			}
		}
	}
#endif
}

symbol_t*
field_declaration(tlist, vlist)
	typeinfo_t *tlist;
	node_t *vlist;
{
	symbol_t *decl_list;

	assert(tlist != NULL);
	assert(vlist != NULL);

	tlist = typeof_typespec(tlist);
	assert(no_typemods(tlist));
	grok_type(tlist);

	decl_list = grok_decl_list(tlist, vlist, 0);
	set_field_names(decl_list);
#ifndef CCPROTO
	gen_tag_types(decl_list);
#endif

	return set_symbol_kind(decl_list);
}

void
typed_external_decl(syms)
	symbol_t *syms;
{
	symbol_t *next;

	for (; syms; syms = next) {
		next = syms->sym_parse_list;
		syms->sym_parse_list = NULL;
		grok_decl(syms);
	}
}

static char*
next_param_name()
{
	char buf[48];
	sprintf(buf, "p%d", next_param());
	return new_string(buf);
}

symbol_t*
noname_simple_param(typ)
	typeinfo_t *typ;
{
	symbol_t *sym;
	char *name;

	assert(typ != NULL);

	name = next_param_name();

	typ = typeof_typespec(typ);
	assert(no_typemods(typ));
	grok_type(typ);

	sym = new_sym();
	sym->sym_ident = new_node(_Ident, name);
#ifndef CCPROTO
	sym->sym_ada_name = name;
#endif
	sym->sym_type = typ;
	sym->sym_kind = param_symbol;

	return sym;
}

symbol_t*
noname_abstract_param(typ, adecl)
	typeinfo_t *typ;
	node_t *adecl;
{
	symbol_t *sym;
	char *name;

	assert(typ != NULL);
	assert(adecl != NULL);

	name = next_param_name();

	typ = typeof_typespec(typ);
	assert(no_typemods(typ));
	grok_type(typ);

	sym = grok_decl_list(typ, adecl, 0);
	sym->sym_ident = new_node(_Ident,name);
#ifndef CCPROTO
	sym->sym_ada_name = name;
#endif
	sym->sym_kind = param_symbol;

	return sym;
}

symbol_t*
named_abstract_param(typ, adecl)
	typeinfo_t *typ;
	node_t *adecl;
{
	symbol_t *sym;

	assert(typ != NULL);
	assert(adecl != NULL);

	next_param();

	typ = typeof_typespec(typ);
	assert(no_typemods(typ));
	grok_type(typ);

	sym = grok_decl_list(typ, adecl, 0);
	sym->sym_kind = param_symbol;

	return sym;
}

typeinfo_t*
noname_type(typ, adecl)
	typeinfo_t *typ;
	node_t *adecl;
{
	symbol_t *sym;

	assert(typ != NULL);
	assert(adecl != NULL);

	typ = typeof_typespec(typ);
	assert(no_typemods(typ));
	grok_type(typ);

	sym = grok_decl_list(typ, adecl, 0);
	return sym->sym_type;
}

static void
KnR_tag_type(p, params)
	symbol_t *p, *params;
{
	for (; params; params = params->sym_parse_list) {
		if (!strcmp(p->sym_ident->node.id.name, params->sym_ident->node.id.name)) {
			p->sym_type = params->sym_type;
			return;
		}
	}
}

void
KnR_params(func, params)
	symbol_t *func, *params;
{
	symbol_t *tags;

	assert(func != NULL);
	assert(params != NULL);

	for (tags = func->sym_tags; tags; tags = tags->sym_parse_list) {
		KnR_tag_type(tags, params);
	}
}

void
function_def(f)
	symbol_t *f;
{
	symbol_t *dup;

	assert(f != NULL);

#ifndef CCPROTO
	if (is_static_function(f->sym_type)) {
		return;
	}
#endif

	assert(f->sym_ident != NULL);
	assert(f->sym_ident->node_kind == _Ident);
	assert(f->sym_ident->node.id.name != NULL);

	dup = find_sym(f->sym_ident->node.id.name);

	if (dup == NULL) {
		grok_decl(f);
		return;
	}

	if (dup->sym_tags == NULL && f->sym_tags != NULL) {
		/* Must be K&R crap */

		dup->sym_tags = f->sym_tags;
#ifndef CCPROTO
		gen_tag_types(dup->sym_tags);
#endif
	}
}

symbol_t*
elipsis_arg()
{
	static symbol_t varg;

	varg.var_params = 1;
	return &varg;
}


syntax highlighted by Code2HTML, v. 0.9.1