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

/*
 * 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 code for convert call tress into C code: */

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

#ifndef ERROR_EXPORTS_H
#include "error_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 OBJECT_DEFS_H
#include "object_defs.h"
#endif

#ifndef ROUTINE_DEFS_H
#include "routine_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 UNIX_UNISTD_H
#include "unix_unistd.h"
#endif

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

/*
 * call_gen(call, indent, gen)
 *	This routine will print "call" out indented by "indent" using "gen".
 */
void
call_gen(
	Call		call,
	int		indent,
	Gen		gen)
{
	switch (call->type) {
	    case Call_type_and_if:
	      {
		Call_binary	binary;

		binary = call->value.binary;
		gen_out(gen, "(");
		call_gen(binary->left, 0, gen);
		gen_out(gen, " && ");
		call_gen(binary->right, 0, gen);
		gen_out(gen, ")");
		break;
	      }
	    case Call_type_assign:
	      {
		Call_assign	assign;

		gen_out(gen, "%\t", indent);
		assign = call->value.assign;
		call_gen(assign->left, 0, gen);
		gen_out(gen, " = ");
		call_gen(assign->right, 0, gen);
		gen_out(gen, ";\n");
		break;
	      }
	    case Call_type_cast:
	      {
		Call_cast	cast;
		Str		name;

		cast = call->value.cast;
		name = cast->type_ref->name;
		if (type_ref_contains_parameter(cast->type_ref)) {
			gen_out(gen, "((void *)");
		} else {
			gen_out(gen, "((%T)", name);
		}
		call_gen(cast->call, 0, gen);
		gen_out(gen, ")");
		break;
	      }
	    case Call_type_error:
		gen_out(gen, "0");
		break;
	    case Call_type_if:
	      {
		Call_if		call_if;

		call_if = call->value.xif;
		gen_out(gen, "(");
		call_gen(call_if->condition, 0, gen);
		gen_out(gen, " ? ");
		call_gen(call_if->true, 0, gen);
		gen_out(gen, " : ");
		call_gen(call_if->false, 0, gen);
		gen_out(gen, ")");
		break;
	      }
	    case Call_type_integer:
		gen_out(gen, "%d", call->value.integer);
		break;
	    case Call_type_invoke:
	      {
		Call		actual;
		Vec(Call)	actuals;
		Call_routine	call_routine;
		Call_invoke	invoke;
		int		index;
		int		proc_var;
		Routine_ref	routine_ref;
		Str		separator;
		int		size;

		separator = "(";
		invoke = call->value.invoke;
		switch(invoke->call->type) {
		    case Call_type_routine:
		    case Call_type_var:
		    case Call_type_temp:
			break;
		    default:
			assert_fail();
		}
		actuals = invoke->actuals;
		size = vec_size(Call, actuals);
		if ((gen->flags->opt_base_types) &&
		    (invoke->call->type == Call_type_routine)) {
			/* See whether routine is a base type: */
			Str		type;
			Str		name;
			Str		operator;
			int		unary;

			call_routine = invoke->call->value.routine;
			routine_ref = call_routine->routine_ref;
			name = routine_ref->name;
			type = routine_ref->type_ref->name;
			unary = 0;
			operator = (Str)0;
			if (strequal(type, "integer") ||
			    strequal(type, "unsigned")) {
				if (strequal(name, "add")) {
					operator = "+";
				} else if (strequal(name, "and")) {
					operator = "&";
				} else if (strequal(name, "divide")) {
					operator = "/";
				} else if (strequal(name, "equal")) {
					operator = "==";
				} else if (strequal(name, "greater_than")) {
					operator = ">";
				} else if (strequal(name, "left_shift")) {
					operator = "<<";
				} else if (strequal(name, "less_than")) {
					operator = "<";
				} else if (strequal(name, "minus")) {
					operator = "-";
					unary = 1;
				} else if (strequal(name, "multiply")) {
					operator = "*";
				} else if (strequal(name, "not")) {
					operator = "~";
					unary = 1;
				} else if (strequal(name, "or")) {
					operator = "|";
				} else if (strequal(name, "remainder")) {
					operator = "%";
				} else if (strequal(name, "subtract")) {
					operator = "-";
				} else if (strequal(name, "xor")) {
					operator = "^";
				}
			}
			if (operator != (Str)0) {
				gen_out(gen, "(");
				if (unary) {
					assert(size == 1);
					actual = vec_fetch(Call, actuals, 0);
					gen_out(gen, "%s", operator);
					call_gen(actual, 0, gen);
				} else {
					assert(size == 2);
					actual = vec_fetch(Call, actuals, 0);
					call_gen(actual, 0, gen);
					gen_out(gen, "%s", operator);
					actual = vec_fetch(Call, actuals, 1);
					call_gen(actual, 0, gen);
				}
				gen_out(gen, ")");
				break;
			}
		}
		gen_out(gen, "%\t", indent);
		proc_var = (invoke->call->type != Call_type_routine);
		if (proc_var) {
			gen_out(gen, "(*");
			call_gen(invoke->call, 0, gen);
			gen_out(gen, "->routine)");
		} else if (invoke->call->type == Call_type_routine) {
			Routine		routine;
			Type_ref	type_ref;

			call_routine = invoke->call->value.routine;
			routine_ref = call_routine->routine_ref;
			type_ref = routine_ref->type_ref;
			routine = gen->routine;

			/* Figure out whether the parameter block is needed: */
			if (type_ref_contains_parameter(type_ref)) {
				gen_out(gen,
					"(*((%s__%s__block__type *)_block_)"
					"->%m__%s->routine)",
					routine->type_ref->name,
					routine->name,
					type_ref, routine_ref->name);
			} else if (type_ref_is_parameterized(type_ref)) {
				gen_out(gen,
					"(*%s__%s__static.%m__%s->routine)",
					routine->type_ref->name,
					routine->name,
					type_ref, routine_ref->name);
			} else if (type_ref_is_routine(type_ref) &&
				   strequal(routine_ref->name, "print")) {
				gen_out(gen, "run__time__routine_print");
				separator = "((void *)";
			} else {
				gen_out(gen, "%s__%s", type_ref->name,
					routine_ref->name);
			}
		} else {
			call_gen(invoke->call, 0, gen);
		}
		for (index = 0; index < size; index++) {
			gen_out(gen, "%s", separator);
			separator = ", ";
			actual = vec_fetch(Call, actuals, index);
			call_gen(actual, 0, gen);
		}

		/* Decide whether to pass in a parameter block. */
		if (proc_var) {
			gen_out(gen, "%s", separator);
			call_gen(invoke->call, 0, gen);
			gen_out(gen, "->parameter");
			separator = ",";
		} else if (invoke->call->type == Call_type_routine) {
			Routine		routine;
			Type_ref	type_ref;

			call_routine = invoke->call->value.routine;
			routine_ref = call_routine->routine_ref;
			type_ref = routine_ref->type_ref;
			routine = gen->routine;

			/* Figure out whether the parameter block is needed: */
			if (type_ref_contains_parameter(type_ref)) {
				gen_out(gen,
					"%s((%s__%s__block__type *)_block_)"
					"->%m__%s->parameter", separator,
					routine->type_ref->name,
					routine->name,
					type_ref, routine_ref->name);
					separator = ",";
			} else if (type_ref_is_parameterized(type_ref)) {
				gen_out(gen,
					"%s%s__%s__static.%m__%s->parameter",
					separator, routine->type_ref->name,
					routine->name, type_ref,
					routine_ref->name);
					separator = ",";
			}
		}

		if (separator[0] == '(') {
			gen_out(gen, "(");
		}
		gen_out(gen, ")");
		if (indent > 0) {
			gen_out(gen, ";\n");
		}
		break;
	      }
	    case Call_type_multi:
	      {
		Type_ref	after_type_ref;
		Type_refs	after_type_refs;
		Type_ref	before_type_ref;
		Type_refs	before_type_refs;
		int		index;
		Call_multi	multi;
		int		size;
		Call		var;
		Vec(Call)	vars;

		multi = call->value.multi;
		vars = multi->vars;
		gen_out(gen, "%\t{\n", indent);
		indent++;

		after_type_refs = call->type_refs;
		before_type_refs = multi->type_refs;
		gen_out(gen, "%\t%s x__;\n", indent,
		        type_refs_multiple_string(before_type_refs, gen->heap));

		gen_out(gen, "%\tx__ = ", indent);
		call_gen(multi->invoke, 0, gen);
		gen_out(gen, ";\n");
		size = vec_size(Call, vars);
		for (index = 0; index < size; index++) {
			var = vec_fetch(Call, vars, index);
			gen_out(gen, "%\t", indent);
			switch (var->type) {
			    case Call_type_temp:
			      {
				Call_temp	call_temp;

				call_temp = var->value.temp;
				gen_out(gen, "t__%d", call_temp->number);
				break;
			      }
			    case Call_type_var:
			      {
				Call_var	call_var;

				call_var = var->value.var;
				gen_out(gen, "%s", call_var->name);
				break;
			      }
			    default:
				assert_fail();
			}
			gen_out(gen, " = ");
			after_type_ref = type_refs_fetch(after_type_refs,
							 index);
			before_type_ref = type_refs_fetch(before_type_refs,
							  index);
			if (!type_ref_equal(before_type_ref, after_type_ref)) {
				gen_out(gen, "(%T)", after_type_ref->name);
			}
			gen_out(gen, "x__.x%d;\n", index);
		}
		indent--;
		gen_out(gen, "%\t}\n", indent);
		break;
	      }
	    case Call_type_not:
	      {
		gen_out(gen, "!(");
		call_gen(call->value.not, 0, gen);
		gen_out(gen, ")");
		break;
	      }
	    case Call_type_object:
	      {
		int		is_initial;
		Str		name;
		Object_ref	object_ref;
		Type_ref	type_ref;
		Routine		routine;

		object_ref = call->value.object;
		name = object_ref->name;
		is_initial = strequal(name, "??");
		if (is_initial) {
			name = "_initial";
		}
		type_ref = object_ref->type_ref;
		routine = gen->routine;
		if (type_ref_contains_parameter(type_ref)) {
			gen_out(gen, "((%s__%s__block__type *)_block_)->%m__%s",
				routine->type_ref->name, routine->name,
				type_ref, name);
		} else if (type_ref_is_parameterized(type_ref)) {
			gen_out(gen, "%s__%s__static.%m__%s",
				routine->type_ref->name, routine->name,
				type_ref, name);
		} else if (is_initial && type_ref_is_routine(type_ref)) {
			gen_out(gen, "(%T)run__time__uninitialized__routine",
				type_ref->name);
		} else {
			gen_out(gen, "%r__%s", type_ref, name);
		}
		break;
	      }
	    case Call_type_or_if:
	      {
		Call_binary	binary;

		binary = call->value.binary;
		gen_out(gen, "(");
		call_gen(binary->left, 0, gen);
		gen_out(gen, " || ");
		call_gen(binary->right, 0, gen);
		gen_out(gen, ")");
		break;
	      }
	    case Call_type_routine_param:
	      {
		Call_routine_param call_routine_param;
		Routine_ref	routine_ref;

		call_routine_param = call->value.routine_param;
		routine_ref = call_routine_param->routine_ref;
		if (call_routine_param->parameterized) {
			gen_out(gen, "_parameters_->%m__%s->parameter",
				routine_ref->type_ref, routine_ref->name);
		} else {
			gen_out(gen, "&%m__%s__struct.parameter",
				routine_ref->type_ref, routine_ref->name);
		}
		break;
	      }
	    case Call_type_routine:
	      {
		Call_routine	call_routine;
		Routine_ref	routine_ref;

		call_routine = call->value.routine;
		routine_ref = call_routine->routine_ref;
		gen_out(gen, "&%m__%s__struct", routine_ref->type_ref,
			routine_ref->name);
		break;
	      }
	    case Call_type_string:
		gen_out(gen, "%#", call->value.string);
		break;
	    case Call_type_temp:
	      {
		Call_temp	call_temp;

		call_temp = call->value.temp;
		gen_out(gen, "t__%d", call_temp->number);
		break;
	      }
	    case Call_type_text:
		gen_out(gen, "%#", call->value.text);
		break;
	    case Call_type_var:
	      {
		Call_var	call_var;

		call_var = call->value.var;
		gen_out(gen, "%s%S", gen->frame, call_var->name);
		break;
	      }
	    default:
		assert_fail();
	}
}

/*
 * call_list_gen(calls, indent, gen)
 *	This routine will print each call object in "calls" out indented
 *	by "indent" using "gen".
 */
void
call_list_gen(
	Vec(Call)	calls,
	int		indent,
	Gen		gen)
{
	Call		call;

	VEC_LOOP(Call, calls, call) {
		call_gen(call, indent, gen);
	}
}
