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

/*
 * Copyright (c) 1992, 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 routines for manipulating routine_ref's: */

#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

/*
 * routine_ref_equal(routine_ref1, routine_ref2)
 *	This routine will return 1 if "routine_ref1" is equal to
 *	"routine_ref2".  They are equal if they have the same name and
 *	type reference name.
 */
int
routine_ref_equal(
	Routine_ref	routine_ref1,
	Routine_ref	routine_ref2)
{
	Type_ref	type_ref1;
	Type_ref	type_ref2;

	assert(routine_ref1 != (Routine_ref)0);
	assert(routine_ref2 != (Routine_ref)0);

	if (!strequal(routine_ref1->name, routine_ref2->name)) {
		return 0;
	}
	type_ref1 = routine_ref1->type_ref;
	type_ref2 = routine_ref2->type_ref;
	if (!type_ref_equal(type_ref1, type_ref2)) {
		return 0;
	}
	return 1;
}

/*
 * routine_ref_hash(routine_ref)
 *	This routine will return a hash value for "routine_ref".
 */
int
routine_ref_hash(
	Routine_ref	routine_ref)
{
	int		hash;

	hash = strhash(routine_ref->name);
	hash += type_ref_hash(routine_ref->type_ref);
	return hash;
}

/*
 * routine_ref_insert(routine_table, routine_name, type_ref, position)
 *	This routine will insert "routine_name"@"type_ref" into "routine_table".
 *	The result routine reference object is returned.
 */
Routine_ref
routine_ref_insert(
	Routine_table	routine_table,
	Str		routine_name,
	Type_ref	type_ref,
	int		position)
{
	Routine_entry	entry_key;
	Routine_entry	entry;
	Routine_ref	ref;
	Type_proto	type_proto;
	Type_tables	type_tables;

	entry_key = routine_table->entry_key;
	entry_key->routine_name = routine_name;
	entry_key->type_name = type_ref->name;
	entry = table_lookup(Routine_entry, Routine_entry,
			     routine_table->entry_table, entry_key);
	type_tables = routine_table->type_tables;
	if (entry == (Routine_entry)0) {
		Type_refs	type_refs_empty;

		msg_out(routine_table->msg, position, "%s%@ does not exist",
			routine_name, type_ref);
		type_refs_empty = type_refs_empty_create(type_tables);
		type_proto = type_proto_create(Type_proto_procedure,
					type_refs_empty,
					type_refs_empty,
					type_refs_empty,
					type_refs_empty,
					type_signals_empty_create(type_tables),
					type_needs_empty_create(type_tables),
					0,
					type_tables);
		entry = routine_entry_insert(routine_table, routine_name,
					     type_ref, type_proto, position, 0);
	}
	type_proto = entry->type_proto;
	if (type_proto_is_parameterized(type_proto)) {
		if (type_refs_size(type_proto->params) ==
		    type_refs_size(type_ref->parameters)) {
			type_proto = type_proto_replace(entry->type_proto,
						entry->type_proto->params,
						type_ref->parameters,
						routine_table->type_tables);
		} else {
			msg_out(routine_table->msg, position,
				"Routine passed %d parameters instead of %d",
				type_refs_size(type_ref->parameters),
				type_refs_size(type_proto->params));
		}
	}

	ref = heap_allocate(routine_table->heap, Routine_ref);
	ref->name = routine_name;
	ref->type_ref = type_ref;
	ref->constant = 0;
	ref->routine_entry = entry;
	ref->type_proto = type_proto;

	assert(table_insert(Routine_ref, Routine_ref,
	       routine_table->ref_table, ref, ref) == 0);
	vec_append(Routine_ref, entry->routine_refs, ref);

	return ref;
}

/*
 * routine_ref_lookup(routine_table, routine_name, type_ref, position)
 *	This routine will lookup and return the routine reference for
 *	"routine_name"@"type_ref" from "routine_table".  (Routine_ref)0
 *	is returned if "routine_name"@"type_name" can not be found.
 */
Routine_ref
routine_ref_lookup(
	Routine_table	routine_table,
	Str		routine_name,
	Type_ref	type_ref,
	int		position)
{
	Routine_entry	entry;
	Routine_entry	entry_key;
	Routine_ref	ref;
	Routine_ref	ref_key;

	ref_key = routine_table->ref_key;
	ref_key->name = routine_name;
	ref_key->type_ref = type_ref;
	ref = table_lookup(Routine_ref, Routine_ref,
			   routine_table->ref_table, ref_key);
	if (ref == (Routine_ref)0) {
		entry_key = routine_table->entry_key;
		entry_key->routine_name = routine_name;
		entry_key->type_name = type_ref->name;
		entry = table_lookup(Routine_entry, Routine_entry,
				     routine_table->entry_table, entry_key);
		if (entry != (Routine_entry)0) {
			ref = routine_ref_insert(routine_table, routine_name,
						 type_ref, position);
		}
	}
	return ref;
}

/*
 * routine_ref_print(routine_ref, out_file)
 *	This routine will print "routine_ref" to "out_file" in human
 *	readable form.
 */
LOCAL void
routine_ref_print(
	Routine_ref	routine_ref,
	Stdio		out_file)
{
	out(out_file, "routine %s%@ ",
	    routine_ref->name, routine_ref->type_ref);
	type_proto_print(routine_ref->type_proto, 1, out_file);
}

#ifndef lint
/*
 * routine_ref_show(routine_ref)
 *	This routine will print out "routine_ref".
 */
void
routine_ref_show(
	Routine_ref	routine_ref)
{
	routine_ref_print(routine_ref, stdout);
}
#endif /* lint */

/*
 * routine_refs_dump(routine_table, out_file)
 *	This routine will dump the routine refs from "routine_table" to 
 *	"out_file" in a human readable form.
 */
void
routine_refs_dump(
	Routine_table	routine_table,
	Stdio		out_file)
{
	Routine_ref	ref;
	Vec(Routine_ref) refs;

	out(out_file, "Routine References Table:\n");
	refs = table_value_list_extract(Routine_ref, Routine_ref,
					routine_table->ref_table);
	VEC_LOOP(Routine_ref, refs, ref) {
		routine_ref_print(ref, out_file);
	}
	out(out_file, "\n");
}

