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

/*
 * Copyright (c) 1990, 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 code for generating routines: */

#ifndef CALL_EXPORTS_H
#include "call_exports.h"
#endif

#ifndef EXP_EXPORTS_H
#include "exp_exports.h"
#endif

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

#ifndef GEN_DEFS_H
#include "gen_defs.h"
#endif

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

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

#ifndef MODULE_DEFS_H
#include "module_defs.h"
#endif

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

#ifndef STATEMENT_DEFS_H
#include "statement_defs.h"
#endif

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

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

LOCAL void		routine_gen(Routine, Gen);
LOCAL void		routine_linkage_gen(Routine, Gen);
LOCAL void		variable_gen(Gen, Str, Type_ref, Str, Routine);

/*
 * routine_gen(routine, gen)
 *	This routine will generate the routine body for "routine" using
 *	"gen" control the generation.
 */
LOCAL void
routine_gen(
	Routine		routine,
	Gen		gen)
{
	int		debug;
	int		index;
	int		is_parameterized;
	Type_refs	returns;
	int		returns_size;
	Str		routine_name;
	Str		routine_type;
	int		size;
	Vars		takes;
	int		takes_size;
	int		total;
	Str		type;
	Type_ref	type_ref;
	Type_proto	type_proto;
	Var		var;
	Vars		vars;
	int		vars_size;
	int		variables_size;

	if (gen->linkage) {
		routine_linkage_gen(routine, gen);
	}
	if (routine->external != (Str)0) {
		return;
	}

	debug = gen->debug;
	routine_name = routine->name;
	routine_type = routine->type_ref->name;
	is_parameterized = type_ref_is_parameterized(routine->type_ref);
	gen->routine = routine;
	takes = routine->takes;
	takes_size = vec_size(Var, takes);
	type_proto = routine->type_proto;
	vars = routine->vars;
	vars_size = vec_size(Var, vars);

	/* Determine the routine type: */
	gen_comments(gen, routine->comments, 0);
	returns = routine->returns;
	returns_size = type_refs_size(returns);
	switch (returns_size) {
	    case 0:
		type = "void";
		break;
	    case 1:
		type_ref = type_refs_fetch(returns, 0);
		if (type_ref_is_parameter(type_ref)) {
			type = "void *";
		} else {
			type = type_ref_string(type_ref, gen->heap);
		}
		break;
	    default:
		type = type_refs_multiple_string(returns, gen->heap);
	}

	/* Emit the routine name with formals: */
	gen_out(gen, "%s %\n", type, routine->returns_comment);
	gen_out(gen, "%s__%s(", routine->type_ref->name, routine_name);
	total = takes_size + (is_parameterized ? 1 : 0);
	if (total == 0) {
		gen_out(gen, "void)\n", 1);
	} else {
		extern Type_refs vars_to_type_refs(Vars, Type_tables);

		gen_out(gen, "\n");
		assert(type_refs_equal(type_proto->takes,
				       vars_to_type_refs(routine->takes,
							 gen->type_tables)));
		for (index = 0; index < takes_size; index++) {
			var = vec_fetch(Var, takes, index);
			type_ref = var->type_ref;
			if (type_ref_is_parameter(type_ref)) {
				gen_out(gen, "%\tvoid *%S", 1, var->name);
			} else {
				gen_out(gen, "%\t%s %S", 1,
					type_ref_string(type_ref, gen->heap),
					var->name);
			}
			if (index + 1 == total) {
				gen_out(gen, ")");
			} else {
				gen_out(gen, ",");
			}
			gen_out(gen, "%\n", var->comment);
		}
	}
	if (is_parameterized) {
		gen_out(gen, "%\tvoid *_block_)\n", 1);
	}

	/* Start the routine body: */
	gen_out(gen, "{\n");

	/* Emit the temporary variables: */
	for (index = routine->temp_first;
		index < routine->temp_last; index++) {
		type_ref = vec_fetch(Type_ref, gen->temps, index);
		gen_out(gen, "%\t%s t__%d;\n", 1,
			type_ref_string(type_ref, gen->heap), index);
	}
 
	/* For debugging, emit the debugging variable declarations: */
	variables_size = total + returns_size + vars_size;
	if (debug) {
		gen_out(gen,
			"%\tactivation___object activation__object;\n", 1);
		if (variables_size > 0) {
			gen_out(gen,
				"%\t%s__%s__frame__type f;\n", 1,
				routine_type, routine_name);
		}
		gen_out(gen, "\n");
	} else { 
		/* Emit the normal variable declarations: */
		VEC_LOOP(Var, vars, var) {
			gen_out(gen, "%\t%s %S;%\n", 1,
			        type_ref_string(var->type_ref, gen->heap),
				var->name, var->comment);
		}
		if ((vars_size != 0) || debug) {
			gen_out(gen, "\n");
		}
	}

	/* Do debugger initialization: */
	if (debug) {
		/* Load the variables into the stack frame: */
		if (variables_size != 0) {
			gen_out(gen, "%\tactivation__object.frame = &f;\n", 1);
		}
		gen_out(gen, "%\tactivation__object.routine = "
			"&%s__%s__routine__object;\n", 1,
			routine_type, routine_name);
		if (is_parameterized) {
			gen_out(gen, "%\tf._block_ = _block_;\n", 1);
		}
		VEC_LOOP(Var, takes, var) {
			gen_out(gen, "%\tf.%S = %S;\n", 1,
			        var->name, var->name);
		}
	}

	/* Initialize the variables: */
	VEC_LOOP(Var, routine->vars, var) {
		gen_out(gen, "%\t%s%S = ", 1, gen->frame, var->name);
		type_init_gen(var->type_ref, gen);
		gen_out(gen, ";\n");
	}
	if (debug) {
		gen_out(gen,
			"%\trun__time__push(&activation__object);\n", 1);
		assert(routine->breakpoint_enter != (Breakpoint)0);
		statement_breakpoint(routine->breakpoint_enter, gen, 1);
	}

	/* Generate the routine body: */
	statement_list_gen(routine->statements, gen, 1);

	/* Generate the routine epilog: */
	if (debug) {
		gen_out(gen, "epilog:\n");
		assert(routine->breakpoint_return != (Breakpoint)0);
		statement_breakpoint(routine->breakpoint_return, gen, 1);
		gen_out(gen, "%\trun__time__pop();\n", 1);
		size = type_refs_size(returns);
		switch (size) {
		    case 0:
			break;
		    case 1:
			gen_out(gen, "%\treturn %sreturn__value__0;\n",
				1, gen->frame);
			break;
		    default:
			gen_out(gen, "%\t{\n", 1);
			gen_out(gen, "%\t%s return__struct;\n", 2,
				type_refs_multiple_string(returns, gen->heap));
			for (index = 0; index < size; index++) {
				gen_out(gen, "%\treturn__struct.x%d = "
					"%sreturn__value__%d;\n",
					2, index, gen->frame, index);
			}
			gen_out(gen, "%\treturn return__struct;\n", 2);
			gen_out(gen, "%\t}\n", 1);
		}
	}
	gen_out(gen, "}\n\n");
}

/*
 * routine_linkage_gen(routine, gen)
 *	This routine will output the linkage information needed for "routine"
 *	to "gen".
 */
void
routine_linkage_gen(
	Routine		routine,
	Gen		gen)
{
	Breakpoint	breakpoint;
	Vec(Breakpoint)	breakpoints;
	int		debug;
	int		external;
	Heap		heap;
	int		index;
	int		need_params;
	Type_ref	return_type_ref;
	Type_refs	returns;
	Str		routine_name;
	Str		routine_type;
	int		size;
	Str		type;
	Type_ref	type_ref;
	Vars		vars;
	Var		var;
	int		variables_size;
	Type_refs	yields;

	heap = gen->heap;
	debug = gen->debug;
	if (!gen->linkage && !debug) {
		return;
	}
	external = (routine->external != (Str)0);
	if (external) {
		debug = 0;
	}
	routine_name = routine->name;
	type_ref = routine->type_ref;
	routine_type = type_ref->name;
	returns = routine->returns;
	/*XXX: Replace this with "type = type_refs_multiple_string(...)" */
	switch (type_refs_size(returns)) {
	    case 0:
		type = "void";
		break;
	    case 1:
		return_type_ref = type_refs_fetch(returns, 0);
		if (type_ref_is_parameter(return_type_ref)) {
			type = "void *";
		} else {
			type = type_ref_string(return_type_ref, heap);
		}
		break;
	    default:
		type = type_refs_multiple_string(returns, heap);
	}

	need_table_typedef_gen(routine->need_table, gen);
	if (debug && !external) {
		/* Emit the stack frame data structure: */
		variables_size = (type_ref_is_parameterized(type_ref) ? 1 : 0) +
			vec_size(Var, routine->takes) +
			vec_size(Var, routine->vars) +
			type_refs_size(routine->returns);
		if (variables_size > 0) {
			gen_out(gen, "typedef struct {\n");
			if (type_ref_is_parameterized(type_ref)) {
				gen_out(gen, "%\tvoid *_block_;\n", 1);
			}
			VEC_LOOP(Var, routine->takes, var) {
				type_ref = var->type_ref;
				if (type_ref_is_parameter(type_ref)) {
					gen_out(gen, "%\tvoid *%S;\n",
						1, var->name);
				} else {
					type = type_ref_string(type_ref, heap);
					gen_out(gen, "%\t%s %S;\n",
						1, type, var->name);
				}
			}
			VEC_LOOP(Var, routine->vars, var) {
				type_ref = var->type_ref;
				if (type_ref_is_parameter(type_ref)) {
					gen_out(gen, "%\tvoid *%S;\n",
						1, var->name);
				} else {
					type = type_ref_string(type_ref, heap);
					gen_out(gen, "%\t%s %S;\n",
						1, type, var->name);
				}
			}
			returns = routine->returns;
			size = type_refs_size(returns);
			for (index = 0; index < size; index++) {
				type_ref = type_refs_fetch(returns, index);
				if (type_ref_is_parameter(type_ref)) {
					gen_out(gen,
						"%\tvoid *return__value__%d;\n",
						1, index);
				} else {
					type = type_ref_string(type_ref, heap);
					gen_out(gen,
						"%\t%s return__value__%d;\n",
						1, type, index);
				}
			}
			gen_out(gen, "} %s__%s__frame__type;\n",
				routine_type, routine_name);
		}

		/* Emit the variables: */
		size = vec_size(Var, routine->takes) +
			vec_size(Var, routine->vars) +
			type_refs_size(routine->returns);
		if (size > 0) {
			gen_out(gen, "static variable___object "
				"%s__%s__variables[%d] = {\n",
				routine_type, routine_name, size);
			VEC_LOOP(Var, routine->takes, var) {
				variable_gen(gen, var->name, var->type_ref,
					     "argument", routine);
			}
			VEC_LOOP(Var, routine->vars, var) {
				variable_gen(gen, var->name, var->type_ref,
					     "variable", routine);
			}
			returns = routine->returns;
			size = type_refs_size(returns);
			for (index = 0; index < size; index++) {
				Str		variable_name;
	
				type_ref = type_refs_fetch(returns, index);
				variable_name = strprintf(heap,
							  "return__value__%d",
							  index);
				variable_gen(gen, variable_name,
					     type_ref, "return", routine);
			}
			gen_out(gen, "};\n");
		}
	}

	need_params = need_table_data_gen(routine->need_table, gen);

	gen_out(gen,
		"static routine___object %s%s__%s__routine__object = {\n",
		gen->flags->package_name, routine_type, routine_name);

	/* Output the information string: */
	gen_out(gen, "%\t\"", 1);

	/* Output the routine name: */
	gen_out(gen, "%s", routine->name);
	if (!strequal(routine->type_ref->name, "global__")) {
		gen_out(gen, "@%s", routine->type_ref->name);
	}

	/* Output the arguments: */
	gen_out(gen, "(");
	vars = routine->takes;
	size = vec_size(Var, vars);
	for (index = 0; index < size; index++) {
		var = vec_fetch(Var, vars, index);
		gen_out(gen, "%s %r", var->name, var->type_ref);
		if (index + 1 < size) {
			gen_out(gen, ",");
		}
	}
	gen_out(gen, ")");

	/* Output the returns: */
	returns = routine->returns;
	size = type_refs_size(returns);
	if (size != 0) {
		gen_out(gen, "=>(");
		for (index = 0; index < size; index++) {
			type_ref = type_refs_fetch(returns, index);
			gen_out(gen, "%r", type_ref);
			if (index + 1 < size) {
				gen_out(gen, ",");
			}
		}
		gen_out(gen, ")");
	}

	/* Output the yields: */
	yields = routine->yields;
	size = type_refs_size(yields);
	if (size != 0) {
		gen_out(gen, "->");
		for (index = 0; index < size; index++) {
			type_ref = type_refs_fetch(yields, index);
			gen_out(gen, "%r", type_ref);
			if (index + 1 < size) {
				gen_out(gen, ",");
			}
		}
	}
	gen_out(gen, "\"");

	/* Output the variable names: */
	vars = routine->vars;
	size = vec_size(Var, vars);
	if (size != 0) {
		gen_out(gen, "\n%\t\"{", 2);
		for (index = 0; index < size; index++) {
			var = vec_fetch(Var, vars, index);
			gen_out(gen, "%s %r", var->name, var->type_ref);
			if (index + 1 < size) {
				gen_out(gen, ",");
			}
		}
		gen_out(gen, "}\"");
	}

	/* Output the line numbers: */
	breakpoints = routine->breakpoints;
	size = vec_size(Breakpoint, breakpoints);
	if (size != 0) {
		gen_out(gen, "\n%\t\"[", 2);
		for (index = 0; index < size; index++) {
			breakpoint = vec_fetch(Breakpoint, breakpoints, index);
			gen_out(gen, "%d", breakpoint->line_number);
			if (index + 1 < size) {
				gen_out(gen, ",");
			}
		}
		gen_out(gen, "]\"");
	}
	gen_out(gen, ",\n");

	gen_out(gen, "%\t(int *)0,\n", 1);	/* Test coverage counts: */
	gen_out(gen, "%\t(routine__info__type)0,\n", 1);

	/* Output the other stuff: */
	gen_out(gen, "%\t&%s%s__module__object,\n",
		1, gen->flags->package_name, routine->module->name);
	if (debug) {
		size = vec_size(Var, routine->takes) +
			vec_size(Var, routine->vars) +
			type_refs_size(routine->returns);
		gen_out(gen, "%\t%d,\n", 1, size);
		if (variables_size == 0) {
			gen_out(gen, "%\t(variable___object *)0,\n", 1);
		} else {
			gen_out(gen, "%\t%s__%s__variables,\n", 1,
				routine_type, routine_name);
		}
	} else {
		gen_out(gen, "%\t0,\n", 1);
		gen_out(gen, "%\t(variable___object *)0,\n", 1);
	}
	gen_out(gen, "%\t(void *)%s__%s,\n", 1, routine_type, routine_name);
	if (need_params) {
		gen_out(gen, "%\t&%s__%s__routine__params,\n",
			1, routine_type, routine_name);
	} else {
		gen_out(gen, "%\t(routine___params *)0,\n", 1);
	}
	gen_out(gen, "};\n");
	gen_out(gen, "\n");
}

/*
 * routines_gen(types, gen)
 *	This routine will generate the type routine declarations for
 *	each routine in "routines" using "gen" control the generation.
 */
void
routines_gen(
	Vec(Routine)	routines,
	Gen		gen)
{
	Routine		routine;

	/* Emit each routine body: */
	VEC_LOOP(Routine, routines, routine) {
		routine_gen(routine, gen);
	}
}

#ifdef OLD
/*
 * routine_signature_gen(routine, gen)
 *	This routine will output the signature for "routine" to "gen".
 */
void
routine_signature_gen(
	Routine		routine,
	Gen		gen)
{
	int		index;
	int		is_parameterized;
	Str		separator;
	int		size;
	Vars		takes;
	Type_ref	type_ref;
	Var		var;

	is_parameterized = type_ref_is_parameterized(routine->type_ref);
	takes = routine->takes;
	size = vec_size(Var, takes);
	if ((size == 0) && !is_parameterized) {
		gen_out(gen, "(void)");
	} else {
		separator = "(";
		for (index = 0; index < size; index++) {
			var = vec_fetch(Var, takes, index);
			type_ref = var->type_ref;
			if (type_ref_is_parameter(type_ref)) {
				gen_out(gen, "%svoid *%s",
					separator, var->name);
			} else {
				gen_out(gen, "%s%s %s", separator,
					type_ref_string(var->type_ref,
							gen->heap),
					var->name);
			}
			separator = ", ";
		}
		if (is_parameterized) {
			gen_out(gen, "%svoid *_block_", separator);
		}
		gen_out(gen, ")");
	}
}
#endif /* OLD */

/*
 * variable_gen(gen, variable_name, type_ref, variable_kind, routine)
 *	This routine will emit a variable for "variable_name" to "gen".
 */
LOCAL void
variable_gen(
	Gen		gen,
	Str		variable_name,
	Type_ref	type_ref,
	Str		variable_kind,
	Routine		routine)
{
	gen_out(gen, "%\tvariable__kind__%s, ", 1, variable_kind);
	gen_out(gen, "%\",\n", variable_name);
	gen_out(gen, "%\t(int)(&((%s__%s__frame__type *)0)->%S),\n",
		2, routine->type_ref->name, routine->name, variable_name);
	if (type_ref_is_routine(type_ref)) {
		gen_out(gen, "%\tvariable__flavor__unparam,\n", 2);
		gen_out(gen, "%\t(void *)run__time__routine_print,\n", 2);
	} else if (type_ref_contains_parameter(type_ref)) {
		gen_out(gen, "%\tvariable__flavor__block,\n", 2);
		gen_out(gen,
		       "%\t(void *)(&((%s__%s__block__type *)0)->%m__print),\n",
		       2, routine->type_ref->name, routine->name, type_ref);
	} else if (type_ref_is_parameterized(type_ref)) {
		gen_out(gen, "%\tvariable__flavor__static,\n", 2);
		gen_out(gen, "%\t(void *)&%s__%s__static.%m__print,\n",
			2, routine->type_ref->name, routine->name, type_ref);
	} else {
		gen_out(gen, "%\tvariable__flavor__unparam,\n", 2);
		gen_out(gen, "%\t(void *)%s__print,\n", 2, type_ref->name);
	}
}
