/* %Z%%M% %I% %E% */

/*
 * Copyright (c) 1992, 1993, 1995 by Wayne C. Gramlich.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/*
 * This file contains the routines for manipulating Type_proto's, Type_need's,
 * and Type_signal's:
 */

#ifndef GEN_DEFS_H
#include "gen_defs.h"
#endif

#ifndef HEAP_EXPORTS_H
#include "heap_exports.h"
#endif

#ifndef LIBC_EXPORTS_H
#include "libc_exports.h"
#endif

#ifndef LINT_H
#include "lint.h"
#endif

#ifndef OUT_EXPORTS_H
#include "out_exports.h"
#endif

#ifndef STR_EXPORTS_H
#include "str_exports.h"
#endif

#ifndef TABLE_EXPORTS_H
#include "table_exports.h"
#endif

#define SPECIAL
#ifndef TYPE_DEFS_H
#include "type_defs.h"
#endif

#ifndef UNIX_ASSERT_H
#include "unix_assert.h"
#endif

#ifndef UNIX_CTYPE_H
#include "unix_ctype.h"
#endif

#ifndef UNIX_STDLIB_H
#include "unix_stdlib.h"
#endif

#ifndef UNIX_UNISTD_H
#include "unix_unistd.h"
#endif

#ifndef VECTOR_DEFS_H
#include "vector_defs.h"
#endif

LOCAL int		type_need_compare(Type_need, Type_need, int);
LOCAL int		type_need_equal(Type_need, Type_need);
LOCAL int		type_need_hash(Type_need);
LOCAL Type_need	type_need_read(Stdio, Heap, Type_tables);
LOCAL Type_need	type_need_replace(Type_need, Type_refs,
					  Type_refs, Type_tables);
LOCAL void		type_need_write(Type_need, Stdio, Type_tables);

LOCAL int		type_needs_compare(Type_needs, Type_needs, int);
LOCAL int		type_needs_equal(Type_needs, Type_needs);
LOCAL int		type_needs_hash(Type_needs);
LOCAL void		type_needs_print(Type_needs, int, Stdio);
LOCAL Type_needs	type_needs_read(Stdio, Heap, Type_tables);
LOCAL Type_needs	type_needs_replace(Type_needs, Type_refs,
					       Type_refs, Type_tables);
LOCAL void		type_needs_write(Type_needs, Stdio, Type_tables);

LOCAL int		type_proto_equal(Type_proto, Type_proto);
LOCAL int		type_proto_hash(Type_proto);

LOCAL int		type_signal_compare(Type_signal, Type_signal, int);
LOCAL int		type_signal_equal(Type_signal, Type_signal);
LOCAL int		type_signal_hash(Type_signal);
LOCAL void		type_signal_needed(Type_signal, Type_tables);
LOCAL void		type_signal_print(Type_signal, Stdio);
LOCAL Type_signal	type_signal_read(Stdio, Heap, Type_tables);
LOCAL Type_signal	type_signal_replace(Type_signal, Type_refs,
					    Type_refs, Type_tables);
LOCAL void		type_signal_write(Type_signal, Stdio, Type_tables);

LOCAL int		type_signals_compare(Type_signals, Type_signals, int);
LOCAL int		type_signals_equal(Type_signals, Type_signals);
LOCAL int		type_signals_hash(Type_signals);
LOCAL void		type_signals_needed(Type_signals, Type_tables);
LOCAL void		type_signals_print(Type_signals, int, Stdio);
LOCAL Type_signals	type_signals_read(Stdio, Heap, Type_tables);
LOCAL Type_signals	type_signals_replace(Type_signals, Type_refs,
					     Type_refs, Type_tables);
LOCAL void		type_signals_write(Type_signals, Stdio, Type_tables);


LOCAL Type_needs	type_needs_table_lookup(Type_need_table);
LOCAL Type_signals	type_signals_table_lookup(Type_signal_table);

/*
 * Type_need routines:
 */

/*
 * type_need_compare(type_need1, type_need2, zilch)
 *	This routine will return -1, 0, or 1 depending upon whether
 *	"type_need1" is less than, equal to, or greater than "type_need2".
 */
/* ARGSUSED */
int
type_need_compare(
	Type_need	type_need1,
	Type_need	type_need2,
	int		zilch)
{
	int		result;

	result = strcmp(type_need1->type_ref->name,
			type_need2->type_ref->name);
	if (result != 0) {
		return result;
	}
	return strcmp(type_need1->name, type_need2->name);
}

/*
 * type_need_create(type_ref, name, proto, type_tables)
 *	This routine will create and return a new type need object
 *	consisting of, "type_ref", "name", and "proto" using "type_tables".
 */
Type_need
type_need_create(
	Type_ref	type_ref,
	Str		name,
	Type_proto	proto,
	Type_tables	type_tables)
{
	Type_need	need_key;
	Table(Type_need, Type_need) need_table;
	Type_need	type_need;
	Type_need_table	type_need_table;

	type_need_table = type_tables->type_need_table;
	need_table = type_need_table->need_table;
	need_key = type_need_table->need_key;
	need_key->name = name;
	need_key->type_ref = type_ref;
	need_key->proto = proto;
	type_need = table_lookup(Type_need, Type_need,
				 need_table, need_key);
	if (type_need == (Type_need)0) {
		type_need = heap_allocate(type_need_table->heap, Type_need);
		type_need->type_ref = type_ref;
		type_need->name = name;
		type_need->proto = proto;
		assert(table_insert(Type_need, Type_need,
				    need_table, type_need, type_need) == 0);
	}
	return type_need;
}

/*
 * type_need_equal(type_need1, type_need2)
 *	This routine will return 1 if "type_need1" equals "type_need2" and
 *	0 otherwise.
 */
int
type_need_equal(
	Type_need		type_need1,
	Type_need		type_need2)
{
	return (type_ref_equal(type_need1->type_ref, type_need2->type_ref) &&
		strequal(type_need1->name, type_need2->name) &&
		type_proto_equal(type_need1->proto, type_need2->proto));
}

/*
 * type_need_hash(type_need)
 *	This routine will return a hash value for "type_need".
 */
int
type_need_hash(
	Type_need	type_need)
{
	return (type_ref_hash(type_need->type_ref) +
		strhash(type_need->name) +
		type_proto_hash(type_need->proto));
}

/*
 * type_need_print(type_need, indent, out_file)
 *	This routine will print "type_need" to "out_file".
 */
void
type_need_print(
	Type_need	type_need,
	int		indent,
	Stdio		out_file)
{
	out(out_file, "%\t%s%@ ", indent, type_need->name, type_need->type_ref);
	type_proto_print(type_need->proto, indent + 1, out_file);
}

/*
 * type_need_read(in_file, heap, type_tables)
 *	This routine will read in and return a Type_need from "in_file" using
 *	"heap".
 */
Type_need
type_need_read(
	Stdio		in_file,
	Heap		heap,
	Type_tables	type_tables)
{
	Str		name;
	Type_proto	type_proto;
	Type_ref	type_ref;
	Type_need	type_need;

	out_marker_read(in_file, "type_need");
	type_ref = type_ref_read(in_file, heap, type_tables);
	name = strread(in_file, heap);
	type_proto = type_proto_read(in_file, heap, type_tables);
	type_need = type_need_create(type_ref, name, type_proto, type_tables);
	return type_need;
}

/*
 * type_need_replace(old_type_need, old_type_refs, new_type_refs, type_tables)
 *	This routine will replace all occurances of "old_type_refs" with
 *	"new_type_refs" in "old_type_need" and return the new Type_need object.
 */
Type_need
type_need_replace(
	Type_need	old_type_need,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	return type_need_create(type_ref_replace(old_type_need->type_ref,
						 old_type_refs,
						 new_type_refs,
						 type_tables),
				old_type_need->name,
				type_proto_replace(old_type_need->proto,
						   old_type_refs,
						   new_type_refs,
						   type_tables),
				type_tables);
}

/*
 * type_need_write(type_need, out_file, type_tables)
 *	This routine will write "type_need" to "out_file".
 */
void
type_need_write(
	Type_need	type_need,
	Stdio		out_file,
	Type_tables	type_tables)
{
	out_marker_write(out_file, "type_need");
	type_ref_write(type_need->type_ref, out_file, type_tables);
	(void)strwrite(type_need->name, out_file);
	type_proto_write(type_need->proto, out_file, type_tables);
}

/*
 * type_need_table_create(type_ref_table, heap)
 *	This routine will create and return a new prototype table.
 */
/* ARGSUSED */
Type_need_table
type_need_table_create(
	Type_ref_table	type_ref_table,
	Heap		heap)
{
	Type_need_table type_need_table;

	type_need_table = heap_allocate(heap, Type_need_table);
	type_need_table->heap = heap;
	type_need_table->need_key = heap_allocate(heap, Type_need);
	type_need_table->need_table = table_create(Type_need, Type_need, 50,
						   type_need_equal,
						   type_need_hash,
						   (Type_need)0,
						   heap);
	type_need_table->needs_table = table_create(Type_needs, Type_needs, 50,
						    type_needs_equal,
						    type_needs_hash,
						    (Type_needs)0,
						    heap);
	type_need_table->needs_key = vec_create(Type_need, heap);
	type_need_table->needs_empty = type_needs_table_lookup(type_need_table);
	return type_need_table;
}

/*
 * type_need_table_dump(type_need_table, out_file)
 *	This routine will dump the contents of "type_need_table" to
 *	"out_file" in human readable format.
 */
void
type_need_table_dump(
	Type_need_table type_need_table,
	Stdio		out_file)
{
	Type_need	type_need;
	Vec(Type_need)	type_need_list;
	Type_needs	type_needs;
	Vec(Type_needs)	type_needs_list;

	type_need_list = table_value_list_extract(Type_need, Type_need,
						  type_need_table->need_table);
	vec_sort(Type_need, type_need_list, type_need_compare, 0);
	out(out_file, "Type Need Table:\n");
	if (vec_empty(Type_need, type_need_list)) {
		out(out_file, "<Empty>\n");
	} else {
		VEC_LOOP(Type_need, type_need_list, type_need) {
			type_need_print(type_need, 0, out_file);
		}
	}
	out(out_file, "\n");

	type_needs_list = table_value_list_extract(Type_needs, Type_needs,
						type_need_table->needs_table);
	out(out_file, "Type Need List Table:\n");
	VEC_LOOP(Type_needs, type_needs_list, type_needs) {
		out(out_file, "----\n");
		type_needs_print(type_needs, 0, out_file);
	}
	out(out_file, "--------\n");
	out(out_file, "\n");
}

/*
 * Type_needs routines:
 */

/*
 * type_needs_append(type_needs, type_need, type_tables)
 *	This routine will return a new type needs list that consists of
 *	"type_needs" appended wity "type_need" allocated from "type_tables".
 */
Type_needs
type_needs_append(
	Type_needs	type_needs,
	Type_need	type_need,
	Type_tables	type_tables)
{
	Type_needs	needs_key;
	Type_need_table	type_need_table;

	type_need_table = type_tables->type_need_table;
	needs_key = type_need_table->needs_key;
	assert(vec_empty(Type_need, needs_key));
	vec_vec_append(Type_need, needs_key, type_needs);
	vec_append(Type_need, needs_key, type_need);
	type_needs = type_needs_table_lookup(type_need_table);
	return type_needs;
}

/*
 * type_needs_compare(type_needs1, type_needs2, zilch)
 *	This routine will return <0, =0, >0, depending upon whether
 *	"type_needs1" is smaller, equal to, or greater than "type_needs2".
 */
/* ARGSUSED */
int
type_needs_compare(
	Type_needs	type_needs1,
	Type_needs	type_needs2,
	int		zilch)
{	
	int		index;
	int		result;
	int		size1;
	int		size2;
	Type_need	type_need1;
	Type_need	type_need2;

	size1 = vec_size(Type_need, type_needs1);
	size2 = vec_size(Type_need, type_needs2);
	result = size1 - size2;
	if (result != 0) {
		return result;
	}
	for (index = 0; index < size1; index++) {
		type_need1 = vec_fetch(Type_need, type_needs1, index);
		type_need2 = vec_fetch(Type_need, type_needs2, index);
		result = type_need_compare(type_need1, type_need2, 0);
		if (result != 0) {
			return result;
		}
	}
	return 0;
}

/*
 * type_needs_empty_create(type_tables)
 *	This routine will return an empty type needs list.
 */
Type_needs
type_needs_empty_create(
	Type_tables	type_tables)
{
	return type_tables->type_need_table->needs_empty;
}

/*
 * type_needs_equal(type_needs1, type_needs2)
 *	This routine will return 1 if "type_needs1" is equal to "type_needs2"
 *	and 0 otherwise.
 */
int
type_needs_equal(
	Type_needs	type_needs1,
	Type_needs	type_needs2)
{
	return (type_needs_compare(type_needs1, type_needs2, 0) == 0);
}

/*
 * type_needs_hash(type_needs)
 *	This routine will return a hash value for "type_needs".
 */
int
type_needs_hash(
	Type_needs	type_needs)
{
	int		hash;
	Type_need	type_need;

	hash = 0;
	VEC_LOOP(Type_need, type_needs, type_need) {
		hash += type_need_hash(type_need);
	}
	return hash;
}

/*
 * type_needs_is_empty(type_needs)
 *	This routine will return 1 if "type_needs" is empty and 0 otherwise.
 */
int
type_needs_is_empty(
	Type_needs	type_needs)
{
	return vec_empty(Type_need, type_needs);
}

/*
 * type_needs_print(type_needs, indent, out_file)
 *	This routine will print "type_needs" to "out_file" indented by "indent".
 */
void
type_needs_print(
	Type_needs	type_needs,
	int		indent,
	Stdio		out_file)
{
	Type_need	type_need;

	VEC_LOOP(Type_need, type_needs, type_need) {
		type_need_print(type_need, indent, out_file);
	}
}

/*
 * type_needs_replace(old_type_needs, old_type_refs,
 *			  new_type_refs, type_tables)
 *	This routine will replace all occurances of "old_type_refs" with
 *	"new_type_refs" in "old_type_needs" and return the resulting list.
 */
Type_needs
type_needs_replace(
	Type_needs	old_type_needs,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	Type_need	new_type_need;
	Type_needs	new_type_needs;
	Type_need	old_type_need;

	new_type_needs = type_tables->type_need_table->needs_empty;
	VEC_LOOP(Type_need, old_type_needs, old_type_need) {
		new_type_need = type_need_replace(old_type_need,
						  old_type_refs,
						  new_type_refs,
						  type_tables);
		new_type_needs = type_needs_append(new_type_needs,
						   new_type_need,
						   type_tables);
	}
	return new_type_needs;
}

/*
 * type_needs_read(in_file, heap, type_tables)
 *	This routine will read in and return list of type references from
 *	"in_file" allocated from "heap".
 */
Type_needs
type_needs_read(
	Stdio		in_file,
	Heap		heap,
	Type_tables	type_tables)
{
	int		count;
	Type_need	type_need;
	Type_needs	type_needs;

	out_marker_read(in_file, "type_needs");
	type_needs = type_tables->type_need_table->needs_empty;
	count = out_count_read(in_file);
	while (count-- > 0) {
		type_need = type_need_read(in_file, heap, type_tables);
		type_needs = type_needs_append(type_needs,
					       type_need, type_tables);
	}
	return type_needs;
}

/*
 * type_needs_write(type_needs, out_file, type_tables)
 *	This routine will write "type_needs" to "out_file".  0 is always
 *	returned.
 */
void
type_needs_write(
	Type_needs	type_needs,
	Stdio		out_file,
	Type_tables	type_tables)
{
	int		index;
	int		size;
	Type_need	type_need;

	out_marker_write(out_file, "type_needs");
	size = vec_size(Type_need, type_needs);
	out_count_write(out_file, size);
	for (index = 0; index < size; index++) {
		type_need = vec_fetch(Type_need, type_needs, index);
		type_need_write(type_need, out_file, type_tables);
	}
}

/*
 * type_needs_table_lookup(type_need_table)
 *	This routine will return the type need list corresponding to
 *	"type_need_table"->needs_key.
 */
LOCAL Type_needs
type_needs_table_lookup(
	Type_need_table	type_need_table)
{
	Type_needs	needs_key;
	Type_needs	type_needs;
	Table(Type_need, Type_need) needs_table;

	needs_key = type_need_table->needs_key;
	vec_sort(Type_need, needs_key, type_need_compare, 0);
	needs_table = type_need_table->needs_table;
	type_needs = table_lookup(Type_needs, Type_needs,
				  needs_table, needs_key);
	if (type_needs == (Type_needs)0) {
		type_needs = vec_create(Type_need, type_need_table->heap);
		vec_vec_append(Type_need, type_needs, needs_key);
		assert(table_insert(Type_needs, Type_needs, needs_table,
				    type_needs, type_needs) == 0);
	}
	vec_trim(Type_needs, needs_key, 0);
	return type_needs;
}

/*
 * Type_proto routines:
 */

#ifdef OLD
/*
 * type_proto_compare(type_proto1, type_proto2, zilch)
 *	This routine is returns <0, 0, or >0 depending upon whether
 *	"type_proto1" is less than, equal to, or greater than "type_proto2".
 */
/* ARGSUSED */
int
type_proto_compare(
	Type_proto	type_proto1,
	Type_proto	type_proto2,
	int		zilch)
{
	Routine_ref	routine_ref1;
	Routine_ref	routine_ref2;
	int		result;

	/* This implementation seems bogus! */

	routine_ref1 = type_proto1->routine_ref;
	routine_ref2 = type_proto2->routine_ref;
	result = type_ref_compare(routine_ref1->type_ref,
				  routine_ref2->type_ref);
	if (result != 0) {
		return result;
	}
	return strcmp(routine_ref1->name, routine_ref2->name);
}
#endif /* OLD */

/*
 * type_proto_create(kind, params, takes, returns, yields,
 *		     signals, needs, no_return, type_tables)
 *	This routine will create and return a type prototype object consisting
 *	of type "kind", having "params" parameters, "takes" arguments,
 *	"returns" return types, "yields" yields types, "signals"
 *	signals, "needs" needs, and "no_return" allocted from "heap".
 *	A side-effect of this routine is that "signals" and "needs"
 *	will be sorted.
 */
Type_proto
type_proto_create(
	Type_proto_kind	kind,
	Type_refs	params,
	Type_refs	takes,
	Type_refs	returns,
	Type_refs	yields,
	Type_signals	signals,
	Type_needs	needs,
	int		no_return,
	Type_tables	type_tables)
{
	char		anonymous[20];
	Heap		heap;
	Type_proto	type_proto;
	Type_proto	key;
	Table(Type_proto, Type_proto) table;
	Vec(Type_proto)	type_proto_list;
	Type_proto_table type_proto_table;
	Type_ref	type_ref;
	Vec(Type_ref)	type_ref_list;

	type_proto_table = type_tables->type_proto_table;
	key = type_proto_table->key;
	key->kind = kind;
	key->params = params;
	key->takes = takes;
	key->returns = returns;
	key->yields = yields;
	key->signals = signals;
	key->needs = needs;
	key->no_return = no_return;
	table = type_proto_table->table;
	type_proto = table_lookup(Type_proto, Type_proto, table, key);
	if (type_proto == (Type_proto)0) {
		heap = type_proto_table->heap;

		type_ref_list = type_proto_table->type_ref_list;
		(void)sprintf(anonymous, "anonymous__%d",
			      vec_size(Type_ref, type_ref_list));
		type_ref = type_ref_create(strdupl(anonymous, heap),
					   type_tables);
		vec_append(Type_ref, type_ref_list, type_ref);

		type_proto = heap_allocate(heap, Type_proto);
		type_proto->generated = 0;
		type_proto->kind = kind;
		type_proto->needs = needs;
		type_proto->needed = 0;
		type_proto->no_return = no_return;
		type_proto->params = params;
		type_proto->returns = returns;
		type_proto->signals = signals;
		type_proto->takes = takes;
		type_proto->type_ref = type_ref;
		type_proto->yields = yields;
		assert(table_insert(Type_proto, Type_proto,
				    table, type_proto, type_proto) == 0);

		type_proto_list = type_proto_table->type_proto_list;
		vec_append(Type_proto, type_proto_list, type_proto);
	}
	return type_proto;
}

/*
 * type_proto_equal(proto1, proto1)
 *	This routine will return 1 if "proto1" equals "proto2" and 0 otherwise.
 */
int
type_proto_equal(
	Type_proto	proto1,
	Type_proto	proto2)
{
	if (proto1 == (Type_proto)0) {
		if (proto2 == (Type_proto)0) {
			return 1;
		} else {
			return 0;
		}
	} else if (proto2 == (Type_proto)0) {
		return 0;
	}
	if (!type_refs_equal(proto1->takes, proto2->takes)) {
		return 0;
	}
	if (proto1->kind != proto2->kind) {
		return 0;
	}
	if (!type_refs_equal(proto1->params, proto2->params)) {
		return 0;
	}
	if (!type_needs_equal(proto1->needs, proto2->needs)) {
		return 0;
	}
	if (proto1->no_return != proto2->no_return) {
		return 0;
	}
	if (!type_refs_equal(proto1->returns, proto2->returns)) {
		return 0;
	}
	if (!type_signals_equal(proto1->signals, proto2->signals)) {
		return 0;
	}
	if (!type_refs_equal(proto1->yields, proto2->yields)) {
		return 0;
	}
	return 1;
#ifdef OLD
	return (type_refs_equal(proto1->takes, proto2->takes) &&
		(proto1->kind == proto2->kind) &&
		(proto1->params == proto2->params) &&
		type_needs_equal(proto1->needs, proto2->needs) &&
		(proto1->no_return == proto2->no_return) &&
		type_refs_equal(proto1->returns, proto2->returns) &&
		type_signals_equal(proto1->signals, proto2->signals) &&
		type_refs_equal(proto1->yields, proto2->yields) &&
		type_proto_equal(proto1->param_proto, proto2->param_proto));
#endif /* OLD */
}

/*
 * type_proto_from_type_ref(type_ref, type_tables)
 *	This routine will return the prototype corresponding to "type_ref"
 *	using "type_tables".  If "type_ref" does not correspond to a
 *	routine type, a fatal error ensues.
 */
Type_proto
type_proto_from_type_ref(
	Type_ref	type_ref,
	Type_tables	type_tables)
{
	Type_proto	type_proto;

	assert(type_ref_is_routine(type_ref));
	type_proto = vec_fetch(Type_proto,
			       type_tables->type_proto_table->type_proto_list,
			       atoi(type_ref->name + 11));
			/* 11 = strlen("anonymous__" */
	return type_proto;
}

/*
 * type_proto_hash(proto)
 *	This routine will return a hash value for "proto".
 */
int
type_proto_hash(
	Type_proto	proto)
{
	if (proto == (Type_proto)0) {
		return 0;
	}
	return (type_refs_hash(proto->takes) +
		(int)proto->kind +
		type_refs_hash(proto->params) +
		type_needs_hash(proto->needs) +
		proto->no_return +
		type_refs_hash(proto->returns) +
		type_signals_hash(proto->signals) +
		type_refs_hash(proto->yields));
}

/*
 * type_proto_is_parameter(type_proto, type_ref)
 *	This routine will return 1 if "type_ref" contains any parameter from
 *	"type_proto".
 */
int
type_proto_is_parameter(
	Type_proto	type_proto,
	Type_ref	type_ref)
{
	Type_ref	param;
	Type_refs	params;

	params = type_proto->params;
	TYPE_REFS_LOOP(params, param) {
		if (type_ref_equal(type_ref, param)) {
			return 1;
		}
	}
	return 0;
}

/*
 * type_proto_is_parameterized(type_proto)
 *	This routine will return 1 if "type_prot" is parameterized and
 *	0 otherwise.
 */
int
type_proto_is_parameterized(
	Type_proto	type_proto)
{
	return !type_refs_is_empty(type_proto->params);
}

/*
 * type_proto_needed(type_proto, type_tables)
 *	This routine will mark "type_proto" as being needed.
 */
void
type_proto_needed(
	Type_proto	type_proto,
	Type_tables	type_tables)
{
	if (!type_proto->needed) {
		type_proto->needed = 1;
	
		type_refs_needed(type_proto->returns, type_tables);
		type_refs_needed(type_proto->takes, type_tables);
		type_signals_needed(type_proto->signals, type_tables);
		type_refs_needed(type_proto->yields, type_tables);

		if (type_refs_size(type_proto->returns) > 1) {
			type_refs_multiple_needed(type_proto->returns,
						  type_tables);
		}
	}
}

/*
 * type_proto_print(type_proto, indent, out_file)
 *	This routine will print "type_proto" to "out_file" indented by
 *	"indent".
 */
void
type_proto_print(
	Type_proto	type_proto,
	int		indent,
	Stdio		out_file)
{
	Type_refs	params;
	Str		separator;

	if (indent < 0) {
		indent -= 3;
		separator = "; ";
	} else {
		separator = "\n";
	}
	params = type_proto->params;
	if (!type_refs_is_empty(params)) {
		out(out_file, "[");
		type_refs_print(params, out_file);
		out(out_file, "] ");
	}
	switch (type_proto->kind) {
	    case Type_proto_procedure:
		out(out_file, "procedure");
		break;
	    case Type_proto_iterator:
		out(out_file, "iterator");
		break;
	    default:
		assert_fail();
	}
	out(out_file, "%s", separator);

	if (type_refs_is_empty(type_proto->takes)) {
		out(out_file, "%\ttakes_nothing%s", indent, separator);
	} else {
		out(out_file, "%\ttakes ", indent);
		type_refs_print(type_proto->takes, out_file);
		out(out_file, "%s", separator);
	}
	if (type_proto->no_return) {
		out(out_file, "%\treturns_never%s", indent, separator);
	} else if (type_refs_is_empty(type_proto->returns)) {
		out(out_file, "%\treturns_nothing%s", indent, separator);
	} else {
		out(out_file, "%\treturns ", indent);
		type_refs_print(type_proto->returns, out_file);
		out(out_file, "%s", separator);
	}
	if (type_proto->kind == Type_proto_iterator) {
		if (type_refs_is_empty(type_proto->yields)) {
			out(out_file, "%\tyields_nothing%s", indent, separator);
		} else {
			out(out_file, "%\tyields ", indent);
			type_refs_print(type_proto->yields, out_file);
			out(out_file, "%s", separator);
		}
	}
	if (!vec_empty(Type_signal, type_proto->signals)) {
		Type_signal	type_signal;

		out(out_file, "%\tsignals%s", indent, separator);
		VEC_LOOP(Type_signal, type_proto->signals, type_signal) {
			out(out_file, "%\t%s ",
			    indent + 1, type_signal->name);
			type_refs_print(type_signal->type_refs, out_file);
			out(out_file, "%s", separator);
		}
	}
	if (!vec_empty(Type_need, type_proto->needs)) {
		Type_need	type_need;

		out(out_file, "%\tneeds%s", indent, separator);
		VEC_LOOP(Type_need, type_proto->needs, type_need) {
			out(out_file, "%\t%s ",
			    indent + 1, type_need->name);
			type_proto_print(type_need->proto,
					 indent + 2, out_file);
		}
	}
}

/*
 * type_proto_replace(old_type_proto, old_type_refs,
 *		      new_type_refs, type_tables)
 *	This routine will replace all occurrances of "old_type_refs" with
 *	"new_type_refs" in "old_type_proto".
 */
Type_proto
type_proto_replace(
	Type_proto	old_type_proto,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	Type_needs	needs;
	Type_proto	new_type_proto;
	Type_refs	params;
	Type_refs	returns;
	Type_signals	signals;
	Type_refs	takes;
	Type_refs	yields;

	params = type_refs_replace(old_type_proto->params,
				   old_type_refs,
				   new_type_refs,
				   type_tables);
	takes = type_refs_replace(old_type_proto->takes,
				  old_type_refs,
				  new_type_refs,
				  type_tables);
	returns = type_refs_replace(old_type_proto->returns,
				    old_type_refs,
				    new_type_refs,
				    type_tables);
	yields = type_refs_replace(old_type_proto->yields,
				   old_type_refs,
				   new_type_refs,
				   type_tables);
	signals = type_signals_replace(old_type_proto->signals,
				       old_type_refs,
				       new_type_refs,
				       type_tables);
	needs = type_needs_replace(old_type_proto->needs,
				   old_type_refs,
				   new_type_refs,
				   type_tables);
	new_type_proto = type_proto_create(old_type_proto->kind,
					   params,
					   takes,
					   returns,
					   yields,
					   signals,
					   needs,
					   old_type_proto->no_return,
					   type_tables);
	return new_type_proto;
}

/*
 * type_proto_read(in_file, heap, type_tables)
 *	This routine will read in and return type proto object from "in_file"
 *	and allocated from "heap".
 */
Type_proto
type_proto_read(
	Stdio		in_file,
	Heap		heap,
	Type_tables	type_tables)
{
	Type_refs	takes;
	Type_proto_kind	kind;
	Type_needs	needs;
	int		no_return;
	Type_refs	params;
	Type_refs	returns;
	Type_signals	signals;
	Type_refs	yields;

	out_marker_read(in_file, "type_proto");
	kind = (Type_proto_kind)getc(in_file);
	params = type_refs_read(in_file, heap, type_tables);
	takes = type_refs_read(in_file, heap, type_tables);
	returns = type_refs_read(in_file, heap, type_tables);
	yields = type_refs_read(in_file, heap, type_tables);
	signals = type_signals_read(in_file, heap, type_tables);
	needs = type_needs_read(in_file, heap, type_tables);
	no_return = (int)getc(in_file);
	return type_proto_create(kind, params, takes, returns, yields,
				 signals, needs, no_return, type_tables);
}

#ifndef lint
/*
 * type_proto_show(type_proto)
 *	This routine is used for debugging and will show "type_proto"
 *	on standard out.
 */ 
void
type_proto_show(
	Type_proto	type_proto)
{
	type_proto_print(type_proto, 0, stdout);
}
#endif /* lint */

/*
 * type_proto_write(type_proto, out_file, type_tables)
 *	This routine will write "type_proto" to "out_file".
 */
void
type_proto_write(
	Type_proto	type_proto,
	Stdio		out_file,
	Type_tables	type_tables)
{
	out_marker_write(out_file, "type_proto");
	(void)putc((char)type_proto->kind, out_file);
	type_refs_write(type_proto->params, out_file, type_tables);
	type_refs_write(type_proto->takes, out_file, type_tables);
	type_refs_write(type_proto->returns, out_file, type_tables);
	type_refs_write(type_proto->yields, out_file, type_tables);
	type_signals_write(type_proto->signals, out_file, type_tables);
	type_needs_write(type_proto->needs, out_file, type_tables);
	(void)putc((char)type_proto->no_return, out_file);
}

/*
 * type_proto_table_create(type_ref_table, heap)
 *	This routine will create and return a new prototype table.
 */
/* ARGSUSED */
Type_proto_table
type_proto_table_create(
	Type_ref_table	type_ref_table,
	Heap		heap)
{
	Type_proto_table type_proto_table;

	type_proto_table = heap_allocate(heap, Type_proto_table);
	type_proto_table->heap = heap;
	type_proto_table->key = heap_allocate(heap, Type_proto);
	type_proto_table->table = table_create(Type_proto, Type_proto, 50,
					       type_proto_equal,
					       type_proto_hash,
					       (Type_proto)0,
					       heap);
	type_proto_table->type_proto_list = vec_create(Type_proto, heap);
	type_proto_table->type_ref_list = vec_create(Type_ref, heap);
	return type_proto_table;
}

/*
 * type_proto_table_dump(type_proto_table, out_file)
 *	This routine will dump the contents of "type_proto_table" to
 *	"out_file" in human readable format.
 */
void
type_proto_table_dump(
	Type_proto_table type_proto_table,
	Stdio		out_file)
{
	Type_proto	type_proto;
	Vec(Type_proto)	type_proto_list;

	type_proto_list = table_value_list_extract(Type_proto, Type_proto,
						   type_proto_table->table);
	out(out_file, "Type Proto Table:\n");
	VEC_LOOP(Type_proto, type_proto_list, type_proto) {
		type_proto_print(type_proto, 1, out_file);
	}
	out(out_file, "\n");
}

/*
 * type_proto_table_gen(type_proto_table, gen)
 *	This routine will generate the type definitions for each type
 *	prototype in "proto" using "gen".
 */
void
type_proto_table_gen(
	Type_proto_table type_proto_table,
	Gen		gen)
{
	int		index;
	int		size;
	Type_proto	type_proto;
	Vec(Type_proto)	type_proto_list;

	Type_refs	takes;
	Type_ref	type_ref;
	Str		type_str;

	type_proto_list = type_proto_table->type_proto_list;
	size = vec_size(Type_proto, type_proto_list);
	for (index = 0; index < size; index++) {
		type_proto = vec_fetch(Type_proto, type_proto_list, index);
		assert(index == atoi(type_proto->type_ref->name + 11));
		if (type_proto->needed == 0) {
			continue;
		}
		type_str = type_refs_multiple_string(type_proto->returns,
						     gen->heap);
		gen_out(gen, "typedef %s (*anonymous__%d___routine)(",
			type_str, index);
		takes = type_proto->takes;
		TYPE_REFS_LOOP(takes, type_ref) {
			gen_out(gen, "%s, ",
				type_ref_string(type_ref, gen->heap));
		}
		gen_out(gen, "void *);\n");
		gen_out(gen, "typedef struct {\n");
		gen_out(gen, "%\tanonymous__%d___routine routine;\n", 1, index);
		gen_out(gen, "%\tvoid *parameter;\n", 1);
		gen_out(gen,
			"} *anonymous__%d___type, anonymous__%d___struct;\n",
			index, index);
	}
	if (size != 0) {
		gen_out(gen, "\n");
	}
}

/*
 * Type_signal routines:
 */

/*
 * type_signal_compare(type_signal1, type_signal2, zilch)
 *	This routine will return -1, 0, or 1 depending upon whether
 *	"type_signal1" is less than, equal to, or greater than "type_signal2".
 */
/* ARGSUSED */
int
type_signal_compare(
	Type_signal	type_signal1,
	Type_signal	type_signal2,
	int		zilch)
{
	int		result;

	result = strcmp(type_signal1->name, type_signal2->name);
	if (result != 0) {
		return result;
	}
	result = type_refs_compare(type_signal1->type_refs,
				   type_signal2->type_refs, 0);
	return result;
}

/*
 * type_signal_create(name, type_refs, type_tables)
 *	This routine will create and return a new type signal object
 *	consisting of "name" and "type_refs" using "type_tables".
 */
Type_signal
type_signal_create(
	Str		name,
	Type_refs	type_refs,
	Type_tables	type_tables)
{
	Heap		heap;
	Type_signal	signal_key;
	Table(Type_signal, Type_signal) signal_table;
	Type_signal	type_signal;
	Type_signal_table type_signal_table;

	type_signal_table = type_tables->type_signal_table;
	signal_key = type_signal_table->signal_key;
	signal_key->name = name;
	signal_key->type_refs = type_refs;
	signal_table = type_signal_table->signal_table;
	type_signal = table_lookup(Type_signal, Type_signal,
				   signal_table, signal_key);
	if (type_signal == (Type_signal)0) {
		heap = type_tables->type_signal_table->heap;
		type_signal = heap_allocate(heap, Type_signal);
		type_signal->name = name;
		type_signal->type_refs = type_refs;
		assert(table_insert(Type_signal, Type_signal,
				    signal_table, type_signal,
				    type_signal) == 0);
	}
	return type_signal;
}

/*
 * type_signal_equal(type_signal1, type_signal2)
 *	This routine will return 1 if "type_signal1" equals "type_signal2" and
 *	0 otherwise.
 */
int
type_signal_equal(
	Type_signal		type_signal1,
	Type_signal		type_signal2)
{
	return (type_signal_compare(type_signal1, type_signal2, 0) == 0);
}

/*
 * type_signal_hash(type_signal)
 *	This routine will return a hash value for "type_signal".
 */
int
type_signal_hash(
	Type_signal	type_signal)
{
	return (strhash(type_signal->name) +
		type_refs_hash(type_signal->type_refs));
}

/*
 * type_signal_needed(type_signal, type_tables)
 *	This routine will mark all of the type references in "type_signal"
 *	as being needed in the output file.
 */
LOCAL void
type_signal_needed(
	Type_signal	type_signal,
	Type_tables	type_tables)
{
	type_refs_needed(type_signal->type_refs, type_tables);
}

/*
 * type_signal_print(type_signal, out_file)
 *	This routine will print "type_signal" to "out_file".
 */
void
type_signal_print(
	Type_signal	type_signal,
	Stdio		out_file)
{
	out(out_file, "%s ", type_signal->name);
	type_refs_print(type_signal->type_refs, out_file);
	out(out_file, "\n");
}

/*
 * type_signal_read(in_file, heap, type_tables)
 *	This routine will read in and return a Type_signal from "in_file"
 *	using "heap".
 */
Type_signal
type_signal_read(
	Stdio		in_file,
	Heap		heap,
	Type_tables	type_tables)
{
	Str		name;
	Type_refs	type_refs;

	out_marker_read(in_file, "type_signal");
	name = strread(in_file, heap);
	type_refs = type_refs_read(in_file, heap, type_tables);
	return type_signal_create(name, type_refs, type_tables);
}

/*
 * type_signal_replace(old_type_signal, old_type_refs,
 *		       new_type_refs, type_tables)
 *	This routine will replace all occurances of "old_type_refs" with
 *	"new_type_refs" in "old_type_signal" and return the new Type_signal
 *	object.
 */
Type_signal
type_signal_replace(
	Type_signal	old_type_signal,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	Type_refs	type_refs;

	type_refs = type_refs_replace(old_type_signal->type_refs,
				      old_type_refs,
				      new_type_refs,
				      type_tables);
	return type_signal_create(old_type_signal->name,
				  type_refs, type_tables);
}

/*
 * type_signal_write(type_signal, out_file, type_tables)
 *	This routine will write "type_signal" to "out_file".
 */
void
type_signal_write(
	Type_signal	type_signal,
	Stdio		out_file,
	Type_tables	type_tables)
{
	out_marker_write(out_file, "type_signal");
	(void)strwrite(type_signal->name, out_file);
	type_refs_write(type_signal->type_refs, out_file, type_tables);
}


/*
 * type_signal_table_create(type_ref_table, heap)
 *	This routine will create and return a new prototype table.
 */
/* ARGSUSED */
Type_signal_table
type_signal_table_create(
	Type_ref_table	type_ref_table,
	Heap		heap)
{
	Type_signal_table type_signal_table;

	type_signal_table = heap_allocate(heap, Type_signal_table);
	type_signal_table->heap = heap;
	type_signal_table->signal_key = heap_allocate(heap, Type_signal);
	type_signal_table->signal_table = table_create(Type_signal, Type_signal,
						       50, type_signal_equal,
						       type_signal_hash,
						       (Type_signal)0,
						       heap);
	type_signal_table->signals_table = table_create(Type_signals,
							Type_signals,
							50,
							type_signals_equal,
							type_signals_hash,
							(Type_signals)0,
							heap);
	type_signal_table->signals_key = vec_create(Type_signa, heap);
	type_signal_table->signals_empty =
				type_signals_table_lookup(type_signal_table);
	return type_signal_table;
}

/*
 * type_signal_table_dump(type_signal_table, out_file)
 *	This routine will dump the contents of "type_signal_table" to
 *	"out_file" in human readable format.
 */
void
type_signal_table_dump(
	Type_signal_table type_signal_table,
	Stdio		out_file)
{
	Type_signal	type_signal;
	Vec(Type_signal) type_signal_list;
	Type_signals	type_signals;
	Vec(Type_signals) type_signals_list;

	type_signal_list = table_value_list_extract(Type_signal, Type_signal,
					type_signal_table->signal_table);
	vec_sort(Type_signal, type_signal_list, type_signal_compare, 0);
	out(out_file, "Type Signal Table:\n");
	if (vec_empty(Type_signal, type_signal_list)) {
		out(out_file, "<Empty>\n");
	} else {
		VEC_LOOP(Type_signal, type_signal_list, type_signal) {
			type_signal_print(type_signal, out_file);
		}
	}
	out(out_file, "\n");

	type_signals_list = table_value_list_extract(Type_signals, Type_signals,
					type_signal_table->signals_table);
	vec_sort(Type_signals, type_signals_list, type_signals_compare, 0);
	out(out_file, "Type Signal List Table:\n");
	VEC_LOOP(Type_signals, type_signals_list, type_signals) {
		out(out_file, "----\n");
		type_signals_print(type_signals, 0, out_file);
	}
	out(out_file, "--------\n");
	out(out_file, "\n");
}

/*
 * Type_signals routines:
 */

/*
 * type_signals_append(type_signals, type_signal, type_tables)
 *	This routine will create and return an new type signal list
 *	that consists of "type_signals" with "type_signal" appended to
 *	the end.
 */
Type_signals
type_signals_append(
	Type_signals	type_signals,
	Type_signal	type_signal,
	Type_tables	type_tables)
{
	Type_signals	signals_key;
	Type_signal_table type_signal_table;

	type_signal_table = type_tables->type_signal_table;
	signals_key = type_signal_table->signals_key;
	assert(vec_empty(Type_signal, signals_key));
	vec_vec_append(Type_signal, signals_key, type_signals);
	vec_append(Type_signal, signals_key, type_signal);
	type_signals = type_signals_table_lookup(type_signal_table);
	return type_signals;
}

/*
 * type_signals_compare(type_signals1, type_signals2, zilch)
 *	This routine will order "type_signals1" with respect to "type_siganls2"
 *	by returning <0, =0, or >1 depending upon whether "type_signals1"
 *	should come before, is equal to, or should come after "type_signals2".
 */
/* ARGSUSED */
int
type_signals_compare(
	Type_signals	type_signals1,
	Type_signals	type_signals2,
	int		zilch)
{	
	int		index;
	int		result;
	int		size1;
	int		size2;
	Type_signal	type_signal1;
	Type_signal	type_signal2;

	size1 = vec_size(Type_signal, type_signals1);
	size2 = vec_size(Type_signal, type_signals2);
	result = size1 - size2;
	if (result != 0) {
		return result;
	}
	for (index = 0; index < size1; index++) {
		type_signal1 = vec_fetch(Type_signal, type_signals1, index);
		type_signal2 = vec_fetch(Type_signal, type_signals2, index);
		result = type_signal_compare(type_signal1, type_signal2, 0);
		if (result != 0) {
			return result;
		}
	}
	return 0;
}

/*
 * type_signals_empty_create(type_tables)
 *	This routine will return an empty type signal list using "type_tables".
 */
Type_signals
type_signals_empty_create(
	Type_tables	type_tables)
{
	return type_tables->type_signal_table->signals_empty;
}

/*
 * type_signals_equal(type_signals1, type_signals2)
 *	This routine will return 1 if "type_signals1" is equal to
 *	"type_signals2" and 0 otherwise.
 */
int
type_signals_equal(
	Type_signals	type_signals1,
	Type_signals	type_signals2)
{
	return (type_signals_compare(type_signals1, type_signals2, 0) == 0);
}

/*
 * type_signals_hash(type_signals)
 *	This routine will return a hash value for "type_signals".
 */
int
type_signals_hash(
	Type_signals	type_signals)
{
	int			hash;
	Type_signal		type_signal;

	hash = 0;
	VEC_LOOP(Type_signal, type_signals, type_signal) {
		hash += type_signal_hash(type_signal);
	}
	return hash;
}

/*
 * type_signals_needed(type_signals, type_tables)
 *	This routine will mark all of the type references in "type_signals"
 *	as being needed in the output file.
 */
LOCAL void
type_signals_needed(
	Type_signals	type_signals,
	Type_tables	type_tables)
{
	Type_signal	type_signal;

	VEC_LOOP(Type_signal, type_signals, type_signal) {
		type_signal_needed(type_signal, type_tables);
	}
}

/*
 * type_signals_print(type_signals, indent, out_file)
 *	This routine will print "type_signals" to "out_file" indented
 *	by "indent".
 */
/* ARGSUSED */
void
type_signals_print(
	Type_signals	type_signals,
	int		indent,
	Stdio		out_file)
{
	Type_signal	type_signal;

	VEC_LOOP(Type_signal, type_signals, type_signal) {
		type_signal_print(type_signal, out_file);
	}
}

/*
 * type_signals_read(in_file, heap, type_tables)
 *	This routine will read in and return list of type references from
 *	"in_file" allocated from "heap".
 */
Type_signals
type_signals_read(
	Stdio		in_file,
	Heap		heap,
	Type_tables	type_tables)
{
	int		count;
	Type_signal	type_signal;
	Type_signals	type_signals;

	out_marker_read(in_file, "type_signals");
	type_signals = type_tables->type_signal_table->signals_empty;
	count = out_count_read(in_file);
	while (count-- > 0) {
		type_signal = type_signal_read(in_file, heap, type_tables);
		type_signals = type_signals_append(type_signals,
						   type_signal, type_tables);
	}
	return type_signals;
}

/*
 * type_signals_replace(old_type_signals, old_type_refs,
 *			    new_type_refs, type_tables, heap)
 *	This routine will replace all occurances of "old_type_refs" with
 *	"new_type_refs" in "old_type_signals" and return the resulting list.
 */
Type_signals
type_signals_replace(
	Type_signals	old_type_signals,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	Type_signal	new_type_signal;
	Type_signals	new_type_signals;
	Type_signal	old_type_signal;

	new_type_signals = type_tables->type_signal_table->signals_empty;
	VEC_LOOP(Type_signal, old_type_signals, old_type_signal) {
		new_type_signal = type_signal_replace(old_type_signal, 
						      old_type_refs,
						      new_type_refs,
						      type_tables);
		new_type_signals = type_signals_append(new_type_signals,
						       new_type_signal,
						       type_tables);
	}
	return new_type_signals;
}

/*
 * type_signals_write(type_signals, out_file, type_tables)
 *	This routine will write "type_signals" to "out_file".
 */
void
type_signals_write(
	Type_signals	type_signals,
	Stdio		out_file,
	Type_tables	type_tables)
{
	int		index;
	int		size;
	Type_signal	type_signal;

	out_marker_write(out_file, "type_signals");
	size = vec_size(Type_signal, type_signals);
	out_count_write(out_file, size);
	for (index = 0; index < size; index++) {
		type_signal = vec_fetch(Type_signal, type_signals, index);
		type_signal_write(type_signal, out_file, type_tables);
	}
}

/*
 * type_signals_table_lookup(type_signal_table)
 *	This routine will return the type signal list corresponding to
 *	"type_signal_table"->signals_key.
 */
LOCAL Type_signals
type_signals_table_lookup(
	Type_signal_table	type_signal_table)
{
	Type_signals	signals_key;
	Type_signals	type_signals;
	Table(Type_signal, Type_signal) signals_table;

	signals_key = type_signal_table->signals_key;
	vec_sort(Type_signal, signals_key, type_signal_compare, 0);
	signals_table = type_signal_table->signals_table;
	type_signals = table_lookup(Type_signals, Type_signals,
				    signals_table, signals_key);
	if (type_signals == (Type_signals)0) {
		type_signals = vec_create(Type_signal, type_signal_table->heap);
		vec_vec_append(Type_signal, type_signals, signals_key);
		assert(table_insert(Type_signals, Type_signals, signals_table,
				    type_signals, type_signals) == 0);
	}
	vec_trim(Type_signals, signals_key, 0);
	return type_signals;
}

