/* %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 set of routines converts an expression tree into a sequence of
 * calls and assignments.
 */

#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 HEAP_EXPORTS_H
#include "heap_exports.h"
#endif

#ifndef LIBC_EXPORTS_H
#include "libc_exports.h"
#endif

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

#ifndef MSG_EXPORTS_H
#include "msg_exports.h"
#endif

#ifndef NEED_EXPORTS_H
#include "need_exports.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 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_CTYPE_H
#include "unix_ctype.h"
#endif

#ifndef UNIX_UNISTD_H
#include "unix_unistd.h"
#endif

#ifndef VAR_DEFS_H
#include "var_defs.h"
#endif

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

LOCAL Call	call_assign_exp_convert(Convert, Exp, Exp,
					Str, int, int, Vec(Call));
LOCAL Vec(Call) call_assign_op_convert(Convert, Exp, Str);
LOCAL Call	call_binary_convert(Convert, Exp,
				    Type_refs, Str, Vec(Call));
LOCAL Call	call_exp_convert_dot(Convert, Exp, Vec(Call));
LOCAL Vec(Call) call_list_cast(Vec(Call), Type_refs, Convert);
LOCAL Call	call_not_relation_convert(Convert, Exp,
					  Type_refs, Str, Str, Vec(Call));
LOCAL Call	call_static_invoke(Routine_ref, Vec(Call), int, Convert);
LOCAL Call	call_static_lookup(Str, Type_ref, int, int, int, int, Convert);
LOCAL Call	call_relation_convert(Convert, Exp,
				      Type_refs, Str, Vec(Call));
LOCAL Call	call_result_convert(Convert, Exp,
				    Type_refs, Str, Vec(Call));
LOCAL Type_ref	call_type_ref_get(Call, int, Convert);
LOCAL Call	call_unary_convert(Convert, Exp,
				   Type_refs, Str, Vec(Call));
LOCAL Vec(Call) exps_to_calls(Vec(Exp), Type_refs, int, Vec(Call), Convert);
LOCAL Routine_ref routine_ref_static_lookup(Str, Type_ref, int,
					     int, int, int, Convert);



/*
 * call_assign_convert(convert, exp)
 *	This routine will return the sequence of calls associated with
 *	"exp", where "exp" is an assignment or routine invocation.
 */
Vec(Call)
call_assign_convert(
	Convert		convert,
	Exp		exp)
{
	switch (exp->op) {
	    case Exp_op_add:
		goto error;
	    case Exp_op_add_assign:
		return call_assign_op_convert(convert, exp, "add");
	    case Exp_op_add_result:
		goto error;
	    case Exp_op_and:
		goto error;
	    case Exp_op_and_assign:
		return call_assign_op_convert(convert, exp, "and");
	    case Exp_op_and_if:
		goto error;
	    case Exp_op_and_result:
		goto error;
	    case Exp_op_arithmetic_if:
		goto error;
	    case Exp_op_at:
		goto error;
	    case Exp_op_array:
		goto error;
	    case Exp_op_assign:
		return call_assign_op_convert(convert, exp, (Str)0);
	    case Exp_op_call:
	      {
		Call		call;
		Vec(Call)	sequence;

		sequence = vec_create(Call, convert->heap);
		call = call_exp_convert(convert, exp,
					 convert->type_refs_empty, sequence);
		vec_append(Call, sequence, call);
		return sequence;
	      }
	    case Exp_op_define:
		goto error;
	    case Exp_op_define_assign:
		return call_assign_op_convert(convert, exp, "define");
	    case Exp_op_define_result:
		goto error;
	    case Exp_op_divide:
		goto error;
	    case Exp_op_divide_assign:
		return call_assign_op_convert(convert, exp, "divide");
	    case Exp_op_divide_result:
		goto error;
	    case Exp_op_dot:
		goto error;
	    case Exp_op_equal:
		goto error;
	    case Exp_op_error:
		goto error;
	    case Exp_op_greater_than:
		goto error;
	    case Exp_op_greater_than_or_equal:
		goto error;
	    case Exp_op_identical:
		goto error;
	    case Exp_op_integer:
		goto error;
	    case Exp_op_left_shift:
		goto error;
	    case Exp_op_left_shift_assign:
		return call_assign_op_convert(convert, exp, "left_shift");
	    case Exp_op_left_shift_result:
		goto error;
	    case Exp_op_less_than:
		goto error;
	    case Exp_op_less_than_or_equal:
		goto error;
	    case Exp_op_list:
		goto error;
	    case Exp_op_minus:
		goto error;
	    case Exp_op_multiply:
		goto error;
	    case Exp_op_multiply_assign:
		return call_assign_op_convert(convert, exp, "multiply");
	    case Exp_op_multiply_result:
		goto error;
	    case Exp_op_not:
		goto error;
	    case Exp_op_not_equal:
		goto error;
	    case Exp_op_not_identical:
		goto error;
	    case Exp_op_or:
		goto error;
	    case Exp_op_or_assign:
		return call_assign_op_convert(convert, exp, "or");
	    case Exp_op_or_if:
		goto error;
	    case Exp_op_or_result:
		goto error;
	    case Exp_op_post_decrement:
		goto error;
	    case Exp_op_post_increment:
		goto error;
	    case Exp_op_power:
		goto error;
	    case Exp_op_power_assign:
		return call_assign_op_convert(convert, exp, "power");
	    case Exp_op_power_result:
		goto error;
	    case Exp_op_pre_decrement:
		goto error;
	    case Exp_op_pre_increment:
		goto error;
	    case Exp_op_remainder:
		goto error;
	    case Exp_op_remainder_assign:
		return call_assign_op_convert(convert, exp, "remainder");
	    case Exp_op_remainder_result:
		goto error;
	    case Exp_op_result:
		goto error;
	    case Exp_op_right_shift:
		goto error;
	    case Exp_op_right_shift_assign:
		return call_assign_op_convert(convert, exp, "right_shift");
	    case Exp_op_right_shift_result:
		goto error;
	    case Exp_op_string:
		goto error;
	    case Exp_op_subtract:
		goto error;
	    case Exp_op_subtract_assign:
		return call_assign_op_convert(convert, exp, "subtract");
	    case Exp_op_subtract_result:
		goto error;
	    case Exp_op_symbol:
		goto error;
	    case Exp_op_text:
		goto error;
	    case Exp_op_twiddle:
	      {

		Call		call;
		Vec(Call)	sequence;

		sequence = vec_create(Call, convert->heap);
		call = call_exp_convert(convert, exp,
					 convert->type_refs_empty, sequence);
		vec_append(Call, sequence, call);
		return sequence;
	      }
	    case Exp_op_xor:
		goto error;
	    case Exp_op_xor_assign:
		return call_assign_op_convert(convert, exp, "xor");
	    case Exp_op_xor_result:
		goto error;
	    default:
		assert_fail();
	}	
    error:
	msg_out(convert->msg, exp->position,
		     "Missing assignment or call statement");
	return vec_create(Call, convert->heap);
}

/*
 * call_assign_op_convert(convert, exp, op_name)
 *	This routine will convert "exp" into a sequence of assignments and
 *	routine invocations and store the result in "convert"->sequence.
 */
Vec(Call)
call_assign_op_convert(
	Convert		convert,
	Exp		exp,
	Str		op_name)
{
	Heap		heap;
	Exp_binary	binary;
	Call		call;
	int		is_define;
	Exp		left_exp;
	Vec(Exp)	left_exps;
	int		left_exps_size;
	Exp		right_exp;
	Vec(Exp)	right_exps;
	int		right_exps_size;
	Vec(Call)	sequence;

	heap = convert->heap;
	binary = exp->operands.binary;
	left_exp = binary->left;
	assert(left_exp->op == Exp_op_list);
	left_exps = left_exp->operands.list;
	left_exps_size = vec_size(Exp, left_exps);
	right_exp = binary->right;
	assert(right_exp->op == Exp_op_list);
	right_exps = right_exp->operands.list;
	right_exps_size = vec_size(Exp, right_exps);
	sequence = vec_create(Call, heap);
	is_define = 0;
	if (exp->op == Exp_op_define_assign) {
		is_define = 1;
		op_name = (Str)0;
	}

	/* Deal with simple assignment first: */
	if ((left_exps_size == 1) && (right_exps_size == 1)) {
		left_exp = vec_fetch(Exp, left_exps, 0);
		right_exp = vec_fetch(Exp, right_exps, 0);
		call = call_assign_exp_convert(convert, left_exp, right_exp,
					       op_name, 1, is_define, sequence);
		vec_append(Call, sequence, call);
		return sequence;
	}

	/* Check for empty lists: */
	if (left_exps_size == 0) {
		msg_out(convert->msg, exp->position,
			     "Empty left side of assignment");
		return sequence;
	}
	if (right_exps_size == 0) {
		msg_out(convert->msg, exp->position,
			     "Empty right side of assignment");
		return sequence;
	}

	/* Try multiple assignement next: */
	if (left_exps_size == right_exps_size) {
		Vec(Call)	calls;
		int		index;

		calls = vec_create(Exp, heap);
		for (index = 0; index < left_exps_size; index++) {
			left_exp = vec_fetch(Exp, left_exps, index);
			right_exp = vec_fetch(Exp, right_exps, index);
			(void)call_assign_exp_convert(convert, left_exp,
						      right_exp, op_name,
						      0, is_define, sequence);
			call = vec_pop(Call, sequence);
			vec_append(Call, calls, call);
		}
		vec_vec_append(Call, sequence, calls);
		return sequence;
	}

	/* Try multiple assignment next: */
	if ((left_exps_size > 1) && (right_exps_size == 1)) {
		Call		var;
		Vec(Call)	vars;
		Call		call;
		Vec(Call)	calls;
		Vec(Call)	finals;
		int		index;

		calls = vec_create(Call, heap);
		finals = vec_create(Call, heap);
		vars = vec_create(Call, heap);
		for (index = 0; index < left_exps_size; index++) {
			left_exp = vec_fetch(Exp, left_exps, index);
			var = call_assign_exp_convert(convert, left_exp,
						      (Exp)0, op_name,
						      0, is_define,calls);
			vec_append(Call, vars, var);
			call = vec_pop(Call, calls);
			vec_append(Call, finals, call);
		}

		/* Get the right expression: */
		right_exp = vec_fetch(Exp, right_exps, 0);
		call = call_exp_convert(convert, right_exp,
					convert->type_refs_empty, sequence);
		if (call->type != Call_type_multi) {
			/*XXX: Serious kludge here! */
			call = call_multi_create(convert,
						 call, call->type_refs);
		}
		call->value.multi->vars = vars;

		/* Construct the final sequence: */
		vec_append(Call, sequence, call);
		vec_vec_append(Call, sequence, calls);
		vec_vec_append(Call, sequence, finals);
		return sequence;
	}
	assert_fail();
	return sequence;
}


/*
 * call_binary_convert(convert, exp, type_refs, op_name, sequence)
 *	This will return a call expression converted from "exp" and "op_name".
 */
Call
call_binary_convert(
	Convert		convert,
	Exp		exp,
	Type_refs	type_refs,
	Str		op_name,
	Vec(Call)	sequence)
{
	Vec(Call)	actuals;
	Exp_binary	binary;
	Call		call;
	Call		left_call;
	Type_ref	left_type_ref;
	int		position;
	Call		right_call;
	Type_ref	right_type_ref;

	position = exp->position;
	binary = exp->operands.binary;
	left_call = call_exp_convert(convert, binary->left,
				      type_refs, sequence);
	left_type_ref = call_type_ref_get(left_call, position, convert);
	right_call = call_exp_convert(convert, binary->right,
				       left_call->type_refs, sequence);
	right_type_ref = call_type_ref_get(right_call, position, convert);
	if ((left_type_ref == (Type_ref)0) || (right_type_ref == (Type_ref)0)) {
		return left_call;
	}

	if (!type_ref_equal(left_type_ref, right_type_ref)) {
		msg_out(convert->msg, exp->position,
		    "Left type for '%s' operator is %r and right type is %r",
		    op_name, left_type_ref, right_type_ref);
		return left_call;
	}

	call = call_static_lookup(op_name, left_type_ref,
				  2, 1, 0, exp->position, convert);
	if (call == (Call)0) {
		msg_out(convert->msg, exp->position,
			     "No %s operation", op_name);
		return left_call;
	}
	actuals = vec_create(Call, convert->heap);
	vec_append(Call, actuals, left_call);
	vec_append(Call, actuals, right_call);
	call = call_invoke_create(convert, call, actuals);
	return call;
}

/*
 * call_exp_constant(convert, exp, type_refs, sequence)
 *	This routine will treat "exp" as a constant and return the
 *	appropriate call node associated with it.
 */
Call
call_exp_constant(
	Convert		convert,
	Exp		exp,
	Type_refs	type_refs,
	Vec(Call)	sequence)
{
	Call		call;
	Str		name;
	Need_table	need_table;
	Object_ref	object_ref;
	Routine_ref	routine_ref;
	Type_ref	type_ref;
	Var		var;

	call = (Call)0;
	if ((exp->op == Exp_op_symbol) && (type_refs_size(type_refs) == 1)) {
		name = exp->operands.symbol;
		type_ref = type_refs_fetch(type_refs, 0);
			
		/* This code was snarched from call_exp_convert() in symbol: */
		object_ref = object_ref_lookup(convert->object_table,
					       name, type_ref);
		if (object_ref != (Object_ref)0) {
			object_ref->used = 1;
			need_table = convert->routine->need_table;
			if (type_ref_contains_parameter(type_ref) ||
			    type_ref_is_parameterized(type_ref)) {
				need_table_object_insert(need_table, name,
							 type_ref, type_ref);
			}
			exp->type_refs = type_ref->type_refs;
			call = call_object_create(convert, object_ref,
						  exp->position);
		}
	}
	if (call == (Call)0) {
		msg_out(convert->msg, exp->position, "Constant not found");
		call = call_error_create(convert, exp->position,
					 "Undefined symbol");
	}
	exp->type_refs = call->type_refs;
	return call;
}

/*
 * call_exp_convert(convert, exp, type_refs, sequence)
 *	This routine will convert "exp" into associated Call object.
 *	"type_refs" are the types to assign to "exp" if exp is a constant.
 *	"type_refs" will be empty (size 0) if there is no appropriate guess.
 *	"convert" contains needed conversion tables.
 */
Call
call_exp_convert(
	Convert		convert,
	Exp		exp,
	Type_refs	type_refs,
	Vec(Call)	sequence)
{
	Call		call;

	switch (exp->op) {
	    case Exp_op_add:
		call = call_binary_convert(convert, exp, type_refs,
					   "add", sequence);
		break;
	    case Exp_op_add_assign:
		goto error;
	    case Exp_op_add_result:
		call = call_result_convert(convert, exp, type_refs,
					   "add", sequence);
		break;
	    case Exp_op_and:
		call = call_binary_convert(convert, exp, type_refs,
					   "and", sequence);
		break;
	    case Exp_op_and_assign:
		goto error;
	    case Exp_op_and_if:
	      {
		Call		left_call;
		Call		right_call;
		Exp_binary	binary;

		binary = exp->operands.binary;
		left_call = call_exp_convert(convert, binary->left,
					convert->type_ref_logical->type_refs,
					sequence);
		right_call = call_exp_convert(convert, binary->right,
					convert->type_ref_logical->type_refs,
					sequence);
		call = call_binary_create(convert, Call_type_and_if,
					  left_call, right_call);
		break;
	      }
	    case Exp_op_and_result:
		call = call_result_convert(convert, exp, type_refs,
					   "and", sequence);
		break;
	    case Exp_op_arithmetic_if:
	      {
		Call		cond_call;
		Call		false_call;
		Type_ref	false_type_ref;
		int		position;
		Call		true_call;
		Type_ref	true_type_ref;
		Exp_trinary	trinary;

		position = exp->position;
		trinary = exp->operands.trinary;
		cond_call = call_exp_convert(convert, trinary->first,
					convert->type_ref_logical->type_refs,
					sequence);
		true_call = call_exp_convert(convert, trinary->second,
					     type_refs, sequence);
		false_call = call_exp_convert(convert, trinary->third,
					      true_call->type_refs, sequence);
		true_type_ref = call_type_ref_get(true_call, position, convert);
		false_type_ref = call_type_ref_get(false_call,
						   position, convert);
		if ((true_type_ref == (Type_ref)0) ||
		    (false_type_ref == (Type_ref)0)) {
			call = true_call;
			break;
		}
		if (!type_ref_equal(true_type_ref, false_type_ref)) {
			call = call_error_create(convert, position,
						 "In <bool> ? <exp1> : <exp2>, "
						 "types of <exp1> and <exp2> "
						 "do not match!");
			break;
		}
		call = call_if_create(convert, cond_call,
				      true_call, false_call);
		break;
	      }
	    case Exp_op_at:
	      {
		Exp_type	exp_type;
		Routine_ref	routine_ref;

		exp_type = exp->operands.type;
		routine_ref = routine_ref_static_lookup(exp_type->name,
							exp_type->type_ref,
							-1, -1, -1,
							exp->position, convert);
		if (routine_ref == (Routine_ref)0) {
			msg_out(convert->msg, exp->position,
				"%s@%r does not exist",
				exp_type->name, exp_type->type_ref);
			call = call_error_create(convert, exp->position,
						 "Undefined routine");
			break;
		}
		type_proto_needed(routine_ref->type_proto,
				  convert->type_tables);
		call = call_routine_create(convert, routine_ref, exp->position);
		break;
	      }
	    case Exp_op_array:
	      {
		Vec(Call)	actuals;
		Exp_access	access;
		Vec(Exp)	exps;
		Str		fetch_name;
		Routine_ref	fetch_ref;
		Heap		heap;
		Call		left_call;
		Type_ref	left_type_ref;
		int		position;
		int		size;

		heap = convert->heap;
		position = exp->position;

		/* Compute the call to the left of the '[': */
		access = exp->operands.access;
		left_call = call_exp_convert(convert, access->exp,
					     convert->type_refs_empty,
					     sequence);
		left_type_ref = call_type_ref_get(left_call, position, convert);
		if (left_type_ref == (Type_ref)0) {
			call = left_call;
			break;
		}

		/* Compute the fetch# routine name: */
		exps = access->list;
		size = vec_size(Exp, exps);
		fetch_name = strprintf(heap, "fetch%d", size);

		/* Lookup fetch#@left_type_ref(): */
		fetch_ref = routine_ref_static_lookup(fetch_name, left_type_ref,
						      size + 1, 1, 0,
						      position, convert);
		if (fetch_ref == (Routine_ref)0) {
			call = left_call;
			break;
		}

		/* Compute expressions in [...]: */
		actuals = exps_to_calls(exps, fetch_ref->type_proto->takes, 1,
					sequence, convert);
		vec_prepend(Call, actuals, left_call);

		call = call_static_invoke(fetch_ref, actuals,
					  position, convert);
		break;
	      }
	    case Exp_op_assign:
		goto error;
	    case Exp_op_define_result:
		goto error;
	    case Exp_op_call:
	      {
		Exp_access	access;
		Vec(Call)	actuals;
		Call		left_call;
		Type_proto	left_type_proto;
		Type_ref	left_type_ref;
		int		position;
		Call		temp_call;
		Type_tables	type_tables;

		type_tables = convert->type_tables;
		position = exp->position;

		/* Compute the call to the left of the '(': */
		access = exp->operands.access;
		left_call = call_exp_convert(convert, access->exp,
					     convert->type_refs_empty,
					     sequence);
		left_type_ref = call_type_ref_get(left_call, position, convert);
		if (left_type_ref == (Type_ref)0) {
			call = left_call;
			break;
		}
		left_type_proto = type_proto_from_type_ref(left_type_ref,
							   type_tables);
		exp->type_refs = left_type_proto->returns;

		/* Compute the calls to the right of the '(': */
		actuals = exps_to_calls(access->list, left_type_proto->takes, 0,

					sequence, convert);
		/* This is a total kludge! */
		if (left_call->type == Call_type_routine) {
			Routine_ref	routine_ref;

			/* Get the parameterized version: */
			routine_ref = left_call->value.routine->routine_ref;
			left_type_proto =
			    routine_ref->routine_entry->type_proto;
		}

		if (type_proto_is_parameterized(left_type_proto)) {
			actuals = call_list_cast(actuals,
						 left_type_proto->takes,
						 convert);
		}

		/*
		 * Complex expressions to the left of the '(' must be
 		 * stored in a temporary variable.  (I think.)
		 */
		switch (left_call->type) {
		    case Call_type_var:
		    case Call_type_routine:
			call = call_invoke_create(convert, left_call, actuals);
			break;	
		    default:
			/* Create a temporary: */
			temp_call = call_temp_create(convert, exp->position,
						     left_type_ref);
			call = call_assign_create(convert,
						  temp_call, left_call);
			vec_append(Call, sequence, call);
			call = call_invoke_create(convert, temp_call, actuals);
		}
		break;
	      }
	    case Exp_op_convert:
	      {
#ifdef INCOMPLETE
		Heap		heap;

		heap = convert->heap;
		sub_call = call_exp_convert(convert, exp->operands.unary,
					    convert->type_refs_empty,
					    sequence);
		sub_call_type_refs = call->type_refs;
		if (type_refs_size(sub_call_type_refs != 1) {
			msg_out(convert->msg, exp->position,
				"No return value for unary `+'");
			call = sub_call;
			break;
		}
		sub_call_type_ref = type_refs_fetch(sub_type_refs, 0);
		takes = vec_create(Call, heap);
		vec_append(Call, takes, sub_call);
		returns = ?->type_refs;
		call = call_invoke_create(convert, call, takes, returns);
#endif /* INCOMPLETE */
		assert_fail();
		break;
	      }
	    case Exp_op_divide:
		call = call_binary_convert(convert, exp, type_refs,
					   "divide", sequence);
		break;
	    case Exp_op_divide_assign:
		goto error;
	    case Exp_op_divide_result:
		call = call_result_convert(convert, exp, type_refs,
					   "divide", sequence);
		break;
	    case Exp_op_dot:
		call = call_exp_convert_dot(convert, exp, sequence);
		break;
	    case Exp_op_equal:
		call = call_relation_convert(convert, exp, type_refs,
					     "equal", sequence);
		break;
	    case Exp_op_error:
		call = call_error_create(convert, exp->position, "Error");
		break;
	    case Exp_op_greater_than:
		call = call_relation_convert(convert, exp, type_refs,
					     "greater_than", sequence);
		break;
	    case Exp_op_greater_than_or_equal:
		call = call_not_relation_convert(convert, exp, type_refs,
						 "greater_equal", "less_than",
						 sequence);
		break;
	    case Exp_op_identical:
		call = call_relation_convert(convert, exp, type_refs,
					     "identical", sequence);
		break;
	    case Exp_op_integer:
		call = call_integer_create(convert, exp->operands.integer,
					   exp->position);
		break;
	    case Exp_op_left_shift:
		call = call_binary_convert(convert, exp, type_refs,
					   "left_shift", sequence);
		break;
	    case Exp_op_left_shift_assign:
		goto error;
	    case Exp_op_left_shift_result:
		call = call_result_convert(convert, exp, type_refs,
					   "left_shift", sequence);
		break;
	    case Exp_op_less_than:
		call = call_relation_convert(convert, exp, type_refs,
					     "less_than", sequence);
		break;
	    case Exp_op_less_than_or_equal:
		call = call_not_relation_convert(convert, exp, type_refs,
						 "lesser_equal", "greater_than",
						 sequence);
		break;
	    case Exp_op_list:
		assert_fail();
		break;
	    case Exp_op_minus:
		call = call_unary_convert(convert, exp, type_refs,
					  "minus", sequence);
		break;
	    case Exp_op_multiply:
		call = call_binary_convert(convert, exp, type_refs,
					   "multiply", sequence);
		break;
	    case Exp_op_multiply_assign:
		goto error;
	    case Exp_op_multiply_result:
		call = call_result_convert(convert, exp, type_refs,
					   "multiply", sequence);
		break;
	    case Exp_op_not:
		call = call_unary_convert(convert, exp, type_refs,
					  "not", sequence);
		break;
	    case Exp_op_not_equal:
		call = call_not_relation_convert(convert, exp, type_refs,
					         "not_equal", "equal",
						 sequence);
		break;
	    case Exp_op_not_identical:
		call = call_not_relation_convert(convert, exp, type_refs,
					         "not_identical", "identical",
						 sequence);
		break;
	    case Exp_op_or:
		call =  call_binary_convert(convert, exp, type_refs,
					   "or", sequence);
		break;
	    case Exp_op_or_assign:
		goto error;
	    case Exp_op_or_if:
	      {
		Call		left_call;
		Call		right_call;
		Exp_binary	binary;

		binary = exp->operands.binary;
		left_call = call_exp_convert(convert, binary->left,
					convert->type_ref_logical->type_refs,
					sequence);
		right_call = call_exp_convert(convert, binary->right,
					convert->type_ref_logical->type_refs,
					sequence);
		call = call_binary_create(convert, Call_type_or_if,
					  left_call, right_call);
		break;
	      }
	    case Exp_op_or_result:
		call = call_result_convert(convert, exp, type_refs,
					   "or", sequence);
		break;
	    case Exp_op_post_decrement:
		assert_fail();
		break;
	    case Exp_op_post_increment:
		assert_fail();
		break;
	    case Exp_op_power:
		call = call_binary_convert(convert, exp, type_refs,
					   "power", sequence);
		break;
	    case Exp_op_power_assign:
		goto error;
	    case Exp_op_power_result:
		call = call_result_convert(convert, exp, type_refs,
					   "power", sequence);
		break;
	    case Exp_op_pre_decrement:
		assert_fail();
		break;
	    case Exp_op_pre_increment:
		assert_fail();
		break;
	    case Exp_op_remainder:
		call = call_binary_convert(convert, exp, type_refs,
					   "remainder", sequence);
		break;
	    case Exp_op_remainder_assign:
		goto error;
	    case Exp_op_remainder_result:
		call = call_result_convert(convert, exp, type_refs,
					   "remainder", sequence);
		break;
	    case Exp_op_result:
		call = call_result_convert(convert, exp, type_refs,
					   (Str)0, sequence);
		break;
	    case Exp_op_right_shift:
		call = call_binary_convert(convert, exp, type_refs,
					   "right_shift", sequence);
		break;
	    case Exp_op_right_shift_assign:
		goto error;
	    case Exp_op_right_shift_result:
		call = call_result_convert(convert, exp, type_refs,
					   "right_shift", sequence);
		break;
	    case Exp_op_string:
		call = call_string_create(convert, exp->operands.string,
					  exp->position);
		break;
	    case Exp_op_subtract:
		call = call_binary_convert(convert, exp, type_refs,
					   "subtract", sequence);
		break;
	    case Exp_op_subtract_assign:
		goto error;
	    case Exp_op_subtract_result:
		call = call_result_convert(convert, exp, type_refs,
					   "subtract", sequence);
		break;
	    case Exp_op_symbol:
	      {
		Str		name;
		int		position;
		Routine_ref	routine_ref;
		Type_ref	type_ref;
		Var		var;

		position = exp->position;

		/* Look for a variable first: */
		name = exp->operands.symbol;
		var = table_lookup(Str, Var, convert->var_table, name);
		if (var != (Var)0) {
			exp->type_refs = var->type_ref->type_refs;
			call = call_var_create(convert, name, position,
					       var->type_ref->type_refs);
			break;
		}

		/* Now try a constant: */
		if (type_refs_size(type_refs) == 1) {
			Object_ref	object_ref;
			Need_table	need_table;

			/* This code is copied in call_exp_constant():*/

			type_ref = type_refs_fetch(type_refs, 0);
			
			object_ref = object_ref_lookup(convert->object_table,
						       name, type_ref);
			if (object_ref != (Object_ref)0) {
				object_ref->used = 1;
				need_table = convert->routine->need_table;
				if (type_ref_contains_parameter(type_ref) ||
				    type_ref_is_parameterized(type_ref)) {
					need_table_object_insert(need_table,
								 name,
								 type_ref,
								 type_ref);
				}
				exp->type_refs = type_ref->type_refs;
				call = call_object_create(convert,
							  object_ref, position);
				break;
			}
		}

		/* Now try a routine: */
		routine_ref = routine_ref_lookup(convert->routine_table,
						 name,
						 convert->type_ref_global,
						 position);
		if (routine_ref != (Routine_ref)0) {
			routine_ref = routine_ref_static_lookup(name,
						convert->type_ref_global,
						-1, -1, -1,
						position, convert);
			type_proto_needed(routine_ref->type_proto,
					  convert->type_tables);
			routine_entry_used(routine_ref->routine_entry,
					   convert->routine_table);
			call = call_routine_create(convert, routine_ref,
						   position);
			break;
		}

		/* No dice: */
		msg_out(convert->msg, position,
			"Symbol %s is not defined", name);
		call = call_error_create(convert,
					 position, "Undefined routine");
		break;
	      }
	    case Exp_op_text:
		call = call_string_create(convert, exp->operands.string,
					  exp->position);
		break;
	    case Exp_op_twiddle:
	      {
		Vec(Call)	actuals;
		Vec(Exp)	exps;
		int		exps_size;
		Call		obj_call;
		Type_ref	obj_type_ref;
		Exp_object	object;
		int		position;
		Routine_ref	routine_ref;
		Type_proto	type_proto;
		Type_tables	type_tables;

		position = exp->position;
		object = exp->operands.object;

		/* Get the object call expression: */
		obj_call = call_exp_convert(convert, object->object,
					    convert->type_refs_empty, sequence);
		obj_type_ref = call_type_ref_get(obj_call, position, convert);
		if (obj_type_ref == (Type_ref)0) {
			call = obj_call;
			break;
		}

		/* Find the routine being called: */
		exps = object->list;
		exps_size = vec_size(Exp, exps);
		routine_ref = routine_ref_static_lookup(object->name,
							obj_type_ref,
							exps_size + 1, -1, 0,
							position, convert);
		if (routine_ref == (Routine_ref)0) {
			call = obj_call;
			break;
		}
		type_proto = routine_ref->type_proto;
		exp->type_refs = type_proto->returns;

		/* Convert the expresions to the right of the '(': */
		type_tables = convert->type_tables;
		actuals = exps_to_calls(exps, type_refs_lop(type_proto->takes,
							    type_tables),
					0, sequence, convert);
		vec_prepend(Call, actuals, obj_call);

		if (type_proto_is_parameterized(type_proto)) {
			actuals = call_list_cast(actuals,
				routine_ref->routine_entry->type_proto->takes,
				convert);
		}

		call = call_static_invoke(routine_ref, actuals,
					  position, convert);
		break;
	      }
	    case Exp_op_xor:
		call = call_binary_convert(convert, exp, type_refs,
					   "xor", sequence);
		break;
	    case Exp_op_xor_assign:
		goto error;
	    case Exp_op_xor_result:
		call = call_result_convert(convert, exp, type_refs,
					   "xor", sequence);
		break;
	    default:
		assert_fail();
	}
	if (exp->type_refs == (Type_refs)0) {
		exp->type_refs = call->type_refs;
	}
	return call;
    error:
	assert_fail();
	/* NOTREACHED */
}

/*
 * call_exp_convert_dot(convert, exp, sequence)
 *	This routine will do all of the work necessary to convert the
 *	dot expression "exp" into a call and return it.
 */
LOCAL Call
call_exp_convert_dot(
	Convert		convert,
	Exp		exp,
	Vec(Call)	sequence)
{
	Vec(Call)	actuals;
	Call		call;
	Exp_field	field;
	Str		get_name;
	Call		left_call;
	Type_ref	left_type_ref;
	int		position;
	Routine_ref	routine_ref;
	Type_proto	type_proto;

	position = exp->position;
	field = exp->operands.field;

	/* Compute the expression to the left of the '.': */
	left_call = call_exp_convert(convert, field->exp,
				     convert->type_refs_empty,
				     sequence);
	left_type_ref = call_type_ref_get(left_call, position, convert);
	if (left_type_ref == (Type_ref)0) {
		return left_call;
	}

	/* Get the prototype: */
	get_name = strprintf(convert->heap, "%s_get", field->name);
	routine_ref = routine_ref_static_lookup(get_name, left_type_ref,
						1, 1, 0, position, convert);
	if (routine_ref == (Routine_ref)0) {
		return left_call;
	}
	type_proto = routine_ref->type_proto;

	actuals = vec_create(Call, convert->heap);
	vec_append(Call, actuals, left_call);
	if (type_proto_is_parameterized(type_proto)) {
		actuals = call_list_cast(actuals,
				routine_ref->routine_entry->type_proto->takes,
				convert);
	}
	call = call_static_invoke(routine_ref, actuals, position, convert);
	exp->type_refs = type_proto->returns;
	return call;
}

/*
 * call_static_lookup(name, type_ref,
 *		      takes, returns, yields, position, convert)
 *	This routine will return a call routine object corresponding to
 *	"type_ref"@"name" using "convert"->routine_table.  This routine
 *	will verify that the number of actuals and returns match.
 */
Call
call_static_lookup(
	Str		name,
	Type_ref	type_ref,
	int		takes,
	int		returns,
	int		yields,
	int		position,
	Convert		convert)
{
	Routine_ref	routine_ref;

	/* Look up the typedef for the routine/iterator */
	routine_ref = routine_ref_static_lookup(name, type_ref,
						takes, returns, yields,
						position, convert);
	if (routine_ref == (Routine_ref)0) {
		msg_out(convert->msg, position,
			"The %s%@ routine does not exist",
			name, type_ref);
		return (Call)0;
	}
	return call_routine_create(convert, routine_ref, position);
}

/*
 * routine_ref_static_lookup(name, type_ref,
 *			     takes, returns, yields, position, convert)
 *	This routine will return the routine ref associated with
 *	"name"@"type_ref".
 */
LOCAL Routine_ref
routine_ref_static_lookup(
	Str		name,
	Type_ref	type_ref,
	int		takes,
	int		returns,
	int		yields,
	int		position,
	Convert		convert)
{
	int		returns_size;
	Routine_entry	routine_entry;
	Routine_ref	routine_ref;
	int		takes_size;
	int		yields_size;
	Type_proto	type_proto;
	Routine_table	routine_table;

	routine_table = convert->routine_table;
	routine_ref = routine_ref_lookup(routine_table, name,
					 type_ref, position);
	if (routine_ref == (Routine_ref)0) {
		routine_entry = routine_entry_lookup(routine_table, name,
						     type_ref->name, position);
		if (routine_entry == (Routine_entry)0) {
			return routine_ref;
		}
		routine_ref = routine_ref_insert(routine_table, name,
						 type_ref, position);
	}
	routine_entry_used(routine_ref->routine_entry, routine_table);
	type_proto = routine_ref->type_proto;

	assert(type_proto->kind == Type_proto_procedure);

	takes_size = type_refs_size(type_proto->takes);
	if ((takes >= 0) &&  (takes_size != takes)) {
		msg_out(convert->msg, position,
			     "%s%@ has %d actuals instead of %d actuals",
			     name, type_ref, takes_size, takes);
	}
	returns_size = type_refs_size(type_proto->returns);
	if ((returns >= 0) &&  (returns_size != returns)) {
		msg_out(convert->msg, position,
			     "%s%@ has %d returns instead of %d returns",
			     name, type_ref, returns_size, returns);
	}
	yields_size = type_refs_size(type_proto->yields);
	if ((yields > 0) && (yields_size != yields)) {
		assert_fail();
	}
	return routine_ref;
}

/*
 * call_not_relation_convert(convert, exp, type_refs,
 *			     message, op_name, sequence)
 *	This will return a call expression converted from "exp" and "op_name".
 */
/* ARGSUSED */
Call
call_not_relation_convert(
	Convert		convert,
	Exp		exp,
	Type_refs	type_refs,
	Str		message,
	Str		op_name,
	Vec(Call)	sequence)
{
	return call_not_create(convert,
			       call_relation_convert(convert, exp, type_refs,
						     op_name, sequence));
}

/*
 * call_relation_convert(convert, exp, type_refs, op_name, sequence)
 *	This will return a call expression converted from "exp" and "op_name".
 */
Call
call_relation_convert(
	Convert		convert,
	Exp		exp,
	Type_refs	type_refs,
	Str		op_name,
	Vec(Call)	sequence)
{
	return call_binary_convert(convert, exp, type_refs, op_name, sequence);
}

/*
 * call_result_convert(convert, exp, op_name, type_refs, sequence)
 *	This will return the call expression resulting from "::<op>=".
 */
/* ARGSUSED */
Call
call_result_convert(
	Convert		convert,
	Exp		exp,
	Type_refs	type_refs,
	Str		op_name,
	Vec(Call)	sequence)
{
	Exp_binary	binary;


	binary = exp->operands.binary;
	return call_assign_exp_convert(convert, binary->left, binary->right,
				       op_name, 0,
				       (op_name != (Str)0) &&
						strequal(op_name, "define"),
				       sequence);
}

/*
 * call_assign_exp_convert(convert, left_exp, right_exp, op_name,
 *			   is_simple, is_define, sequence)
 *	This routine will return the call expression resulting from
 *	<left_exp> ::<op>= <right_exp>.  If "is_simple" is 1, the
 *	resulting intermediate result is not stored in a temporary.
 *	If "is_define" is 1, <left_exp> is meant to be defined as
 *	a variable.
 */
LOCAL Call
call_assign_exp_convert(
	Convert		convert,
	Exp		left_exp,
	Exp		right_exp,
	Str		op_name,
	int		is_simple,
	int		is_define,
	Vec(Call)	sequence)
{
	Call		call;
	Call		left_call;
	Str		name;
	Call		right_call;

	switch (left_exp->op) {
	    case Exp_op_define:
	      {
		Exp_define	define;
		Type_ref	synonym_type_ref;
		Type_ref	type_ref;
		Var		var;

		if (is_define) {
			msg_out(convert->msg, left_exp->position,
			    "Definition assigment to already defined variable");
		}
		define = left_exp->operands.define;
		name = define->name;
		type_ref = define->type_ref;
		synonym_type_ref = table_lookup(Str, Type_ref,
						convert->synonyms,
						type_ref->name);
		if (synonym_type_ref != (Type_ref)0) {
			type_ref = synonym_type_ref;
		}
		var = heap_allocate(convert->heap, Var);
		var->comment = "";
		var->name = name;
		var->position = left_exp->position;
		var->type_ref = type_ref;
		if (table_insert(Str, Var,
				 convert->routine->var_table, name, var)) {
			var = table_lookup(Str, Var,
					   convert->routine->var_table, name);
			assert(var != (Var)0);
			if (!type_ref_equal(var->type_ref, type_ref)) {
				msg_out(convert->msg, left_exp->position,
					"%s is defined more than once", name);
			}
		} else {
			vec_append(Var, convert->routine->vars, var);
		}
		goto symbol_cont;
	      }
	    case Exp_op_symbol:
	      {
		Call	exp_call;
		Call	temp_call;
		Var	var;

		/* Lookup variable: */
		name = left_exp->operands.symbol;
	    symbol_cont:
		var = table_lookup(Str, Var, convert->var_table, name);
		if (is_define) {
			Type_ref	type_ref;

			assert(right_exp != (Exp)0);
			right_call = call_exp_convert(convert, right_exp,
						convert->type_refs_empty,
						sequence);
			if (type_refs_size(right_call->type_refs) != 1) {
				msg_out(convert->msg, right_exp->position,
					"Expression should return one value");
				return right_call;
			}
			type_ref = type_refs_fetch(right_call->type_refs, 0);
			if (left_exp->op != Exp_op_symbol) {
				msg_out(convert->msg, left_exp->position,
				  "Definition assigment must be to a variable");
				return right_call;
			}
			name = left_exp->operands.symbol;
			var = table_lookup(Str, Var, convert->var_table, name);
			if (var != (Var)0) {
				if (!type_ref_equal(var->type_ref, type_ref)) {
					msg_out(convert->msg,
						left_exp->position,
						"Variable %s already exists!",
						name);
					return right_call;
				}
			}
			var = heap_allocate(convert->heap, Var);
			var->comment = (Str)0;
			var->name = name;
			var->position = left_exp->position;
			var->type_ref = type_ref;
			if (table_insert(Str, Var,
					 convert->var_table, name, var) == 0) {
				vec_append(Var, convert->routine->vars, var);
			}
			left_call = call_var_create(convert, name,
						    left_exp->position,
						    type_ref->type_refs);
		} else {
			if (var == (Var)0) {
				if (!strequal(name, "??")) {
					msg_out(convert->msg,
						left_exp->position,
						"Undefined variable %s", name);
					call = call_var_create(convert, name,
						left_exp->position,
						convert->type_refs_empty);
					if (!is_simple) {
						vec_append(Call,
							   sequence, call);
					}
					return call;
				}
				/*
				 * It sure would be nice to know
				 * the real type here.
				 */
				var = heap_allocate(convert->heap, Var);
				var->name = "??";
				var->type_ref = convert->type_ref_global;
			}
			left_call = call_var_create(convert, name,
						    left_exp->position,
						    var->type_ref->type_refs);
			if (right_exp == (Exp)0) {
				right_call = call_temp_create(convert,
						      left_exp->position,
						      var->type_ref);
				exp_call = right_call;
			} else {
				right_call = call_exp_convert(convert,
						right_exp,
						var->type_ref->type_refs,
						sequence);
			}
		}

		if (op_name != (Str)0) {
			Vec(Call)	actuals;
			Routine_ref	op_routine_ref;

			op_routine_ref = routine_ref_static_lookup(op_name,
						var->type_ref, 2, 1, 0,
						left_exp->position, convert);
			if (op_routine_ref == (Routine_ref)0) {
				return left_call;
			}

			actuals = vec_create(Call, convert->heap);
			vec_append(Call, actuals, left_call);
			vec_append(Call, actuals, right_call);
			right_call = call_static_invoke(op_routine_ref,
							actuals,
							left_exp->position,
							convert);
		}
		if (is_simple) {
			return call_assign_create(convert,
						  left_call, right_call);
		} else {
			if (right_call->type == Call_type_temp) {
				temp_call = right_call;
			} else {
				temp_call = call_temp_create(convert,
							left_exp->position,
							var->type_ref);
				call = call_assign_create(convert,
							  temp_call,
							  right_call);
				vec_append(Call, sequence, call);
			}
			call = call_assign_create(convert,
						  left_call, temp_call);
			vec_append(Call, sequence, call);
			return (right_exp == (Exp)0) ? exp_call : temp_call;
		}
	      }
	    case Exp_op_dot:
	      {
		Vec(Call)	actuals;
		Call		assign_call;
		Call		base_call;
		Type_ref	base_type_ref;
		Call		exp_call;
		Exp_field	field;
		Heap		heap;
		int		position;
		Type_ref	right_type_ref;
		Call		set_call;
		Str		set_name;
		Routine_ref	set_routine_ref;
		Type_proto	set_type_proto;
		Call		temp_call;

		heap = convert->heap;
		position = left_exp->position;

		/*
		 * Compute the expression to the left of the '.'
		 * (i.e. the base expression):
		 * base := <left_exp>
		 */
		field = left_exp->operands.field;
		base_call = call_exp_convert(convert, field->exp,
					     convert->type_refs_empty,
					     sequence);
		base_type_ref = call_type_ref_get(base_call, position, convert);
		if (base_type_ref == (Type_ref)0) {
			return base_call;
		}

		/*
		 * For complex assignments (i.e, '::<op>='),
		 * assign the base expression to a temporary variable,
		 * provided that it isn't already a variable.
		 */
		if ((op_name != (Str)0) && (field->exp->op != Exp_op_symbol)) {
			Call		assign_call;
			Call		temp_call;

			temp_call = call_temp_create(convert,
						     left_exp->position,
						     base_type_ref);
			assign_call = call_assign_create(convert,
							 temp_call, base_call);
			vec_append(Call, sequence, assign_call);
			base_call = temp_call;
		}

		/* Find the <field>_set routine: */
		set_name = strprintf(heap, "%s_set", field->name);
		set_routine_ref = routine_ref_static_lookup(set_name,
							    base_type_ref,
							    2, 0, 0,
							    position, convert);
		if (set_routine_ref == (Routine_ref)0) {
			return base_call;
		}
		set_type_proto = set_routine_ref->type_proto;

		/* Evaluate the right expression: */
		right_type_ref = type_refs_fetch(set_type_proto->takes, 1);
		if (right_exp == (Exp)0) {
			right_call = call_temp_create(convert,
						      left_exp->position,
						      right_type_ref);
			exp_call = right_call;
		} else {
			right_call = call_exp_convert(convert, right_exp,
						      right_type_ref->type_refs,
						      sequence);
		}

		/*
		 * For complex result assignments '::<op>=', perform the
		 * operation using the <field>_get routine.
		 */
		if (op_name != (Str)0) {
			Call		get_call;
			Routine_ref	get_routine_ref;
			Str		get_name;
			Routine_ref	op_routine_ref;

			/* Find the <field>_get routine: */
			get_name = strprintf(heap, "%s_get", field->name);
			get_routine_ref = routine_ref_static_lookup(get_name,
							base_type_ref,
							1, 1, 0,
							position, convert);
			op_routine_ref = routine_ref_static_lookup(op_name,
							right_type_ref,
							2, 1, 0,
							position, convert);
			if ((get_routine_ref == (Routine_ref)0) ||
			    (op_routine_ref == (Routine_ref)0)) {
				return base_call;
			}

			actuals = vec_create(Call, convert->heap);
			vec_append(Call, actuals, base_call);
			get_call = call_static_invoke(get_routine_ref,
						      actuals,
						      position,
						      convert);

			actuals = vec_create(Call, convert->heap);
			vec_append(Call, actuals, get_call);
			vec_append(Call, actuals, right_call);
			right_call = call_static_invoke(op_routine_ref,
							actuals,
							position,
							convert);
			/* <op>(<field>_get(<base>), <right> */
		}

		/* <temp2> := <right> */
		if (is_simple || (right_call->type == Call_type_temp)) {
			temp_call = right_call;
		} else {
			temp_call = call_temp_create(convert,
						     left_exp->position,
						     right_type_ref);
			assign_call = call_assign_create(convert,
							 temp_call,
							 right_call);
			vec_append(Call, sequence, assign_call);
		}

		/* <field>_set(<base>, <temp2> */
		actuals = vec_create(Call, heap);
		vec_append(Call, actuals, base_call);
		vec_append(Call, actuals, temp_call);
		set_call = call_static_invoke(set_routine_ref, actuals,
					      position, convert);
		if (is_simple) {
			return set_call;
		} else {
			vec_append(Call, sequence, set_call);
			return (right_exp == (Exp)0) ? exp_call : temp_call;
		}
	      }
	    case Exp_op_array:
	      {
		Exp_access	access;
		Call		assign_call;
		Call		base_call;
		Type_ref	base_type_ref;
		Heap		heap;
		int		index;
		int		position;
		Type_ref	right_type_ref;
		int		size;
		Call		store_call;
		Str		store_name;
		Routine_ref	store_routine_ref;
		Type_proto	store_type_proto;
		Vec(Call)	sub_calls;
		Vec(Exp)	sub_exps;
		Vec(Call)	takes;
		Call		temp_call;

		heap = convert->heap;
		position = left_exp->position;

		/*
		 * Compute the base expression to the left of the '['
		 * base := <left_exp>
		 */
		access = left_exp->operands.access;
		base_call = call_exp_convert(convert, access->exp,
					     convert->type_refs_empty,
					     sequence);
		base_type_ref = call_type_ref_get(base_call, position, convert);
		if (base_type_ref == (Type_ref)0) {
			return base_call;
		}

		/*
		 * For complex assignments (i.e, '::<op>='),
		 * assign the base expression to a temporary variable,
		 * provided that it isn't already a variable.
		 */
		if ((op_name != (Str)0) &&
		    (access->exp->op != Exp_op_symbol)) {
			Call		assign_call;
			Call		temp_call;

			temp_call = call_temp_create(convert,
						     left_exp->position,
						     base_type_ref);
			assign_call = call_assign_create(convert,
							 temp_call,
							 base_call);
			vec_append(Call, sequence, assign_call);
			base_call = temp_call;
		}

		/* Find the store# routine: */
		size = vec_size(Exp, access->list);
		store_name = strprintf(heap, "store%d", size);
		store_routine_ref = routine_ref_static_lookup(store_name,
						base_type_ref, size + 2, 0, 0,
						position, convert);
		if (store_routine_ref == (Routine_ref)0) {
			return base_call;
		}
		store_type_proto = store_routine_ref->type_proto;

		/* Evaluate the right expression: */
		right_type_ref = type_refs_fetch(store_type_proto->takes,
						 size + 1);
		right_call = call_exp_convert(convert, right_exp,
					      right_type_ref->type_refs,
					      sequence);

		/* Evaluate all of the expressions between the brackets: */
		sub_exps = access->list;
		sub_calls = vec_create(Call, heap);
		for (index = 0; index < size; index++) {
			Call		sub_call;
			Exp		sub_exp;
			Type_ref	sub_type_ref;

			sub_exp = vec_fetch(Exp, sub_exps, index);
			sub_type_ref = type_refs_fetch(store_type_proto->takes,
						       index + 1);
			sub_call = call_exp_convert(convert, sub_exp,
						    sub_type_ref->type_refs,
						    sequence);

			/*
			 * For complex assignments ('::<op>=') store complex
			 * sub-script expressions into temporaries:
			 */
			if (op_name != (Str)0) {
				Call		temp_call;

				switch (sub_call->type) {
				    case Call_type_integer:
				    case Call_type_var:
				    case Call_type_temp:
				    case Call_type_text:
					break;
				    default:
					temp_call = call_temp_create(convert,
							     left_exp->position,
							     sub_type_ref);
					sub_call = call_assign_create(convert,
							      temp_call,
							      sub_call);
					vec_append(Call, sequence, sub_call);
					sub_call = temp_call;
				}
			}
			vec_append(Call, sub_calls, sub_call);
		}

		/*
		 * For complex result assignments '::<op>=', perform the
		 * operation using the store# routine.
		 */
		if (op_name != (Str)0) {
			Vec(Call)	actuals;
			Call		fetch_call;
			Str		fetch_name;
			Routine_ref	fetch_routine_ref;
			Routine_ref	op_routine_ref;

			/* Find the <field>_fetch routine: */
			fetch_name = strprintf(heap, "fetch%d", size);
			fetch_routine_ref =
				routine_ref_static_lookup(fetch_name,
							  base_type_ref,
							  size + 1, 1, 0,
							  position, convert);
			if (fetch_routine_ref == (Routine_ref)0) {
				return base_call;
			}

			actuals = vec_create(Call, heap);
			vec_append(Call, actuals, base_call);
			vec_vec_append(Call, actuals, sub_calls);
			fetch_call = call_static_invoke(fetch_routine_ref,
							actuals,
							position,
							convert);
			op_routine_ref = routine_ref_static_lookup(op_name,
							right_type_ref,
							2, 1, 0,
							position, convert);
			if (op_routine_ref == (Routine_ref)0) {
				return base_call;
			}

			actuals = vec_create(Call, convert->heap);
			vec_append(Call, actuals, fetch_call);
			vec_append(Call, actuals, right_call);
			right_call = call_static_invoke(op_routine_ref,
							actuals,
							position,
							convert);
			/* <op>(fetch#(<base>, <sub_calls>), <right> */


		}

		/* <temp2> := <right> */
		if (is_simple || (right_call->type == Call_type_temp)) {
			temp_call = right_call;
		} else {
			temp_call = call_temp_create(convert,
						     left_exp->position,
						     right_type_ref);
			assign_call = call_assign_create(convert,
							 temp_call, right_call);
			vec_append(Call, sequence, assign_call);
		}

		/* store#(<base>, <sub_calls>, <temp2> */
		takes = vec_create(Call, heap);
		vec_append(Call, takes, base_call);
		vec_vec_append(Call, takes, sub_calls);
		vec_append(Call, takes, temp_call);

		if (type_proto_is_parameterized(store_type_proto)) {
			takes = call_list_cast(takes,
			  store_routine_ref->routine_entry->type_proto->takes,
			  convert);
		}

		store_call = call_static_invoke(store_routine_ref,
						takes,
						position,
						convert);
		if (is_simple) {
			return store_call;
		} else {
			vec_append(Call, sequence, store_call);
			return store_call;	/* This may be wrong! */
		}
	      }
	}
    error:
	assert(call != call);
	/* NOTREACHED */
}


/*
 * call_unary_convert(convert, exp, type_refs, op_name, sequence)
 *	This will return a call expression converted from "exp" and "op_name".
 */
Call
call_unary_convert(
	Convert		convert,
	Exp		exp,
	Type_refs	type_refs,
	Str		op_name,
	Vec(Call)	sequence)
{
	Vec(Call)	actuals;
	Call		call;
	int		position;
	Routine_ref	routine_ref;
	Call		unary_call;
	Exp		unary_exp;
	Type_ref	unary_type_ref;

	position = exp->position;
	unary_exp = exp->operands.unary;
	unary_call = call_exp_convert(convert, unary_exp,
				      type_refs, sequence);
	unary_type_ref = call_type_ref_get(unary_call, position, convert);
	if (unary_type_ref == (Type_ref)0) {
		return unary_call;
	}

	routine_ref = routine_ref_static_lookup(op_name, unary_type_ref,
						1, 1, 0, position, convert);
	if (routine_ref == (Routine_ref)0) {
		return unary_call;
	}

	actuals = vec_create(Call, convert->heap);
	vec_append(Call, actuals, unary_call);
	call = call_static_invoke(routine_ref, actuals, position, convert);
	return call;
}

/*
 * call_list_cast(calls, types, convert)
 *	This routine will insert a cast for each parmaterized type in "types"
 *	and return the the result.
 */
Vec(Call)
call_list_cast(
	Vec(Call)	calls,
	Type_refs	types,
	Convert		convert)
{
	Call		call;
	int		index;
	Vec(Call)	new_calls;
	int		size;
	Type_ref	type_ref;

	new_calls = (Vec(Call))0;
	size = vec_size(Call, calls);
	if (size != type_refs_size(types)) {
		/* There is some sort of problem that will be caught later. */
		return calls;
	}
	for (index = 0; index < size; index++) {
		call = vec_fetch(Call, calls, index);
		type_ref = type_refs_fetch(types, index);
		if (type_ref_is_parameter(type_ref)) {
			if (new_calls == (Vec(Call))0) {
				new_calls = vec_copy(Call, calls);
			}
			call = call_cast_create(convert, call, type_ref);
			vec_store(Call, new_calls, index, call);
		}
	}
	return (new_calls == (Vec(Call))0) ? calls : new_calls;
}

/*
 * call_type_ref_get(call, position, convert)
 *	This routine will return the type reference associated with "call".
 *	If the number of type references in "call" is not one, an error
 *	message is generated using "position" and "convert" and (Type_ref)0
 *	is returned.
 */
LOCAL Type_ref
call_type_ref_get(
	Call		call,
	int		position,
	Convert		convert)
{
	int		size;
	Type_ref	type_ref;
	Type_refs	type_refs;

	type_refs = call->type_refs;
	size = type_refs_size(type_refs);
	if (size == 1) {
		type_ref = type_refs_fetch(type_refs, 0);
	} else {
		msg_out(convert->msg, position,
			"Expression returns %d types instead of 1 type!",
			size);
		type_ref = (Type_ref)0;
	}
	return type_ref;
}

/*
 * exps_to_calls(exps, type_refs, offset, sequence, convert)
 *	This routine will return a call list corresponding to "exps".
 *	The default type for each expression can be found in "type_refs"
 *	offset by "offset".  "sequence" and "convert" are needed to
 *	pull off the conversion.
 */
LOCAL Vec(Call)
exps_to_calls(
	Vec(Exp)	exps,
	Type_refs	type_refs,
	int		offset,
	Vec(Call)	sequence,
	Convert		convert)
{
	Call		call;
	Vec(Call)	calls;
	Exp		exp;
	int		exps_size;
	Heap		heap;
	int		index;
	Type_ref	temp_type_ref;
	Type_refs	temp_type_refs;
	int		size;

	heap = convert->heap;
	exps_size = vec_size(Exp, exps);
	size = type_refs_size(type_refs);
	calls = vec_create(Call, heap);
	for (index = 0; index < exps_size; index++) {
		exp = vec_fetch(Exp, exps, index);
		if (index + offset < size) {
			temp_type_ref = type_refs_fetch(type_refs,
							index + offset);
			temp_type_refs = temp_type_ref->type_refs;
		} else {
			temp_type_refs = convert->type_refs_empty;
		}
		call = call_exp_convert(convert, exp, temp_type_refs, sequence);
		vec_append(Call, calls, call);
	}
	return calls;
}

/*
 * call_static_invoke(routine_ref, actuals, position, convert)
 *	This routine will return the call that results from invoking
 *	the routine specified by "routine_ref" with parameters of "actuals".
 */
LOCAL Call
call_static_invoke(
	Routine_ref	routine_ref,
	Vec(Call)	actuals,
	int		position,
	Convert		convert)
{
	Call		call;
	Type_ref	return_type_ref;
	Type_refs	return_type_refs;
	Routine_entry	routine_entry;
	int		size;

	call = call_routine_create(convert, routine_ref, position);
	call = call_invoke_create(convert, call, actuals);

	/* Figure out whether or not to cast the return value: */
	routine_entry = routine_ref->routine_entry;
	return_type_refs = routine_entry->type_proto->returns;
	size = type_refs_size(return_type_refs);
	switch (size) {
	    case 0:
		break;
	    case 1:
		return_type_ref = type_refs_fetch(return_type_refs, 0);
		if (type_ref_is_parameter(return_type_ref)) {
			return_type_ref =
			   type_refs_fetch(routine_ref->type_proto->returns, 0);
			call = call_cast_create(convert, call, return_type_ref);
		}
		break;
	    default:
		call = call_multi_create(convert, call, return_type_refs);
		break;
	}
	return call;
}

