/* %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 the code generator for statements: */

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

#ifndef ERROR_EXPORTS_H
#include "error_exports.h"
#endif

#ifndef EXP_DEFS_H
#include "exp_defs.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 MSG_EXPORTS_H
#include "msg_exports.h"
#endif

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

#ifndef ROUTINE_TYPES_H
#include "routine_types.h"
#endif

#ifndef STATEMENT_DEFS_H
#include "statement_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 UNIX_unistd_H
#include "unix_unistd.h"
#endif

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

LOCAL void		statement_gen(Statement, Gen, int);
LOCAL Statement_loop	statement_loop_find(Str, Gen);

/*
 * statement_breakpoint(breakpoint, gen, indent)
 *	This routine will output breakpoint code for "breakpoint" indended by
 *	"indent" if "gen"->debug is 1.
 */
void
statement_breakpoint(
	Breakpoint	breakpoint,
	Gen		gen,
	int		indent)
{
	int		number;

	if (gen->debug) {
		number = breakpoint->number;
		assert(number >= 0);
		gen_out(gen, "%\tbreak___point(%d);\n", indent, number);
	}
}

/*
 * statement_gen(statement, gen, indent)
 *	This routine will generate code for "statement" indented by "indent"
 *	using "gen" to control the code generation.
 */
void
statement_gen(
	Statement	statement,
	Gen		gen,
	int		indent)
{
	switch (statement->kind) {
	    case Statement_kind_assert:
	      {
		statement_breakpoint(statement->breakpoint, gen, indent);
		gen_out(gen, "%\tif (!(", indent);
		call_gen(statement->value.exp->call, 0, gen);
		gen_out(gen, ")) {\n");
		gen_out(gen, "%\trun__time__assertion_failed(%\", %d);\n",
			indent + 1, gen->flags->in_file->full,
			msg_line_get(gen->msg, statement->position));
		gen_out(gen, "%\t}\n", indent);
		break;
	      }
	    case Statement_kind_break:
	    case Statement_kind_continue:
	      {
		Str		comment;
		Str		keyword;
		Statement_label	label;
		Statement_loop	loop;
		Str		name;

		label = statement->value.label;
		name = label->name;
		loop = statement_loop_find(name, gen);
		if (loop == (Statement_loop)0) {
			/* Error message already generated */
			break;
		}
		statement_breakpoint(statement->breakpoint, gen, indent);
		comment = statement->comment;
		switch (statement->kind) {
		    case Statement_kind_break:
			keyword = "break";
			if (loop->in_switch || !loop->is_inner_most) {
				label->use_goto = 1;
				loop->need_break = 1;
			}
			break;
		    case Statement_kind_continue:
			keyword = "continue";
			if (!loop->is_inner_most) {
				label->use_goto = 1;
				loop->need_continue = 1;
			}
			break;
		}
		if (label->use_goto) {
			if (loop->name == (Str)0) {
				gen_out(gen, "%\tgoto %s___%d;%\n",
					indent, keyword,
					loop->number, comment);
			} else {
				gen_out(gen, "%\tgoto %s___%s;%\n",
					indent, keyword,
					loop->name, comment);
			}
		} else {
			gen_out(gen, "%\t%s;%\n",
				indent, keyword, comment);
		}
		break;
	      }
	    case Statement_kind_eval:
		statement_breakpoint(statement->breakpoint, gen, indent);
		call_list_gen(statement->calls, indent, gen);
		break;
	    case Statement_kind_extract:
	      {
		Exp			exp;
		Statement_extract	extract;
		Str			first_tag_name;
		int			index;
		int			size;
		Statement_tag		tag;
		Str			tag_field_type;
		Str			tag_name;
		Vec(Str)		tag_names;
		Vec(Statement_tag)	tags;
		Type_def		type_def;
		Type_ref		type_ref;
		Type_refs		type_refs;
		Type_variant		variant;

		statement_breakpoint(statement->breakpoint,
				     gen, indent);
		gen_out(gen, "%\t{\n", indent);
		indent++;
		extract = statement->value.extract;
		exp = extract->exp;
		type_refs = exp->type_refs;
		assert(type_refs != (Type_refs)0);
		assert(type_refs_size(type_refs) == 1);
		type_ref = type_refs_fetch(type_refs, 0);
		type_def = type_def_table_lookup(gen->type_def_table,
						 type_ref->name);
		if ((type_def == (Type_def)0) ||
		    (type_def->kind != Type_kind_variant)) {
			/* Error message already generated: */
			break;
		}
		variant = type_def->value.variant;

		gen_out(gen, "%\t%T _temp1_;\n", indent, type_def->name);
		gen_out(gen, "%\tunion %s___variant _temp2_;\n",
			indent, type_def->name);
		gen_out(gen, "\n");

		call_list_gen(statement->calls, indent, gen);
		gen_out(gen, "%\t_temp1_ = ", indent);
		call_gen(exp->call, 0, gen);
		gen_out(gen, ";%\n", statement->comment);

		gen_out(gen, "%\t_temp2_._entire_ = _temp1_->_entire_;\n",
			indent);
		gen_out(gen, "%\tswitch (_temp2_._tag_.%s) {\n",
			indent, variant->tag_field->name);

		tags = extract->tags;
		vec_append(Statement_loop,
			   gen->loop_stack, (Statement_loop)0);
		size = vec_size(Statement_tag, tags);
		indent++;
		size = vec_size(Statement_tag, tags);
		for (index = 0; index < size; index++) {
			tag = vec_fetch(Statement_tag, tags, index);
			tag_names = tag->tag_names;
			if (vec_empty(Str, tag_names)) {
				gen_out(gen, "%\tdefault:%\n",
				        indent, tag->comment);
			} else {
				first_tag_name = (Str)0;
				VEC_LOOP(Str, tag_names, tag_name) {
					if (first_tag_name == (Str)0) {
						first_tag_name = tag_name;
					}
					tag_field_type =
					   variant->tag_field->type_ref->name;
					gen_out(gen,
						"%\tcase %s__item__%s:\n",
						indent, tag_field_type,
						tag_name);
				}
				gen_out(gen, "%\t", indent + 1);
				gen_out(gen, "%s%S = _temp2_.%S;%\n",
					gen->frame, tag->var_name,
					first_tag_name, tag->comment);
			}
			statement_list_gen(tag->statements,
					   gen, indent + 1);
			if (index + 1 != size) {
				gen_out(gen, "%\tbreak;\n", indent + 1);
			}
		}
		indent--;
		(void)vec_pop(Statement_loop, gen->loop_stack);

		gen_out(gen, "%\t}\n", indent);
		indent--;
		gen_out(gen, "%\t}\n", indent);
		break;
	      }
	    case Statement_kind_if:
	      {
		Statement_cond		cond;
		Vec(Statement_cond)	conds;
		int			debug;
		int			else_count;
		int			index;

		debug = gen->debug;
		else_count = 0;
		conds = statement->value.xif;
		VEC_LOOP(Statement_cond, conds, cond) {
			index = vec_index(Statement_cond, conds);
			if (debug) {
				if (index == 0) {
					/* if */
					statement_breakpoint(
						cond->breakpoint,
						gen, indent);
					gen_out(gen, "%\tif (", indent);
					call_gen(cond->exp->call, 0, gen);
					gen_out(gen, ") {%\n",
						cond->comment);
					statement_list_gen(
						cond->statements,
						gen, indent + 1);
				} else if (cond->exp == (Exp)0) {
					/* else */
					gen_out(gen, "%\t} else {%\n",
						indent + index - 1,
						cond->comment);
					statement_list_gen(
						cond->statements,
						gen, indent + index);
				} else {
					/* else_if */
					gen_out(gen, "%\t} else {%\n",
						indent + index - 1,
						cond->comment);
					statement_breakpoint(
						cond->breakpoint,
						gen, indent + index);
					else_count++;
					gen_out(gen, "%\tif (",
						indent + index);
					call_gen(cond->exp->call, 0, gen);
					gen_out(gen, ") {\n");
					statement_list_gen(
						cond->statements,
						gen,
						indent + index + 1);
				}
			} else {
				if (index == 0) {
					call_list_gen(cond->calls,
						      indent, gen);
					gen_out(gen, "%\tif (", indent);
					call_gen(cond->exp->call, 0, gen);
					gen_out(gen, ") {%\n",
						cond->comment);
				} else if (cond->exp == (Exp)0) {
					assert((cond->calls == (Vec(Call))0)
					 || (vec_empty(Call, cond->calls)));
					gen_out(gen, "%\t} else {%\n",
						indent, cond->comment);
				} else {
					assert((cond->calls == (Vec(Call))0)
					 || (vec_empty(Call, cond->calls)));
					gen_out(gen, "%\t} else if (",
						indent);
					call_gen(cond->exp->call, 0, gen);
					gen_out(gen, ") {%\n",
						cond->comment);
				}
				statement_list_gen(cond->statements,
						   gen, indent + 1);
			}
		}
		if (debug) {
			while (--else_count >= 0) {
				gen_out(gen, "%\t}\n",
					indent + else_count + 1);
			}
		}
		gen_out(gen, "%\t}\n", indent);
		break;
	      }
	    case Statement_kind_initialize:
	      {
		Statement_initialize	initialize;

		statement_breakpoint(statement->breakpoint, gen, indent);
		gen_out(gen, "%\t{\n", indent);
		initialize = statement->value.initialize;
		call_list_gen(statement->calls, indent, gen);
		if (initialize->exp != (Exp)0) {
			gen_out(gen, "%\t%s%S = ", indent + 1,
				gen->frame, initialize->var_name);
			call_gen(initialize->exp->call, 0, gen);
			gen_out(gen, ";\n");
		}
		statement_list_gen(initialize->statements, gen, indent + 1);
		gen_out(gen, "%\t}\n", indent);
		break;
	      }
	    case Statement_kind_loop:
	      {
		Statement_loop	loop;

		loop = statement->value.loop;
		statement_breakpoint(statement->breakpoint, gen, indent);
		gen_out(gen, "%\twhile (1) {%\n",
			indent, statement->comment);
		vec_append(Statement_loop, gen->loop_stack, loop);
		statement_list_gen(loop->statements, gen, indent + 1);
		(void)vec_pop(Statement_loop, gen->loop_stack);
		if (loop->need_continue) {
			if (loop->name == (Str)0) {
				gen_out(gen, "%\t  continue___%d:;\n",
					indent, loop->number);
			} else {
				gen_out(gen, "%\t  continue___%s:;\n",
					indent, loop->name);
			}
		}
		gen_out(gen, "%\t}\n", indent);
		if (loop->need_break) {
			if (loop->name == (Str)0) {
				gen_out(gen, "%\t  break___%d:;\n",
					indent - 1, loop->number);
			} else {
				gen_out(gen, "%\t  break___%s:;\n",
					indent - 1, loop->name);
			}
		}
		break;
	      }
	    case Statement_kind_none:
		if (statement->comment == (Str)0) {
			gen_out(gen, "\n");
		} else {
			gen_out(gen, "%\t%\n", indent, statement->comment);
		}
		break;
	    case Statement_kind_return:
	      {
		Exp		exp;
		Vec(Exp)	exps;
		int		index;
		int		size;

		exps = statement->value.exps;
		size = vec_size(Exp, exps);
		statement_breakpoint(statement->breakpoint, gen, indent);
		call_list_gen(statement->calls, indent, gen);
		switch (size) {
		    case 0:
			assert_fail();
			break;
		    case 1:
			exp = vec_fetch(Exp, exps, 0);
			if (gen->debug) {
				gen_out(gen, "%\t%sreturn__value__0 = ",
					indent, gen->frame);
				call_gen(exp->call, 0, gen);
				gen_out(gen, ";%\n", statement->comment);
				gen_out(gen, "%\tgoto epilog;\n", indent);
			} else {
				gen_out(gen, "%\treturn ", indent);
				call_gen(exp->call, 0, gen);
				gen_out(gen, ";%\n", statement->comment);
			}
			break;
		    default:
		      {
			Type_refs	type_refs;

			if (gen->debug) {
				gen_out(gen, "%\t{\n", indent);
				indent++;
				for (index = 0; index < size; index++) {
					exp = vec_fetch(Exp, exps, index);
					gen_out(gen,
						"%\t%sreturn__value__%d = ",
						indent, gen->frame, index);
					call_gen(exp->call, 0, gen);
					gen_out(gen, ";\n");
				}
				gen_out(gen, "%\tgoto epilog;\n", indent);
				indent--;
				gen_out(gen, "%\t}\n", indent);
			} else {
				gen_out(gen, "%\t{\n", indent);
				indent++;
				type_refs = exp_list_types_extract(exps, gen);
				gen_out(gen, "%\t%s x__;\n", indent,
					type_refs_multiple_string(type_refs,
								  gen->heap));
				for (index = 0; index < size; index++) {
					exp = vec_fetch(Exp, exps, index);
					gen_out(gen, "%\tx__.x%d = ",
						indent, index);
					call_gen(exp->call, 0, gen);
					gen_out(gen, ";\n");
				}
				gen_out(gen, "%\treturn x__;\n", indent);
				indent--;
				gen_out(gen, "%\t}\n", indent);
			}
			break;
		      }
		}
		break;
	      }
	    case Statement_kind_switch:
	      {
		Call			call;
		Statement_case		xcase;
		Vec(Statement_case)	cases;
		Exp			exp;
		Vec(Exp)		exps;
		int			index;
		Statement		last_statement;
		int			last_statement_index;
		Vec(Statement)		statements;
		int			size;
		Statement_switch	xswitch;
		Type_def		type_def;
		Type_def_table		type_def_table;
		Type_ref		type_ref;
		Type_refs		type_refs;

		statement_breakpoint(statement->breakpoint, gen, indent);
		xswitch = statement->value.xswitch;
		call = xswitch->exp->call;
		type_refs = call->type_refs;
		if (type_refs_size(type_refs) != 1) {
			msg_out(gen->msg, statement->position,
				"Switch expression returns %d types",
				type_refs_size(type_refs));
			return;
		}
		type_ref = type_refs_fetch(type_refs, 0);
		type_def_table = gen->type_tables->type_def_table;
		type_def = type_def_table_lookup(type_def_table,
						 type_ref->name);
		if ((type_def == (Type_def)0) ||
		    (type_def->kind != Type_kind_enumeration)) {
			/* Error message already generated: */
			break;
		}
		if (gen->debug && type_def->imported) {
			gen_out(gen, "%\tswitch (%s___convert[(int)",
				indent, type_ref->name);
			call_gen(call, 0, gen);
			gen_out(gen, "]) {%\n", statement->comment);
			gen_out(gen, "%\tcase %s__item___none:\n",
				indent + 1, type_ref->name);
			gen_out(gen, "%\trun__time__bad__case();\n",
				indent + 2);
			gen_out(gen, "%\tbreak;\n", indent + 2);
		} else {
			gen_out(gen, "%\tswitch ((%s___type)",
				indent, type_ref->name);
			call_gen(call, 0, gen);
			gen_out(gen, ") {%\n", statement->comment);
		}
		cases = xswitch->cases;
		vec_append(Statement_loop, gen->loop_stack,
			   (Statement_loop)0);
		size = vec_size(Statement_case, cases);
		for (index = 0; index < size; index++) {
			xcase = vec_fetch(Statement_case, cases, index);
			exps = xcase->exps;
			if (vec_empty(Exp, exps)) {
				gen_out(gen, "%\tdefault:%\n", indent + 1,
					xcase->comment);
			} else {
				VEC_LOOP(Exp, exps, exp) {
					Object_ref	object_ref;

					assert(exp->call->type ==
					       Call_type_object);
					object_ref = exp->call->value.object;
					gen_out(gen, "%\tcase %s__item__%s:\n",
						indent + 1,
						object_ref->type_ref->name,
						object_ref->name);
				}
			}
			statements = xcase->statements;
			statement_list_gen(statements, gen, indent + 2);
			if (vec_empty(Statement, statements)) {
				gen_out(gen, "%\tbreak;\n", indent + 2);
			} else {
				last_statement_index = vec_size(Statement,
								statements) - 1;
				last_statement = vec_fetch(Statement,
							  statements,
							  last_statement_index);
				switch (last_statement->kind) {
				    case Statement_kind_break:
				    case Statement_kind_continue:
				    case Statement_kind_return:
					break;
				    default:
					if (index + 1 != size) {
						gen_out(gen, "%\tbreak;\n",
							indent + 2);
					}
				}
			}
		}
		(void)vec_pop(Statement_loop, gen->loop_stack);
		gen_out(gen, "%\t}\n", indent);
		break;
	      }
	    case Statement_kind_until:
	    case Statement_kind_while:
	      {
		Str		comment;
		Statement_loop	loop;

		loop = statement_loop_find((Str)0, gen);
		if (loop == (Statement_loop)0) {
			/* Error already generated */
			break;
		}
		statement_breakpoint(statement->breakpoint, gen, indent);
		comment = statement->comment;
		call_list_gen(statement->calls, indent, gen);
		switch (statement->kind) {
		    case Statement_kind_while:
			gen_out(gen, "%\tif (!(", indent);
			call_gen(statement->value.exp->call, 0, gen);
			gen_out(gen, ")) {%\n", comment);
			break;
		    case Statement_kind_until:
			gen_out(gen, "%\tif (", indent);
			call_gen(statement->value.exp->call, 0, gen);
			gen_out(gen, ") {%\n", comment);
			break;
		}
		if (loop->is_inner_most && !loop->in_switch) {
			gen_out(gen, "%\tbreak;\n", indent + 1);
		} else {
			if (loop->name == (Str)0) {
				gen_out(gen, "%\tgoto break___%d;\n",
					indent + 1, loop->number);
			} else {
				gen_out(gen, "%\tgoto break___%s;\n",
					indent + 1, loop->name);
			}
		}
		gen_out(gen, "%\t}\n", indent);
		break;
	      }
	    default:
		error_abort("Not implemented");
	}
}

/*
 * statement_list_gen(statements, gen, indent)
 *	This routine will generate code for each statement in "statements"
 *	indented by "indent" using "gen" to control the code generation.
 */
void
statement_list_gen(
	Vec(Statement)	statements,
	Gen		gen,
	int		indent)
{
	Statement	statement;

	VEC_LOOP(Statement, statements, statement) {
		statement_gen(statement, gen, indent);
	}
}

/*
 * statement_loop_find(name, gen)
 *	This routine will find the first loop object in "gen"->loop_stack
 *	that matches "name".  (Statement_loop)0 is returned if there is
 *	no match.
 */
Statement_loop
statement_loop_find(
	Str			name,
	Gen			gen)
{
	int			in_switch;
	int			index;
	Statement_loop		inner_loop;
	Statement_loop		loop;
	Vec(Statement_loop)	loop_stack;
	int			size;

	in_switch = 0;
	inner_loop = (Statement_loop)0;
	loop_stack = gen->loop_stack;
	size = vec_size(Statement_loop, loop_stack);
	for (index = size - 1; index >= 0; index--) {
		loop = vec_fetch(Statement_loop, loop_stack, index);
		if (loop == (Statement_loop)0) {
			in_switch = 1;
		} else {
			if (inner_loop == (Statement_loop)0) {
				inner_loop = loop;
			}
			if ((name == (Str)0) ||
			    ((loop->name != (Str)0) &&
			      strequal(name, loop->name))) {
				loop->is_inner_most = (loop == inner_loop);
				loop->in_switch = in_switch;
				return loop;
			}
		}
	}
	return (Statement_loop)0;
}

