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

/*
 * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995 by Wayne C. Gramlich.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/* This file contains code for parsing types: */

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

#ifndef FILE_DEFS_H
#include "file_defs.h"
#endif

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

#ifndef GENERATE_EXPORTS_H
#include "generate_exports.h"
#endif

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

#ifndef KEYWORD_DEFS_H
#include "keyword_defs.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 MODULE_DEFS_H
#include "module_defs.h"
#endif

#ifndef OBJECT_EXPORTS_H
#include "object_exports.h"
#endif

#ifndef PARSER_DEFS_H
#include "parser_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 TOKEN_DEFS_H
#include "token_defs.h"
#endif

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

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

#ifndef UNIX_MEMORY_H
#include "unix_memory.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 Type_field	type_field_parse(Parser);
LOCAL Type_item		type_item_parse(Parser);
LOCAL Type_need		type_need_parse(Parser);
LOCAL Type_proto	type_proto_parse(Parser, Type_proto_kind);
LOCAL Type_ref		type_routines_replace(Vec(Type_routines),
					      Type_ref, Parser);

/*
 * routine_types_list_parse(parser)
 *	This routine will parse and return a list of routine types.
 */
Vec(Routine_type)
routine_types_list_parse(
	Parser		parser)
{
	Routine_type	routine_type;
	Vec(Routine_type) routine_types;
	Token		token;
	Type_proto	type_proto;

	routine_types = vec_create(Routine_type, parser->heap);
	(void)parser_token_read(parser);
	PARSER_LOOP(parser) {
		switch (parser_keyword_parse(parser, 0, 0)) {
		    case Key_procedure:
			token = parser_symbol_parse(parser);
			(void)parser_eol_read(parser);
			type_proto = type_proto_parse(parser,
						      Type_proto_procedure);
			break;
		    case Key_iterator:
			token = parser_symbol_parse(parser);
			(void)parser_eol_read(parser);
			type_proto = type_proto_parse(parser,
						      Type_proto_iterator);
			break;
		    default:
			msg_out(parser->msg, token->position,
				"No routine declaration found");
		}
		if (type_proto != (Type_proto)0) {
			routine_type = heap_allocate(parser->heap,
						     Routine_type);
			routine_type->name = token->value.symbol;
			routine_type->position = token->position;
			routine_type->type_proto = type_proto;
			vec_append(Routine_type,
				   routine_types, routine_type);
		}
	}
	return routine_types;
}

/*
 * type_routines_replace(routine_types, type_ref, parser)
 *	Will return an anonymous prototype type if "type_ref" matches
 *	any routine type in "routine_types"; otherwise, "type_ref" is
 *	returned unchanged.
 */
LOCAL Type_ref
type_routines_replace(
	Vec(Routine_type) routine_types,
	Type_ref	type_ref,
	Parser		parser)
{
	unsigned	index;
	Routine_type	routine_type;
	unsigned	size;

	size = vec_size(Routine_type, routine_types);
	for (index = 0; index < size; index++) {
		Type_refs	empty;
		Type_proto	type_proto;
		Type_proto	print_proto;
		Type_refs	takes;
		Type_tables	type_tables;

		routine_type = vec_fetch(Routine_type, routine_types, index);
		if (!strequal(routine_type->name, type_ref->name)) {
			continue;
		}
		type_proto = routine_type->type_proto;

		/* Create a print routine: */
		type_tables = parser->type_tables;
		type_proto_needed(type_proto, type_tables);
		empty = type_refs_empty_create(type_tables);
		takes = type_proto->type_ref->type_refs;
		takes = type_refs_append(takes,
			    type_ref_create("out_stream", type_tables),
			    type_tables);
		print_proto = type_proto_create(Type_proto_procedure,
				type_proto->params,
				takes,
				empty,
				empty,
				type_signals_empty_create(type_tables),
				type_needs_empty_create(type_tables),
				0,
				type_tables);
		(void)routine_entry_insert(parser->routine_table,
					   "print",
					   type_proto->type_ref,
					   print_proto,
					   0,
					   1);
		return type_proto->type_ref;
	}
	return type_ref;
}

/*
 * type_def_parse(parser)
 *	This routine will read and return a type declaration from "parser".
 */
/* ARGSUSED */
Type_def
type_def_parse(
	Vec(Routine)	routines,
	Module		module,
	Parser		parser)
{
	Heap		heap;
	int		index;
	int		loop_count;
	Str		name;
	Type_ref	new_parameter;
	Type_refs	new_parameters;
	Type_ref	old_parameter;
	Type_refs	old_parameters;
	Table(Str, Type_ref) parameter_table;
	int		size;
	Token		token;
	Type_def	type_def;
	Type_ref	type_ref;
	Type_tables	type_tables;
	Vec(Routine_type) routine_types;

	heap = parser->heap;
	type_tables = parser->type_tables;

	/* Get the type name: */
	token = parser_token_peek(parser);
	if (token->type != Token_type_symbol) {
		msg_out(parser->msg, parser->file->position,
			     "No type name specified");
		return (Type_def)0;
	}
	type_ref = type_ref_parse(parser);
	name = type_ref->name;
	old_parameters = type_ref->parameters;

	/* Insert each parameter into the parameter table: */
	parameter_table = parser->parameter_table;
	new_parameters = type_refs_empty_create(type_tables);
	size = type_refs_size(old_parameters);
	for (index = 0; index < size; index++) {
		old_parameter = type_refs_fetch(old_parameters, index);
		new_parameter = type_ref_create(strprintf(heap, "%d", index),
						type_tables);
		assert(table_insert(Str, Type_ref, parameter_table,
				    old_parameter->name, new_parameter) == 0);
		new_parameters = type_refs_append(new_parameters,
						  new_parameter, type_tables);
	}
	type_ref = type_ref_parameters_create(name,
					      new_parameters, type_tables);
	type_def = type_def_create(name, token->position, type_tables, heap);
	type_def->type_ref = type_ref;
	routine_types = type_def->routine_types;

	if (token->type == Token_type_at) {
		/*XXX: For need decl only? */
		(void)parser_token_read(parser);
		token = parser_symbol_parse(parser);
		type_def->routine_name = token->value.symbol;
	} else {
		type_def->routine_name = (Str)0;
	}
	type_def->comment = parser_eol_read(parser);
	type_def->kind = Type_kind_undefined;

	loop_count = 0;
	PARSER_LOOP(parser) {
		loop_count++;
		switch (parser_keyword_parse(parser, 0, 0)) {
		    case Key_enumeration:
		      {
			Type_enumeration enumeration;
			Vec(Type_item)	items;

			if (loop_count != 1) {
				goto define_error;
			}
			type_def->kind = Type_kind_enumeration;
			(void)parser_eol_read(parser);
			items =
			    (Vec(Type_item))parser_indented_list_parse(parser,
					(Parser_routine)type_item_parse,
					"Empty enumeration list");
			enumeration = heap_allocate(heap, Type_enumeration);
			enumeration->items = items;
			type_def->value.enumeration = enumeration;
			break;
		      }
		    case Key_external:
		      {
			if (loop_count != 1) {
				goto define_error;
			}
			type_def->kind = Type_kind_external;
			type_def->comment = parser_eol_read(parser);
			break;
		      }
		    case Key_generate:
		      {
			Generate		generate;
			Vec(Generate)		generate_list;
			int			position;

			if (loop_count == 1) {
				msg_out(parser->msg, type_def->position,
				    "generate must occur after definition");
				break;

			}
			generate_list = parser_comma_list_parse(parser,
					(Parser_routine)generate_parse,
					"Empty generate list");
			(void)parser_eol_read(parser);
			vec_vec_append(Type_predfined,
				       type_def->generates, generate_list);
			position = token->position;
			VEC_LOOP(Generate, generate_list, generate) {
				generate_type_def_routine(generate, type_def,
							  position, parser);
			}
			break;
		      }
		    case Key_iterator:
		      {
			type_def->kind = Type_kind_proto;
			type_def->value.proto =
				type_proto_parse(parser, Type_proto_iterator);
			break;
		      }
		    case Key_procedure:
		      {
			type_def->kind = Type_kind_proto;
			type_def->value.proto =
			    type_proto_parse(parser, Type_proto_procedure);
			break;
		      }
		    case Key_record:
		      {
			unsigned	index;
			Type_fields	fields;
			Type_record	record;
			unsigned	size;

			if (loop_count != 1) {
				goto define_error;
			}
			type_def->kind = Type_kind_record;
			type_def->comment = parser_eol_read(parser);
			record = heap_allocate(heap, Type_record);
			fields =
			   (Type_fields)parser_indented_list_parse(parser,
					(Parser_routine)type_field_parse,
					"Empty structure");
			record->fields = fields;
			type_def->value.record = record;

			size = vec_size(Type_field, fields);
			for (index = 0; index < size; index++) {
				Type_field field;

				field = vec_fetch(Type_field, fields, index);
				field->type_ref =
				  type_routines_replace(routine_types,
							field->type_ref,
							parser);
			}
			break;
		      }
		    case Key_routine_types:
		      {
			loop_count--;
			routine_types = routine_types_list_parse(parser);
			type_def->routine_types = routine_types;
			break;
		      }
		    case Key_variant:
		      {
			unsigned	index;
			Type_fields	fields;
			unsigned	size;
			Type_variant	variant;

			if (loop_count != 1) {
				goto define_error;
			}
			type_def->kind = Type_kind_variant;
			type_def->comment = (Str)0; /* EOL comment in field */
			variant = heap_allocate(heap, Type_variant);
			variant->tag_field = type_field_parse(parser);
			fields =
			   (Type_fields)parser_indented_list_parse(parser,
					(Parser_routine)type_field_parse,
					"Empty variant");
			variant->fields = fields;
			type_def->value.variant = variant;

			size = vec_size(Type_field, fields);
			for (index = 0; index < size; index++) {
				Type_field field;

				field = vec_fetch(Type_field, fields, index);
				field->type_ref =
				  type_routines_replace(routine_types,
							field->type_ref,
							parser);
			}
			break;
		      }
		    default:
			msg_out(parser->msg, token->position,
				"Misspelled or missing keyword");
			return (Type_def)0;
		}
	}
	if (type_def->kind == Type_kind_undefined) {
		msg_out(parser->msg, type_def->position, "No type defined");
		return (Type_def)0;
	}
	generate_type_def_routines(type_def, parser);
	type_ref = type_def->type_ref;
	object_create(parser->object_table, "??", type_ref,
		      type_def, type_def->position);
    done:
	/* Remove the parameters from the parameter table: */
	TYPE_REFS_LOOP(old_parameters, old_parameter) {
		assert(table_delete(Str, Type_ref,
				    parameter_table, old_parameter->name) == 0);
	}
	return type_def;
    define_error:
	msg_out(parser->msg, type_def->position,
		"Only one definition permited per define");
	type_def = (Type_def)0;
	goto done;
}

/*
 * type_field_parse(parser)
 *	This routine will parse and return a field declaration.
 */
LOCAL Type_field
type_field_parse(
	Parser		parser)
{
	Type_field	field;
	Heap		heap;
	Token		token;

	heap = parser->heap;
	field = heap_allocate(heap, Type_field);
	token = parser_symbol_parse(parser);
	field->name = token->value.symbol;
	field->position = token->position;
	token = parser_token_peek(parser);
#ifdef OLD
	if (token->type == Token_type_colon) {
		Type_proto	type_proto;

		(void)parser_token_read(parser);
		switch (parser_keyword_parse(parser, 0, 0)) {
		    case Key_procedure:
			type_proto = type_proto_parse(parser,
						      Type_proto_procedure);
			break;
		    case Key_iterator:
			type_proto = type_proto_parse(parser,
						      Type_proto_iterator);
			break;
		    default:
			msg_out(parser->msg, token->position,
				"No routine declaration found after colon");
			type_proto = (Type_proto)0;
		}
		if (type_proto == (Type_proto)0) {
			assert_fail();
		} else {
			/* Create a print routine: */
			Type_refs	empty;
			Type_proto	print_proto;
			Type_refs	takes;
			Type_tables	type_tables;

			type_tables = parser->type_tables;
			type_proto_needed(type_proto, type_tables);
			empty = type_refs_empty_create(type_tables);
			takes = type_proto->type_ref->type_refs;
			takes = type_refs_append(takes,
				    type_ref_create("out_stream", type_tables),
				    type_tables);
			print_proto = type_proto_create(Type_proto_procedure,
					type_proto->params,
					takes,
					empty,
					empty,
					type_signals_empty_create(type_tables),
					type_needs_empty_create(type_tables),
					0,
					type_tables);
			(void)routine_entry_insert(parser->routine_table,
						   "print",
						   type_proto->type_ref,
						   print_proto,
						   token->position,
						   1);

			field->type_ref = type_proto->type_ref;
		}
		field->comment = (Str)0;
	} else {
#endif /* OLD */
		field->type_ref = type_ref_parse(parser);
		field->comment = parser_eol_read(parser);
#ifdef OLD
	}
#endif /* OLD */
	return field;
}

/*
 * type_item_parse(parser)
 *	This routine will parse and return an enumeration item from "parser".
 */
Type_item
type_item_parse(
	Parser		parser)
{
	Type_item	item;
	Token		token;

	token = parser_symbol_parse(parser);
	item = heap_allocate(parser->heap, Type_item);
	item->name = token->value.symbol;
	item->position = token->position;
	item->comment = parser_eol_read(parser);
	return item;
}

/*
 * type_proto_parse(parser, kind)
 *	This routine will parser and return a routine/iterator prototype.
 */
Type_proto
type_proto_parse(
	Parser		parser,
	Type_proto_kind	kind)
{
	Type_refs	empty;
	Type_needs	needs;
	int		no_return;
	Type_refs	params;
	Type_refs	returns;
	Type_signals	signals;
	Type_refs	takes;
	Type_tables	type_tables;
	Type_refs	yields;

	type_tables = parser->type_tables;
	empty = type_refs_empty_create(type_tables);
	takes = empty;
	needs = type_needs_empty_create(type_tables);
	no_return = 0;
	returns = empty;
	signals = type_signals_empty_create(type_tables);
	yields = empty;

	/*XXX: Need to add yields, signals, & needs: */

	PARSER_LOOP(parser) {
		switch (parser_keyword_parse(parser, 1, 0)) {
		    case Key_eol:
			(void)parser_eol_read(parser);
			continue;
		    case Key_takes:
			takes = type_refs_parse(parser);
			(void)parser_eol_read(parser);
			break;
		    case Key_takes_nothing:
			(void)parser_eol_read(parser);
			break;
		    case Key_returns:
			returns = type_refs_parse(parser);
			(void)parser_eol_read(parser);
			break;
		    case Key_returns_nothing:
			returns = empty;
			break;
		    default:
			msg_out(parser->msg, parser->file->position,
				     "Bad routine type declaration");
		}
	}
	params = empty;
	return type_proto_create(kind, params, takes, returns, yields,
				 signals, needs, no_return, type_tables);
}

/* 
 * type_refs_parse(parser)
 *	This routine will parse and return a comma separated list of type
 *	references.
 */
Type_refs
type_refs_parse(parser)
	Parser		parser;
{
	Token		token;
	Type_ref	type_ref;
	Type_tables	type_tables;
	Type_refs	type_refs;

	type_tables = parser->type_tables;
	type_refs = type_refs_empty_create(type_tables);
	for (;;) {
		if (!type_refs_is_empty(type_refs)) {
			token = parser_token_peek(parser);
			if (token->type != Token_type_comma) {
				break;
			}
			(void)parser_token_read(parser);
		}
		type_ref = type_ref_parse(parser);
		type_refs = type_refs_append(type_refs,
					     type_ref, type_tables);
	}
	return type_refs;
}

/*
 * type_ref_parse(parser)
 *	This routine will parse and return a type reference.
 */
Type_ref
type_ref_parse(
	Parser		parser)
{
	Str		name;
	Type_ref	parameter;
	Type_refs	parameters;
	Token		token;
	Type_ref	type_ref;
	Type_tables	type_tables;

	type_tables = parser->type_tables;
	token = parser_symbol_parse(parser);
	name = token->value.symbol;
	token = parser_token_peek(parser);
	if (token->type == Token_type_left_bracket) {
		parameters = type_refs_empty_create(type_tables);
		for (;;) {
			(void)parser_token_read(parser);
			parameter = type_ref_parse(parser);
			parameters = type_refs_append(parameters, parameter,
						      type_tables);
			token = parser_token_peek(parser);
			switch (token->type) {
			    case Token_type_comma:
				continue;
			    case Token_type_right_bracket:
				(void)parser_token_read(parser);
				break;
			    default:
				msg_out(parser->msg, parser->file->position,
					     "Bad parameter list");
				break;
			}
			break;
		}
		type_ref = type_ref_parameters_create(name, parameters,
						      type_tables);
	} else {
		type_ref = table_lookup(Str, Type_ref,
					parser->parameter_table, name);
		if (type_ref == (Type_ref)0) {
			type_ref = type_ref_create(name, type_tables);
		}
	}
	return type_ref;
}

/*
 * routine_need_parse(parser)
 *	This routine will parse and return a need clause from parser.
 *	(Type_need)0 is returned if there are any problems.
 */
Type_need
type_need_parse(
	Parser		parser)
{
	Str		error_msg;
	Str		name;
	Type_proto	proto;
	Token		token;
	Type_ref	type_ref;
	Type_need	type_need;
	Type_proto_kind	type_proto_kind;

	/* Get keyword: */
	switch (parser_keyword_parse(parser, 0, 0)) {
	    case Key_procedure:
		type_proto_kind = Type_proto_procedure;
		break;
	    case Key_iterator:
		type_proto_kind = Type_proto_iterator;
		break;
	    default:
		error_msg = "No object/routine declaration found";
		goto error;
	}


	/* Get the routine/object name: */
	token = parser_token_peek(parser);
	if (token->type != Token_type_symbol) {
		error_msg = "Object/routine name needed";
		goto error;
	}
	name = token->value.symbol;
	(void)parser_token_read(parser);

	/* Slurp up the '@' sign: */
	token = parser_token_peek(parser);
	if (token->type != Token_type_at) {
		error_msg = "Object/routine name must be followed by '@'";
		goto error;
	}
	(void)parser_token_read(parser);

	/* Slurp up the type reference: */
	type_ref = type_ref_parse(parser);
	if (type_ref == (Type_ref)0) {
		error_msg = "'@' must be followed by a type name";
		goto error;
	}

	/* Verify that the type reference is OK: */
	if (type_ref_is_parameterized(type_ref)) {
		error_msg = "Type name can not be parameterized";
		goto error;
	}
	/*XXX: Verify that the type reference is a valid parameter: */

	/* Slurp up the new-line: */
	(void)parser_eol_read(parser);

	/* Parse the rest of the declaration: */
	proto = type_proto_parse(parser, type_proto_kind);
	if (proto == (Type_proto)0) {
		return (Type_need)0;
	}

	type_need = type_need_create(type_ref, name,
				     proto, parser->type_tables);
	return type_need;
    error:
	(void)parser_eol_read(parser);
	msg_out(parser->msg, parser->file->position, error_msg);
	return (Type_need)0;
}

/*
 * type_needs_parse(parser)
 *	This routine will parse the needs statement from "parser" and and
 *	return them.
 */
Type_needs
type_needs_parse(
	Parser		parser)
{
	Type_need	type_need;
	Type_needs	type_needs;
	Type_tables	type_tables;

	type_tables = parser->type_tables;
	type_needs = type_needs_empty_create(type_tables);
	PARSER_LOOP(parser) {
		type_need = type_need_parse(parser);
		if (type_need != (Type_need)0) {
			type_needs = type_needs_append(type_needs, type_need,
						       type_tables);
		}
	}
	return type_needs;
}

