/* %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_ref's and Type_refs'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

#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 VECTOR_DEFS_H
#include "vector_defs.h"
#endif

LOCAL int		type_ref_compare(Type_ref, Type_ref, int);
LOCAL int		type_ref_is_in(Type_ref, Type_ref);
LOCAL void		type_ref_needed(Type_ref, Type_tables);
LOCAL Type_ref_table	type_ref_table_create(Heap);
LOCAL void		type_ref_table_dump(Type_ref_table, Stdio);
LOCAL int		type_refs_contains_parameter(Type_refs);
LOCAL Type_refs		type_refs_table_lookup(Type_ref_table);

Type_tables	type_tables_global;

/*
 * Type_ref routines:
 */

/*
 * type_ref_compare(type_ref1, type_ref2, zilch)
 *	This routine is returns <0, 0, or >0 depending upon whether
 *	"type_ref1" is less than, equal to, or greater than "type_ref2".
 */
/* ARGSUSED */
LOCAL int
type_ref_compare(
	Type_ref	type_ref1,
	Type_ref	type_ref2,
	int		zilch)
{
	int		result;

	result = strcmp(type_ref1->name, type_ref2->name);
	if (result != 0) {
		return result;
	}
	result = type_refs_compare(type_ref1->parameters,
		 		   type_ref2->parameters, 0);
	return result;
}

/*
 * type_ref_contains_parameter(type_ref)
 *	This routine will return 1 if "type_ref" contains any parameterized
 *	types.
 */
int
type_ref_contains_parameter(
	Type_ref	type_ref)
{
	return type_ref_is_parameter(type_ref) || 
	       type_refs_contains_parameter(type_ref->parameters);
}

/*
 * type_ref_create(name, type_tables)
 *	This routine will create and return a new type reference object
 *	with a name of "name" using "type_tables".
 */
Type_ref
type_ref_create(
	Str		name,
	Type_tables	type_tables)
{
	Type_ref	type_ref;

	type_ref = type_ref_parameters_create(name,
				type_refs_empty_create(type_tables),
				type_tables);
	return type_ref;
}

/*
 * type_ref_equal(type_ref1, type_ref2)
 *	This routine will return 1 if "type_ref1" equals "type_ref2" and
 *	0 otherwise.
 */
int
type_ref_equal(
	Type_ref	type_ref1,
	Type_ref	type_ref2)
{
	return (type_ref_compare(type_ref1, type_ref2, 0) == 0);
}

/*
 * type_ref_gen(type_ref, gen)
 *	This routine will output a type reference data structure for "type_ref"
 *	to "gen".
 */
void
type_ref_gen(
	Type_ref	type_ref,
	Gen		gen)
{
	Type_refs	parameters;
	int		size;

	if (type_ref->generated) {
		return;
	}
	type_ref->generated = 1;

	parameters = type_ref->parameters;
	size = type_refs_size(parameters);
	if (size == 0) {
		gen_out(gen,
			"static type___reference %m__type__reference =\n",
			type_ref);
		gen_out(gen, "%\t{%\", 0, (type___reference **)0};\n",
			1, type_ref->name);
	} else {
		type_refs_gen(parameters, gen);
		gen_out(gen,
			"static type___reference %m__type__reference = \n",
			 type_ref);
		gen_out(gen, "%\t{%\", %d, %M__type__references};\n",
			1, type_ref->name, size, parameters);
	}
}

/*
 * type_ref_hash(type_ref)
 *	This routine will return a hash value for "type_ref".
 */
int
type_ref_hash(
	Type_ref	type_ref)
{
	return (strhash(type_ref->name) +
		type_refs_hash(type_ref->parameters));
}

/*
 * type_ref_is_in(type_ref, target_type_ref)
 *	 This routine will return 1 if "type_ref" occurs" anywhere in
 *	"target_type_ref"; otherwise, 0 is returned.
 */
LOCAL int
type_ref_is_in(
	Type_ref	type_ref,
	Type_ref	target_type_ref)
{
	Type_ref	parameter;
	Type_refs	parameters;

	if (type_ref_equal(type_ref, target_type_ref)) {
		return 1;
	}
	parameters = target_type_ref->parameters;
	TYPE_REFS_LOOP(parameters, parameter) {
		if (type_ref_is_in(type_ref, parameter)) {
			return 1;
		}
	}
	return 0;
}

/*
 * type_ref_needed(type_ref, type_tables)
 *	This routine will mark "type_ref" as needed in the output file.
 */
LOCAL void
type_ref_needed(
	Type_ref	type_ref,
	Type_tables	type_tables)
{
	if (!type_ref->needed) {
		type_ref->needed = 1;
		if (type_ref_is_routine(type_ref)) {
			Type_proto	type_proto;

			type_proto = type_proto_from_type_ref(type_ref,
							      type_tables);
			type_proto_needed(type_proto, type_tables);
		}
		type_refs_needed(type_ref->parameters, type_tables);
	}
}

/*
 * type_ref_is_parameter(type_ref)
 *	This routine will return 1 if "type_ref" is a parameter type.
 */
int
type_ref_is_parameter(
	Type_ref	type_ref)
{
	return isdigit(type_ref->name[0]);
}

/*
 * type_ref_is_parameterized(type_ref)
 *	This routine will return 1 if "type_ref" has any parameters.
 */
int
type_ref_is_parameterized(
	Type_ref	type_ref)
{
	return !type_refs_is_empty(type_ref->parameters);
}

/*
 * type_ref_is_routine(type_ref)
 *	This routine will return 1 if "type_ref" is a routine procedure
 *	variable type and 0 otherwise.
 */
int
type_ref_is_routine(
	Type_ref	type_ref)
{
	return (strncmp(type_ref->name, "anonymous__", 11) == 0);
}

/*
 * type_ref_mangle(type_ref, out_file)
 *	This routine will print out a mangled version of "type_ref"
 *	to "out_file".
 */
void
type_ref_mangle(
	Type_ref	type_ref,
	Stdio		out_file)
{
	Str		name;

	name = type_ref->name;
	out(out_file, type_ref_is_parameter(type_ref) ? "_%s": "%s", name);
	if (!type_refs_is_empty(type_ref->parameters)) {
		out(out_file, "__");
		type_refs_mangle(type_ref->parameters, 1, out_file);
	}
}

/*
 * type_ref_print(type_ref, out_file)
 *	This routine will print "type_ref" to "out_file".
 */
void
type_ref_print(
	Type_ref	type_ref,
	Stdio		out_file)
{
	Type_refs	parameters;

	if (type_ref_is_parameter(type_ref)) {
		out(out_file, "PARAMETER_%s", type_ref->name);
/*
 * Sigh, the code generator use %r to output routine types.
 * These types have to have the form `anonymousN' rather than
 * the expanded form.  When you are having problems figuring
 * out anonymous types, reinsert the code below to improve the
 * error message.
 */
#ifdef PROBLEM
	} else if (type_ref_is_routine(type_ref)) {
		Type_proto	type_proto;
		extern Type_tables type_tables_global;

		type_proto = type_proto_from_type_ref(type_ref,
						      type_tables_global);
		out(out_file, "{");
		type_proto_print(type_proto, -1, out_file);
		out(out_file, "}");
#endif /* PROBLEM */
	} else {
		out(out_file, "%s", type_ref->name);
	}
	parameters = type_ref->parameters;
	if (!type_refs_is_empty(parameters)) {
		out(out_file, "[");
		type_refs_print(parameters, out_file);
		out(out_file, "]");
	}
}

/*
 * type_ref_parameters_create(name, parameters, type_tables)
 *	This routine will create and return a new type reference with
 *	a name of "name" and parameters of "parameters" using "type_tables".
 */
Type_ref
type_ref_parameters_create(
	Str		name,
	Type_refs	parameters,
	Type_tables	type_tables)
{
	Heap		heap;
	Table(Type_ref, Type_ref) ref_table;
	Type_ref	type_ref;
	Type_ref	type_ref_key;
	Type_refs	type_refs_key;
	Type_ref_table	type_ref_table;

	/* Temporary: */
	assert(!strequal(name, "unsigned__keyword"));
	type_ref_table = type_tables->type_ref_table;
	type_ref_key = type_ref_table->type_ref_key;
	type_ref_key->name = name;
	type_ref_key->parameters = parameters;
	ref_table = type_ref_table->ref_table;
	type_ref = table_lookup(Type_ref, Type_ref, ref_table, type_ref_key);
	if (type_ref == (Type_ref)0) {
		heap = type_ref_table->heap;
		type_ref = heap_allocate(heap, Type_ref);
		type_ref->generated = 0;
		type_ref->name = strdupl(name, heap);
		type_ref->needed = 0;
		type_ref->parameters = parameters;

		assert(table_insert(Type_ref, Type_ref,
				    ref_table, type_ref, type_ref) == 0);

		type_refs_key = type_ref_table->type_refs_key;
		assert(vec_empty(Type_ref, type_refs_key->type_refs));
		vec_append(Type_ref, type_refs_key->type_refs, type_ref);

		type_ref->type_refs = type_refs_table_lookup(type_ref_table);
	} else {
		assert(type_ref == type_refs_fetch(type_ref->type_refs, 0));
	}
	return type_ref;
}

/*
 * type_ref_read(in_file, heap, type_tables)
 *	This routine will read in and return a Type_ref from "in_file" using
 *	"heap".
 */
Type_ref
type_ref_read(
	Stdio		in_file,
	Heap		heap,
	Type_tables	type_tables)
{
	Str		name;
	Type_ref	type_ref;
	int		is_routine;
	
	assert(type_tables != (Type_tables)0);
	out_marker_read(in_file, "type_ref");
	is_routine = fgetc(in_file);
	if (is_routine) {
		Type_proto	type_proto;

		type_proto = type_proto_read(in_file, heap, type_tables);
		type_ref = type_proto->type_ref;
	} else {
		Type_refs	parameters;

		name = strread(in_file, heap);
		parameters = type_refs_read(in_file, heap, type_tables);
		type_ref = type_ref_parameters_create(name,
						      parameters, type_tables);
	}
	return type_ref;
}

/*
 * type_ref_replace(type_ref, old_type_refs, new_type_refs, type_tables)
 *	This routine will replace all occurances of "old_type_refs" with
 *	"new_type_refs" for "type_ref" and return the resulting type
 *	reference.
 */
Type_ref
type_ref_replace(
	Type_ref	type_ref,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	int		index;
	Type_ref	old_type_ref;
	Type_refs	parameters;
	int		size;

	/* Do any needed substitution: */
	if (type_ref_is_routine(type_ref)) {
		Type_proto	type_proto;

		type_proto = type_proto_from_type_ref(type_ref, type_tables);
		type_proto = type_proto_replace(type_proto, old_type_refs,
						new_type_refs, type_tables);
		type_ref = type_proto->type_ref;
	} else {
		size = type_refs_size(old_type_refs);
		assert(type_refs_size(new_type_refs) == size);
		for (index = 0; index < size; index++) {
			old_type_ref = type_refs_fetch(old_type_refs, index);
			if (type_ref_equal(type_ref, old_type_ref)) {
				return type_refs_fetch(new_type_refs, index);
			}
		}
		parameters = type_ref->parameters;
		if (type_refs_is_empty(parameters)) {
			return type_ref;
		}
		parameters = type_refs_replace(parameters, old_type_refs,
					       new_type_refs, type_tables);
		type_ref = type_ref_parameters_create(type_ref->name,
						      parameters, type_tables);
	}
	return type_ref;
}

#ifndef lint
/*
 * type_ref_show(type_ref)
 *	This routine is used for debugging an displays "type_ref" to
 *	standard out.
 */
void
type_ref_show(
	Type_ref	type_ref)
{
	type_ref_print(type_ref, stdout);
	(void)printf("\n");
}
#endif /* lint */

/*
 * type_ref_string(type_ref, heap)
 *	This routine will return the ANSI-C type string assocaited with
 *	"type_ref" allocated from "heap".
 */
Str
type_ref_string(
	Type_ref	type_ref,
	Heap		heap)
{
	return type_translate(type_ref->name, heap);
}

/*
 * type_ref_write(type_ref, out_file, type_tables)
 *	This routine will write "type_ref" to "out_file".
 */
void
type_ref_write(
	Type_ref	type_ref,
	Stdio		out_file,
	Type_tables	type_tables)
{
	assert(type_tables != (Type_tables)0);
	out_marker_write(out_file, "type_ref");
	if (type_ref_is_routine(type_ref)) {
		Type_proto	type_proto;

		type_proto = type_proto_from_type_ref(type_ref, type_tables);
		(void)fputc(1, out_file);
		type_proto_write(type_proto, out_file, type_tables);
	} else {
		(void)fputc(0, out_file);
		(void)strwrite(type_ref->name, out_file);
		type_refs_write(type_ref->parameters, out_file, type_tables);
	}
}

/*
 * type_ref_table_create(heap)
 *	This routine will return a new type reference table allocated from
 *	"heap".
 */
LOCAL Type_ref_table
type_ref_table_create(
	Heap		heap)
{
	Table(Type_ref, Type_ref) ref_table;
	Table(Type_refs, Type_refs) refs_table;
	Type_ref_table	type_ref_table;

	ref_table = table_create(Type_ref, Type_ref, 100,
				 type_ref_equal, type_ref_hash,
				 (Type_ref)0, heap);
	refs_table = table_create(Type_refs, Type_refs, 100,
				  type_refs_equal, type_refs_hash,
				  (Type_refs)0, heap);

	type_ref_table = heap_allocate(heap, Type_ref_table);
	type_ref_table->heap = heap;
	type_ref_table->multiple = 0;
	type_ref_table->ref_table = ref_table;
	type_ref_table->refs_table = refs_table;
	type_ref_table->type_ref_key = heap_allocate(heap, Type_ref);
	type_ref_table->type_refs_key = heap_allocate(heap, Type_refs);
	type_ref_table->type_refs_key->type_refs =vec_create(Type_ref, heap);
	type_ref_table->empty = type_refs_table_lookup(type_ref_table);
	return type_ref_table;
}

/*
 * type_ref_table_dump(type_ref_table, out_file)
 *	This routine will dump the contents of the "type_ref_table" to
 *	"out_file"
 */
LOCAL void
type_ref_table_dump(
	Type_ref_table	type_ref_table,
	Stdio		out_file)
{
	Type_ref	type_ref;
	Vec(Type_ref)	type_ref_list;
	Type_refs	type_refs;
	Vec(Type_refs)	type_refs_list;

	/* Dump the type reference table: */
	type_ref_list = table_value_list_extract(Type_ref, Type_ref,
						 type_ref_table->ref_table);
	vec_sort(Type_ref, type_ref_list, type_ref_compare, 0);
	out(out_file, "Type Reference Table:\n");
	VEC_LOOP(Type_ref, type_ref_list, type_ref) {
		type_ref_print(type_ref, out_file);
		out(out_file, "\n");
	}
	out(out_file, "\n");

	/* Dump the type reference list table: */
	type_refs_list = table_value_list_extract(Type_refs, Type_refs,
						  type_ref_table->refs_table);
	vec_sort(Type_refs, type_refs_list, type_refs_compare, 0);
	out(out_file, "type_refs_table:\n");
	VEC_LOOP(Type_refs, type_refs_list, type_refs) {
		out(out_file, "{");
		type_refs_print(type_refs, out_file);
		out(out_file, "}\n");
	}
	out(out_file, "\n");
}

/*
 * Type_refs routines:
 */

/*
 * type_refs_append(type_refs, type_ref, type_tables)
 *	This routine will return a type_reference list that constists of
 *	"type_refs" concatonated with "type_ref".
 */
Type_refs
type_refs_append(
	Type_refs	type_refs,
	Type_ref	type_ref,
	Type_tables	type_tables)
{
	Type_refs	type_refs_key;
	Type_ref_table	type_ref_table;

	type_ref_table = type_tables->type_ref_table;
	type_refs_key = type_ref_table->type_refs_key;
	assert(vec_empty(Type_ref, type_refs_key->type_refs));
	vec_vec_append(Type_ref,
		       type_refs_key->type_refs, type_refs->type_refs);
	vec_append(Type_ref, type_refs_key->type_refs, type_ref);
	type_refs = type_refs_table_lookup(type_ref_table);
	return type_refs;
}

/*
 * type_refs_compare(type_refs1, type_refs2, zilch)
 *	This routine is returns <0, 0, or >0 depending upon whether
 *	"type_refs1" is less than, equal to, or greater than "type_refs2".
 */
/* ARGSUSED */
int
type_refs_compare(
	Type_refs	type_refs1,
	Type_refs	type_refs2,
	int		zilch)
{
	int		index;
	int		result;
	int		size1;
	int		size2;
	Type_ref	type_ref1;
	Type_ref	type_ref2;

	size1 = vec_size(Type_ref, type_refs1->type_refs);
	size2 = vec_size(Type_ref, type_refs2->type_refs);
	result = size1 - size2;
	if (result != 0) {
		return result;
	}
	for (index = 0; index < size1; index++) {
		type_ref1 = vec_fetch(Type_ref, type_refs1->type_refs, index);
		type_ref2 = vec_fetch(Type_ref, type_refs2->type_refs, index);
		result = type_ref_compare(type_ref1, type_ref2, 0);
		if (result != 0) {
			return result;
		}
	}
	return result;
}

/*
 * type_refs_concat(type_refs1, type_refs2, type_tables)
 *	This routine will return a concatonation of "type_refs1" and
 *	"type_refs2".
 */
Type_refs
type_refs_concat(
	Type_refs	type_refs1,
	Type_refs	type_refs2,
	Type_tables	type_tables)
{
	Type_refs	type_refs;
	Type_refs	type_refs_key;
	Type_ref_table	type_ref_table;

	type_ref_table = type_tables->type_ref_table;
	type_refs_key = type_ref_table->type_refs_key;
	assert(vec_empty(Type_ref, type_refs_key->type_refs));
	vec_vec_append(Type_ref,
		       type_refs_key->type_refs, type_refs1->type_refs);
	vec_vec_append(Type_ref,
		       type_refs_key->type_refs, type_refs2->type_refs);
	type_refs = type_refs_table_lookup(type_ref_table);
	return type_refs;
}

/*
 * type_refs_empty_create(type_tables)
 *	This routine will create and return an empty type reference list.
 */
Type_refs
type_refs_empty_create(
	Type_tables	type_tables)
{
	return type_tables->type_ref_table->empty;
}

/*
 * type_refs_equal(type_refs1, type_refs2)
 *	This routine will return 1 if "type_refs1" is equal to "type_refs2"
 *	and 0 otherwise.
 */
int
type_refs_equal(
	Type_refs	type_refs1,
	Type_refs	type_refs2)
{
	return (type_refs_compare(type_refs1, type_refs2, 0) == 0);
}

/*
 * type_refs_fetch(type_refs, index)
 *	This routine will return the "index"'th type reference from "type_refs".
 */
Type_ref
type_refs_fetch(
	Type_refs	type_refs,
	int		index)
{
	return vec_fetch(Type_ref, type_refs->type_refs, index);
}

/*
 * type_refs_gen(type_refs, gen)
 *	This routine will generate a static data structure containing
 *	"type_refs" to "gen".
 */
void
type_refs_gen(
	Type_refs	type_refs,
	Gen		gen)
{
	Type_ref	type_ref;

	if (type_refs->list_generated) {
		return;
	}
	type_refs->list_generated = 1;
	TYPE_REFS_LOOP(type_refs, type_ref) {
		type_ref_gen(type_ref, gen);
	}
	gen_out(gen, "static type___reference *%M__type__references[%d] = {\n",
		type_refs, type_refs_size(type_refs));
	TYPE_REFS_LOOP(type_refs, type_ref) {
		gen_out(gen, "%\t&%m__type__reference,\n", 1, type_ref);
	}
	gen_out(gen, "};\n");
}

/*
 * type_refs_hash(type_refs)
 *	This routine will return a hash value for "type_refs".
 */
int
type_refs_hash(
	Type_refs	type_refs)
{
	int		hash;
	Type_ref	type_ref;

	hash = 0;
	TYPE_REFS_LOOP(type_refs, type_ref) {
		hash += type_ref_hash(type_ref);
	}
	return hash;
}

/*
 * type_refs_needed(type_refs, type_tables)
 *	This routine will mark each type reference in "type_refs" as
 *	needed in the final output file.
 */
void
type_refs_needed(
	Type_refs	type_refs,
	Type_tables	type_tables)
{
	Type_ref	type_ref;

	TYPE_REFS_LOOP(type_refs, type_ref) {
		type_ref_needed(type_ref, type_tables);
	}
}

/*
 * type_refs_is_empty(type_refs)
 *	This routine will return 1 if "type_refs" is empty and 0 otherwise.
 */
int
type_refs_is_empty(
	Type_refs	type_refs)
{
	return (type_refs->type_refs->size == 0);
}

#ifdef OLD
/*
 * type_refs_is_in(type_refs, target_type_ref)
 *	This routine will return 1 if any type_reference in "type_refs" occurs
 *	anywhere in "type_ref"; otherwise, 0 is returned.
 */
int
type_refs_is_in(
	Type_refs	type_refs,
	Type_ref	target_type_ref)
{
	Type_ref	type_ref;

	TYPE_REFS_LOOP(type_refs, type_ref) {
		if (type_ref_is_in(type_ref, target_type_ref)) {
			return 1;
		}
	}
	return 0;
}

/*
 * type_refs_is_one_of(type_refs, target_type_ref)
 *	This routine will return 1 if "type_ref" is one of the type references
 *	in "type_refs"; otherwise, 0 is returned.
 */
int
type_refs_is_one_of(
	Type_refs	type_refs,
	Type_ref	target_type_ref)
{
	Type_ref	type_ref;

	TYPE_REFS_LOOP(type_refs, type_ref) {
		if (type_ref_equal(type_ref, target_type_ref)) {
			return 1;
		}
	}
	return 0;
}
#endif /* OLD */

/*
 * type_refs_contains_parameter(type_refs)
 *	This routine 1 if any type reference in "type_refs" is a parameterized
 *	type.
 */
LOCAL int
type_refs_contains_parameter(
	Type_refs	type_refs)
{
	Type_ref	type_ref;

	TYPE_REFS_LOOP(type_refs, type_ref) {
		if (type_ref_contains_parameter(type_ref)) {
			return 1;
		}
	}
	return 0;
}

/*
 * type_refs_keyword_print(list, gen, keyword, indent)
 *	This routine will print "keyword", indented by "indent", followed by
 *	a comma separated version of "list" to "gen".
 */
void
type_refs_keyword_print(
	Type_refs	list,
	Stdio		out_file,
	Str		keyword,
	int		indent)
{
	if (!type_refs_is_empty(list)) {
		out(out_file, "%\t%s ", indent, keyword);
		type_refs_print(list, out_file);
		out(out_file, "\n");
	}
}

/*
 * type_refs_lop(type_refs, type_tables)
 *	This routine will return the type reference list corresponding to
 *	"type_refs" with the first type reference removed.
 */
Type_refs
type_refs_lop(
	Type_refs	type_refs,
	Type_tables	type_tables)
{
	Type_refs	type_refs_key;
	Type_ref_table	type_ref_table;

	type_ref_table = type_tables->type_ref_table;
	type_refs_key = type_ref_table->type_refs_key;
	assert(vec_empty(Type_ref, type_refs_key->type_refs));
	vec_vec_append(Type_ref,
		       type_refs_key->type_refs, type_refs->type_refs);
	vec_delete(Type_ref, type_refs_key->type_refs, 0);
	type_refs = type_refs_table_lookup(type_ref_table);
	return type_refs;
}

/*
 * type_refs_mangle(type_refs, level, out_file)
 *	This routine will print out a mangled version of "type_refs"
 *	to "out_file".
 */
void
type_refs_mangle(
	Type_refs	type_refs,
	int		level,
	Stdio		out_file)
{
	int		index;
	Type_ref	type_ref;
	int		size;

	size = type_refs_size(type_refs);
	for (index = 0; index < size; index++) {
		type_ref = type_refs_fetch(type_refs, index);
		if (index == 0) {
			if (type_ref_is_parameter(type_ref)) {
				out(out_file, "_");
			}
		} else {
			out(out_file, "__");
		}
		if (level > 0) {
			out(out_file, "%d_", level);
		}
		out(out_file, "%s", type_ref->name);
		if (!type_refs_is_empty(type_ref->parameters)) {
			out(out_file, "__");
			type_refs_mangle(type_ref->parameters,
					 level + 1, out_file);
		}
	}
}

/*
 * type_refs_multiple_needed(type_refs, type_tables)
 *	This will mark "type_refs" as being needed for mutiple return type.
 */
void
type_refs_multiple_needed(
	Type_refs	type_refs,
	Type_tables	type_tables)
{
	assert(type_refs_size(type_refs) > 1);
	if (type_refs->multiple < 0) {
		type_refs->multiple = type_tables->type_ref_table->multiple++;
	}
}

/*
 * type_refs_multiple_gen(type_tables, gen)
 *	This routine will output a typedef for each type reference
 *	list in "type_tables" that is needed for a multiple return
 *	using "gen".
 */
void
type_refs_multiple_gen(
	Type_tables	type_tables,
	Gen		gen)
{
	Heap		heap;
	int		index;
	int		size;
	Type_ref	type_ref;
	Type_refs	type_refs;
	Vec(Type_refs)	type_refs_list;

	heap = gen->heap;
	type_refs_list = table_value_list_extract(Type_refs, Type_refs,
				type_tables->type_ref_table->refs_table);
	VEC_LOOP(Type_refs, type_refs_list, type_refs) {
		if (type_refs->multiple < 0) {
			continue;
		}
		gen_out(gen, "typedef struct {\n");
		size = type_refs_size(type_refs);
		for (index = 0; index < size; index++) {
			type_ref = type_refs_fetch(type_refs, index);
			gen_out(gen, "%\t%s x%d;\n", 1,
				type_ref_string(type_ref, heap), index);
		}
		gen_out(gen, "} type__multiple__%d;\n", type_refs->multiple);
	}
}

/*
 * type_refs_multiple_string(type_refs, heap)
 *	This routine return the ANSI-C type corresponding to "type_refs"
 *	allocated using "heap".
 */
Str
type_refs_multiple_string(
	Type_refs	type_refs,
	Heap		heap)
{
	Type_ref	type_ref;

	switch (type_refs_size(type_refs)) {
	    case 0:
		return "void";
	    case 1:
		type_ref = type_refs_fetch(type_refs, 0);
		return type_ref_string(type_ref, heap);
	    default:
		assert(type_refs->multiple >= 0);
		return strprintf(heap, "type__multiple__%d",
				 type_refs->multiple);
	}
	/* NOTREACHED */
}

/*
 * type_refs_print(type_refs, out_file)
 *	This routine will print "type_refs" to "out_file".
 */
void
type_refs_print(
	Type_refs	type_refs,
	Stdio		out_file)
{
	int		index;
	int		size;
	Type_ref	type_ref;

	size = type_refs->type_refs->size;
	for (index = 0; index < size; index++) {
		if (index != 0) {
			out(out_file, ", ");
		}
		type_ref = vec_fetch(Type_ref, type_refs->type_refs, index);
		type_ref_print(type_ref, out_file);
	}
}

/*
 * type_refs_read(in_file, heap, type_tables)
 *	This routine will read in and return list of type references from
 *	"in_file" allocated from "heap".
 */
Type_refs
type_refs_read(
	Stdio		in_file,
	Heap		heap,
	Type_tables	type_tables)
{
	int		size;
	Type_ref	type_ref;
	Type_refs	type_refs;
	Type_ref_table	type_ref_table;

	out_marker_read(in_file, "type_refs");
	type_ref_table = type_tables->type_ref_table;
	type_refs = type_ref_table->empty;
	size = out_count_read(in_file);
	while (size -- > 0) {
		type_ref = type_ref_read(in_file, heap, type_tables);
		type_refs = type_refs_append(type_refs,
					     type_ref, type_tables);
	}
	return type_refs;
}

/*
 * type_refs_replace(type_refs, old_type_refs, new_type_refs, type_tables)
 *	This routine will replace all occurances of "old_type_refs" with
 *	"new_type_refs" for each type reference in "type_refs" and
 *	return the resulting list.
 */
Type_refs
type_refs_replace(
	Type_refs	type_refs,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	Type_ref	type_ref;
	Type_refs	result_type_refs;
	Type_ref_table	type_ref_table;

	type_ref_table = type_tables->type_ref_table;
	result_type_refs = type_ref_table->empty;
	TYPE_REFS_LOOP(type_refs, type_ref) {
		type_ref = type_ref_replace(type_ref,
					    old_type_refs,
					    new_type_refs,
					    type_tables);
		result_type_refs = type_refs_append(result_type_refs,
						    type_ref,
						    type_tables);
	}
	return result_type_refs;
}

#ifndef lint
/*
 * type_refs_show(type_refs)
 *	This routine is used for debugging and prints "type_refs" to
 *	standard out.
 */
void
type_refs_show(
	Type_refs	type_refs)
{
	(void)printf("{");
	type_refs_print(type_refs, stdout);
	(void)printf("}\n");
}
#endif /* lint */

/*
 * type_refs_size(type_refs)
 *	This routine will return the number of type references in "type_refs".
 */
int
type_refs_size(
	Type_refs	type_refs)
{
	return type_refs->type_refs->size;
}

#ifdef OLD
/*
 * type_refs_store(type_refs, index, type_ref, type_tables)
 *	This routine will return the type reference list that results from
 *	substituting "type_ref" for the "index"'th type reference in
 *	"type_refs".
 */
Type_refs
type_refs_store(
	Type_refs	type_refs,
	int		index,
	Type_ref	type_ref,
	Type_tables	type_tables)
{
	Type_refs	type_refs_key;
	Type_ref_table	type_ref_table;

	type_ref_table = type_tables->type_ref_table;
	type_refs_key = type_ref_table->type_refs_key;
	assert(vec_empty(Type_ref, type_refs_key->type_refs));
	vec_vec_append(Type_ref,
		       type_refs_key->type_refs, type_refs->type_refs);
	vec_store(Type_ref, type_refs_key->type_refs, index, type_ref);
	type_refs = type_refs_table_lookup(type_ref_table);
	return type_refs;
}
#endif /* OLD */

/*
 * type_refs_write(type_refs, out_file)
 *	This routine will write "type_refs" to "out_file".
 */
void
type_refs_write(
	Type_refs	type_refs,
	Stdio		out_file,
	Type_tables	type_tables)
{
	Type_ref	type_ref;

	out_marker_write(out_file, "type_refs");
	out_count_write(out_file, vec_size(Type_ref, type_refs->type_refs));
	TYPE_REFS_LOOP(type_refs, type_ref) {
		type_ref_write(type_ref, out_file, type_tables);
	}
}


/*
 * type_refs_table_lookup(type_ref_table)
 *	This routine will return an immutable copy of
 *	"type_ref_table"->type_refs_key.
 */
LOCAL Type_refs
type_refs_table_lookup(
	Type_ref_table	type_ref_table)
{
	Heap		heap;
	Table(Type_refs, Type_refs) refs_table;
	Type_refs	type_refs;
	Type_refs	type_refs_key;
	Type_refs	new_type_refs;

	refs_table = type_ref_table->refs_table;
	type_refs_key = type_ref_table->type_refs_key;
	type_refs = table_lookup(Type_refs, Type_refs,
				 refs_table, type_refs_key);
	if (type_refs == (Type_refs)0) {
		heap = type_ref_table->heap;
		new_type_refs = heap_allocate(heap, Type_refs);
		new_type_refs->type_refs = vec_create(Type_ref, heap);
		vec_vec_append(Type_ref,
			       new_type_refs->type_refs,
			       type_refs_key->type_refs);
		new_type_refs->list_generated = 0;
		new_type_refs->multiple = -1;
		assert(table_insert(Type_refs, Type_refs,
				    refs_table, new_type_refs,
				    new_type_refs) == 0);
		type_refs = new_type_refs;
	}
	vec_trim(Type_ref, type_refs_key->type_refs, 0);
	return type_refs;
}

/*
 * Type_tables routines:
 */

/*
 * type_tables_create(heap)
 *	This routine will create and return all of the various type tables
 *	allocated from "heap".
 */
Type_tables
type_tables_create(
	Heap		heap)
{
	Type_tables	type_tables;
	Type_ref_table	type_ref_table;

	type_ref_table = type_ref_table_create(heap);
	type_tables = heap_allocate(heap, Type_tables);
	type_tables->type_def_table = type_def_table_create(heap);
	type_tables->type_need_table =
			type_need_table_create(type_ref_table, heap);
	type_tables->type_proto_table =
			type_proto_table_create(type_ref_table, heap);
	type_tables->type_ref_table = type_ref_table;
	type_tables->type_signal_table =
			type_signal_table_create(type_ref_table, heap);
	type_tables_global = type_tables;
	return type_tables;
}

/*
 * type_tables_dump(type_tables, out_file)
 *	This routine will dump each type table in "type_tables" out to
 *	"out_file" in a human readable form.
 */
void
type_tables_dump(
	Type_tables	type_tables,
	Stdio		out_file)
{
	type_ref_table_dump(type_tables->type_ref_table, out_file);
	type_need_table_dump(type_tables->type_need_table, out_file);
	type_signal_table_dump(type_tables->type_signal_table, out_file);
	type_proto_table_dump(type_tables->type_proto_table, out_file);
}

