/* @(#)routine_table.c 1.40 95/04/05 */

/*
 * Copyright (c) 1991, 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 code for manipulating the routine table: */

#ifndef ERROR_EXPORTS_H
#include "error_exports.h"
#endif

#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 MSG_EXPORTS_H
#include "msg_exports.h"
#endif

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

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

LOCAL int
routine_skip(
	Routine_entry	entry)
{
	return (!entry->used) || entry->deleted ||
	       (strncmp(entry->type_name, "anonymous__", 11) == 0);
}

/*
 * routine_table_create(msg, type_tables, heap)
 *	This routine will create and return an empty routine table
 *	allocated from "heap".  "msg" and "type_tables" are needed.
 */
Routine_table
routine_table_create(
	Msg		msg,
	Type_tables	type_tables,
	Heap		heap)
{
	Routine_table	routine_table;

	routine_table = heap_allocate(heap, Routine_table);
	routine_table->heap = heap;
	routine_table->entry_key = heap_allocate(heap, Routine_entry);
	routine_table->entry_table = table_create(Routine_entry, Routine_entry,
						  100,
						  routine_entry_equal,
						  routine_entry_hash,
						  (Routine_entry)0,
						  heap);
	routine_table->entrys = vec_create(Routine_entry, heap);
	routine_table->msg = msg;
	routine_table->parameters = vec_create(Vec(Type_refs), heap);
	vec_append(Type_refs,
		   routine_table->parameters,
		   type_refs_empty_create(type_tables));
	routine_table->ref_key = heap_allocate(heap, Routine_ref);
	routine_table->ref_table = table_create(Routine_ref, Routine_entry,
						100,
						routine_ref_equal,
						routine_ref_hash,
						(Routine_entry)0,
						heap);
	routine_table->type_tables = type_tables;
	return routine_table;
}
	
/*
 * routine_table_dump(routine_table, out_file)
 *	This routine will print "routine_table" to "out_file".
 */
void
routine_table_dump(
	Routine_table	routine_table,
	Stdio		out_file)
{
	type_tables_dump(routine_table->type_tables, out_file);
	routine_refs_dump(routine_table, out_file);
	routine_entrys_dump(routine_table, out_file);
}

/*
 * routine_table_proto_gen(routine_table, out_file)
 *	This will output all of the used routines in "routine_table"
 *	to "out_file" in ANSI-C prototype form.
 */
void
routine_table_proto_gen(
	Routine_table	routine_table,
	Gen		gen)
{
	Heap		heap;
	Routine_entry	entry;
	Vec(Routine_entry) entrys;
	int		parameterized;
	Type_ref	return_ref;
	Type_refs	returns;
	Routine_ref	routine_ref;
	Vec(Routine_ref) routine_refs;
	Type_ref	take;
	Type_refs	takes;
	int		takes_index;
	Str		type_name;
	int		takes_size;
	Type_proto	type_proto;
	Type_ref	type_ref;

	/* Generate all needed routine prototypes: */
	heap = gen->heap;
	entrys = routine_table->entrys;
	VEC_LOOP(Routine_entry, entrys, entry) {
		if (routine_skip(entry)) {
			continue;
		}
		type_proto = entry->type_proto;
		parameterized = type_proto_is_parameterized(type_proto);
		returns = type_proto->returns;
			gen_out(gen, "extern ");
		switch (type_refs_size(returns)) {
		    case 0:
			gen_out(gen, "void ");
			break;
		    case 1:
			return_ref = type_refs_fetch(returns, 0);
			if (type_proto_is_parameter(type_proto, return_ref)) {
				gen_out(gen, "void *");
			} else {
				type_name = type_ref_string(return_ref, heap);
				gen_out(gen, "%s ", type_name);
			}
			break;
		    default:
			type_name = type_refs_multiple_string(returns, heap);
			gen_out(gen, "%s ", type_name);
		}
		gen_out(gen, "%s__%s(", entry->type_name, entry->routine_name);
		takes = type_proto->takes;
		takes_size = type_refs_size(takes);
		if ((takes_size == 0) && !parameterized) {
			gen_out(gen, "void");
		} else {
			for (takes_index = 0; takes_index < takes_size;
			     takes_index++) {
				if (takes_index != 0) {
					gen_out(gen, ", ");
				}
				take = type_refs_fetch(takes, takes_index);
				type_name = type_translate(take->name, heap);
				if (type_proto_is_parameter(type_proto, take)) {
					type_name = "void *";
				}
				gen_out(gen, "%s", type_name);
			}
			if (parameterized) {
				if (takes_size != 0) {
					gen_out(gen, ", ");
				}
				gen_out(gen, "void *");
			}
		}
		gen_out(gen, ");\n");
	}
	gen_out(gen, "\n");

	/* Generate any routine constants: */
	VEC_LOOP(Routine_entry, entrys, entry) {
		if (routine_skip(entry)) {
			continue;
		}
		routine_refs = entry->routine_refs;
		VEC_LOOP(Routine_ref, routine_refs, routine_ref) {
			if (routine_ref->constant == 0) {
				continue;
			}
			type_ref = routine_ref->type_proto->type_ref;
			gen_out(gen, "%s___struct %s__%s__struct = {\n",
				type_ref->name, routine_ref->type_ref->name,
				routine_ref->name);
			gen_out(gen, "%\t(%s___routine)%s__%s,\n",
				1, type_ref->name, routine_ref->type_ref->name,
				routine_ref->name);
			gen_out(gen, "%\t(void *)0,\n", 1);
			gen_out(gen, "};\n");
		}
	}
	gen_out(gen, "\n");
}

/*
 * routine_table_signature_gen(routine_table, gen)
 *	This routine will generate a signature record for each used
 *	routine in "routine_table".
 */
void
routine_table_signature_gen(
	Routine_table	routine_table,
	Gen		gen)
{
	Routine_entry	entry;
	Vec(Routine_entry) entrys;
	Type_refs	returns;
	Type_refs	takes;
	Type_proto	type_proto;
	Type_ref	type_ref;
	Vec(Type_ref)	type_ref_list;
	Table(Type_ref, Type_ref) type_tables;

	/* Determine which type refs are used: */
	type_tables = table_create(Type_ref, Type_ref, 100,
				   type_ref_equal, type_ref_hash,
				   (Type_ref)0, gen->heap);
	entrys = routine_table->entrys;
	VEC_LOOP(Routine_entry, entrys, entry) {
		if (routine_skip(entry)) {
			continue;
		}
		type_proto = entry->type_proto;
		takes = type_proto->takes;
		TYPE_REFS_LOOP(takes, type_ref) {
			(void)table_insert(Type_ref, Type_ref, type_tables,
					   type_ref, type_ref);
		}
		returns = type_proto->returns;
		TYPE_REFS_LOOP(returns, type_ref) {
			(void)table_insert(Type_ref, Type_ref, type_tables,
					   type_ref, type_ref);
		}
		/*XXX: Need to do yields some time! */
		assert(type_refs_is_empty(type_proto->yields));
	}

	/* Emit all of the type reference objects: */
	type_ref_list = table_key_list_extract(Type_ref, Type_ref,
					       type_tables);
	VEC_LOOP(Type_ref, type_ref_list, type_ref) {
		type_ref_gen(type_ref, gen);
	}

#ifdef OLD
	/* Output the signature for each used routine. */
	VEC_LOOP(Routine_entry, entrys, entry) {
		if (routine_skip(entry)) {
			continue;
		}
		type_proto = entry->type_proto;
		routine_name = entry->routine_name;
		type_name = entry->type_name;

		/* Output the takes vector: */
		takes = type_proto->takes;
		takes_size = type_refs_size(takes);
		gen_out(gen, "static type___reference *%s__%s__takes",
			type_name, routine_name);
		if (takes_size == 0) {
			gen_out(gen, "[1];\n");
		} else {
			gen_out(gen,"[%d] = {\n", takes_size);
			TYPE_REFS_LOOP(takes, type_ref) {
				gen_out(gen, "%\t&%m__type__reference,\n",
					1, type_ref);
			}
			gen_out(gen, "};\n");
		}

		/* Output the returns vector: */
		returns = type_proto->returns;
		returns_size = type_refs_size(returns);
		gen_out(gen, "static type___reference *%s__%s__returns",
			type_name, routine_name);
		if (returns_size == 0) {
			gen_out(gen, "[1];\n");
		} else {
			gen_out(gen, "[%d] = {\n", returns_size);
			TYPE_REFS_LOOP(returns, type_ref) {
				gen_out(gen, "%\t&%m__type__reference,\n",
					1, type_ref);
			}
			gen_out(gen, "};\n");
		}

		/* Output the signature: */
		gen_out(gen,
			"static signature___object %s__%s__signature = {\n",
			type_name, routine_name);
		gen_out(gen, "%\t0,\n", 1);	/* parameter count */
		gen_out(gen, "%\t(char **)0,\n", 1); /* param names */
		gen_out(gen, "%\t%d,\n", 1, takes_size);
		gen_out(gen, "%\t%s__%s__takes,\n",
			1, type_name, routine_name);
		gen_out(gen, "%\t%d,\n", 1, returns_size);
		gen_out(gen, "%\t%s__%s__returns,\n",
			1, type_name, routine_name);
		gen_out(gen, "%\t0,\n", 1);	/* signals count */
		gen_out(gen, "%\t(signal___object **)0,\n", 1); /* signals */
		gen_out(gen, "};\n");

		/* Output the routine use: */
		gen_out(gen, "static routine___use %s__%s__routine__use =\n",
			type_name, routine_name);
		gen_out(gen, "%\t{%\", %\", %\", &%s__%s__signature};\n",
			1, "", type_name, routine_name,
			type_name, routine_name);
	}

	/* Output the used routines list: */
	count = 0;
	VEC_LOOP(Routine_entry, entrys, entry) {
		if (!routine_skip(entry)) {
			count++;
		}
	}
	gen_out(gen, "static routine___use *routines___used[%d] = {\n", count);
	VEC_LOOP(Routine_entry, entrys, entry) {
		if (!routine_skip(entry)) {
			gen_out(gen, "%\t&%s__%s__routine__use,\n", 1,
				entry->type_name, entry->routine_name);
		}
	}
	gen_out(gen, "};\n");
	gen_out(gen, "\n");
#endif /* OLD */
}

/*
 * routine_table_used_size(routine_table)
 *	This routine will return the number of routines used in "routine_table."
 */
int
routine_table_used_size(
	Routine_table	routine_table)
{
	Routine_entry	entry;
	Vec(Routine_entry) entrys;
	int		size;

	size = 0;
	entrys = routine_table->entrys;
	VEC_LOOP(Routine_entry, entrys, entry) {
		if (routine_skip(entry)) {
			continue;
		}
		size++;
	}
	return size;
}

/*
 * routine_table_parameters_get(routine_table, size)
 *	This routine will return a type refererence list of size "size"
 *	from "routine_table".
 */
Type_refs
routine_table_parameters_get(
	Routine_table	routine_table,
	int		size)
{
	Heap		heap;
	int		index;
	Vec(Type_refs)	parameters;
	Str		type_name;
	Type_ref	type_ref;
	Type_refs	type_refs;
	Type_tables	type_tables;

	heap = routine_table->heap;
	parameters = routine_table->parameters;
	type_tables = routine_table->type_tables;
	for (index = vec_size(Type_refs, parameters);
	     index < size + 1; index++) {
		type_refs = vec_fetch(Type_refs, parameters, index - 1);
		type_name = strprintf(heap, "%d", index - 1);
		type_ref = type_ref_create(type_name, type_tables);
		type_refs = type_refs_append(type_refs, type_ref, type_tables);
		vec_append(Type_refs, parameters, type_refs);
	}
	type_refs = vec_fetch(Type_refs, parameters, size);
	return type_refs;
}

