/* %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 code for manipulating routine_entry objects: */

#ifndef FILE_DEFS_H
#include "file_defs.h"
#endif

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

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

#ifndef MODULE_TYPES_H
#include "module_types.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 void routine_entry_print(Routine_entry, Stdio);

/*
 * routine_entry_delete(routine_entry)
 */
void
routine_entry_delete(
	Routine_entry	routine_entry)
{
	assert(!routine_entry->deleted);
	routine_entry->deleted = 1;
}

/*
 * routine_entry_equal(routine_entry1, routine_entry2)
 *	This routine will return 1 if "routine_entry1" is equal to
 *	"routine_entry2"; othwise 0 is returned.
 */
int
routine_entry_equal(
	Routine_entry	routine_entry1,
	Routine_entry	routine_entry2)
{
	return (strequal(routine_entry1->routine_name,
			 routine_entry2->routine_name) &&
		strequal(routine_entry1->type_name, routine_entry2->type_name));
}

/*
 * routine_entry_hash(routine_entry)
 *	This routine will return a hash value for "routine_entry".
 */
int
routine_entry_hash(
	Routine_entry	routine_entry)
{
	return strhash(routine_entry->routine_name) +
	       strhash(routine_entry->type_name);
}

/*
 * routine_entry_insert(routine_table, routine_name, type_ref,
 *		        type_proto, position, no_error)
 *	This routine will create and insert a routine entry for
 *	"routine_name"@"type_ref" with a prototype of "type_proto" into
 *	"routine_table"; the resulting routine entry is returned.  If
 *	"routine_name"@"type_ref" is already in "routine_table", an error
 *	msg is output using "position" and the previously defined
 *	routine entry is returned.
 */
Routine_entry
routine_entry_insert(
	Routine_table	routine_table,
	Str		routine_name,
	Type_ref	type_ref,
	Type_proto	type_proto,
	int		position,
	int		no_error)
{
	Routine_entry	entry;
	Routine_entry	entry_key;
	Table(Routine_entry, Routine_entry) entry_table;
	Heap		heap;
	Msg		msg;
	Type_refs	params;
	Type_refs	parameters;
	int		size;

	entry_key = routine_table->entry_key;
	entry_key->routine_name = routine_name;
	entry_key->type_name = type_ref->name;
	entry_table =  routine_table->entry_table;
	entry = table_lookup(Routine_entry, Routine_entry,
			     entry_table, entry_key);
	if (entry == (Routine_entry)0) {
		parameters = type_ref->parameters;
		size = type_refs_size(parameters);
		if (size > 0) {
			params = routine_table_parameters_get(routine_table,
							      size);
			assert(type_refs_equal(parameters, params));
#ifdef OLD
			type_proto = type_proto_replace(type_proto,
						parameters,
						params,
						routine_table->type_tables);
#endif /* OLD */
		}
		heap = routine_table->heap;
		entry = heap_allocate(heap, Routine_entry);
		entry->deleted = 0;
		entry->routine = (Routine)0;
		entry->routine_name = routine_name;
		entry->type_name = type_ref->name;
		entry->type_proto = type_proto;
		entry->routine_refs = vec_create(Routine_ref, heap);
		entry->used = 0;
		assert(table_insert(Routine_entry, Routine_entry,
				    entry_table, entry, entry) == 0);
		vec_append(Routine_entry, routine_table->entrys, entry);
	} else if (entry->deleted) {
		entry->deleted = 0;
	} else if (!no_error) {
		/* There seems to be a problem with the print routine
		   for routine types.
		if (type_proto_equal(entry->type_proto, type_proto)) {
			(void)printf("The prototypes match\n");
		} else {
			(void)printf("The prototypes do not match\n");
			(void)printf("First one:\n");
			type_proto_show(entry->type_proto);
			(void)printf("Second one:\n");
			type_proto_show(type_proto);
		} */
		
		msg = routine_table->msg;
		if (strequal(type_ref->name, "global__")) {
			/* msg_out(msg, position, */
			(void)printf("Warning: Routine %s is being redefined!\n",
			  routine_name);
		} else {
			/* msg_out(msg, position, */
			(void)printf("Warning: Routine %s@%s is being redefined!\n",
				routine_name, type_ref->name);
		}
	}
	return entry;
}

/*
 * routine_entry_lookup(routine_table, routine_name, type_name, position)
 *	This routine lookup and return the routine entry for
 *	"name"@"type" from "routine_table".  If there is no routine
 *	entry for "name"@"type", an error message is output usin "position"
 *	and (Routine_entry)0 is returned.
 */
Routine_entry
routine_entry_lookup(
	Routine_table	routine_table,
	Str		routine_name,
	Str		type_name,
	int		position)
{
	Routine_entry	entry;
	Routine_entry	entry_key;
	Msg		msg;

	entry_key = routine_table->entry_key;
	entry_key->routine_name = routine_name;
	entry_key->type_name = type_name;
	entry = table_lookup(Routine_entry, Routine_entry,
			     routine_table->entry_table, entry_key);
	if (entry == (Routine_entry)0) {
		msg = routine_table->msg;
		if (strequal(type_name, "global__")) {
			msg_out(msg, position,
				"Routine %s is undeclared!", routine_name);
		} else {
			msg_out(msg, position, "Routine %s@%s is undeclared!",
				routine_name, type_name);
		}
	}
	return entry;
}

/*
 * routine_entry_print(routine_entry, out_file)
 *	This routine will print "routine_entry" to "out_file" in
 *	human readable form.
 */
LOCAL void
routine_entry_print(
	Routine_entry	routine_entry,
	Stdio		out_file)
{
	if (strequal(routine_entry->type_name, "global__")) {
		out(out_file, "routine %s ", routine_entry->routine_name);
	} else {
		out(out_file, "routine %s@%s ",
		    routine_entry->routine_name, routine_entry->type_name);
	}
	type_proto_print(routine_entry->type_proto, 1, out_file);
}

#ifndef lint
/*
 * routine_entry_show(routine_entry)
 *	This routine will print out "routine_entry".
 */
void
routine_entry_show(
	Routine_entry	routine_entry)
{
	routine_entry_print(routine_entry, stdout);
}
#endif /* lint */

/*
 * routine_entry_replace(routine_table, name, type_ref, type_proto)
 *	This routine will replace the "type_proto" for "name"@"type_ref"
 *	in "routine_table".
 */
Routine_entry
routine_entry_replace(
	Routine_table	routine_table,
	Str		name,
	Type_ref	type_ref,
	Type_proto	type_proto)
{
	Routine_entry	routine_entry;

	routine_entry = routine_entry_lookup(routine_table,
					     name, type_ref->name, -1);
	assert(routine_entry != (Routine_entry)0);
	routine_entry->type_proto = type_proto;
	return routine_entry;
}

/*
 * Routine_entry_used(routine_entry, routine_table)
 * 	This routine will mark "routine_entry" as used.
 */
/* ARGSUSED */
void
routine_entry_used(
	Routine_entry	routine_entry,
	Routine_table	routine_table)
{
	routine_entry->used++;
}

/*
 * routine_entrys_dump(routine_table, out_file)
 *	This routine will dump the routine entrys from "routine_table" to
 *	"out_file" in a human readable form.
 */
void
routine_entrys_dump(
	Routine_table	routine_table,
	Stdio		out_file)
{
	Routine_entry	entry;
	Vec(Routine_entry) entrys;

	out(out_file, "Routine Entry Table:\n");
	entrys = routine_table->entrys;
	VEC_LOOP(Routine_entry, entrys, entry) {
		routine_entry_print(entry, out_file);
	}
	out(out_file, "\n");
}

/*
 * routine_entrys_export(routine_table, out_file)
 *	This routine will write out the routine entries in "routine_table"
 *	to "out_file" such that they can be reread in by
 *	"routine_entrys_import".
 */
void
routine_entrys_export(
	Routine_table	routine_table,
	Stdio		out_file)
{
	int		index;
	Routine_entry	entry;
	Vec(Routine_entry) entrys;
	int		size;
	Type_tables	type_tables;

	type_tables = routine_table->type_tables;
	entrys = routine_table->entrys;
	size = vec_size(Routine_entry, entrys);
	(void)putw(size, out_file);
	for (index = 0; index < size; index++) {
		entry = vec_fetch(Routine_entry, entrys, index);
		(void)strwrite(entry->routine_name, out_file);
		(void)strwrite(entry->type_name, out_file);
		type_proto_write(entry->type_proto, out_file, type_tables);
	}
}

/*
 * routine_entrys_import(routine_table, in_file, position)
 *	This routine will import a bunch of routine entrys from "in_file"
 *	into "routine_table".  Any error messages are output using
 *	"position".
 */
void
routine_entrys_import(
	Routine_table	routine_table,
	Stdio		in_file,
	int		position)
{
	int		count;
	Heap		heap;
	int		index;
	Str		routine_name;
	Str		type_name;
	Type_proto	type_proto;
	Type_ref	type_ref;
	Type_tables	type_tables;

	count = getw(in_file);
	heap = routine_table->heap;
	type_tables = routine_table->type_tables;
	for (index = 0; index < count; index++) {
		routine_name = strread(in_file, heap);
		type_name = strread(in_file, heap);
		type_proto = type_proto_read(in_file, heap, type_tables);
		type_ref = type_ref_parameters_create(type_name,
						      type_proto->params,
						      type_tables);
		(void)routine_entry_insert(routine_table, routine_name,
					   type_ref, type_proto, position, 0);
	}
}

