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

/*
 * Copyright (c) 1990, 1991, 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 routine contains code for converting routines from expression trees
 * to call trees:
 */

#ifndef CALL_DEFS_H
#include "call_defs.h"
#endif

#ifndef FLAGS_DEFS_H
#include "flags_defs.h"
#endif

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

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

#ifndef MSG_EXPORTS_H
#include "msg_exports.h"
#endif

#ifndef NEED_EXPORTS_H
#include "need_exports.h"
#endif

#ifndef OBJECT_DEFS_H
#include "object_defs.h"
#endif

#ifndef ROUTINE_DEFS_H
#include "routine_defs.h"
#endif

#ifndef STATEMENT_EXPORTS_H
#include "statement_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 VAR_DEFS_H
#include "var_defs.h"
#endif

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

LOCAL void	routine_initial_access(Routine, Type_ref, Object_table, int);
LOCAL void	routine_print_access(Routine, Type_ref, Routine_table, int);
LOCAL void	vars_replace(Vars, Type_refs, Type_refs, Type_tables);

/*
 * routine_convert(routine, convert)
 *	This routine will generate the routine body for "routine" using
 *	"gen" control the generation.
 */
void
routine_convert(
	Routine			routine,
	Convert			convert)
{
	Heap			heap;
	Type_need		need;
	Need_table		need_table;
	Type_needs		needs;
	Type_refs		new_type_refs;
	Object_ref		object_ref;
	Vec(Object_ref)		object_refs;
	Object_table		object_table;
	Type_refs		old_type_refs;
	Type_ref		parameter;
	Type_refs		parameters;
	Type_refs		returns;
	Routine_entry		routine_entry;
	Vec(Routine_entry)	routine_entrys;
	Routine_table		routine_table;
	Routine_type		routine_type;
	Vec(Routine_type)	routine_types;
	Table(Str, Type_ref)	synonyms;
	Vars			takes;
	Type_ref		type_ref;
	Type_tables		type_tables;
	Type_proto		type_proto;
	Var			var;
	Vars			vars;


	/* Temporarily insert all needs routines into the routine table: */
	heap = convert->heap;
	need_table = routine->need_table;
	type_tables = convert->type_tables;
	routine->temp_first = vec_size(Type_ref, convert->temps);
	routine_entrys = vec_create(Routine_entry, heap);
	routine_table = convert->routine_table;
	needs = routine->needs;
	VEC_LOOP(Type_need, needs, need) {
		routine_entry = routine_entry_insert(routine_table,
						     need->name,
						     need->type_ref,
						     need->proto,
						     -1,
						     0);
		vec_append(Routine_entry, routine_entrys, routine_entry);
		need_table_routine_insert(need_table, need->name,
					  need->type_ref, need->proto);
	}

	/*XXX: This should probably be eliminated: */
	/* Temporarily insert initial objects into the constant table: */
	object_table = convert->object_table;
	object_refs = vec_create(Object_ref, heap);
	parameters = routine->type_ref->parameters;
	TYPE_REFS_LOOP(parameters, parameter) {
		object_ref = object_ref_insert(object_table, "??",
					       parameter, parameter, -1);
		vec_append(Object_ref, object_refs, object_ref);
	}

	/* Temporarily insert all routine types into the synonym table: */
	synonyms = convert->synonyms;
	routine_types = routine->routine_types;
	old_type_refs = type_refs_empty_create(type_tables);
	new_type_refs = old_type_refs;
	type_proto = routine->type_proto;
	VEC_LOOP(Routine_type, routine_types, routine_type) {
		type_ref = routine_type->type_proto->type_ref;
		old_type_refs = type_refs_append(old_type_refs,
			type_ref_create(routine_type->name, type_tables),
			type_tables);
		new_type_refs = type_refs_append(new_type_refs,
						 type_ref, type_tables);
		if (table_insert(Str, Type_ref,
				 synonyms, routine_type->name, type_ref)) {
			msg_out(convert->msg, routine->position,
				"%s is already defined");
		}
	}
	if (!type_refs_is_empty(new_type_refs)) {
		type_proto = type_proto_replace(routine->type_proto,
						old_type_refs,
						new_type_refs,
						type_tables);
		routine->needs = type_proto->needs;
		routine->returns = type_proto->returns;
		routine->signals = type_proto->signals;
		vars_replace(routine->takes, old_type_refs,
			     new_type_refs, type_tables);
		routine->type_proto = type_proto;
		vars_replace(routine->vars, old_type_refs,
			     new_type_refs, type_tables);
		routine->yields = type_proto->yields;
		(void)routine_entry_replace(convert->routine_table,
					    routine->name,
					    routine->type_ref,
					    type_proto);
	}
	type_proto_needed(type_proto, type_tables);

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

	/* Now we can finally scan over all statements: */
	statement_list_convert(routine->statements, convert);

	/* For debugging, force print routines for all variables: */
	takes = routine->takes;
	vars = routine->vars;
	returns = routine->returns;
	if (convert->flags->debug && (routine->external == (Str)0)) {
		VEC_LOOP(Var, takes, var) {
			routine_print_access(routine, var->type_ref,
					     routine_table, var->position);
		}
		VEC_LOOP(Var, vars, var) {
			routine_print_access(routine, var->type_ref,
					     routine_table, var->position);
		}
		TYPE_REFS_LOOP(returns, type_ref) {
			routine_print_access(routine, type_ref,
					     routine_table,
					     routine->returns_position);
		}
	}

	/* Force the need entries for all variable initial objects: */
	VEC_LOOP(Var, takes, var) {
		routine_initial_access(routine, var->type_ref,
				       object_table, var->position);
	}
	VEC_LOOP(Var, vars, var) {
		routine_initial_access(routine, var->type_ref,
				       object_table, var->position);
	}
	TYPE_REFS_LOOP(returns, type_ref) {
		routine_initial_access(routine, type_ref, object_table,
				       routine->returns_position);
	}

	/* Now remove the temporarily inserted routines: */
	VEC_LOOP(Routine_entry, routine_entrys, routine_entry) {
		routine_entry_delete(routine_entry);
	}
	routine->temp_last = vec_size(Type_ref, convert->temps);

	/* Remove temporarily inserted initial objects: */
	VEC_LOOP(Object_ref, object_refs, object_ref) {
		object_ref_delete(object_ref);
	}

	/* Remove the synonyms: */
	VEC_LOOP(Routine_type, routine_types, routine_type) {
		assert(table_delete(Str, Type_ref,
				    synonyms, routine_type->name) == 0);
	}

	/* Mark the routine as used in the routine table: */
	routine_entry = routine_entry_lookup(routine_table, routine->name,
					     routine->type_ref->name,
					     routine->position);
	assert(routine_entry != (Routine_entry)0);
	routine_entry_used(routine_entry, routine_table);
}

/*
 * routine_initial_access(routine, type_ref, object_table, position)
 */
/* ARGSUSED */
LOCAL void
routine_initial_access(
	Routine		routine,
	Type_ref	type_ref,
	Object_table	object_table,
	int		position)
{
	Object_ref	object_ref;

	if (type_ref_is_routine(type_ref)) {
		return;
	} else if (type_ref_contains_parameter(type_ref) ||
		   type_ref_is_parameterized(type_ref)) {
		need_table_object_insert(routine->need_table,
					 "??", type_ref, type_ref);
	} else {
		object_ref = object_ref_lookup(object_table, "??", type_ref);
		if (object_ref == (Object_ref)0) {
			object_ref = object_ref_insert(object_table, "??",
						       type_ref, type_ref,
						       position);
		}
		object_ref->used++;
	}
}

/*
 * routine_print_access(routine, type_ref, routine_table)
 */
/* ARGSUSED */
LOCAL void
routine_print_access(
	Routine		routine,
	Type_ref	type_ref,
	Routine_table	routine_table,
	int		position)
{
	Routine_ref	routine_ref;

	if (type_ref_is_routine(type_ref)) {
		return;
	} else if (type_ref_contains_parameter(type_ref) ||
		   type_ref_is_parameterized(type_ref)) {
		Type_refs	takes;
		Type_proto	type_proto;
		Type_tables 	type_tables;
		Type_refs	type_refs_empty;

		type_tables = routine_table->type_tables;
		type_refs_empty = type_refs_empty_create(type_tables);
		takes = type_ref->type_refs;
		takes = type_refs_append(takes,
					 type_ref_create("out_stream",
							 type_tables),
					 type_tables);
		type_proto = type_proto_create(Type_proto_procedure,
					type_ref->parameters,
					takes,
					type_refs_empty,
					type_refs_empty,
					type_signals_empty_create(type_tables),
					type_needs_empty_create(type_tables),
					0,
					type_tables);
		need_table_routine_insert(routine->need_table, "print",
					  type_ref, type_proto);
	} else {
		routine_ref = routine_ref_lookup(routine_table, "print",
						 type_ref, position);
			if (routine_ref == (Routine_ref)0) {
			routine_ref = routine_ref_insert(routine_table, "print",
							 type_ref, position);
		}
		routine_entry_used(routine_ref->routine_entry, routine_table);
	}
}

/*
 * vars_replace(vars, old_type_refs, new_type_refs, type_tables)
 *	This routine will update each variable "vars" so that each occurrance 
 *	of a type reference in "old_types_refs" is replaced by the
 *	corresponding type reference in "new_type_refs".
 */
LOCAL void
vars_replace(
	Vars		vars,
	Type_refs	old_type_refs,
	Type_refs	new_type_refs,
	Type_tables	type_tables)
{
	Var		var;

	VEC_LOOP(Var, vars, var) {
		var->type_ref = type_ref_replace(var->type_ref, old_type_refs,
						 new_type_refs, type_tables);
	}
}
