/* %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 code for printing an exp object: */

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

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

#ifndef EXP_DEFS_H
#include "exp_defs.h"
#endif

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

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

#ifndef TYPE_EXPORTS_H
#include "type_exports.h"
#endif

#ifndef VECTOR_EXPORTS_H
#include "vector_exports.h"
#endif

LOCAL void	exp_access_print(Exp_access, Stdio, Str, Str, Vec(Type_ref));
LOCAL void	exp_assign_print(Exp_binary, Stdio, Str, Vec(Type_ref));
LOCAL void	exp_binary_print(Exp_binary, Stdio, Str, Vec(Type_ref));
LOCAL void	exp_post_unary_print(Exp, Stdio, Str, Vec(Type_ref));
LOCAL void	exp_pre_unary_print(Exp, Stdio, Str, Vec(Type_ref));

/*
 * exp_access_print(access, out_file, left_label, right_label, type_refs)
 *	This routine will print out "access" to "out_file" using "left_label"
 *	and "right_label".
 */
void
exp_access_print(
	Exp_access	access,
	Stdio		out_file,
	Str		left_label,
	Str		right_label,
	Vec(Type_ref)	type_refs)
{
	exp_print(access->exp, out_file, type_refs);
	out(out_file, "%s", left_label);
	exp_list_print(access->list, out_file, type_refs);
	out(out_file, "%s", right_label);
}

/*
 * exp_assign_print(binary, out_file, operator, type_refs)
 *	This will print out "binary" to "out_file" labeled with "operator".
 */
void
exp_assign_print(
	Exp_binary	binary,
	Stdio		out_file,
	Str		operator,
	Vec(Type_ref)	type_refs)
{
	exp_print(binary->left, out_file, type_refs);
	out(out_file, " %s ", operator);
	exp_print(binary->right, out_file, type_refs);
}

/*
 * exp_binary_print(binary, out_file, operator, type_refs)
 *	This will print out "binary" to "out_file" labeled with "operator".
 */
void
exp_binary_print(
	Exp_binary	binary,
	Stdio		out_file,
	Str		operator,
	Vec(Type_ref)	type_refs)
{
	out(out_file, "(");
	exp_print(binary->left, out_file, type_refs);
	out(out_file, operator);
	exp_print(binary->right, out_file, type_refs);
	out(out_file, ")");
}

/*
 * exp_list_print(list, out_file, type_refs)
 *	This routine will print "list" to "out_file".
 */
void
exp_list_print(
	Vec(Exp)	list,
	Stdio		out_file,
	Vec(Type_ref)	type_refs)
{
	int		index;
	Exp		exp;
	int		size;

	size = vec_size(Exp, list);
	for (index = 0; index < size - 1; index++) {
		exp = vec_fetch(Exp, list, index);
		exp_print(exp, out_file, type_refs);
		out(out_file, ", ");
	}
	if (size > 0) {
		exp = vec_fetch(Exp, list, size - 1);
		exp_print(exp, out_file, type_refs);
	}
}

/*
 * exp_post_unary_print(exp, out_file, operator, type_refs)
 *	This will print out "exp" to "out_file" and labeled with "operator".
 */
void
exp_post_unary_print(
	Exp		exp,
	Stdio		out_file,
	Str		operator,
	Vec(Type_ref)	type_refs)
{
	out(out_file, "(");
	exp_print(exp, out_file, type_refs);
	out(out_file, "%s)", operator);
}

/*
 * exp_pre_unary_print(exp, out_file, operator, type_refs)
 *	This will print out "exp" to "out_file" and labeled with "operator".
 */
void
exp_pre_unary_print(
	Exp		exp,
	Stdio		out_file,
	Str		operator,
	Vec(Type_ref)	type_refs)
{
	out(out_file, "(%s", operator);
	exp_print(exp, out_file, type_refs);
	out(out_file, ")");
}

/*
 * exp_print(exp, out_file, type_refs)
 *	This routine will print "exp" to "out_file" indented by "indent".
 */
void
exp_print(
	Exp		exp,
	Stdio		out_file,
	Vec(Type_ref)	type_refs)
{
	if (exp->call != (Call)0) {
		call_print(exp->call, out_file, 0, type_refs);
		return;
	}
	switch (exp->op) {
	    case Exp_op_add:
		exp_binary_print(exp->operands.binary,
				 out_file, "+", type_refs);
		break;
	    case Exp_op_add_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":+=", type_refs);
		break;
	    case Exp_op_add_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::+=", type_refs);
		break;
	    case Exp_op_and:
		exp_binary_print(exp->operands.binary,
				 out_file, "&", type_refs);
		break;
	    case Exp_op_and_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":&=", type_refs);
		break;
	    case Exp_op_and_if:
		exp_binary_print(exp->operands.binary,
				 out_file, "&&", type_refs);
		break;
	    case Exp_op_and_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::&=", type_refs);
		break;
	    case Exp_op_arithmetic_if:
		out(out_file, "(");
		exp_print(exp->operands.trinary->first, out_file, type_refs);
		out(out_file, "?");
		exp_print(exp->operands.trinary->second, out_file, type_refs);
		out(out_file, ":");
		exp_print(exp->operands.trinary->third, out_file, type_refs);
		out(out_file, ")");
		break;
	    case Exp_op_array:
		exp_access_print(exp->operands.access,
				 out_file, "[", "]", type_refs);
		break;
	    case Exp_op_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":=", type_refs);
		break;
	    case Exp_op_at:
	      {
		Exp_type	exp_type;

		exp_type = exp->operands.type;
		out(out_file, "%s@", exp_type->name);
		type_ref_print(exp_type->type_ref, out_file);
		break;
	      }
	    case Exp_op_call:
		exp_access_print(exp->operands.access,
				 out_file, "(", ")", type_refs);
		break;
	    case Exp_op_convert:
		exp_pre_unary_print(exp->operands.unary,
				    out_file, "+", type_refs);
		break;
	    case Exp_op_define:
		out(out_file, "%s:: ", exp->operands.define->name);
		type_ref_print(exp->operands.define->type_ref, out_file);
		break;
	    case Exp_op_define_assign:
		exp_print(exp->operands.binary->left, out_file, type_refs);
		out(out_file, " :@= ");
		exp_print(exp->operands.binary->right, out_file, type_refs);
		break;
	    case Exp_op_divide:
		exp_binary_print(exp->operands.binary,
				 out_file, "/", type_refs);
		break;
	    case Exp_op_divide_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":/=", type_refs);
		break;
	    case Exp_op_divide_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::/=", type_refs);
		break;
	    case Exp_op_dot:
		exp_print(exp->operands.field->exp, out_file, type_refs);
		out(out_file, ".%s", exp->operands.field->name);
		break;
	    case Exp_op_equal:
		exp_binary_print(exp->operands.binary,
				 out_file, "=", type_refs);
		break;
	    case Exp_op_error:
		out(out_file, exp->operands.error);
		break;
	    case Exp_op_greater_than:
		exp_binary_print(exp->operands.binary,
				out_file, ">", type_refs);
		break;
	    case Exp_op_greater_than_or_equal:
		exp_binary_print(exp->operands.binary,
				 out_file, ">=", type_refs);
		break;
	    case Exp_op_identical:
		exp_binary_print(exp->operands.binary,
				 out_file, "==", type_refs);
		break;
	    case Exp_op_integer:
		out(out_file, "%d", exp->operands.integer);
		break;
	    case Exp_op_left_shift:
		exp_binary_print(exp->operands.binary,
				 out_file, "<<", type_refs);
		break;
	    case Exp_op_left_shift_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":<<=", type_refs);
		break;
	    case Exp_op_left_shift_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::<<=", type_refs);
		break;
	    case Exp_op_less_than:
		exp_binary_print(exp->operands.binary,
				 out_file, "<", type_refs);
		break;
	    case Exp_op_less_than_or_equal:
		exp_binary_print(exp->operands.binary,
				 out_file, "<=", type_refs);
		break;
	    case Exp_op_list:
		exp_list_print(exp->operands.list, out_file, type_refs);
		break;
	    case Exp_op_minus:
		exp_pre_unary_print(exp->operands.unary,
				    out_file, "-", type_refs);
		break;
	    case Exp_op_multiply:
		exp_binary_print(exp->operands.binary,
				 out_file, "*", type_refs);
		break;
	    case Exp_op_multiply_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":*=", type_refs);
		break;
	    case Exp_op_multiply_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::*=", type_refs);
		break;
	    case Exp_op_not:
		exp_pre_unary_print(exp->operands.unary,
				    out_file, "!", type_refs);
		break;
	    case Exp_op_not_equal:
		exp_binary_print(exp->operands.binary,
				 out_file, "!=", type_refs);
		break;
	    case Exp_op_not_identical:
		exp_binary_print(exp->operands.binary,
				 out_file, "!==", type_refs);
		break;
	    case Exp_op_or:
		exp_binary_print(exp->operands.binary,
				 out_file, "|", type_refs);
		break;
	    case Exp_op_or_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":|=", type_refs);
		break;
	    case Exp_op_or_if:
		exp_binary_print(exp->operands.binary,
				 out_file, "||", type_refs);
		break;
	    case Exp_op_or_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::|=", type_refs);
		break;
	    case Exp_op_post_decrement:
		exp_post_unary_print(exp->operands.unary,
				     out_file, "--", type_refs);
		break;
	    case Exp_op_post_increment:
		exp_post_unary_print(exp->operands.unary,
				     out_file, "++", type_refs);
		break;
	    case Exp_op_power:
		exp_binary_print(exp->operands.binary,
				 out_file, "**", type_refs);
		break;
	    case Exp_op_power_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":**=", type_refs);
		break;
	    case Exp_op_power_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::**=", type_refs);
		break;
	    case Exp_op_pre_decrement:
		exp_pre_unary_print(exp->operands.unary,
				    out_file, "--", type_refs);
		break;
	    case Exp_op_pre_increment:
		exp_pre_unary_print(exp->operands.unary,
				    out_file, "++", type_refs);
		break;
	    case Exp_op_remainder:
		exp_binary_print(exp->operands.binary,
				 out_file, "%", type_refs);
		break;
	    case Exp_op_remainder_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":%=", type_refs);
		break;
	    case Exp_op_remainder_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::%=", type_refs);
		break;
	    case Exp_op_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::=", type_refs);
		break;
	    case Exp_op_right_shift:
		exp_binary_print(exp->operands.binary,
				 out_file, ">>", type_refs);
		break;
	    case Exp_op_right_shift_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":>>=", type_refs);
		break;
	    case Exp_op_right_shift_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::>>=", type_refs);
		break;
	    case Exp_op_string:
		out_quoted_string(out_file, exp->operands.string, '"', 0);
		break;
	    case Exp_op_subtract:
		exp_binary_print(exp->operands.binary,
				 out_file, "-", type_refs);
		break;
	    case Exp_op_subtract_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":-=", type_refs);
		break;
	    case Exp_op_subtract_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::-=", type_refs);
		break;
	    case Exp_op_symbol:
		out(out_file, exp->operands.symbol);
		break;
	    case Exp_op_text:
		out_quoted_string(out_file, exp->operands.text, '\'', 0);
		break;
	    case Exp_op_twiddle:
	      {
		Exp_object	exp_object;

		exp_object = exp->operands.object;
		out(out_file, "(");
		exp_print(exp_object->object, out_file, type_refs);
		out(out_file, "~%s(", exp_object->name);
		exp_list_print(exp_object->list, out_file, type_refs);
		out(out_file, "))");
		break;
	      }
	    case Exp_op_xor:
		exp_binary_print(exp->operands.binary,
				 out_file, "^", type_refs);
		break;
	    case Exp_op_xor_assign:
		exp_assign_print(exp->operands.binary,
				 out_file, ":^=", type_refs);
		break;
	    case Exp_op_xor_result:
		exp_binary_print(exp->operands.binary,
				 out_file, "::^=", type_refs);
		break;
	    default:
		error_abort("Unknown expression");
	}
}

#ifndef lint
/*
 * exp_show(exp)
 *	This routine is used for debugging and displays "exp" to standard out.
 */
void
exp_show(
	Exp		exp)
{
	Heap		heap;
	Vec(Type_ref)	type_refs;

	heap = (Heap)heap_standard_create();
	type_refs = vec_create(Type_ref, heap);
	exp_print(exp, stdout, type_refs);
	(void)fprintf(stdout, "\n");
	(void)fflush(stdout);
}
#endif /* lint */
