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

/*
 * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1999 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 parse a routine: */

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

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

#ifndef KEYWORD_DEFS_H
#include "keyword_defs.h"
#endif

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

#ifndef MODULE_TYPES_H
#include "module_types.h"
#endif

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

#ifndef NEED_EXPORTS_H
#include "need_exports.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 STATEMENT_DEFS_H
#include "statement_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_UNISTD_H
#include "unix_unistd.h"
#endif

#ifndef VAR_TYPES_H
#include "var_types.h"
#endif

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

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

LOCAL void		routine_signals_parse(Vec(Signal), Parser);
LOCAL Type_signals	signal_list2type_signals(Vec(Signal), Type_tables);

#ifdef OLD
/*
 * type_def_list2type_needs(type_defs, heap)
 *	This routine will convert "type_defs" into a list of Type_need
 *	objects.
 */
Type_needs
type_def_list2type_needs(
	Type_defs	type_defs,
	Heap		heap)
{
	Type_def	type_def;
	Type_need	type_need;
	Type_needs	type_needs;
	Type_proto	type_proto;
	Type_ref	type_ref;

	type_needs = type_needs_empty_create(heap);
	VEC_LOOP(Type_def, type_defs, type_def) {
		assert(type_def->kind == Type_kind_proto);
		type_proto = type_def->value.proto;
		type_ref = type_ref_create(type_def->name, type_tables);
		type_need = type_need_create(type_ref,
					     type_def->routine_name,
					     type_proto,
					     heap);
		type_needs = type_needs_append(type_needs, type_need, heap);
	}
	return type_needs;
}
#endif /* OLD */

/*
 * routine_breakpoint_create(routine, position, parser)
 *	This routine will create and return a new breakpoint object assocated
 *	with "routine" and "position" using "parser".
 */
Breakpoint
routine_breakpoint_create(
	Routine		routine,
	int		position,
	Msg		msg,
	Heap		heap)
{
	Breakpoint	breakpoint;

	breakpoint = heap_allocate(heap, Breakpoint);
	breakpoint->number = vec_size(Breakpiont, routine->breakpoints);
	breakpoint->line_number = msg_line_get(msg, position);
	vec_append(Breakpoint, routine->breakpoints, breakpoint);
	return breakpoint;
}

/*
 * routine_create(name, type_ref, position, module, type_tables, heap)
 *	This routine will create and return a new routine object for
 *	"name"@"type_ref" at "position" allocated from "heap".
 *	"module" is store in the returned routine object.
 */
LOCAL Routine
routine_create(
	Str		name,
	Type_ref	type_ref,
	int		position,
	Module		module,
	Type_tables	type_tables,
	Heap		heap)
{
	Routine		routine;

	if (type_ref == (Type_ref)0) {
		type_ref = type_ref_create("global__", type_tables);
	}
	routine = heap_allocate(heap, Routine);
	routine->block_entrys = vec_create(Block_entry, heap);
	routine->breakpoints = vec_create(Breakpoint, heap);
	routine->breakpoint_enter = (Breakpoint)0;
	routine->breakpoint_return = (Breakpoint)0;
	routine->external = (Str)0;
	routine->external_comment = (Str)0;
	routine->is_procedure = 0;
	routine->module = module;
	routine->name = name;
	routine->need_table = need_table_create(routine, name,
						type_ref, type_tables, heap);
	routine->needs = type_needs_empty_create(type_tables);
	routine->needs_comment = (Str)0;
	routine->position = position;
	routine->returns = type_refs_empty_create(type_tables);
	routine->returns_comment = (Str)0;
	routine->returns_position = routine->position;
	routine->routine_entry = (Routine_entry)0;
	routine->routine_ref = (Routine_ref)0;
	routine->routine_types = vec_create(Routine_Type, heap);
	routine->statements = vec_create(Statement, heap);
	routine->signals = vec_create(Signal, heap);
	routine->signals_comment = (Str)0;
	routine->takes = vec_create(Var, heap);
	routine->temp_first = -1;
	routine->temp_last = -1;
	routine->type_proto = (Type_proto)0;
	routine->type_ref = type_ref;
	routine->var_table = table_create(Str, Var, 30, strequal,
					  strhash, (Var)0, heap);
	routine->vars = vec_create(Var, heap);
	routine->yields = type_refs_empty_create(type_tables);
	routine->yields_comment = (Str)0;
	routine->yields_position = position;
	return routine;
}

/*
 * routine_parse(parser, is_procedure, module)
 *	This routine will parse and return a routine object from "parser".
 *	"module" will be stored in the resulting routine object.
 */
Routine
routine_parse(
	Parser		parser,
	int		is_procedure,
	Module		module)
{
	Vec(Statement_cond) conds;
	int		has_returns;
	Heap		heap;
	int		in_body;
	int		index;
	Keyword		keyword;
	Table(Str, Keyword) keywords;
	Type_proto_kind	kind;
	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		position;
	Type_refs	takes;
	Token		token;
	Routine		routine;
	Vec(Routine_type) routine_types;
	int		size;
	Vec(Statement)	statements;
	Type_proto	type_proto;
	Type_ref	type_ref;
	static Type_ref	type_ref_none = (Type_ref)0;
	Type_tables	type_tables;

	/* Do some initialization: */
	heap = parser->heap;
	type_tables = parser->type_tables;
	if (type_ref_none == (Type_ref)0) {
		type_ref_none = type_ref_create("global__", type_tables);
	}
	position = parser->file->position;
	conds = (Vec(Statement_cond))0;
	type_ref = type_ref_none;

	/* Get the routine name: */
	token = parser_token_peek(parser);
	if (token->type != Token_type_symbol) {
		msg_out(parser->msg, token->position,
			"No routine name found");
		return (Routine)0;
	}
	(void)parser_token_read(parser);
	name = token->value.symbol;

	/* If present, get the routine type: */
	token = parser_token_peek(parser);
	if (token->type == Token_type_at) {

		(void)parser_token_read(parser);
		type_ref = type_ref_parse(parser);
		old_parameters = type_ref->parameters;
	} else {
		old_parameters = type_refs_empty_create(type_tables);
	}

	/* 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(type_ref->name,
					      new_parameters,
					      type_tables);

	/* Create the routine object: */
	routine = routine_create(name, type_ref, position, module,
				 parser->type_tables, heap);
	routine->breakpoint_enter =
		routine_breakpoint_create(routine, position, parser->msg, heap);
	statements = routine->statements;
	routine->comment = parser_eol_read(parser);

	/* Start parsing the routine clauses: */
	keywords = parser->keywords;
	in_body = 0;
	has_returns = 0;
	PARSER_LOOP(parser) {
		if (in_body) {
			conds = statement_parse(parser, statements,
						conds, routine);
			continue;
		}
		token = parser_token_peek(parser);
		if (token->type == Token_type_symbol) {
			keyword = table_lookup(Str, Keyword,
					       keywords, token->value.symbol);
		} else {
			keyword = Key_none;
		}
		switch (keyword) {
		    case Key_eol:
			(void)parser_eol_read(parser);
			break;
		    case Key_takes:
			(void)parser_token_read(parser);
			routine->takes = vars_parse(parser, routine);
			break;
		    case Key_takes_nothing:
			(void)parser_eol_read(parser);
			break;
		    case Key_external:
			(void)parser_token_read(parser);
			token = parser_symbol_parse(parser);
			routine->external = token->value.symbol;
			routine->external_comment = parser_eol_read(parser);
			break;
		    case Key_needs:
			(void)parser_token_read(parser);
			routine->needs_comment = parser_eol_read(parser);
			routine->needs = type_needs_parse(parser);
			break;
		    case Key_needs_nothing:
			/*
			 * This version of the compiler does not do anything
			 * with the needs_nothing clause.
			 */
			(void)parser_eol_read(parser);
			break;
		    case Key_no_side_effects:
			/*
			 * This version of the compiler does not do anything
			 * with the no_side_effects clause.
			 */
			(void)parser_eol_read(parser);
			break;
		    case Key_returns:
			(void)parser_token_read(parser);
			routine->returns_position = position;
			routine->returns = type_refs_parse(parser);
			routine->breakpoint_return =
				routine_breakpoint_create(routine,
							  token->position,
							  parser->msg,
							  heap);
			routine->returns_comment = parser_eol_read(parser);
			has_returns = 1;
			break;
		    case Key_returns_never:
			(void)parser_token_read(parser);
			routine->breakpoint_return =
				routine_breakpoint_create(routine,
							  token->position,
							  parser->msg,
							  heap);
			has_returns = 1;
			assert_fail();
			break;
		    case Key_returns_nothing:
			(void)parser_token_read(parser);
			routine->returns_position = position;
			routine->returns =
				type_refs_empty_create(type_tables);
			routine->breakpoint_return =
				routine_breakpoint_create(routine,
							  token->position,
							  parser->msg,
							  heap);
			routine->returns_comment = parser_eol_read(parser);
			has_returns = 1;
			break;
		    case Key_routine_types:
			routine->routine_types =
				routine_types_list_parse(parser);
			break;
		    case Key_signals:
			(void)parser_token_read(parser);
			routine->signals_comment = parser_eol_read(parser);
			routine_signals_parse(routine->signals, parser);
			break;
		    case Key_yields:
			(void)parser_token_read(parser);
			routine->yields_position = position;
			routine->yields = type_refs_parse(parser);
			routine->yields_comment = parser_eol_read(parser);
			break;
		    case Key_none:
		    default:
			in_body = 1;
			conds = statement_parse(parser, statements,
						conds, routine);
			break;
		}
	}
	if (!has_returns) {
		msg_out(parser->msg, routine->position,
		       "No returns, returns_never, or returns_nothing found");
	}

	/* Insert routine type into routine table: */
	kind = is_procedure ? Type_proto_procedure : Type_proto_iterator;
	takes = vars_to_type_refs(routine->takes, type_tables);
	type_proto = type_proto_create(kind,
				       type_ref->parameters,
				       takes,
				       routine->returns,
				       routine->yields,
				       signal_list2type_signals(
						routine->signals, type_tables),
				       routine->needs,
				       0,
				       type_tables);

	/* Replace any references to routine types: */
	routine_types = routine->routine_types;
	if (!vec_empty(Routine_type, routine_types)) {
		Type_ref		old_type_ref;
		Type_refs		old_type_refs;
		Type_ref		new_type_ref;
		Type_refs		new_type_refs;
		Routine_type		routine_type;

		old_type_refs = type_refs_empty_create(type_tables);
		new_type_refs = type_refs_empty_create(type_tables);
		VEC_LOOP(Routine_type, routine_types, routine_type) {
			old_type_ref = type_ref_create(routine_type->name,
						       type_tables);
			new_type_ref = routine_type->type_proto->type_ref;
			old_type_refs = type_refs_append(old_type_refs,
							 old_type_ref,
							 type_tables);
			new_type_refs = type_refs_append(new_type_refs,
							 new_type_ref,
							 type_tables);
		}
		type_proto = type_proto_replace(type_proto, old_type_refs,
						new_type_refs, type_tables);
	}
	routine->parameters = old_parameters;
	routine->type_proto = type_proto;
	routine->routine_entry = routine_entry_insert(parser->routine_table,
						      routine->name,
						      type_ref,
						      type_proto,
						      routine->position,
						      0);
	routine->routine_ref = routine_ref_insert(parser->routine_table,
						  routine->name,
						  type_ref,
						  routine->position);
	routine->routine_ref->routine_entry->routine = routine;

	/* 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 routine;
}

/*
 * routine_signals_parse(signals, parser)
 *	This routine will parse the signals statement from "parser" and store
 *	the result in "signals".
 */
void
routine_signals_parse(
	Type_defs	signals,
	Parser		parser)
{
	Heap		heap;
	Signal		signal;
	Token		token;
	Type_tables	type_tables;

	heap = parser->heap;
	type_tables = parser->type_tables;
	PARSER_LOOP(parser) {
		/* Get signal name: */
		token = parser_symbol_parse(parser);
		signal = heap_allocate(heap, Signal);
		signal->name = token->value.symbol;

		/* Get signal type list */
		token = parser_token_peek(parser);
		if (token->type == Token_type_left_paren) {
			(void)parser_token_read(parser);
			signal->args = type_refs_parse(parser);
			token = parser_token_peek(parser);
			if (token->type == Token_type_right_paren) {
				(void)parser_token_read(parser);
			} else {
				msg_out(parser->msg, token->position,
					     "Missing right parenthesis");
			}
		} else {
			signal->args = type_refs_empty_create(type_tables);
		}
		signal->comment = parser_eol_read(parser);
		vec_append(Signal, signals, signal);
	}
}

Type_signals
signal_list2type_signals(
	Vec(Signal)	signals,
	Type_tables	type_tables)
{
	Signal		signal;
	Type_signal	type_signal;
	Type_signals	type_signals;

	type_signals = type_signals_empty_create(type_tables);
	VEC_LOOP(Signal, signals, signal) {
		type_signal = type_signal_create(signal->name,
						 signal->args, type_tables);
		type_signals = type_signals_append(type_signals, type_signal,
						   type_tables);
	}
	return type_signals;
}

