/* %Z%%M% %I% %E% */

/*
 * Copyright (c) 1992, 1993, 1994, 1995, 2000 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 code for generating the STIPPLE routine declarations
 * for generate clauses:
 */

#ifndef FLAGS_DEFS_H
#include "flags_defs.h"
#endif

#ifndef GENERATE_DEFS_H
#include "generate_defs.h"
#endif

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

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

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

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

#ifndef PARSER_DEFS_H
#include "parser_defs.h"
#endif

#ifndef STR_EXPORTS_H
#include "str_exports.h"
#endif

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

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

#ifndef UNIX_ASSERT_H
#include "unix_assert.h"
#endif

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

LOCAL Stdio	generate_gen_file_open(Parser);

/*
 * generate_gen_file_open(parser)
 *	This routine will open a generate file from "parser".
 */
LOCAL Stdio
generate_gen_file_open(
	Parser		parser)
{
	Stdio		gen_file;

	gen_file = parser->gen_file;
	if (gen_file == (Stdio)0) {
		gen_file = fopen(parser->flags->gen_file->full, "w");
		parser->gen_file = gen_file;
	}
	return gen_file;
}

/*
 * routine_address_get_output(type_def, gen_file)
 *	This routine will output an address_get routine for "type_def"
 *	to "gen_file".
 */
LOCAL void
routine_address_get_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure address_get@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\tobject %r\n", 2, type_ref);
	out(gen_file, "%\treturns address\n", 1);
	out(gen_file, "%\texternal %s__address_get\n", 1, type_name);
	out(gen_file, "\n");
}

/*
 * routine_new_output(type_def, gen_file)
 *	This routine will output a new routine for "type_def" to "gen_file".
 */
LOCAL void
routine_allocate_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure allocate__helper@%r\n", type_ref);
	out(gen_file, "%\ttakes_nothing\n", 1);
	out(gen_file, "%\treturns %r\n", 1, type_ref);
	out(gen_file, "%\texternal %s__allocate__helper\n", 1, type_name);
	out(gen_file, "\n");

	out(gen_file, "procedure allocate@%r\n", type_ref);
	out(gen_file, "%\ttakes_nothing\n", 1);
	out(gen_file, "%\treturns %r\n", 1, type_ref);
	out(gen_file, "\n");
	out(gen_file, "%\tallocate:: %r := allocate__helper@%r()\n",
	    1, type_ref, type_ref);
	out(gen_file, "%\terase@(allocate)\n", 1);
	out(gen_file, "%\treturn allocate\n", 1);
	out(gen_file, "\n");
}

/*
 * routine_copy_output(type_def, gen_file)
 *	This routine will output an copy routine for "type_def" to "gen_file".
 */
LOCAL void
routine_copy_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Type_ref	parameter;
	Type_refs	parameters;
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure copy@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s1 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\theap heap\n", 2);
	out(gen_file, "%\treturns %r\n", 1, type_ref);

	if (type_ref_is_parameterized(type_def->type_ref)) {
		parameters = type_def->type_ref->parameters;
		out(gen_file, "%\tneeds\n", 1);
		TYPE_REFS_LOOP(parameters, parameter) {
			out(gen_file, "%\tprocedure copy@%r\n", 2, parameter);
			out(gen_file, "%\ttakes %r, heap\n", 3, parameter);
			out(gen_file, "%\treturns %r\n", 3, parameter);
		}
	}
	out(gen_file, "\n");

	switch (type_def->kind) {
	    case Type_kind_enumeration:
		out(gen_file, "%\treturn %s1\n", 1, type_name);
		break;
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;

		out(gen_file, "%\t%s2:: %r := new@%r(heap)\n",
		    1, type_name, type_ref, type_ref);
		fields = type_def->value.record->fields;
		VEC_LOOP(Type_field, fields, field) {
			field_name = field->name;
			out(gen_file, "%\t%s2.%s := copy@(%s1.%s, heap)\n",
			    1, type_name, field_name, type_name, field_name);
		}
		out(gen_file, "%\treturn %s2\n", 1, type_name);
		break;
	      }
	    case Type_kind_variant:
		assert_fail();
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * routine_equal_output(type_def, gen_file)
 *	This routine will output an equal routine for "type_def" to "gen_file".
 */
LOCAL void
routine_equal_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Type_ref	parameter;
	Type_refs	parameters;
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure equal@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s1 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\t%s2 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\treturns logical\n", 1);

	if (type_ref_is_parameterized(type_def->type_ref)) {
		parameters = type_def->type_ref->parameters;
		out(gen_file, "%\tneeds\n", 1);
		TYPE_REFS_LOOP(parameters, parameter) {
			out(gen_file, "%\tprocedure equal@%r\n", 2, parameter);
			out(gen_file, "%\ttakes %r, %r\n",
			    3, parameter, parameter);
			out(gen_file, "%\treturns logical\n", 3);
		}
	}

	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		out(gen_file, "%\texternal %s__equal\n", 1, type_name);
		break;
	      }
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;
		int		index;
		int		size;

		out(gen_file, "\n");
		fields = type_def->value.record->fields;
		size = vec_size(Type_field, fields);
		for (index = 0; index < size; index++) {
			field = vec_fetch(Type_field, fields, index);
			field_name = field->name;
			if (index == 0) {
				out(gen_file, "%\treturn ", 1);
			} else {
				out(gen_file, "%\t", 2);
			}
			out(gen_file, "%s1.%s = %s2.%s",
			    type_name, field_name, type_name, field_name);
			if (index < size - 1) {
				out(gen_file, " &&");
			}
			out(gen_file, "\n");
		}
		break;
	      }
	    case Type_kind_variant:
		assert_fail();
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * routine_erase_output(type_def, gen_file)
 *	This routine will output an erase routine for "type_def" to "gen_file".
 */
LOCAL void
routine_erase_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure erase@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s %r\n", 2, type_name, type_ref);
	out(gen_file, "%\treturns_nothing\n", 1);

	switch (type_def->kind) {
	    case Type_kind_enumeration:
		assert_fail();
		break;
	    case Type_kind_record:
	      {
		Type_field	field;
		Type_fields	fields;

		out(gen_file, "\n");
		fields = type_def->value.record->fields;
		VEC_LOOP(Type_field, fields, field) {
			out(gen_file, "%\t%s.%s := ??\n",
			    1, type_name, field->name);
		}
		break;
	      }
	    case Type_kind_variant:
	      {
		Type_field	field;
		Type_fields	fields;

		out(gen_file, "\n");
		fields = type_def->value.record->fields;
		assert(!vec_empty(Type_field, fields));
		field = vec_fetch(Type_field, fields, 0);
		out(gen_file, "%\t%s.%s := ??\n", 1, type_name, field->name);
		break;
	      }
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * routine_hash_output(type_def, gen_file)
 *	This routine will output a hash routine for "type_def" to "gen_file".
 */
LOCAL void
routine_hash_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Type_ref	parameter;
	Type_refs	parameters;
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure hash@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s1 %s\n", 2, type_name, type_name);
	out(gen_file, "%\treturns unsigned\n", 1);

	if (type_ref_is_parameterized(type_def->type_ref)) {
		parameters = type_def->type_ref->parameters;
		out(gen_file, "%\tneeds\n", 1);
		TYPE_REFS_LOOP(parameters, parameter) {
			out(gen_file, "%\tprocedure hash@%r\n", 2, parameter);
			out(gen_file, "%\ttakes %r\n", 3, parameter);
			out(gen_file, "%\treturns unsigned\n", 3);
		}
	}
	out(gen_file, "\n");

	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		out(gen_file, "%\treturn unsigned_convert@(%s1)\n",
		    1, type_name);
		break;
	      }
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;
		int		index;
		int		size;

		fields = type_def->value.record->fields;
		size = vec_size(Type_field, fields);
		for (index = 0; index < size; index++) {
			field = vec_fetch(Type_field, fields, index);
			field_name = field->name;
			if (index == 0) {
				out(gen_file, "%\treturn ", 1);
			} else {
				out(gen_file, "%\t", 2);
			}
			out(gen_file, "hash@(%s1.%s)", type_name, field_name);
			if (index < size - 1) {
				out(gen_file, " +");
			}
			out(gen_file, "\n");
		}
		break;
	      }
	    case Type_kind_variant:
		assert_fail();
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * routine_identical_output(type_def, gen_file)
 *	This routine will output an equal routine for "type_def" to "gen_file".
 */
LOCAL void
routine_identical_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure identical@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s1 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\t%s2 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\treturns logical\n", 1);
	out(gen_file, "%\texternal %s__equal\n", 1, type_name);
	out(gen_file, "\n");
}

/*
 * routine_integer_convert_output(type_def, gen_file)
 *	This routine will output a integer_convert routine for "type_def"
 *	to "gen_file".
 */
LOCAL void
routine_integer_convert_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure integer_convert@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s1 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\treturns integer\n", 1);
	out(gen_file, "\n");

	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		Type_item	item;
		Str		item_name;
		Vec(Type_item)	items;
		int		index;
		int		size;

		out(gen_file, "%\tswitch %s1\n", 1, type_name);
		items = type_def->value.enumeration->items;
		size = vec_size(Type_item, items);
		for (index = 0; index < size; index++) {
			item = vec_fetch(Type_item, items, index);
			item_name = item->name;
			out(gen_file, "%\tcase %s\n", 2, item_name);
			out(gen_file, "%\treturn %d\n", 3, index);
		}
		break;
	      }
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * routine_new_output(type_def, gen_file)
 *	This routine will output a new routine for "type_def" to "gen_file".
 */
LOCAL void
routine_new_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure new__helper@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\theap heap\n", 2);
	out(gen_file, "%\treturns %r\n", 1, type_ref);
	out(gen_file, "%\texternal %s__new__helper\n", 1, type_name);
	out(gen_file, "\n");

	out(gen_file, "procedure new@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\theap heap\n", 2);
	out(gen_file, "%\treturns %r\n", 1, type_ref);
	out(gen_file, "\n");
	out(gen_file, "%\tnew:: %r := new__helper@%r(heap)\n",
	    1, type_ref, type_ref);
	out(gen_file, "%\terase@(new)\n", 1);
	out(gen_file, "%\treturn new\n", 1);
	out(gen_file, "\n");
}

/*
 * routine_print_output(type_def, gen_file)
 *	This routine will output an print routine for "type_def" to "gen_file".
 */
LOCAL void
routine_print_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Type_ref	parameter;
	Type_refs	parameters;
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure print@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s1 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\tout_stream out_stream\n", 2);
	out(gen_file, "%\treturns_nothing\n", 1);

	if (type_ref_is_parameterized(type_def->type_ref)) {
		parameters = type_def->type_ref->parameters;
		out(gen_file, "%\tneeds\n", 1);
		TYPE_REFS_LOOP(parameters, parameter) {
			out(gen_file, "%\tprocedure print@%r\n", 2, parameter);
			out(gen_file,
			    "%\ttakes %r, out_stream\n", 3, parameter);
			out(gen_file, "%\treturns_nothing\n", 3);
		}
	}
	out(gen_file, "\n");

	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		Type_item	item;
		Str		item_name;
		Vec(Type_item)	items;

		out(gen_file, "%\tswitch %s1\n", 1, type_name);
		items = type_def->value.enumeration->items;
		VEC_LOOP(Type_item, items, item) {
			item_name = item->name;
			out(gen_file, "%\tcase %s\n", 2, item_name);
			out(gen_file, "%\tput@(\"%s\", out_stream)\n",
			    3, item_name);
		}
		break;
	      }
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;
		int		index;
		int		size;

		fields = type_def->value.record->fields;
		size = vec_size(Type_field, fields);
		for (index = 0; index < size; index++){
			field = vec_fetch(Type_field, fields, index);
			field_name = field->name;
			out(gen_file, "%\tput@(\"%s%s=\", out_stream)\n",
			    1, (index == 0) ? "{" : ", ", field_name);
			out(gen_file, "%\tprint@(%s1.%s, out_stream)\n",
			    1, type_name, field_name);
		}
		out(gen_file, "%\tput@(\"}\", out_stream)\n", 1);
		break;
	      }
	    case Type_kind_variant:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;

		out(gen_file, "%\textract %s1\n", 1, type_name);
		fields = type_def->value.record->fields;
		VEC_LOOP(Type_field, fields, field) {
			field_name = field->name;
			out(gen_file, "%\ttag %s:: %r := %s\n", 2,
			    field_name, field->type_ref, field_name);
			out(gen_file, "%\tprint@(%s, out_stream)\n",
			    3, field_name);
		}
		break;
	      }
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * routine_save_output(type_def, gen_file)
 *	This routine will output an copy routine for "type_def" to "gen_file".
 */
LOCAL void
routine_save_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Type_ref	parameter;
	Type_refs	parameters;
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure save@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\tobject %r\n", 2, type_ref);
	out(gen_file, "%\tsave save\n", 2);
	out(gen_file, "%\toffset unsigned\n", 2);
	out(gen_file, "%\treturns_nothing\n", 1);

	if (type_ref_is_parameterized(type_def->type_ref)) {
		parameters = type_def->type_ref->parameters;
		out(gen_file, "%\tneeds\n", 1);
		TYPE_REFS_LOOP(parameters, parameter) {
			out(gen_file, "%\tprocedure save@%r\n", 2, parameter);
			out(gen_file,
			    "%\ttakes %r, save, unsigned\n", 3, parameter);
			out(gen_file, "%\treturns_nothing\n", 3, parameter);
		}
	}
	out(gen_file, "\n");

	switch (type_def->kind) {
	    case Type_kind_enumeration:
		out(gen_file,
		    "%\tsave@(unsigned_convert@(object), save, offset)\n", 1);
		break;
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Type_ref	field_type;
		Type_fields	fields;
		int		index;
		int		size;

		fields = type_def->value.record->fields;
		size = vec_size(Type_field, fields);
		out(gen_file, "%\trecord_offset :@= "
		    "read_write_lookup@(save, object.address)\n", 1);
		out(gen_file, "%\tif record_offset = 0\n", 1);
		out(gen_file, "%\trecord_offset := "
		    "read_write_allocate@(save, object.address, %d)\n",
		    2, size);
		for (index = 0; index < size; index++) {
			field = vec_fetch(Type_field, fields, index);
			field_name = field->name;
			field_type = field->type_ref;
			if (type_ref_is_routine(field_type)) {
				out(gen_file,
				    "%\tprocedure@save1[%s](object.%s, save)\n",
				    2, "proc type", field_name);
			} else {
				out(gen_file, "%\tsave@(object.%s, save, "
				    	      "record_offset + %d)\n",
	 			    2, field_name, index);
			}
		}
		out(gen_file,
		    "%\tsave[offset] := read_write@(save, record_offset)\n", 1);
		break;
	      }
	    case Type_kind_variant:
	      {
		Type_field	field;
		Type_fields	fields;
		Str		field_name;
		Type_ref	field_type;
		int		index;
		int		size;
		Type_field	tag_field;
		Str		tag_field_name;
		Type_ref	tag_field_type;
		Type_variant	variant;

		out(gen_file, "%\tvariant_offset :@= "
		    "read_write_lookup@(save, object.address)\n", 1);
		out(gen_file, "%\tif variant_offset = 0\n", 1);
		out(gen_file, "%\tvariant_offset := "
		    "read_write_allocate@(save, object.address, 2)\n", 2);
		out(gen_file, "%\textract object\n", 2);

		variant = type_def->value.variant;
		tag_field = variant->tag_field;
		tag_field_name = tag_field->name;
		tag_field_type = tag_field->type_ref;
		fields = variant->fields;
		size = vec_size(Type_field, fields);
		for (index = 0; index < size; index++) {
			field = vec_fetch(Type_field, fields, index);
			field_name = field->name;
			field_type = field->type_ref;
			out(gen_file, "%\ttag tag_%d:: %r := %s\n",
			    3, index, field_type, field_name);
/*			out(gen_file, "%\tsave@(unsigned_convert@(%s@%r), "
			    "save, variant_offset + 0)\n",
			    4, field_name, tag_field_type); */
			if (type_ref_is_routine(field_type)) {
				out(gen_file,
				    "%\tprocedure@save1[%s](object.%s, save)\n",
				    4, "proc type", field_name);
			} else {
				out(gen_file, "%\tsave@(tag_%d, save, "
				    "variant_offset)\n", 4, index);
			}
			out(gen_file,
			    "%\tsave@(%d, save, variant_offset + 1)\n",
			    4, index);
		}
		out(gen_file, "%\tsave[offset] := "
		    "read_write@(save, variant_offset)\n", 1);
		break;
	      }
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * routine_unsigned_convert_output(type_def, gen_file)
 *	This routine will output a unsigned_convert routine for "type_def"
 *	to "gen_file".
 */
LOCAL void
routine_unsigned_convert_output(
	Type_def	type_def,
	Stdio		gen_file)
{
	Str		type_name;
	Type_ref	type_ref;

	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	out(gen_file, "procedure unsigned_convert@%r\n", type_ref);
	out(gen_file, "%\ttakes\n", 1);
	out(gen_file, "%\t%s1 %r\n", 2, type_name, type_ref);
	out(gen_file, "%\treturns unsigned\n", 1);
	out(gen_file, "\n");

	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		Type_item	item;
		Str		item_name;
		Vec(Type_item)	items;
		int		index;
		int		size;

		out(gen_file, "%\tswitch %s1\n", 1, type_name);
		items = type_def->value.enumeration->items;
		size = vec_size(Type_item, items);
		for (index = 0; index < size; index++) {
			item = vec_fetch(Type_item, items, index);
			item_name = item->name;
			out(gen_file, "%\tcase %s\n", 2, item_name);
			out(gen_file, "%\treturn %d\n", 3, index);
		}
		break;
	      }
	    default:
		assert_fail();
	}
	out(gen_file, "\n");
}

/*
 * generate_type_def_routine(type_def, generate, position, parser)
 *	Generate the STIPPLE code for each generate clause.
 */
void
generate_type_def_routine(
	Generate	generate,
	Type_def	type_def,
	int		position,
	Parser		parser)
{
	int		error;
	Stdio		gen_file;

	gen_file = generate_gen_file_open(parser);
	error = 0;
	switch (type_def->kind) {
	    case Type_kind_enumeration:
		switch (generate->kind) {
		    case Generate_address_get:
			error = 1;
		    case Generate_copy:
			break;
		    case Generate_equal:
			break;
		    case Generate_erase:
			error = 1;
			break;
		    case Generate_hash:
			break;
		    case Generate_integer_convert:
			break;
		    case Generate_identical:
			break;
		    case Generate_print:
			break;
		    case Generate_save:
			break;
		    case Generate_unsigned_convert:
			break;
		    default:
			assert_fail();
			break;
		}
		break;
	    case Type_kind_record:
		break;
	    case Type_kind_variant:
		break;
	    default:
		assert_fail();
	}
	if (error) {
		msg_out(parser->msg, position, "generate error");
	} else {
		switch (generate->kind) {
		    case Generate_address_get:
			routine_address_get_output(type_def, gen_file);
			break;
		    case Generate_allocate:
			routine_allocate_output(type_def, gen_file);
			break;
		    case Generate_copy:
			routine_copy_output(type_def, gen_file);
			break;
		    case Generate_equal:
			routine_equal_output(type_def, gen_file);
			break;
		    case Generate_erase:
			routine_erase_output(type_def, gen_file);
			break;
		    case Generate_hash:
			routine_hash_output(type_def, gen_file);
			break;
		    case Generate_identical:
			routine_identical_output(type_def, gen_file);
			break;
		    case Generate_integer_convert:
			routine_integer_convert_output(type_def, gen_file);
			break;
		    case Generate_new:
			routine_new_output(type_def, gen_file);
			break;
		    case Generate_print:
			routine_print_output(type_def, gen_file);
			break;
		    case Generate_save:
			routine_save_output(type_def, gen_file);
			break;
		    case Generate_unsigned_convert:
			routine_unsigned_convert_output(type_def, gen_file);
			break;
		    default:
			assert_fail();
		}
	}
}

/*
 * generate_type_def_routines(type_def, parser)
 */
void
generate_type_def_routines(
	Type_def	type_def,
	Parser		parser)
{
	Type_ref	type_ref;
	Str		type_name;
	Stdio		gen_file;

	gen_file = generate_gen_file_open(parser);
	type_ref = type_def->type_ref;
	type_name = type_ref->name;
	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		/*XXX: A bunch of object statements should be emitted here! */
		break;
	      }
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;

		fields = type_def->value.record->fields;
		VEC_LOOP(Type_field, fields, field) {
			field_name = field->name;
			out(gen_file, "procedure %s_get@%r\n",
			    field_name, type_ref);
			out(gen_file, "%\ttakes\n", 1);
			out(gen_file, "%\t%s %r\n", 2, type_name, type_ref);
			out(gen_file, "%\treturns %r\n", 1, field->type_ref);
			out(gen_file, "%\texternal %s__%s_get\n",
			    1, type_name, field_name);
			out(gen_file, "\n");

			out(gen_file, "procedure %s_set@%r\n",
			    field_name, type_ref);
			out(gen_file, "%\ttakes\n", 1);
			out(gen_file, "%\t%s %r\n", 2, type_name, type_ref);
			out(gen_file, "%\t%s %r\n",
			    2, field_name, field->type_ref);
			out(gen_file, "%\treturns_nothing\n", 1);
			out(gen_file, "%\texternal %s__%s_get\n",
			    1, type_name, field_name);
			out(gen_file, "\n");
		}
		break;
	      }
	    case Type_kind_variant:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;
		Type_variant	variant;

		/* Output the type get routine: */
		variant = type_def->value.variant;
		field = variant->tag_field;
		field_name = field->name;
		out(gen_file, "procedure %s_get@%r\n", field_name, type_ref);
		out(gen_file, "%\ttakes\n", 1);
		out(gen_file, "%\t%s %r\n", 2, type_name, type_ref);
		out(gen_file, "%\treturns %r\n", 1, field->type_ref);
		out(gen_file, "%\texternal %s__%s_get\n",
		    1, type_name, field_name);
		out(gen_file, "\n");

		fields = variant->fields;
		VEC_LOOP(Type_field, fields, field) {
			field_name = field->name;
			out(gen_file, "procedure %s_get@%r\n",
			    field_name, type_ref);
			out(gen_file, "%\ttakes\n", 1);
			out(gen_file, "%\t%s %r\n", 2, type_name, type_ref);
			out(gen_file, "%\treturns %r\n", 1, field->type_ref);
			out(gen_file, "%\texternal %s__%s_get\n",
			    1, type_name, field_name);
			out(gen_file, "\n");

			out(gen_file, "procedure %s_set@%r\n",
			    field_name, type_ref);
			out(gen_file, "%\ttakes\n", 1);
			out(gen_file, "%\t%s %r\n", 2, type_name, type_ref);
			out(gen_file, "%\t%s %r\n",
			    2, field_name, field->type_ref);
			out(gen_file, "%\treturns_nothing\n", 1);
			out(gen_file, "%\texternal %s__%s_set\n",
			    1, type_name, field_name);
			out(gen_file, "\n");
		}
		break;
	      }
	    case Type_kind_external:
		break;
	    default:
		assert_fail();
	}
}
