/******************************************************************************
 *
 *		EVAL.C
 *		Expression Evaluator for 68000 Assembler
 *
 *    Function: eval()
 *		Evaluates a constant expression. The p argument points
 *		to the string to be evaluated, and the function returns
 *		a pointer to the first character beyond the end of the
 *		expression. The value of the expression and an error
 *		code are returned via output arguments. The function 
 *		handles errors according to the following table:
 *
 *				   Pass1        Pass1   Pass2         Pass2
 *		Condition          Error        *refPtr Error         *refPtr
 *		----------------   ----------   -----   -----------   -----
 *		Undefined symbol   INCOMPLETE   FALSE   UNDEFINED     FALSE
 *		Division by zero   INCOMPLETE   FALSE   DIV_BY_ZERO   FALSE
 *		Syntax error       SYNTAX	  --      SYNTAX        --
 * 		Constant error     x_TOO_BIG    T/F     x_TOO_BIG     T/F
 *		No error           OK           T/F     OK            T/F
 *
 *		The char pointed to by refPtr is set to TRUE if all the
 *		symbols encountered in the expression are backwards
 *		references or FALSE if at least one symbol is a forward
 *		reference. 
 *
 *	 Usage:	char *eval(p, valuePtr, refPtr, errorPtr)
 *		char *p;
 *		int *valuePtr;
 *		char *refPtr;
 *		int *errorPtr;
 *
 *	Errors: ASCII_TOO_BIG
 *		DIV_BY_ZERO
 *		INCOMPLETE
 *		NUMBER_TOO_BIG
 *		REG_LIST_SPEC
 *		SYNTAX
 *		UNDEFINED
 *
 *      Author: Paul McKee
 *		ECE492    North Carolina State University
 *
 *        Date:	9/24/86
 *
 *   Copyright 1990-1991 North Carolina State University. All Rights Reserved.
 *
 ******************************************************************************
 * $Id: eval.c,v 1.1 1996/08/02 14:42:05 bwmott Exp $
 *****************************************************************************/


#include <stdio.h>
#include <ctype.h>
#include "asm.h"

extern char pass2;
extern int loc;

/* Largest number that can be represented in an unsigned int - MACHINE DEPENDENT */
#define INTLIMIT 0xFFFFFFFF

#define STACKMAX 5

char *eval(p, valuePtr, refPtr, errorPtr)
char *p;
int *valuePtr;
char *refPtr;
int *errorPtr;
{
char *evalNumber();
int valStack[STACKMAX], opStack[STACKMAX-1];
int valPtr = 0, opPtr = 0;
int op;
int t, prec;
int i;
char evaluate, backRef;
int status;

/*	printf("EVAL: Input string is \"%s\"\n", p); */
	/* Assume that the expression is to be evaluated,
	   at least until an undefined symbol is found */
	evaluate = TRUE;
	/* Assume initially that all symbols are backwards references */
	*refPtr = TRUE;
	/* Loop until terminator character is found (loop is exited via return) */
	while (TRUE) {
		/************************************************
		 *						*
		 *		EXPECT AN OPERAND		*
		 *						*
		 ************************************************/
		/* Use evalNumber to read in a number or symbol */
		status = OK;
		p = evalNumber(p, &t, &backRef, &status);
		NEWERROR(*errorPtr, status);
		if (!backRef && (status > ERROR || status == INCOMPLETE)) {
			/* Stop evaluating the expression */
			evaluate = FALSE;
			*refPtr = FALSE;
			}
		else if (*errorPtr > SEVERE)
			/* Pass any other error to the caller */
			return NULL;
		else {
			/* If OK or WARNING, push the value on the stack */
			if (evaluate)
				valStack[valPtr++] = t;
			/* Set *refPtr to reflect the symbol just parsed */
			*refPtr = *refPtr && backRef;
			}
/*		printf("Operand read - stack contains (bottom to top):\n");
		for (i = 0; i < valPtr || i < opPtr; i++) {
			printf("%2d: ", i);
			if (i < valPtr)
				printf("%10d ", valStack[i]);
			else
				printf("           ");
			if (i < opPtr)
				putchar(opStack[i]);
			putchar('\n');
			} */

		/************************************************
		 *						*
		 *		EXPECT AN OPERATOR		*
		 *						*
		 ************************************************/
		/* Handle the >> and << operators */
		if (*p == '>' || *p == '<') {
			p++;
			if (*p != *(p-1)) {
				NEWERROR(*errorPtr, SYNTAX);
				return NULL;
				}
			}
		prec = precedence(*p);
		/* Do all stacked operations that are of higher
		   precedence than the operator just examined. */
		while (opPtr && evaluate && (prec <= precedence(opStack[opPtr-1]))) {
			/* Pop operands and operator and do the operation */
			t = valStack[--valPtr];
			i = valStack[--valPtr];
			status = doOp(i, t, opStack[--opPtr], &t);
			if (status != OK) {
				/* Report error from doOp */
				if (pass2) {
					NEWERROR(*errorPtr, status);
					}
				else
					NEWERROR(*errorPtr, INCOMPLETE);
				evaluate = FALSE;
				*refPtr = FALSE;
				}
			else
				/* Otherwise push result on the stack */
				valStack[valPtr++] = t;
			}
		if (prec) {
			if (evaluate)
				/* If operator is valid, push it on the stack */
				opStack[opPtr++] = *p;
			p++;
			}
		else if (*p == ',' || *p == '(' || *p == ')' || !(*p) || isspace(*p)) {
			/* If the character terminates the expression,
			   then return the various results needed. */
			if (evaluate)
				*valuePtr = valStack[--valPtr];
			else
				*valuePtr = 0;
/*			printf("EVAL: The expression is \"");
			while (start < p)
				putchar(*start++);
			printf("\"\n"); */
			return p;
			}
		else {
			/* Otherwise report the syntax error */
			NEWERROR(*errorPtr,  SYNTAX);
			return NULL;
			}

/*		printf("Operator processed - stack contains (bottom to top):\n");
		for (i = 0; i < valPtr || i < opPtr; i++) {
			printf("%2d: ", i);
			if (i < valPtr)
				printf("%10d ", valStack[i]);
			else
				printf("           ");
			if (i < opPtr)
				putchar(opStack[i]);
			putchar('\n');
			} */
		}
}


char *evalNumber(p, numberPtr, refPtr, errorPtr)
char *p;
int *numberPtr;
char *refPtr;
int *errorPtr;
{
int status, x, base;
char name[SIGCHARS+1];
symbolDef *symbol, *lookup();
int i;
char endFlag;

	*refPtr = TRUE;
	if (*p == '-') {
		/* Evaluate unary minus operator recursively */
		p = evalNumber(++p, &x, refPtr, errorPtr);
		*numberPtr = -x;
		return p;
		}
	else if (*p == '~') {
		/* Evaluate one's complement operator recursively */
		p = evalNumber(++p, &x, refPtr, errorPtr);
		*numberPtr = ~x;
		return p;
		}
	else if (*p == '(') {
		/* Evaluate parenthesized expressions recursively */
		p = eval(++p, &x, refPtr, errorPtr);
		if (*errorPtr > SEVERE)
			return NULL;
		else if (*p != ')') {
			NEWERROR(*errorPtr, SYNTAX);
			return NULL;
			}
		else {
			*numberPtr = x;
			return ++p;
			}
		}
	else if (*p == '$' && isxdigit(*(p+1))) {
		/* Convert hex digits until another character is
		   found. (At least one hex digit is present.) */
		x = 0;
		while (isxdigit(*++p)) {
			if ((unsigned)x > (unsigned)INTLIMIT/16)
				NEWERROR(*errorPtr, NUMBER_TOO_BIG);
			if (*p > '9')
				x = 16 * x + (*p - 'A' + 10);
			else
				x = 16 * x + (*p - '0');
			}
		*numberPtr = x;
		return p;
		}
	else if (*p == '%' || *p == '@' || isdigit(*p)) {
		/* Convert digits in the appropriate base (binary, 
		   octal, or decimal) until an invalid digit is found. */
		if (*p == '%') {
			base = 2;
			p++;
			}
		else if (*p == '@') {
			base = 8;
			p++;
			}
		else base = 10;
		/* Check that at least one digit is present */
		if (*p < '0' || *p >= '0' + base) {
			NEWERROR(*errorPtr, SYNTAX);
			return NULL;
			}
		x = 0;
		/* Convert the digits into an integer */
		while (*p >= '0' && *p < '0' + base) {
			if ((unsigned)x > (unsigned)(INTLIMIT - (*p-'0'))/base)
				NEWERROR(*errorPtr, NUMBER_TOO_BIG);
			x = base * x + (*p - '0');
			p++;
			}
		*numberPtr = x;
		return p;
		}
	else if (*p == '\'') {
		endFlag = FALSE;
		i = 0;
		x = 0;
		p++;
		while (!endFlag) {
			if (*p == '\'') 
				if (*(p+1) == '\'') {
					x = (x << 8) + *p;
					i++;
					p++;
					}
				else
					endFlag = TRUE;
			else {
				x = (x << 8) + *p;
				i++;
				}
			p++;
			}
		if (i == 0) {
			NEWERROR(*errorPtr, SYNTAX);
			return NULL;
			}
		else if (i == 3)
			x = x << 8;
		else if (i > 4)
			NEWERROR(*errorPtr, ASCII_TOO_BIG);
		*numberPtr = x;
		return p;
		}
	else if (isalpha(*p) || *p == '.') {
		/* Determine the value of a symbol */
		i = 0;
		/* Collect characters of the symbol's name
		   (only SIGCHARS characters are significant) */
		do {
			if (i < SIGCHARS)
				name[i++] = *p;
			p++;
		} while (isalnum(*p) || *p == '.' || *p == '_' || *p == '$');
		name[i] = '\0';
		/* Look up the name in the symbol table, resulting
		   in a pointer to the symbol table entry */
		status = OK;
		symbol = lookup(name, FALSE, &status);
/*		printf("EvalNumber: Status from lookup = %04X\n", status); */
		if (status == OK)
			/* If symbol was found, and it's not a register
			   list symbol, then return its value */
			if (!(symbol->flags & REG_LIST_SYM)) {
				*numberPtr = symbol->value;
/*				printf("The value of the symbol \"%s\" is %08X\n",
					name, *numberPtr); */
				if (pass2)
					*refPtr = (symbol->flags & BACKREF);
				}
			else {
				/* If it is a register list symbol, return error */
				*numberPtr = 0;
				NEWERROR(*errorPtr, REG_LIST_SPEC);
				}
		else {
			/* Otherwise return an error */
			if (pass2) {
				NEWERROR(*errorPtr, UNDEFINED);
				}
			else
				NEWERROR(*errorPtr, INCOMPLETE);
			*refPtr = FALSE;
			}
/*		printf("The symbol \"%s\" is%s a backwards reference\n",
			name, (*refPtr) ? "" : " not"); */
		return p;
 		}
	else {
		/* Otherwise, the character was not a valid operand */
		NEWERROR(*errorPtr, SYNTAX);
		return NULL;
		}
}

int precedence(op)
char op;
{
	/* Compute the precedence of an operator. Higher numbers indicate
	   higher precedence, e.g., precedence('*') > precedence('+').
	   Any character which is not a binary operator will be assigned
	   a precedence of zero. */
	switch (op) {
		case '+' :
		case '-' : return 1;
		case '&' :
 		case '!' : return 3;
		case '>' :
		case '<' : return 4;
		case '*' :
		case '/' : 
		case '\\': return 2;
		default  : return 0;
		}
}


int doOp(val1, val2, op, result)
int val1, val2;
char op;
int *result;
{

	/* Performs the operation of the operator on the two operands.
	   Returns OK or DIV_BY_ZERO. */

	switch (op) {
		case '+' : *result = val1 + val2;  return OK;
		case '-' : *result = val1 - val2;  return OK;
		case '&' : *result = val1 & val2;  return OK;
		case '!' : *result = val1 | val2;  return OK;
		case '>' : *result = val1 >> val2; return OK;
		case '<' : *result = val1 << val2; return OK;
		case '*' : *result = val1 * val2;  return OK;
		case '/' : if (val2 != 0) {
				   *result = val1 / val2;
				   return OK;
				   }
			   else
				   return DIV_BY_ZERO;
		case '\\': if (val2 != 0) {
				   *result = val1 % val2;
				   return OK;
				   }
			   else
				   return DIV_BY_ZERO;
		default  : printf("DoOp: Operator error  op = '%c' val1 = %d val2 = %d\n", op, val1, val2);
		}
}
