/*
**      code-gen.c - PCode generation routines
**
**
** Copyright (c) 1996,97  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** This software is provided "as is" without any expressed or implied warranty.
**
**
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "lite.h"


funct_t	mainFunct = {"main",NULL,NULL,NULL};
funct_t	*functions = &mainFunct,
	*curFunct = &mainFunct;

code_t	*codeHead = NULL,
	*codeTail = NULL;

sym_t	*paramHead = NULL,
	*paramTail = NULL;

extern	int	yylineno;



/**************************************************************************
**      Function management routines
*/

int addFunction(name)
	char	*name;
{
	funct_t	*cur,
		*prev;
	char	errBuf[100];
	efunct_t *efunct;


	/*
	** Check user defined functions
	*/
	cur = functions;
	while(cur)
	{
		if(strcmp(name,cur->name) == 0)
		{
			sprintf(errBuf,"Non-unique function name '%s'",name);
			parseError(errBuf);
			return(-1);
		}
		prev = cur;
		cur = cur->next;
	}

	/*
	** Check module functions
	*/
        efunct = (efunct_t *)findExtern(name);
	if (efunct)
	{
		sprintf(errBuf,"Non-unique function name '%s' (module)",name);
		parseError(errBuf);
		return(-1);
	}

	/*
	** The name's cool, do the rest
	*/
	cur = (funct_t*)malloc(sizeof(funct_t));
	cur->name = (char *)strdup(name);
	cur->params = paramHead;
	cur->code = NULL;
	cur->next = functions;
	functions = cur;
	curFunct = cur;
	codeHead = codeTail = NULL;
	paramHead = NULL;
	return(0);
}


int addParam(type, name)
	char	*type,
		*name;
{
	sym_t	*new;
	char	errBuf[100];

	new = (sym_t *)malloc(sizeof(sym_t));
	strcpy(new->name,name);
	new->next = NULL;
	if (strcmp(type, "array") == 0)
	{
		new->type = TYPE_CHAR;
		new->array = ARRAY;
	}
	else
	{
		if ( (new->type = typeGetTypeIndex(type)) < 0)
		{
			sprintf(errBuf,"Unknown type '%s'",type);
			parseError(errBuf);
			free(new);
			return(-1);
		}
	}

	if (paramHead)
	{
		paramTail->next = new;
		paramTail= new;
	}
	else
	{
		paramHead = paramTail = new;
	}
	return(0);
}


void endOfFunction()
{
	codeHead = codeTail = NULL;
	curFunct = &mainFunct;
}



/**************************************************************************
**      Code list management routines
*/

static void addCode(code)
	code_t	*code;
{
	code_t	*new;

	new = (code_t *)malloc(sizeof(code_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	new->op = code->op;
	new->label = code->label;
	new->type = code->type;
	new->line = code->line;
	new->next = NULL;
	new->prev = codeTail;
	if (code->arg)
		new->arg = (char *)strdup(code->arg);
	if (!codeHead)
	{
		codeHead = curFunct->code = new;
	}
	else
	{
		codeTail->next = new;
	}
	codeTail = new;
}



/**************************************************************************
**      Code generation Routines
*/


void codeBlockIn()
{
	code_t	code;

	code.op = OP_BLOCK_IN;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}




void codeBlockOut()
{
	code_t	code;

	code.op = OP_BLOCK_OUT;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}


void codeStore(name)
	char	*name;
{
	code_t	code;

	code.op = OP_STORE;
	code.arg = name;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}


void codeArrayStore(name)
	char	*name;
{
	code_t	code;

	code.op = OP_A_STORE;
	code.arg = name;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}


void codeDeref(name)
	char	*name;
{
	code_t	code;

	code.op = OP_DEREF;
	code.arg = name;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}


void codeCall(name)
	char	*name;
{
	code_t	code;

	code.op = OP_CALL;
	code.arg = (char *)name;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codePush(name)
	char	*name;
{
	code_t	code;

	code.op = OP_PUSH;
	code.arg = (char *)name;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codeCount()
{
	code_t	code;

	code.op = OP_COUNT;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codeCast(type)
	char	*type;
{
	code_t	code;

	code.op = OP_CAST;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = typeGetTypeIndex(type);
	if (code.type == 255)
	{
		runError("Unknown type!");
		exit(1);
	}
	addCode(&code);
}


void codeALU(op)
	int	op;
{
	code_t	code;

	code.op = OP_ALU;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = op;
	addCode(&code);
}


void codeLabel(label)
	int	label;
{
	code_t	code;

	code.op = OP_LABEL;
	code.arg = NULL;
	code.label = label;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codeJmp(label)
	int	label;
{
	code_t	code;

	code.op = OP_JMP;
	code.arg = NULL;
	code.label = label;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codeJmpBack(label)
	int	label;
{
	code_t	code;

	code.op = OP_JMP_BACK;
	code.arg = NULL;
	code.label = label;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codeJmpFalse(label)
	int	label;
{
	code_t	code;

	code.op = OP_JMP_FALSE;
	code.arg = NULL;
	code.label = label;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codeCmp(op)
	int	op;
{
	code_t	code;

	code.op = OP_CMP;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = op;
	addCode(&code);
}



void codeEcho(name)
	char	*name;
{
	code_t	code;

	code.op = OP_ECHO;
	code.arg = name;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codePushRet()
{
	code_t	code;

	code.op = OP_PUSHRET;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}

void codeReturn()
{
	code_t	code;

	code.op = OP_RETURN;
	code.arg = NULL;
	code.label = 0;
	code.line = yylineno;
	code.type = 0;
	addCode(&code);
}


/**************************************************************************
**      Label management routines
*/

typedef struct label_s {
	int	label;
	struct	label_s *next;
} stk_t;

stk_t	*labelStack 	= NULL,
	*breakStack	= NULL,
	*continueStack	= NULL;


void codePushLabel(label)
	int	label;
{
	stk_t	*new;

	new = (stk_t *)malloc(sizeof(stk_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	new->label = label;
	new->next = labelStack;
	labelStack = new;
}


int codePopLabel()
{
	int	label;
	stk_t	*tmp;

	if (!labelStack)
	{
		runError("Label stack underflow! (internal error)");
		exit(1);
	}
	label = labelStack->label;
	tmp = labelStack;
	labelStack = labelStack->next;
	free(tmp);
	return(label);
}


void codePushContinue(label)
	int	label;
{
	stk_t	*new;

	new = (stk_t *)malloc(sizeof(stk_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	new->label = label;
	new->next = continueStack;
	continueStack = new;
}


int codePopContinue()
{
	int	label;
	stk_t	*tmp;

	if (!continueStack)
	{
		return(-1);
	}
	label = continueStack->label;
	tmp = continueStack;
	continueStack = continueStack->next;
	free(tmp);
	return(label);
}

void codePushBreak(label)
	int	label;
{
	stk_t	*new;

	new = (stk_t *)malloc(sizeof(stk_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	new->label = label;
	new->next = breakStack;
	breakStack = new;
}


int codePopBreak()
{
	int	label;
	stk_t	*tmp;

	if (!breakStack)
	{
		return(-1);
	}
	label = breakStack->label;
	tmp = breakStack;
	breakStack = breakStack->next;
	free(tmp);
	return(label);
}





/**************************************************************************
**      Code generation debug routines
*/

#include "y.tab.h"		/* Just for debugger output */

char *opCodes[] = {
	"????",
	"CALL",
	"PUSH",
	"CMP",
	"JMP",
	"JMP_FALSE",
	"JMP_BACK",
	"LABEL",
	"STORE",
	"A_STORE",
	"DEREF",
	"ALU",
	"BLOCK_IN",
	"BLOCK_OUT",
	"EXIT",
	"ECHO",
	"CAST",
	"COUNT",
	"PUSHRET",
	"RETURN",
	"????"};


static char *aluCodes[] = {
	"????",
	"ADD",
	"SUB",
	"MUL",
	"DIV",
	"AND",
	"OR",
	"????"};

static char *typeCodes[] = {
	"REAL",
	"INT",
	"CHAR",
	"UINT",
	"????"};

void dumpCode()
{
	code_t	*cur;
	char	*arg;
	sym_t	*sym;
	int	count;
	funct_t	*curFunct;
	extern	sym_t *litHead[];




	printf("Literals :-\n\n");
	count = 0;
	while(count < SYM_HASH_SIZE)
	{
		sym = litHead[count];
		while(sym)
		{
			printf("\t'%s' = '%s'\n", sym->name, 
				symUnpackSymbol(sym));
			sym = sym->next;
		}
		count++;
	}
	printf("\n\n");

	curFunct = functions;
	while(curFunct)
	{
		printf("\n=============\n");
		printf("Function %s(",curFunct->name);
		sym = curFunct->params;
		while(sym)
		{
			if (sym == curFunct->params)
				printf("%s", sym->name);
			else
				printf(", %s", sym->name);
			sym = sym->next;
		}
		printf(")\n");

		cur = curFunct->code;
		while(cur)
		{
			printf("Line %-3d  %-11s ",cur->line,
				opCodes[(int)cur->op]);
			switch(cur->op)
			{
				case OP_PUSH:
				case OP_STORE:
				case OP_A_STORE:
				case OP_DEREF:
				case OP_ECHO:
					if (*(cur->arg)=='$'||*(cur->arg)=='@')
						printf("Arg   = '%s'",cur->arg);
					else
					{
						sym = symGetSymbol(cur->arg);
						arg = symUnpackSymbol(sym);
						if (strlen(arg) > 20)
						{
							*(arg+18)= ' ';
							*(arg+19)= '.';
							*(arg+20)= '.';
							*(arg+21)= '.';
							*(arg+22)= 0;
						}
						printf("Arg   = '%s'",arg);
						free(arg);
					}
					break;
	
				case OP_CALL:
					printf("Arg   = '%s'",cur->arg);
					break;
	
				case OP_CAST:
					printf("Type  = '%s'", 
						typeCodes[cur->type]);
					break;
	
				case OP_LABEL:
				case OP_JMP:
				case OP_JMP_BACK:
				case OP_JMP_FALSE:
					printf("Label = %d",cur->label);
					break;

				case OP_ALU:
					printf("Op    = %s", aluCodes[cur->type-99]);
					break;

				case OP_CMP:
					switch (cur->type)
					{
						case EQ:
							printf("Op    = EQ");
							break;
						case NE:
							printf("Op    = NE");
							break;
						case LT:
							printf("Op    = LT");
							break;
						case GT:
							printf("Op    = GT");
							break;
						case LE:
							printf("Op    = LE");
							break;
						case GE:
							printf("Op    = GE");
							break;
						case RE:
							printf("Op    = RE");
							break;
						default:
							printf("Op    = ????");
					}
					
			}
			printf("\n");
			cur = cur->next;
		}
		curFunct = curFunct->next;
	}
}
