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

/*
 * Copyright (c) 1990, 1991, 1992, 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 codes for parsing statements: */

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

#ifndef EXP_DEFS_H
#include "exp_defs.h"
#endif

#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 MSG_EXPORTS_H
#include "msg_exports.h"
#endif

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

#ifndef ROUTINE_EXPORTS_H
#include "routine_exports.h"
#endif

#ifndef STATEMENT_DEFS_H
#include "statement_defs.h"
#endif

#ifndef TABLE_EXPORTS_H
#include "table_exports.h"
#endif

#ifndef TOKEN_DEFS_H
#include "token_defs.h"
#endif

#ifndef TYPE_EXPORTS_H
#include "type_exports.h"
#endif

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

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

LOCAL Statement_case	statement_case_parse(Parser, Routine);
LOCAL Statement_cond	statement_cond_create(Exp, int, Str, Vec(Statement),
					      Routine, Parser);
LOCAL Statement	statement_create(Statement_kind, int, Heap);
LOCAL Vec(Statement)	statement_list_parse(Parser, Routine);
LOCAL Statement_tag	statement_tag_parse(Parser, Routine);

/*
 * statement_case_parse(parser, routine)
 *	This routine will parse and return a case label.  (Statement_case)0
 *	is returned if any error occurs.
 */
LOCAL Statement_case
statement_case_parse(
	Parser		parser,
	Routine		routine)
{
	Statement_case	xcase;
	Heap		heap;
	Token		token;

	heap = parser->heap;
	token = parser_token_peek(parser);
	xcase = heap_allocate(heap, Statement_case);
	switch (parser_keyword_parse(parser, 0, 0)) {
	    case Key_case:
		xcase->exps = exp_list_parse(parser, 0);
		xcase->comment = parser_eol_read(parser);
		xcase->position = token->position;
		xcase->statements = statement_list_parse(parser, routine);
		break;
	    case Key_default:
		xcase->exps = vec_create(Exp, heap);
		xcase->comment = parser_eol_read(parser);
		xcase->position = token->position;
		xcase->statements = statement_list_parse(parser, routine);
		break;
	    default:
		return (Statement_case)0;
	}
	return xcase;
}

/*
 * statement_create(kind, position, heap)
 *	This routine will create and return a new statement object of kind
 *	"kind" at position "position" using "heap".
 */
LOCAL Statement
statement_create(
	Statement_kind	kind,
	int		position,
	Heap		heap)
{
	Statement	statement;

	statement = heap_allocate(heap, Statement);
	statement->breakpoint = (Breakpoint)0;
	statement->calls = (Vec(Call))0;
	statement->comment = (Str)0;
	statement->kind = kind;
	statement->position = position;
	return statement;
}

/*
 * statement_cond_create(exp, position, comment, statements, routine, parser)
 *	This routine will create and return a new Statement_cond
 *	object containing "exp", "position", "comment", and "statements"
 *	using "parser".  If "routine" is (Routine)0, no breakpoint is created.
 */
LOCAL Statement_cond
statement_cond_create(
	Exp		exp,
	int		position,
	Str		comment,
	Vec(Statement)	statements,
	Routine		routine,
	Parser		parser)
{
	Statement_cond	cond;

	cond = heap_allocate(parser->heap, Statement_cond);
	cond->breakpoint = (Breakpoint)0;
	cond->calls = (Vec(Call))0;
	cond->comment = comment;
	cond->exp = exp;
	cond->position = position;
	cond->statements = statements;
	if (routine != (Routine)0) {
		cond->breakpoint = routine_breakpoint_create(routine,
							     position,
							     parser->msg,
							     parser->heap);
	}
	return cond;
}

/*
 * statement_list_parse(parser, routine)
 *	This routine will parse and return a statement list from "parser".
 *	Routine global information is stored in "routine".
 */
LOCAL Vec(Statement)
statement_list_parse(
	Parser			parser,
	Routine			routine)
{
	Vec(Statement_cond)	conds;
	Heap			heap;
	Vec(Statement)		statements;

	heap = parser->heap;
	conds = (Vec(Statement_cond))0;
	statements = vec_create(Statement, heap);
	PARSER_LOOP(parser) {
		conds = statement_parse(parser, statements, conds, routine);
	}
	return statements;
}

/*
 * statement_parse(parser, statements, conds, routine)
 *	This routine will parse one statement level from "parser".
 *	Either a statement will be appended to "statements" or a
 *	conditional will be appended to "conds".  "conds" should
 *	be set to (Vec(Statement_cond))0 on the first call.  All
 *	subsequent calls should pass in the value returned from the
 *	previous call.
 */
Vec(Statement_cond)
statement_parse(
	Parser			parser,
	Vec(Statement)		statements,
	Vec(Statement_cond)	conds,
	Routine			routine)
{
	Heap			heap;
	Keyword			keyword;
	int			position;
	Statement		statement;
	int			supress_eol_parse;
	Token			token;

	heap = parser->heap;
	supress_eol_parse = 0;
	statement = (Statement)0;
	token = parser_token_peek(parser);
	position = token->position;
	switch (token->type) {
	    case Token_type_eol:
		keyword = Key_eol;
		break;
	    case Token_type_symbol:
		keyword = table_lookup(Str, Keyword,
				       parser->keywords, token->value.symbol);
		break;
	    default:
		keyword = Key_none;
	}
	switch (keyword) {
	    case Key_eol:
		(void)parser_token_read(parser);
		statement = statement_create(Statement_kind_none,
					     position, heap);
		statement->comment = parser_eol_read(parser);
		vec_append(Statement, statements, statement);
		return conds;
	    case Key_assert:
		(void)parser_token_read(parser);
		statement = statement_create(Statement_kind_assert,
					     position, heap);
		statement->value.exp = exp_parse(parser);
		break;
	    case Key_break:
	    case Key_continue:
	      {
		Statement_label	label;
		Statement_kind	kind;

		(void)parser_token_read(parser);
		switch (keyword) {
		    case Key_break:
			kind = Statement_kind_break;
			break;
		    case Key_continue:
			kind = Statement_kind_continue;
			break;
		}
		label = heap_allocate(heap, Statement_label);
		label->name = (Str)0;
		label->loop = (Statement_loop)0;
		label->use_goto = 0;
		token = parser_token_peek(parser);
		statement = statement_create(kind, position, heap);
		statement->value.label = label;
		switch (token->type) {
		    case Token_type_symbol:
			(void)parser_token_read(parser);
			label->name = token->value.symbol;
			break;
		    case Token_type_eol:
			break;
		    default:
			msg_out(parser->msg, position,
				"Bad break/continue statement");
			statement->kind = Statement_kind_none;
			statement->value.none = 0;
			return conds;
		}
		break;
	      }
	    case Key_else:
		(void)parser_token_read(parser);
		if (conds == (Vec(Statement_cond))0) {
			msg_out(parser->msg, position,
				"else must follow if or else_if");
		} else {
			Str		comment;
			Statement_cond	cond;
			Vec(Statement)	cond_statements;

			comment = parser_eol_read(parser);
			cond_statements = statement_list_parse(parser,
							       routine);
			cond = statement_cond_create((Exp)0,
						     position,
						     comment,
						     cond_statements,
						     (Routine)0,
						     parser);
			vec_append(Statement_cond, conds, cond);
			conds = (Vec(Statement_cond))0;
		}
		return conds;
	    case Key_else_if:
		(void)parser_token_read(parser);
		if (conds == (Vec(Statement_cond))0) {
			msg_out(parser->msg, position,
				"else_if must follow if or else_if");
		} else {
			Str		comment;
			Statement_cond	cond;
			Exp		exp;
			Vec(Statement)	cond_statements;

			exp = exp_parse(parser);
			comment = parser_eol_read(parser);
			cond_statements = statement_list_parse(parser,
							       routine);
			cond = statement_cond_create(exp,
						     position,
						     comment,
						     cond_statements,
						     routine,
						     parser);
			vec_append(Statement_cond, conds, cond);
		}
		return conds;
	    case Key_extract:
	      {
		Str			comment;
		Exp			exp;
		Statement_extract	extract;
		Statement_tag		tag;
		Vec(Statement_tag)	tags;

		/* Parse the rest of the statement: */
		(void)parser_token_read(parser);
		exp = exp_parse(parser);
		comment = parser_eol_read(parser);

		/* Parse the extract tags: */
		tags = vec_create(Statement_tag, heap);
		PARSER_LOOP(parser) {
			tag = statement_tag_parse(parser,
						  routine);
			if (tag != (Statement_tag)0) {
				vec_append(Statement_tag,
					   tags, tag);
			}
		}

		/* Construct the extract object: */
		extract = heap_allocate(heap, Statement_extract);
		extract->exp = exp;
		extract->tags = tags;

		/* Construct the statement object: */
		statement = statement_create(
					Statement_kind_extract,
					position, heap);
		statement->comment = comment;
		statement->value.extract = extract;
		statement->breakpoint = routine_breakpoint_create(
							routine,
							position,
							parser->msg,
							heap);
		vec_append(Statement, statements, statement);
		return conds;
	      }
	    case Key_if:
	      {
		Str		comment;
		Statement_cond	cond;
		Exp		exp;
		Vec(Statement)	cond_statements;

		(void)parser_token_read(parser);
		exp = exp_parse(parser);
		comment = parser_eol_read(parser);
		cond_statements = statement_list_parse(parser,
						       routine);
		cond = statement_cond_create(exp,
					     position,
					     comment,
					     cond_statements,
					     routine,
					     parser);
		conds = vec_create(Statement_cond, heap);
		vec_append(Statement_cond, conds, cond);
		statement = statement_create(Statement_kind_if,
					     position, heap);
		statement->value.xif = conds;
		vec_append(Statement, statements, statement);
		return conds;
	      }
	    case Key_initialize:
	      {
		Exp			exp;
		Statement_initialize	initialize;
		Token			token;
		Str			var_name;
		Type_ref		var_type_ref;

		/* Get the variable: */
		(void)parser_token_read(parser);
		token = parser_token_peek(parser);
		if (token->type != Token_type_symbol) {
			msg_out(parser->msg, token->position,
				"initialize statement must be "
				"followed by a variable");
			return conds;
		}
		var_name = token->value.symbol;
		(void)parser_token_read(parser);

		/* If present, get the variable type: */
		token = parser_token_peek(parser);
		if (token->type == Token_type_define) {
			(void)parser_token_read(parser);
			var_type_ref = type_ref_parse(parser);
		} else {
			var_type_ref = (Type_ref)0;
		}

		/* If present, get the expression: */
		token = parser_token_peek(parser);
		if (token->type == Token_type_assign) {
			(void)parser_token_read(parser);
			exp = exp_parse(parser);
		} else {
			exp = (Exp)0;
		}

		/* Fill in the initialize object: */
		initialize = heap_allocate(heap,
					   Statement_initialize);
		initialize->var_name = var_name;
		initialize->var_type_ref = var_type_ref;
		initialize->exp = exp;
		initialize->comment = parser_eol_read(parser);
		initialize->statements =
			statement_list_parse(parser, routine);
		statement =
			statement_create(Statement_kind_initialize,
					 position, heap);
		statement->value.initialize = initialize;
		statement->breakpoint = routine_breakpoint_create(
							routine,
							position,
							parser->msg,
							heap);
		vec_append(Statement, statements, statement);
		return conds;
	      }
	    case Key_loop:
	      {
		Statement_loop		loop;
		statement = statement_create(Statement_kind_loop,
					     position, heap);
		(void)parser_token_read(parser);
		token = parser_token_peek(parser);
		loop = heap_allocate(heap, Statement_loop);
		switch (token->type) {
		    case Token_type_symbol:
			(void)parser_symbol_parse(parser);
			loop->name = token->value.symbol;
			break;
		    case Token_type_eol:
			loop->name = (Str)0;
			break;
		    default:
			msg_out(parser->msg, parser->file->position,
				"Bad loop statement");
			return conds;
		}
		loop->in_switch = 0;
		loop->is_inner_most = 0;
		loop->need_break = 0;
		loop->need_continue = 0;
		loop->number = parser->loop_number++;
		statement->comment = parser_eol_read(parser);
		loop->statements = statement_list_parse(parser,
							routine);
		supress_eol_parse = 1;
		statement->kind = Statement_kind_loop;
		statement->value.loop = loop;
		break;
	      }
	    case Key_return:
		(void)parser_token_read(parser);
		statement = statement_create(Statement_kind_return,
					     position, heap);
		statement->value.exps = exp_list_parse(parser, 1);
		break;
	    case Key_switch:
	      {
		Statement_switch	xswitch;
		Statement_case		xcase;
		Vec(Statement_case)	cases;

		(void)parser_token_read(parser);
		statement = statement_create(
					Statement_kind_switch,
					position, heap);
		xswitch = heap_allocate(heap, Statement_switch);
		xswitch->exp = exp_parse(parser);
		statement->comment = parser_eol_read(parser);
		cases = vec_create(Statement_case, heap);
		PARSER_LOOP(parser) {
			xcase = statement_case_parse(parser,
						     routine);
			if (xcase != (Statement_case)0) {
				vec_append(Statement_case,
					   cases, xcase);
			}
		}
		xswitch->cases = cases;
		statement->value.xswitch = xswitch;
		statement->breakpoint = routine_breakpoint_create(
							routine,
							position,
							parser->msg,
							heap);
		vec_append(Statement, statements, statement);
		return conds;
	      }
	    case Key_until:
		(void)parser_token_read(parser);
		statement = statement_create(Statement_kind_until,
					     position, heap);
		statement->value.exp = exp_parse(parser);
		break;
	    case Key_while:
		(void)parser_token_read(parser);
		statement = statement_create(Statement_kind_while,
					     position, heap);
		statement->value.exp = exp_parse(parser);
		break;
	    case Key_yield:
		(void)parser_token_read(parser);
		statement = statement_create(Statement_kind_yield,
					     position, heap);
		statement->value.exps = exp_list_parse(parser, 1);
		break;
	    default:
		statement = statement_create(Statement_kind_eval,
					     position, heap);
		statement->value.exp =  exp_assign_parse(parser, 1);
	}

	/* Common end-of-line processing: */
	conds = (Vec(Statement_cond))0;
	if (supress_eol_parse) {
		statement->comment = (Str)0;
	} else {
		statement->comment = parser_eol_read(parser);
	}
	statement->breakpoint = routine_breakpoint_create(routine,
							  position,
							  parser->msg,
							  heap);
	vec_append(Statement, statements, statement);
	return conds;
}

/*
 * statement_tag_parse(parser, routine)
 *	This routine will parse and return a tag label.  (Statement_tag)0
 *	is returned if any error occurs.
 */
LOCAL Statement_tag
statement_tag_parse(
	Parser		parser,
	Routine		routine)
{
	Exp		exp;
	Vec(Exp)	exps;
	Heap		heap;
	Msg		msg;
	Statement_tag	tag;
	Exp		tag_exp;
	Vec(Str)	tag_names;
	Token		token;
	Exp		var_exp;
	Str		var_name;
	Type_ref	var_type_ref;

	heap = parser->heap;
	msg = parser->msg;
	token = parser_token_peek(parser);
	tag = heap_allocate(heap, Statement_tag);
	switch (parser_keyword_parse(parser, 0, 0)) {
	    case Key_tag:
		exp = exp_assign_parse(parser, 1);
		if (exp->op != Exp_op_assign) {
			msg_out(msg, exp->position,
				"No assignment (:=) expression after tag");
			return (Statement_tag)0;
		}
		var_exp = exp->operands.binary->left;
		assert(var_exp->op == Exp_op_list);
		exps = var_exp->operands.list;
		if (vec_size(Exp, exps) != 1) {
			msg_out(msg, exp->position,
				"Tags may only have a single variable");
			return (Statement_tag)0;
		}
		var_exp = vec_fetch(Exp, exps, 0);
		switch (var_exp->op) {
		    case Exp_op_symbol:
			var_name = var_exp->operands.symbol;
			var_type_ref = (Type_ref)0;
			break;
		    case Exp_op_define:
			var_name = var_exp->operands.define->name;
			var_type_ref = var_exp->operands.define->type_ref;
			break;
		    default:
			msg_out(msg, var_exp->position,
				"Tag not followed by variable");
			return (Statement_tag)0;
		}

		tag_exp = exp->operands.binary->right;
		assert(tag_exp->op == Exp_op_list);
		exps = tag_exp->operands.list;
		tag_names = vec_create(Str, parser->heap);
		VEC_LOOP(Exp, exps, tag_exp) {
			switch (tag_exp->op) {
			    case Exp_op_symbol:
				vec_append(Str, tag_names,
					   tag_exp->operands.symbol);
				break;
			    default:
				msg_out(msg, tag_exp->position,
					"No tag name after assignment (:=)");
				return (Statement_tag)0;
			}
		}
		tag->var_name = var_name;
		tag->var_type_ref = var_type_ref;
		tag->tag_names = tag_names;
		break;
	    case Key_default:
		tag->var_name = (Str)0;
		tag->var_type_ref = (Type_ref)0;
		tag->tag_names = vec_create(Str, heap);
		break;
	    default:
		return (Statement_tag)0;
	}
	tag->comment = parser_eol_read(parser);
	tag->position = token->position;
	tag->statements = statement_list_parse(parser, routine);
	return tag;
}

