/* expr.c */

#include "symbol.h"
#include "expr.h"
#include "asmpar.tab.h"

static EXPR_NODE *exprroot;
static EXPR_NODE **currentexpr;

extern SYMBOL *lookupsymbol();
extern int yyerror();

/* initialise the expr table */

void initexpr()
{
	exprroot = NULL;
	currentexpr = &exprroot;
}

/* return a pointer to the root expression */

EXPR_NODE *retrootexpr()
{
	return exprroot;
}

/* create a exprnode */

EXPR_NODE *createexpr(int type, int value, char *idptr)
{
	EXPR_NODE *ptr;

	ptr = (EXPR_NODE *)malloc(sizeof(EXPR_NODE));

	if (ptr == NULL)
	{
		yyerror("Out of Memory");
		exit(1);
	}

	if (idptr != NULL)
	{
		ptr->idptr = strdup(idptr);

		if (ptr->idptr == NULL)
		{
			yyerror("Out of Memory");
			exit(1);
		}
	}

	ptr->type    = type;
	ptr->value   = value;
	ptr->next    = NULL;

	return ptr;
}

/* delete a expr (doesn't follow tree!) */
/* returns the value of sym->next         */

EXPR_NODE *deleteexpr(EXPR_NODE *sym)
{
	EXPR_NODE *ptr;

	ptr = sym->next;

	free(sym);		/* free the structure     */

	return ptr;
}

/* delete the whole thing */

void deletewholeexpr(EXPR_NODE *root)
{
	EXPR_NODE *ptr;

	ptr = root;

	while(ptr != NULL)
		ptr = deleteexpr(ptr);
}

/* add a expr to the expr table */

void addexpr(int type, int value, char *idptr)
{
	EXPR_NODE *ptr;

	ptr = createexpr(type, value, idptr);

	*currentexpr = ptr;
	currentexpr = &(ptr->next);
}

/* debugging function, list the exprs */

void printop(int op)
{
	switch(op)
	{
		case	ADD:	printf("+ "); break;
		case	SUB:	printf("- "); break;
		case	MUL:	printf("* "); break;
		case	DIV:	printf("/ "); break;
		case	MOD:	printf("% "); break;
		case	SHL:	printf("<< "); break;
		case	SHR:	printf(">> "); break;
		case	LAND:	printf("&& "); break;
		case	LOR:	printf("|| "); break;
		case	LNOT:	printf("! "); break;
		case	BNOT:	printf("~ "); break;
		case	BAND:	printf("& "); break;
		case	BOR:	printf("| "); break;
		case	BXOR:	printf("^ "); break;
		case	EQ:	printf("== "); break;
		case	NEQ:	printf("!= "); break;
		case	GT:	printf("> "); break;
		case	GEQ:	printf(">= "); break;
		case	LS:	printf("< "); break;
		case	LEQ:	printf("<="); break;
		case	UMINUS:	printf("}- "); break;
		default:	printf("printop error %d\n",op);
				exit(1);
	}
}

void anexprlist(EXPR_NODE *anroot)
{
	EXPR_NODE *ptr;

	ptr = anroot;

	while(ptr != NULL)
	{
		switch(ptr->type)
		{
			case EXPR_NUM:	printf("%d ", ptr->value);
					break;

			case EXPR_OP:	printop(ptr->value);
					break;

			case EXPR_ID:	printf("%s ",ptr->idptr);
					break;

			default:	printf("Expression Error\n");
					break;
		}
		ptr = ptr->next;
	}
	printf("\n");
}

void exprlist()
{
	anexprlist(exprroot);
}

/* checks to see if an expression is evaluatable at this time */
/* returns 0 if it can else 1 if any ID's are currently undefined */

int exprcheck(EXPR_NODE *rootptr)
{
	EXPR_NODE *ptr;
	int undefids;

	ptr = rootptr;
	undefids = 0;

	while(ptr != NULL)
	{
		switch(ptr->type)
		{
			case EXPR_NUM:	break;

			case EXPR_OP:	break;

			case EXPR_ID:	if (lookupsymbol(ptr->idptr) == NULL)
						undefids = 1;
					break;

			default:	yyerror("Expression Error");
					break;
		}
		ptr = ptr->next;
	}
	return undefids;
}

static int exprstack[MAX_STACK];	/* this is a ridiculous expression! */
static int exprsp;

static expr_stack_init()
{
	exprsp = 0;
}

static void expr_push(int value)
{
	exprstack[exprsp] = value;
	exprsp++;

	if (exprsp >= MAX_STACK)
	{
		yyerror("Error expression too complex");
		exit(1);
	}
}

static int expr_pop()
{
	--exprsp;
	if (exprsp < 0)
	{
		yyerror("Error in expression!");
		exit(1);
	}

	return exprstack[exprsp];
}

static void dostackops(int whatwehave)
{
	int a,b;

	switch(whatwehave)
	{
		case	ADD:	a = expr_pop();
				b = expr_pop();
				expr_push(b + a);
				break;
		case	SUB:	a = expr_pop();
				b = expr_pop();
				expr_push(b - a);
				break;
		case	MUL:	a = expr_pop();
				b = expr_pop();
				expr_push(b * a);
				break;
		case	DIV:	a = expr_pop();
				b = expr_pop();
				expr_push(b / a);
				break;
		case	MOD:	a = expr_pop();
				b = expr_pop();
				expr_push(b % a);
				break;
		case	SHL:	a = expr_pop();
				b = expr_pop();
				expr_push(b << a);
				break;
		case	SHR:	a = expr_pop();
				b = expr_pop();
				expr_push(b >> a);
				break;
		case	LAND:	a = expr_pop();
				b = expr_pop();
				expr_push(b && a);
				break;
		case	LOR:	a = expr_pop();
				b = expr_pop();
				expr_push(b || a);
				break;
		case	LNOT:	a = expr_pop();
				expr_push(!a);
				break;
		case	BNOT:	a = expr_pop();
				expr_push(~a);
				break;
		case	BAND:	a = expr_pop();
				b = expr_pop();
				expr_push(b & a);
				break;
		case	BOR:	a = expr_pop();
				b = expr_pop();
				expr_push(b | a);
				break;
		case	BXOR:	a = expr_pop();
				b = expr_pop();
				expr_push(b ^ a);
				break;
		case	EQ:	a = expr_pop();
				b = expr_pop();
				expr_push(b == a);
				break;
		case	NEQ:	a = expr_pop();
				b = expr_pop();
				expr_push(b != a);
				break;
		case	GT:	a = expr_pop();
				b = expr_pop();
				expr_push(b > a);
				break;
		case	GEQ:	a = expr_pop();
				b = expr_pop();
				expr_push(b >= a);
				break;
		case	LS:	a = expr_pop();
				b = expr_pop();
				expr_push(b < a);
				break;
		case	LEQ:	a = expr_pop();
				b = expr_pop();
				expr_push(b <= a);
				break;
		case	UMINUS:	a = expr_pop();
				expr_push(-a);
				break;
		default:	yyerror("Expression error, undefined operation");
				exit(1);
	}
}

int evaluateexpr(EXPR_NODE *rootptr)
{
	EXPR_NODE *ptr;
	SYMBOL *symptr;

	expr_stack_init();

	ptr = rootptr;

	while(ptr != NULL)
	{
		switch(ptr->type)
		{
			case EXPR_NUM:	expr_push(ptr->value);
					break;

			case EXPR_OP:	dostackops(ptr->value);
					break;

			case EXPR_ID:	symptr = lookupsymbol(ptr->idptr);

					if (symptr == NULL)
					{
						yyerror("Cannot evaluate with an undefined ID\n");
						exit(1);
					}

					expr_push(symptr->value);
					break;

			default:	yyerror("Expression Error");
					break;
		}

		ptr = ptr->next;
	}

	if (exprsp != 1)	/* ie 1 value left on stack at [0] */
	{
		yyerror("Error unbalanced expression\n");
		exit(1);
	}

	return exprstack[0];
}

/* ... The End ... */

