/*
 * Copyright (c) 1995, 1996, 1997, 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <rpc/types.h>

#include <mom/compiler.h>
#include <mom/libaoi.h>
#include <mom/libmeta.h>

#include "rpc_parse.h"

extern definition *defs;
aoi outaoi;

/*
 * This is the scope of the corresponding AOI nodes.  This is necessary to
 * prevent references to structures that are defined within other scopes
 * (structures, etc.).
 */
int curr_scope = 0;

int aoi_max = 0;

#define CAN_CREATE (0)
#define MUST_CREATE (1)
#define NO_CREATE (2)

static aoi_type xl_enum(definition *def_ptr, enum_def def, char *name);
static int xl_eval(definition *def_ptr, char *val);

static int IsDigit(char c)
{
	return (c == '-') || (c == '~') || isdigit(((int) c));
}

static void itoa(unsigned int i, char *a)
{
	char temp[20];
	int x, y;
	
	for (x = 0; x < 20; x++)
		temp[x] = 0;
	x = 0;
	do {
		temp[x++] = i % 10;
		i /= 10;
	} while (i);

	for (y = 0; x; y++, x--)
		a[y] = temp[x - 1] + '0';
	a[y] = 0;
}

static char *trim_string(char *in_string)
{
	/*
	 * Return a copy of `in_string' but with the first and last characters
	 * removed.  This function is used to trim the double-quotes from
	 * string constants.
	 */
	int in_length;
	char *out_string;
	
	in_length = strlen(in_string);
	out_string = (char *) mustmalloc(sizeof(char) * (in_length - 1));
	strncpy(out_string, (in_string + 1), (in_length - 2));
	out_string[in_length - 2] = 0;
	
	return out_string;
}

static int get_def()
{
	/*
	 * This will dynamically size the outaoi array to fit as many slots as
	 * are needed.
	 */
	int i = outaoi.defs.defs_len++;
	
	if ((signed int) outaoi.defs.defs_len > aoi_max) {
		aoi_max += 10;
		outaoi.defs.defs_val = (aoi_def *)
				 mustrealloc(outaoi.defs.defs_val,
					     sizeof(aoi_def) * aoi_max);
	}
	
	return i;
}

static int aoi_name(definition *def, char *name, int errs)
{
	/*
	 * This will find the node named 'name' and will return a reference
	 * to it.  This is not necessarily the best code, but it works fine.
	 */
	int i;
	int min_scope = curr_scope;
	
	for (i = outaoi.defs.defs_len - 1; i >= 0; i--) {
		/* Look in all scopes from this point, and above... */
		if (outaoi.defs.defs_val[i].scope <= min_scope) {
			min_scope = outaoi.defs.defs_val[i].scope;
			if (!strcmp(outaoi.defs.defs_val[i].name, name)) {
				if (errs == MUST_CREATE)
					return -1;
				else
					return i;
			}
		}
	}
	/* Not already defined. */
	if (errs == NO_CREATE)
		return -1;
	
	i = get_def();
	outaoi.defs.defs_val[i].name     = name;
	outaoi.defs.defs_val[i].binding  = 0;
	outaoi.defs.defs_val[i].scope    = curr_scope;
	outaoi.defs.defs_val[i].idl_file = def->idl_file;
	
	return i;
}

static aoi_type new_int(int min, unsigned int range)
{
	/* This allocates a new integer type and sets the min/range values. */
	aoi_type res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	
	res->kind = AOI_INTEGER;
	res->aoi_type_u_u.integer_def.min = min;
	res->aoi_type_u_u.integer_def.range = range;
	
	return res;
}

static aoi_type make_optional(aoi_type type)
{
	/*
	 * Create and return an AOI_OPTIONAL that refers to the given
	 * `aoi_type'.
	 */
	aoi_type res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	
	res->kind = AOI_OPTIONAL;
	res->aoi_type_u_u.optional_def.type = type;
	
	return res;
}

static aoi_const xl_deref_const(definition *def_ptr, const_def def,
				int must_be_int)
{
	/*
	 * This function returns the value associated with the definition of a
	 * constant --- in other words, this function "dereferences" the given
	 * constant.  Literals are translated into AOI and returned; symbolic
	 * names are looked up.  NOTE that this function will panic if it is
	 * given a name that does not refer to an already-defined constant.
	 * This means that we don't handle forward references to constants.
	 * `rpcgen' can handle certain forward references, such as this:
	 *
	 *   const a = b; const b = 1; (OK for `rpcgen', but not OK for us.)
	 *
	 * `rpcgen' can also handle constants that don't refer to anything, so
	 * long as the constant is never needed:
	 *
	 *   const bar = foo; (`bar' is never used, `foo' is never defined.)
	 *
	 * `rpcgen' can handle these cases because it doesn't need to know
	 * anything about constants in order to generate code.  We, however,
	 * have different requirements, and therefore enforce the rule that the
	 * definition of a constant must precede its use in all cases.
	 */
	aoi_const val;
	
	if (!def)
		panic("Nil constant definition!");
	
	if (IsDigit(def[0]))
		/* If it is a number, evaluate it. */
		val = aoi_new_const_int(xl_eval(def_ptr, def));
	else if (def[0] == '"') {
		/* If it is a string, evaluate it. */
		if (must_be_int)
			panic("`%s' is not an integer constant.", def);
		else
			val = aoi_new_const_string(trim_string(def));
	} else {
		/* Otherwise, it is a name.  Find its binding. */
		int ref = aoi_name(def_ptr, def, NO_CREATE);
		
		if ((ref < 0) || (outaoi.defs.defs_val[ref].binding == 0)) {
			/* If it's not defined, it's an error. */
			panic("`%s' is not a defined constant.", def);
		} else {
			/*
			 * The name has a binding.  It must be a CONST_INT or a
			 * CONST_ARRAY, otherwise stuff won't work as expected.
			 * So we need to verify that it is.
			 */
			aoi_type bind = outaoi.defs.defs_val[ref].binding;
			aoi_const refd;
			
			if (bind->kind != AOI_CONST)
				panic("`%s' does not refer to a constant.",
				      def);
			
			refd = bind->aoi_type_u_u.const_def.value;
			if (refd->kind == AOI_CONST_INT)
				val = refd;
			else if (must_be_int)
				panic(("`%s' does not refer to an integer "
				       "constant."),
				      def);
			else if (refd->kind == AOI_CONST_ARRAY)
				val = refd;
			else
				panic(("`%s' does not refer to a constant "
				       "integer or array."),
				      def);
		}
	}
	return val;
}

static aoi_type xl_const(definition *def_ptr, const_def def)
{
	/*
	 * Constants are UNTYPED in XDR.  This causes some pretty hokey
	 * problems in the optimizer, so they are constrained to be
	 * dereferenceable if used.  This means that you can't just say
	 * `const a = b', unless `b' is declared as `const b = 1'.
	 */
	aoi_type res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	
	res->kind = AOI_CONST;
	res->aoi_type_u_u.const_def.type = new_int(-2147483647-1, ~0U);
	res->aoi_type_u_u.const_def.value = xl_deref_const(def_ptr, def, 0);
	
	return res;
}

static aoi_type xl_enum(definition *def_ptr, enum_def def, char *name)
{
	/*
	 * Iterate through the list of enum's, creating a new const in the
	 * list of data types.  Note that there is a disclaimer regarding
	 * enums: it states that they must be ints, but then we lose data
	 * about the enumeration.  (CORBA doesn't specify.)  BE CAREFUL!
	 */
	enumval_list *curr;
	aoi_type res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	aoi_enum *val = &(res->aoi_type_u_u.enum_def);
	aoi_field *vals;
	int currval = 0;
	int pos = 0;
	
	/*****/
	
	res->kind = AOI_ENUM;
	
	val->enum_label = name;
	
	/* Count the # of fields. */
	val->defs.defs_len = 0;
	for (curr = def.vals; curr; curr = curr->next)
		val->defs.defs_len++;
	/* Allocate space for the values. */
	vals = (aoi_field *)
	       mustmalloc(sizeof(aoi_field) * val->defs.defs_len);
	
	/* Create new const's for each value. */
	for (curr = def.vals; curr; curr = curr->next, pos++) {
		vals[pos].name = curr->name;
		/*
		 * If they haven't specified the value, choose the next value.
		 */
		if (!curr->assignment) {
			char name[15];
			itoa(currval, name);
			vals[pos].type = xl_const(def_ptr, name);
		} else
			vals[pos].type = xl_const(def_ptr, curr->assignment);
		
		if (vals[pos].type->aoi_type_u_u.const_def.value->kind
		    != AOI_CONST_INT)
			panic("Invalid value for enumeration.");
		
		/* Increment the next default value. */
		currval = (vals[pos].type->aoi_type_u_u.const_def.value->
			   aoi_const_u_u.const_int)
			  + 1;
	}
	
	val->defs.defs_val = vals;
	return res;
}

static aoi_type xl_td_buildtype(definition *def_ptr, char *type)
{
	aoi_type res;
	
	if (!strcmp(type, "float")) {
		res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_FLOAT;
		res->aoi_type_u_u.float_def.bits = 32;
		
	} else if (!strcmp(type, "double")) {
		res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_FLOAT;
		res->aoi_type_u_u.float_def.bits = 64;
		
	} else if (!strcmp(type, "char")) {
		res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_CHAR;
		res->aoi_type_u_u.char_def.bits = 8;
		res->aoi_type_u_u.char_def.flags = AOI_CHAR_FLAG_NONE;
		
	} else if (!strcmp(type, "u_char")) {
		res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_CHAR;
		res->aoi_type_u_u.char_def.bits = 8;
		res->aoi_type_u_u.char_def.flags = AOI_CHAR_FLAG_UNSIGNED;
		
	} else if (!strcmp(type, "void")) {
		res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_VOID;
		
	} else if (!strcmp(type, "string")) {
		aoi_type chr, len;
		
		res = (aoi_type)mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_ARRAY;
		res->aoi_type_u_u.array_def.flgs
			= AOI_ARRAY_FLAG_NULL_TERMINATED_STRING;
		chr = (aoi_type)mustmalloc(sizeof(aoi_type_u));
		chr->kind = AOI_CHAR;
		chr->aoi_type_u_u.char_def.bits = 8;
		chr->aoi_type_u_u.char_def.flags = AOI_CHAR_FLAG_NONE;
		len = new_int(0, 4294967295U);
		res->aoi_type_u_u.array_def.element_type = chr;
		res->aoi_type_u_u.array_def.length_type = len;
		
	} else if (!strcmp(type, "opaque")) {
		aoi_type chr, len;
		
		res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_ARRAY;
		res->aoi_type_u_u.array_def.flgs = AOI_ARRAY_FLAG_OPAQUE;
		chr = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		chr->kind = AOI_INTEGER;
		chr->aoi_type_u_u.integer_def.min = 0;
		chr->aoi_type_u_u.integer_def.range = 255U;
		len = new_int(0, 4294967295U);
		res->aoi_type_u_u.array_def.element_type = chr;
		res->aoi_type_u_u.array_def.length_type = len;
		
	} else if (!strcmp(type, "int") || !strcmp(type, "long"))
		res = new_int(-2147483647-1, ~0U);
	
	else if (!strcmp(type, "u_int") || !strcmp(type, "u_long"))
		res = new_int(0, ~0U);
	
	else if (!strcmp(type, "short"))
		res = new_int(-32768, 65535);
	
	else if (!strcmp(type, "u_short"))
		res = new_int(0, 65535);
	
	else if (!strcmp(type, "bool"))
		res = new_int(0, 1);
	
	else {
		/*
		 * At this point, it had better be a reference, and it should
		 * already exist in the file.  If not, we're dead...
		 */
		int ref = aoi_name(def_ptr, type, NO_CREATE);
		
		if (ref < 0)
			panic("Undefined type `%s'.", type);
		res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		res->kind = AOI_INDIRECT;
		res->aoi_type_u_u.indirect_ref = ref;
	}
	
	return res;
}

static int xl_eval(definition *def_ptr, char *val)
{
	if (!val)
		panic("Null value found in evaluation!");
	if (IsDigit(val[0])) {
		if (val[0] == '~')
			return ~xl_eval(def_ptr, &val[1]);
		if (val[1] == '-')
			return -xl_eval(def_ptr, &val[1]);
		if ((val[0] == '0') && (val[1] == 'x')) {
			/* Hexadecimal. */
			int num = 0;
			int pos = 2;
			
			while (val[pos]) {
				num = num << 4;
				if (isdigit(((int) val[pos])))
					num += (val[pos] - '0');
				else {
					num += (((val[pos] - 'A') & 0x1f)
						+ 10);
					if ((val[pos] < 'A')
					    || (val[pos] > 'f'))
						panic(("Invalid hex value "
						       "`%s'."),
						      val);
					if ((val[pos] & 0x1f) > 5)
						panic(("Invalid hex value "
						       "`%s'."),
						      val);
				}
				pos++;
			}
			return num;
		} else
			return atoi(val);
		
	} else {
		int ref = aoi_name(def_ptr, val, NO_CREATE);
		aoi_type bind = outaoi.defs.defs_val[ref].binding;
		
		if (ref < 0)
			panic("Undefined const `%s'.", val);
		if (bind->kind != AOI_CONST)
			panic("Invalid type found in evaluation.");
		if (bind->aoi_type_u_u.const_def.value->kind != AOI_CONST_INT)
			panic("Invalid const type found in evaluation.");
		
		return (bind->aoi_type_u_u.const_def.value->aoi_const_u_u.
			const_int);
	}
}

static aoi_type xl_typedef(definition *def_ptr, typedef_def def)
{
	/*
	 * This should be an indirect to the appropriate type, be it structure,
	 * union, enum, etc.  It will also possible be an array/pointer to that
	 * structure (requires deeper indirection old_prefix is necessary for
	 * forward references).
	 */
	aoi_type component_type, result_type;
	int array_maximum;
	
	/* First, build the component type. */
	component_type = xl_td_buildtype(def_ptr, def.old_type);
	
	if (def.rel == REL_ALIAS)
		return component_type;
	else if (def.rel == REL_POINTER)
		return make_optional(component_type);
	
	if (!strcmp(def.old_type, "string") || !strcmp(def.old_type, "opaque"))
		/*
		 * `xl_td_buildtype' builds strings and opaques as variable
		 * length arrays.  We need to reset the array bounds below.
		 */
		result_type = component_type;
	
	else if ((def.rel == REL_VECTOR) || (def.rel == REL_ARRAY)) {
		/* It's an array. */
		result_type = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		result_type->kind = AOI_ARRAY;
		result_type->aoi_type_u_u.array_def.element_type
			= component_type;
		result_type->aoi_type_u_u.array_def.flgs = AOI_ARRAY_FLAG_NONE;
		
	} else
		panic("Unknown typedef type %d in `xl_typedef'.", def.rel);
	
	/*
	 * At this point we are dealing with a REL_VECTOR or a REL_ARRAY.
	 * We need to determine the array's length.
	 */
	array_maximum = xl_eval(def_ptr, def.array_max);
	switch (def.rel) {
	case REL_VECTOR:
		result_type->aoi_type_u_u.array_def.length_type =
			new_int(array_maximum, 0);
		break;
	case REL_ARRAY:
		result_type->aoi_type_u_u.array_def.length_type =
			new_int(0, array_maximum);
		break;
	default:
		panic("Unknown array type %d in `xl_typedef'.", def.rel);
		break;
	}
	
	return result_type;
}

static aoi_type xl_decl(definition *def_ptr, declaration decl)
{
	typedef_def td;
	
	td.old_prefix = decl.prefix;
	td.old_type = decl.type;
	td.rel = decl.rel;
	td.array_max = decl.array_max;
	
	return xl_typedef(def_ptr, td);
}

static aoi_type xl_struct(definition *def_ptr, struct_def def)
{
	/*
	 * Since XDR doesn't allow nested _definitions_, this code is REALLY
	 * simple.  We just count the number of slots, and then define them
	 * using the typedef code.
	 */
	aoi_type res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	int len = 0;
	decl_list *curr_slot;
	aoi_struct_slot *slots;
	
	/* Count the number of slots to allocate. */
	for (curr_slot = def.decls; curr_slot; curr_slot = curr_slot->next)
		len++;
	
	res->kind = AOI_STRUCT;
	slots = (aoi_struct_slot *) mustmalloc(len * sizeof(aoi_struct_slot));
	res->aoi_type_u_u.struct_def.slots.slots_len = len;
	res->aoi_type_u_u.struct_def.slots.slots_val = slots;
	
	/* Step through the list, dealing with each item in the struct. */
	len = 0;
	for (curr_slot = def.decls;
	     curr_slot;
	     curr_slot = curr_slot->next, len++) {
		slots[len].name = curr_slot->decl.name;
		slots[len].type = xl_decl(def_ptr, curr_slot->decl);
	}
	
	return res;
}

static aoi_type xl_union(definition *def_ptr, union_def def, char *name)
{
	/*
	 * Since XDR doesn't allow nested _definitions_, this code is REALLY
	 * simple.  We just count the number of slots and then define them
	 * using the typedef code.
	 */
	aoi_type res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	int len = 0;
	case_list *curr_slot;
	aoi_union_case *slots;
	char *newname;
	
	/* Set up the discriminator. */
	res->aoi_type_u_u.union_def.discriminator.type
		= xl_decl(def_ptr, def.enum_decl);
	res->aoi_type_u_u.union_def.discriminator.name
		= def.enum_decl.name;
	
	len = strlen(name);
	newname = (char *) mustmalloc(len + 2);
	strcpy(newname, name);
	newname[len]     = '_';
	newname[len + 1] = 'u';
	newname[len + 2] = 0;
	res->aoi_type_u_u.union_def.union_label = newname;
	
	/* Allocate space for the variants. */
	len = 0;
	for (curr_slot = def.cases; curr_slot; curr_slot = curr_slot->next)
		len++;
	
	res->kind = AOI_UNION;
	slots = (aoi_union_case *) mustmalloc(len * sizeof(aoi_union_case));
	res->aoi_type_u_u.union_def.cases.cases_len = len;
	res->aoi_type_u_u.union_def.cases.cases_val = slots;
	
	/* Set up the default case (if it exists). */
	if (def.default_decl) {
		res->aoi_type_u_u.union_def.dfault
			= (aoi_field *) mustmalloc(sizeof(aoi_field));
		res->aoi_type_u_u.union_def.dfault->name
			= def.default_decl->name;
		res->aoi_type_u_u.union_def.dfault->type
			= xl_decl(def_ptr, *def.default_decl);
	} else
		res->aoi_type_u_u.union_def.dfault = (aoi_field *) 0;
	
	/* Set the individual cases. */
	len = 0;
	for (curr_slot = def.cases;
	     curr_slot;
	     curr_slot = curr_slot->next, len++) {
		slots[len].var.name = curr_slot->case_decl.name;
		slots[len].var.type = xl_decl(def_ptr, curr_slot->case_decl);
		
		if (res->aoi_type_u_u.union_def.discriminator.type->kind
		    == AOI_INDIRECT) {
			int ref = (res->aoi_type_u_u.union_def.discriminator.
				   type->aoi_type_u_u.indirect_ref);
			unsigned int pos;
			aoi_enum enum_def;
			
			if (outaoi.defs.defs_val[ref].binding->kind != AOI_ENUM)
				panic(("Type `%s' used in a union is not an "
				       "enum."),
				      def.enum_decl);
			/*
			 * Now we look up the appropriate name and insert the
			 * correct value.
			 */
			enum_def = outaoi.defs.defs_val[ref].binding->aoi_type_u_u.
				   enum_def;
			for (pos = 0; pos < enum_def.defs.defs_len; pos++)
				if (!strcmp(curr_slot->case_name,
					    enum_def.defs.defs_val[pos].name))
					break;
			if (pos == enum_def.defs.defs_len)
				slots[len].val
					= xl_deref_const(def_ptr,
							 curr_slot->case_name,
							 1);
			else {
				aoi_type bind = enum_def.defs.defs_val[pos].
						type;
				int val = bind->aoi_type_u_u.const_def.value->
					  aoi_const_u_u.const_int;
				
				slots[len].val = aoi_new_const_int(val);
			}
			
	        } else
			slots[len].val = xl_deref_const(def_ptr,
							curr_slot->case_name,
							1);
	}
	
	return res;
}

static aoi_interface xl_new_prog_interface(definition *def_ptr, char *code)
{
	/* This builds an empty interface to represent a program. */
	aoi_interface res;
	
	res.idl = AOI_IDL_SUN;
	res.code_type = new_int(0, 4294967295U);
	res.code = aoi_new_const_int(xl_eval(def_ptr, code));
	res.parents.parents_len = 0;
	res.ops.ops_len = 0;
	res.attribs.attribs_len = 0;
	res.excepts.excepts_len = 0;
	res.op_code_type = new_int(0, 4294967295U);
	
	return res;
}

static aoi_type xl_new_prog(definition *def_ptr, char *prog_num)
{
	/*
	 * This just creates an empty interface to represent the program
	 * containing the individual versions.
	 */
	aoi_type res = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	
	res->kind = AOI_INTERFACE;
	res->aoi_type_u_u.interface_def = xl_new_prog_interface(def_ptr,
								prog_num);
	
	return res;
}

static aoi_type xl_get_vers_discriminator_type()
{
	static aoi_type type = 0;
	
	if (type == 0) {
		aoi_struct *type_struct;
		
		type = (aoi_type) mustmalloc(sizeof(aoi_type_u));
		type->kind = AOI_STRUCT;
		type_struct = &(type->aoi_type_u_u.struct_def);
		
		/* Allocate and fill in the structure type slots. */
		type_struct->slots.slots_len
			= 2;
		type_struct->slots.slots_val
			= ((aoi_struct_slot *)
			   mustmalloc(sizeof(aoi_struct_slot) *
				      type_struct->slots.slots_len));
		
		type_struct->slots.slots_val[0].name = "prog_code";
		type_struct->slots.slots_val[0].type = new_int(0, 4294967295U);
		type_struct->slots.slots_val[1].name = "vers_code";
		type_struct->slots.slots_val[1].type = new_int(0, 4294967295U);
	}
	
	return type;
}

static aoi_interface xl_new_vers_interface(definition *def_ptr,
					   int prog_ref, char *vers_code)
{
	/* This builds an interface to represent a version. */
	aoi_interface res;
	aoi_const_struct *discriminator;
	aoi_type prog_binding;
	aoi_const prog_code;
	
	/*
	 * First, make sure we're inheriting from a program, and extract the
	 * program's discriminator code.
	 */
	prog_binding = outaoi.defs.defs_val[prog_ref].binding;
	if (prog_binding->kind != AOI_INTERFACE)
		panic("`xl_new_vers_interface' received a bogus `prog_ref'.");
	prog_code = prog_binding->aoi_type_u_u.interface_def.code;
	
	res.idl = AOI_IDL_SUN;
	
	/*
	 * Construct the union discriminator value.
	 * XXX --- Say something about why we use a struct here.
	 */
	res.code = aoi_new_const(AOI_CONST_STRUCT);
	discriminator = &(res.code->aoi_const_u_u.const_struct);
	discriminator->aoi_const_struct_len
		= 2;
	discriminator->aoi_const_struct_val
		= ((aoi_const *)
		   mustmalloc(sizeof(aoi_const) *
			      discriminator->aoi_const_struct_len));
	
	discriminator->aoi_const_struct_val[0]
		= prog_code;
	discriminator->aoi_const_struct_val[1]
		= aoi_new_const_int(xl_eval(def_ptr, vers_code));
	
	res.code_type = xl_get_vers_discriminator_type();
	
	/* Inherit the parent program. */
	res.parents.parents_len = 1;
	res.parents.parents_val = (aoi_type *) mustmalloc(sizeof(aoi_type));
	res.parents.parents_val[0] = (aoi_type) mustmalloc(sizeof(aoi_type_u));
	res.parents.parents_val[0]->kind = AOI_INDIRECT;
	res.parents.parents_val[0]->aoi_type_u_u.indirect_ref = prog_ref;
	
	/* Initialize the remaining interface fields. */
	res.ops.ops_len = 0;
	res.attribs.attribs_len = 0;
	res.excepts.excepts_len = 0;
	res.op_code_type = new_int(0, 4294967295U);
	
	return res;
}

static void xl_add_vers(definition *def_ptr, version_list *v, int prog_ref)
{
	/*
	 * This builds an interface for the version given, which inherits the
	 * program that contains this version.
	 */
	int ref = get_def();
	aoi_type bind;
	aoi_interface *ver;
	proc_list *curr_proc;
	int proc_ref;
	
	/* Create the interface. */
	outaoi.defs.defs_val[ref].name = v->vers_name;
	outaoi.defs.defs_val[ref].binding = (aoi_type)
				      mustmalloc(sizeof(aoi_type_u));
	outaoi.defs.defs_val[ref].scope = curr_scope;
	outaoi.defs.defs_val[ref].idl_file = def_ptr->idl_file;
	bind = outaoi.defs.defs_val[ref].binding;
        bind->kind = AOI_INTERFACE;
	bind->aoi_type_u_u.interface_def = xl_new_vers_interface(def_ptr,
								 prog_ref,
								 v->vers_num);
	ver = &(bind->aoi_type_u_u.interface_def);
	
	/* Add the operations. */
	proc_ref = 0;
	for (curr_proc = v->procs; curr_proc; curr_proc = curr_proc->next)
		proc_ref++;
	ver->ops.ops_len = proc_ref;
	ver->ops.ops_val = (aoi_operation *)
			   mustmalloc(sizeof(aoi_operation) * proc_ref);
	
	for (curr_proc = v->procs, proc_ref = 0;
	     curr_proc;
	     curr_proc = curr_proc->next, proc_ref++) {
		aoi_operation *curr_op = &(ver->ops.ops_val[proc_ref]);
		
		curr_op->name = curr_proc->proc_name;
		curr_op->request_code
			= aoi_new_const_int(xl_eval(def_ptr,
						    curr_proc->proc_num));
		/*
		 * XXX --- Adding 1024 to the procedure number is a hack.
		 * Flick currently requires that the request and reply codes be
		 * different.
		 */
		curr_op->reply_code
			= aoi_new_const_int(xl_eval(def_ptr,
						    curr_proc->proc_num)
					    + 1024);
		curr_op->flags = AOI_OP_FLAG_NONE;
		curr_op->return_type = xl_td_buildtype(def_ptr,
						       curr_proc->res_type);
		curr_op->params.params_len = 1;
		curr_op->params.params_val
			= (aoi_parameter *) mustmalloc(sizeof(aoi_parameter));
		/*
		 * curr_op->params.params_val[0].name = (char *) 0;
		 * ENE: A parameter with no name is bad news when we check the
		 * CAST.  We might as well give the parameter a name now.
		 */
		curr_op->params.params_val[0].name = "arg";
		curr_op->params.params_val[0].direction = AOI_DIR_IN;
		curr_op->params.params_val[0].type
			= xl_td_buildtype(def_ptr, curr_proc->arg_type);
	}
}

static aoi_type xl_program(definition *def_ptr, program_def def, int ref)
{
	/*
	 * Create an empty interface for return, but also create interfaces for
	 * each version, each of which inherit the program interface, and
	 * contain the individual procedures.
	 */
	aoi_type res = xl_new_prog(def_ptr, def.prog_num);
	version_list *curr_vers;
	
	/*
	 * We must establish the `outaoi' binding for this program now, so that
	 * `xl_new_vers_intreface' can dereference `ref' to find the current
	 * program.  (Normally, the binding is established by `translate'.)
	 * Alternately, we could pass the current program code down, but it
	 * seems like a better idea just to establish the binding.
	 */
	outaoi.defs.defs_val[ref].binding = res;
	for (curr_vers = def.versions; curr_vers; curr_vers = curr_vers->next)
		xl_add_vers(def_ptr, curr_vers, ref);
	
	return res;
}

static aoi_type xl_scope(definition *def, int refnum)
{
	switch (def->def_kind){
	case DEF_TYPEDEF:
		return xl_typedef(def, def->def.ty);
		break;
		
	case DEF_CONST:
		return xl_const(def, def->def.co);
		break;
		
	case DEF_STRUCT:
		return xl_struct(def, def->def.st);
		break;
		
	case DEF_UNION:
		return xl_union(def, def->def.un, def->def_name);
		break;
		
	case DEF_ENUM:
		return xl_enum(def, def->def.en, def->def_name);
		break;
		
	case DEF_PROGRAM:
		return xl_program(def, def->def.pr, refnum);
		break;
		
	default:
		panic("Don't know def_kind %d.", def->def_kind);
	}
	
	return ((aoi_type) 0);
}

void translate(void)
{
	definition *def;
	
	outaoi.defs.defs_len = 0;
	outaoi.defs.defs_val = 0;
	aoi_max = 0;
	
	for (def = defs; def; def = def->next)
		if (aoi_name(def, def->def_name, MUST_CREATE) < 0)
			panic("Duplicate symbol `%s'.", def->def_name);
	
	for (def = defs; def; def = def->next) {
		int refnum = aoi_name(def, def->def_name, NO_CREATE);
		
		/* Find its binding. */
		outaoi.defs.defs_val[refnum].binding = xl_scope(def, refnum);
	}
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1