/* %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 to generate the routines associated with a
 * generate clause:
 */

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

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

#ifndef ROUTINE_DEFS_H
#include "routine_defs.h"
#endif

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

#ifndef STRVEC_EXPORTS_H
#include "strvec_exports.h"
#endif

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

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

LOCAL void	type_def_access_gen(Type_def, Generate, Gen);
LOCAL void	type_address_get_gen(Type_def, Generate, Gen);
LOCAL void	type_def_new_gen(Type_def, Generate, Gen);
LOCAL int	type_opt_base_types(Str, Gen);

/*
 * type_def_access_gen(type_def, gen)
 *	This routine will generate the access routines for "type_def"
 *	"type_def" using "gen".
 */
/* ARGSUSED */
LOCAL void
type_def_access_gen(
	Type_def	type_def,
	Generate	generate,
	Gen		gen)
{
	int		is_parameterized;
	static Strvec	routine_strvec = (Strvec)0;
	Str		type_name;

	if (routine_strvec == (Strvec)0) {
		routine_strvec = strvec_create(gen->heap);
	}
	is_parameterized = type_ref_is_parameterized(type_def->type_ref);
	type_name = type_def->name;
	switch (type_def->kind) {
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Str		field_type;
		Type_fields	fields;
		Str		name;
		int		is_parameter;

		fields = type_def->value.record->fields;
		VEC_LOOP(Type_field, fields, field) {
			/* Generate the get routine: */
			field_name = field->name;
			field_type = field->type_ref->name;
			name = type_translate(field_type, gen->heap);
			is_parameter = type_ref_is_parameter(field->type_ref);
			if (is_parameter) {
				gen_out(gen, "void *");
			} else {
				gen_out(gen, "%s ", name);
			}
			gen_out(gen, "%s__%s_get(", type_name, field_name);
			gen_out(gen, "%T %s", type_name, type_name);
			if (is_parameterized > 0) {
				gen_out(gen, ", void *_block_");
			}
			gen_out(gen, ")\n");
			gen_out(gen, "{\n");
			gen_out(gen, "%\treturn %s->%s;\n",
				1, type_name, field_name);
			gen_out(gen, "}\n");
			gen_out(gen, "\n");

			/* Generate the set routine: */
			gen_out(gen, "void %s__%s_set(%T %s, ",
				type_name, field_name, type_name, type_name);
			if (is_parameter) {
				gen_out(gen, "void *%S", field_name);
			} else if (type_opt_base_types(field_type, gen)) {
				gen_out(gen, "%s %S", name, field_name);
			} else {
				gen_out(gen, "%T %S", field_type, field_name);
			}
			if (is_parameterized > 0) {
				gen_out(gen, ", void *_block_");
			}
			gen_out(gen, ")\n");
			gen_out(gen, "{\n");
			gen_out(gen, "%\t%s->%S = %S;\n", 1,
				type_name, field_name, field_name);
			gen_out(gen, "}\n");
			gen_out(gen, "\n");
		}
		break;
	      }
	    case Type_kind_variant:
	      {
		Type_field	field;
		Str		field_name;
		Str		field_type;
		Type_fields	fields;
		Type_field	tag_field;
		Str		tag_field_name;
		Str		tag_field_type;
		Type_variant	variant;

		variant = type_def->value.variant;

		/* Generate tag field access routine: */
		tag_field = variant->tag_field;
		tag_field_name = tag_field->name;
		tag_field_type = tag_field->type_ref->name;
		gen_out(gen, "%T %s__%s_get(%T %s%s)\n",
			tag_field_type, type_name, tag_field_name,
			type_name, type_name,
			is_parameterized ? ", void *_block_" : "");
		gen_out(gen, "{\n");
		gen_out(gen, "%\treturn %s->_tag_.%s;\n",
			1, type_name, tag_field_name);
		gen_out(gen, "}\n");
		gen_out(gen, "\n");

		fields = variant->fields;
		VEC_LOOP(Type_field, fields, field) {
			/* Generate the get routine: */
			field_name = field->name;
			field_type = field->type_ref->name;
			gen_out(gen, "%T %s__%s_get(%T %s%s)\n",
				field_type, type_name, field_name,
				type_name, type_name,
				is_parameterized ? ", void *_block_" : "");
			gen_out(gen, "{\n");

			/* Output local variable: */
			gen_out(gen, "%\tunion %s___variant _temp_;\n",
				1, type_name);
			gen_out(gen, "\n");

			/* Output the statements: */
			gen_out(gen, "%\t_temp_._entire_ = %s->_entire_;\n",
				1, type_name);
			gen_out(gen,
				"%\tif (_temp_._tag_.%S != %s__item__%s) {\n",
				1, tag_field_name, tag_field_type, field_name);
			gen_out(gen, "%\trun__time__signal(%\");\n",
				2, "Bad tag");
			gen_out(gen, "%\t}\n", 1);
			gen_out(gen, "%\treturn _temp_.%S;\n", 1, field_name);
			gen_out(gen, "}\n");
			gen_out(gen, "\n");

			/* Generate the set routine: */
			field_name = field->name;
			field_type = field->type_ref->name;
			gen_out(gen, "void %s__%s_set(%T %s, %s %S %s)\n",
				type_name, field_name, type_name, type_name,
				type_translate(field_type, gen->heap),
				field_name,
				is_parameterized ? ", void *_block" : "");
			gen_out(gen, "{\n");

			/* Output local variable: */
			gen_out(gen, "%\tunion %s___variant _temp_;\n",
				1, type_name);
			gen_out(gen, "\n");

			/* Output the statements: */
			gen_out(gen, "%\t_temp_._tag_.%S = %s__item__%s;\n",
				1, tag_field_name,
				tag_field_type, field_name);
			gen_out(gen, "%\t_temp_.%S = %S;\n",
				1, field_name, field_name);
			gen_out(gen, "%\t%s->_entire_ = _temp_._entire_;\n",
				1, type_name);

			gen_out(gen, "}\n");
			gen_out(gen, "\n");
		}
		break;
	      }
	}
}

/*
 * type_def_address_get_gen(type_def, gen)
 *	This routine will generate the address_get routine for "type_def"
 *	using "gen".
 */
/* ARGSUSED */
LOCAL void
type_def_address_get_gen(
	Type_def	type_def,
	Generate	generate,
	Gen		gen)
{
	Str		type_name;

	type_name = type_def->name;
	gen_out(gen, "void *%s__address_get(%T %s1",
		type_name, type_name, type_name);
	if (type_ref_is_parameterized(type_def->type_ref) > 0) {
		gen_out(gen, ", void *_block_");
	}
	gen_out(gen, ")\n");
	gen_out(gen, "{\n");
	gen_out(gen, "%\treturn (void *)%s1;\n", 1, type_name);
	gen_out(gen, "}\n");
	gen_out(gen, "\n");
}

/*
 * type_def_new_gen(type_def, gen)
 *	This routine will generate the new routine for "type_def"
 *	using "gen".
 */
/* ARGSUSED */
LOCAL void
type_def_allocate_gen(
	Type_def	type_def,
	Generate	generate,
	Gen		gen)
{
	Str		type_name;

	type_name = type_def->name;
	gen_out(gen, "%T %s__allocate__helper(void",
		type_name, type_name);
	if (type_ref_is_parameterized(type_def->type_ref)) {
		gen_out(gen, " *_block_");
	}
	gen_out(gen, ")\n");
	gen_out(gen, "{\n");
	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		Type_item	item;
		Vec(Type_item)	items;

		items = type_def->value.enumeration->items;
		item = vec_fetch(Type_item, items, 0);
		gen_out(gen, "%\treturn %s__item__%s;\n",
			1, type_name, item->name);
		break;
	      }
	    case Type_kind_record:
	    case Type_kind_variant:
		gen_out(gen, "%\textern void *malloc(unsigned int);\n", 1);
		gen_out(gen, "%\t%T %s;\n", 1, type_name, type_name);
		gen_out(gen, "\n");
		gen_out(gen, "%\t%s = (%T)malloc(sizeof(*%s));\n",
			1, type_name, type_name, type_name);
		gen_out(gen, "%\treturn %s;\n", 1, type_name);
		break;
	    default:
		assert(gen != gen);
	}
	gen_out(gen, "}\n");
	gen_out(gen, "\n");
}

/*
 * type_def_equal_gen(type_def, gen)
 *	This routine will generate the equal routine for "type_def"
 *	using "gen".
 */
/* ARGSUSED */
LOCAL void
type_def_equal_gen(
	Type_def	type_def,
	Generate	generate,
	Gen		gen)
{
	Str		type_name;

	type_name = type_def->name;
	gen_out(gen, "int %s__equal(%T %s1, %T %s2",
		type_name, type_name, type_name, type_name, type_name);
	if (type_ref_is_parameterized(type_def->type_ref) > 0) {
		gen_out(gen, ", void *_block_");
	}
	gen_out(gen, ")\n");
	gen_out(gen, "{\n");
	switch (type_def->kind) {
	    case Type_kind_enumeration:
		gen_out(gen, "%\treturn %s1 == %s2;\n",
			1, type_name, type_name);
		break;
	    case Type_kind_record:
	      {
		Type_field	field;
		Str		field_name;
		Type_fields	fields;
		int		index;
		Str		name;
		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) {
				gen_out(gen, "%\treturn ", 1);
			} else {
				gen_out(gen, "%\t", 2);
			}

			name = field->type_ref->name;
			if (type_opt_base_types(name, gen)) {
				gen_out(gen, "(%s1->%s == %s2->%s)",
					type_name, field_name,
					type_name, field_name);
			} else {
				gen_out(gen, "%s__equal(%s1->%s, %s2->%s)",
					name, type_name, field_name,
					type_name, field_name);
			}

			if (index + 1 == size) {
				gen_out(gen, ";\n");
			} else {
				gen_out(gen, " &&\n");
			}
		}
		break;
	      }
	    case Type_kind_variant:
	      {
		Type_field	field;
		Str		field_name;
		Str		field_type;
		Type_fields	fields;
		Type_field	tag_field;
		Str		tag_field_type;
		Type_variant	variant;

		/* Output the local variable: */
		variant = type_def->value.variant;
		tag_field = variant->tag_field;
		tag_field_type = tag_field->type_ref->name;
		gen_out(gen, "%\t%T _tag_;\n", 1, tag_field_type);
		gen_out(gen, "\n");

		/* Output the if statement: */
		gen_out(gen, "%\t_tag_ = %s1->_tag_.%s;\n",
			1, type_name, tag_field->name);
		gen_out(gen, "%\tif (_tag_ != %s2->_tag_.%s) {\n",
			1, type_name, tag_field->name);
		gen_out(gen, "%\treturn 0;\n", 2);
		gen_out(gen, "%\t}\n", 1);

		/* Output the switch statement: */
		gen_out(gen, "%\tswitch (_tag_) {\n", 1);
		fields = variant->fields;
		VEC_LOOP(Type_field, fields, field) {
			field_name = field->name;
			field_type = field->type_ref->name;
			gen_out(gen, "%\tcase %s__item__%s:\n",
				1, tag_field_type, field_name);
			if (type_opt_base_types(field_type, gen)) {
				gen_out(gen,
					"%\treturn %s1->%s == %s2->%s;\n",
					2, type_name, field_name,
					type_name, field_name);
			} else {
				gen_out(gen,
					"%\treturn %s__equal(%s1->%s, "
					"%s2->%s);\n",
					2, field_type, type_name, field_name,
					type_name, field_name);
			}
		}
		gen_out(gen, "%\t}\n", 1);
		break;
	      }
	    default:
		assert(gen != gen);
	}
	gen_out(gen, "}\n");
	gen_out(gen, "\n");
}

/*
 * type_def_identical_gen(type_def, gen)
 *	This routine will generate the identical routine for "type_def"
 *	using "gen".
 */
/* ARGSUSED */
LOCAL void
type_def_identical_gen(
	Type_def	type_def,
	Generate	generate,
	Gen		gen)
{
	Str		type_name;

	type_name = type_def->name;
	gen_out(gen, "int %s__identical(%T %s1, %T %s2",
		type_name, type_name, type_name, type_name, type_name);
	if (type_ref_is_parameterized(type_def->type_ref) > 0) {
		gen_out(gen, ", void *_block_");
	}
	gen_out(gen, ")\n");
	gen_out(gen, "{\n");
	gen_out(gen, "%\treturn %s1 == %s2;\n", 1, type_name, type_name);
	gen_out(gen, "}\n");
	gen_out(gen, "\n");
}

/*
 * type_def_initial_object_gen(type_def, gen)
 *	This routine will generate the initial object(s) for "type_def"
 *	using "gen".
 */
void
type_def_initial_object_gen(
	Type_def	type_def,
	Gen		gen)
{
	Str		type_name;

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

		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;
			if (index == 0) {
				gen_out(gen,
					"%T %s___initial = %s__item__%s;\n",
					type_name, type_name,
					type_name, item_name);
				gen_out(gen, "%T %s__first = %s__item__%s;\n",
					type_name, type_name,
					type_name, item_name);
			}
			gen_out(gen, "%T %s__%s = %s__item__%s;\n",
				type_name, type_name, item_name,
				type_name, item_name);
			if (index + 1 == size) {
				gen_out(gen, "%T %s__last = %s__item__%s;\n",
					type_name, type_name,
					type_name, item_name);
			}
		}
		gen_out(gen, "int %s__enumeration__size = %d;\n",
			type_name, size);
		break;
	      }
	    case Type_kind_record:
		gen_out(gen,
			"struct %s___record %I_object;\n",
			type_name, type_name);
		gen_out(gen, "%T %I = &%s___initial_object;\n",
			type_name, type_name, type_name);
		break;
	    case Type_kind_variant:
		gen_out(gen,
			"union %s___variant %I_object;\n",
			type_name, type_name);
		gen_out(gen, "%T %I = &%s___initial_object;\n",
			type_name, type_name, type_name);
		break;
	}
}

/*
 * type_def_new_gen(type_def, gen)
 *	This routine will generate the new routine for "type_def"
 *	using "gen".
 */
/* ARGSUSED */
LOCAL void
type_def_new_gen(
	Type_def	type_def,
	Generate	generate,
	Gen		gen)
{
	Str		type_name;

	type_name = type_def->name;
	gen_out(gen, "%T %s__new__helper(%T _heap_",
		type_name, type_name, "heap");
	if (type_ref_is_parameterized(type_def->type_ref)) {
		gen_out(gen, ", void *_block_");
	}
	gen_out(gen, ")\n");
	gen_out(gen, "{\n");
	switch (type_def->kind) {
	    case Type_kind_enumeration:
	      {
		Type_item	item;
		Vec(Type_item)	items;

		items = type_def->value.enumeration->items;
		item = vec_fetch(Type_item, items, 0);
		gen_out(gen, "%\treturn %s__item__%s;\n",
			1, type_name, item->name);
		break;
	      }
	    case Type_kind_record:
	    case Type_kind_variant:
		gen_out(gen, "%\t%T %s;\n", 1, type_name, type_name);
		gen_out(gen, "\n");
		gen_out(gen, "%\t%s = (%T)heap_alloc(_heap_, sizeof(*%s));\n",
				1, type_name, type_name, type_name);
		gen_out(gen, "%\treturn %s;\n", 1, type_name);
		break;
	    default:
		assert(gen != gen);
	}
	gen_out(gen, "}\n");
	gen_out(gen, "\n");
}

/*
 * type_def_routines_gen(type_def, gen)
 *	This routine will generate all of the access routines for "type_def"
 *	using "gen".
 */
void
type_def_routines_gen(
	Type_def	type_def,
	Gen		gen)
{
	Generate	generate;
	Vec(Generate) generates;

	generates = type_def->generates;
	switch (type_def->kind) {
	    case Type_kind_enumeration:
		VEC_LOOP(Generate, generates, generate) {
			switch (generate->kind) {
			    case Generate_address_get:
				break;
			    case Generate_allocate:
				break;
			    case Generate_copy:
				break;
			    case Generate_equal:
				type_def_equal_gen(type_def, generate, gen);
				break;
			    case Generate_hash:
				break;
			    case Generate_identical:
				type_def_identical_gen(type_def, generate, gen);
				break;
			    case Generate_input:
				break;
			    case Generate_integer_convert:
				break;
			    case Generate_new:
				break;
			    case Generate_output:
				break;
			    case Generate_print:
				break;
			    case Generate_save:
				break;
			    case Generate_unsigned_convert:
				break;
			    default:
				assert_fail();
			}
		}
		break;
	    case Type_kind_record:
	    case Type_kind_variant:
		type_def_access_gen(type_def, generate, gen);
		VEC_LOOP(Generate, generates, generate) {
			switch (generate->kind) {
			    case Generate_address_get:
				type_def_address_get_gen(type_def,
							 generate, gen);
				break;
			    case Generate_allocate:
				type_def_allocate_gen(type_def, generate, gen);
				break;
			    case Generate_copy:
				break;
			    case Generate_erase:
				break;
			    case Generate_equal:
				break;
			    case Generate_hash:
				break;
			    case Generate_identical:
				type_def_identical_gen(type_def, generate, gen);
				break;
			    case Generate_input:
				break;
			    case Generate_integer_convert:
				break;
			    case Generate_new:
				type_def_new_gen(type_def, generate, gen);
				break;
			    case Generate_output:
				break;
			    case Generate_print:
				break;
			    case Generate_save:
				break;
			    case Generate_unsigned_convert:
				break;
			    default:
				assert_fail();
			}
		}
		break;
	    case Type_kind_external:
		break;
	    default:
		assert_fail();
	}
}

/*
 * type_opt_base_types(type_name, gen)
 *	This routine will return 1 if "type_name" is an integer base type
 *	and "gen"->flags->opt_base_types is enabled.  Otherwise, 0 is
 *	returned.
 */
LOCAL int
type_opt_base_types(
	Str		type_name,
	Gen		gen)
{
	return (gen->flags->opt_base_types && 
		(strequal(type_name, "integer") ||
		 strequal(type_name, "unsigned") ||
		 strequal(type_name, "logical") ||
		 strequal(type_name, "character")));
}

