/* %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 file contains debugging routines forp printing out statements: */

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

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

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

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

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

#ifndef OUT_EXPORTS_H
#include "out_exports.h"
#endif

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

#ifndef TYPE_DEFS_H
#include "type_defs.h"
#endif

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

LOCAL void	statement_print(Statement, Stdio, int);
LOCAL void	statement_keyword_exp_list_print(Vec(Exp),
						 Stdio, Str, Vec(Type_ref));
LOCAL void	statement_keyword_exp_print(Exp, Stdio, Str, Vec(Type_ref));

/*
 * statement_keyword_exp_list_print(exps, out_file, keyword)
 *	This routine will print "exps" to "out_file" and preceeded by "keyword".
 */
void
statement_keyword_exp_list_print(
	Vec(Exp)	exps,
	Stdio		out_file,
	Str		keyword,
	Vec(Type_ref)	type_refs)
{
	out(out_file, "%s ", keyword);
	exp_list_print(exps, out_file, type_refs);
	out(out_file, "\n");
}

/*
 * statement_keyword_exp_print(exp, out_file, keyword)
 *	This routine will print "exp" to "out_file" and preceeded by "keyword".
 */
LOCAL void
statement_keyword_exp_print(
	Exp		exp,
	Stdio		out_file,
	Str		keyword,
	Vec(Type_ref)	type_refs)
{
	out(out_file, "%s ", keyword);
	exp_print(exp, out_file, type_refs);
	out(out_file, "\n");
}

/*
 * statement_list_print(statements, out_file, indent)
 *	This routine will print "statement" to "out_file" indented by "indent".
 */
void
statement_list_print(
	Vec(Statement)	statements,
	Stdio		out_file,
	int		indent)
{
	Statement	statement;

	VEC_LOOP(Statement, statements, statement) {
		statement_print(statement, out_file, indent);
	}
}

/*
 * statement_print(statement, out_file, indent)
 *	This routine will print "statement" to "out_file" indented by "indent".
 */
void
statement_print(
	Statement	statement,
	Stdio		out_file,
	int		indent)
{
	Heap		heap;
	Type_ref	type_ref;
	Vec(Type_refs)	type_refs;

	heap = heap_standard_create();
	type_refs = vec_create(Type_ref, heap);
	switch (statement->kind) {
	    case Statement_kind_assert:
		out(out_file, "%\tassert ", indent);
		exp_print(statement->value.exp, out_file, type_refs);
		out(out_file, "\n");
		break;
	    case Statement_kind_break:
		out(out_file, "%\t", indent);
		if (statement->value.label->name == (Str)0) {
			out(out_file, "break\n");
		} else {
			out(out_file, "break %s\n",
				statement->value.label->name);
		}
		break;
	    case Statement_kind_continue:
		out(out_file, "%\t", indent);
		if (statement->value.label->name == (Str)0) {
			out(out_file, "continue\n");
		} else {
			out(out_file, "continue %s\n",
				statement->value.label->name);
		}
		break;
	    case Statement_kind_eval:
		if (statement->calls != (Vec(Call))0) {
			call_list_print(statement->calls, out_file,
					1, indent, type_refs);
		} else {
			out(out_file, "%\t", indent);
			exp_print(statement->value.exp, out_file, type_refs);
		}
		VEC_LOOP(Type_ref, type_refs, type_ref) {
			if (type_ref_is_routine(type_ref)) {
				Type_proto	type_proto;
				extern Type_tables type_tables_global;

				type_proto =
					type_proto_from_type_ref(type_ref,
							type_tables_global);
				out(out_file, "%\t%r\n", indent, type_ref);
				type_proto_print(type_proto,
						 indent + 1, out_file);
			}
		}
		out(out_file, "\n");
		break;
	    case Statement_kind_extract:
	      {
		Statement_extract	extract;
		int			index;
		Statement_tag		tag;
		Str			tag_name;
		Vec(Str)		tag_names;
		Vec(Statement_tag)	tags;
		int			size;

		out(out_file, "%\t", indent);
		extract = statement->value.extract;
		statement_keyword_exp_print(extract->exp,
					    out_file, "extract", type_refs);
		tags = extract->tags;
		VEC_LOOP(Statement_tag, tags, tag) {
			if (vec_empty(Exp, tag->tag_names)) {
				out(out_file, "%\tdefault\n",
					indent + 1);
			} else {
				out(out_file, "%\ttag %s",
					indent + 1, tag->var_name);
				if (tag->var_type_ref != (Type_ref)0) {
					out(out_file, ":: %r",
						tag->var_type_ref);
				}
				out(out_file, " := ");
				tag_names = tag->tag_names;
				size = vec_size(Str, tag_names);
				for (index = 0; index < size; index++) {
					tag_name = vec_fetch(Str, tag_names,
							     index);
					out(out_file, "%s", tag_name);
					if (index + 1 < size) {
						out(out_file, ", ");
					}
				}
				out(out_file, "\n");
			}
			statement_list_print(tag->statements,
					     out_file, indent + 2);
		}
		break;
	      }
	    case Statement_kind_if:
	      {
		Statement_cond		cond;
		Vec(Statement_cond)	conds;

		out(out_file, "%\t", indent);
		conds = statement->value.xif;
		VEC_LOOP(Statement_cond, conds, cond) {
			if (vec_index(Statement_cond, conds) == 0) {
				out(out_file, "if ");
				exp_print(cond->exp, out_file, type_refs);
			} else if (cond->exp == (Exp)0) {
				out(out_file, "%\telse", indent);
			} else {
				out(out_file, "%\telse_if ", indent);
				exp_print(cond->exp, out_file, type_refs);
			}
			out(out_file, "\n");
			statement_list_print(cond->statements,
					     out_file, indent + 1);
		}
		break;
	      }
	    case Statement_kind_initialize:
	      {
		Statement_initialize	initialize;

		out(out_file, "%\t", indent);
		initialize = statement->value.initialize;
		out(out_file, "initialize %s", initialize->var_name);
		if (initialize->var_type_ref != (Type_ref)0) {
			out(out_file, ":: %r", initialize->var_type_ref);
		}
		if (initialize->exp != (Exp)0) {
			out(out_file, " := ");
			exp_print(initialize->exp, out_file, type_refs);
		}
		out(out_file, "\n");
		statement_list_print(initialize->statements, out_file, indent + 1);
		break;
	      }
	    case Statement_kind_loop:
		out(out_file, "%\t", indent);
		if (statement->value.loop->name == (Str)0) {
			out(out_file, "loop\n");
		} else {
			out(out_file, "loop %s\n",
				statement->value.loop->name);
		}
		statement_list_print(statement->value.loop->statements,
				     out_file, indent + 1);
		break;
	    case Statement_kind_none:
		out(out_file, "%\t", indent);
		out(out_file, "\n");
		break;
	    case Statement_kind_return:
		out(out_file, "%\t", indent);
		statement_keyword_exp_list_print(statement->value.exps,
					         out_file, "return",
						 type_refs);
		break;
	    case Statement_kind_switch:
	      {
		Statement_case		xcase;
		Vec(Statement_case)	cases;
		Statement_switch	xswitch;

		out(out_file, "%\t", indent);
		xswitch = statement->value.xswitch;
		statement_keyword_exp_print(xswitch->exp,
					    out_file, "switch", type_refs);
		cases = xswitch->cases;
		VEC_LOOP(Statement_case, cases, xcase) {
			if (vec_empty(Exp, xcase->exps)) {
				out(out_file, "%\tdefault\n", indent + 1);
			} else {
				out(out_file, "%\t", indent + 1);
				statement_keyword_exp_list_print(
					xcase->exps, out_file, "case",
					type_refs);
			}
			statement_list_print(xcase->statements, out_file,
					     indent + 2);
		}
		break;
	      }
	    case Statement_kind_until:
		out(out_file, "%\t", indent);
		statement_keyword_exp_print(statement->value.exp,
				            out_file, "until", type_refs);
		break;
	    case Statement_kind_while:
		out(out_file, "%\t", indent);
		statement_keyword_exp_print(statement->value.exp,
				            out_file, "while", type_refs);
		break;
	    case Statement_kind_yield:
		out(out_file, "%\t", indent);
		statement_keyword_exp_list_print(statement->value.exps,
					         out_file, "yield", type_refs);
		break;
	    default:
		error_abort("Unknown statement");
	}
}

#ifndef lint
/*
 * statement_show(statement)
 *	This routine is used for debugging and prints "statement" to standard
 *	output.
 */
void
statement_show(
	Statement	statement)
{
	statement_print(statement, stdout, 0);
	(void)fflush(stdout);
}
#endif /* lint */
