1329 lines · 23.9 KB · C
RawDownload
/*
	HEEXPR.C

	Expression/value management functions:

		EvalExpr
			Precedence
		GetValue
			GetVal
		Increment
		IsIncrement
		SetupExpr
		TrimExpr

	for the Hugo Engine

	Copyright (c) 1995-2006 by Kent Tessman
*/


#include "heheader.h"


/* Function prototypes: */
int GetVal(void);
int Precedence(int t);
void TrimExpr(int ptr);

#if defined (DEBUG_EXPR_EVAL)
void PrintExpr(void);
int exprt = true;
#endif


#define MAX_EVAL_ELEMENTS 256

int eval[MAX_EVAL_ELEMENTS];		/* expression components           */
int evalcount;                          /* # of expr. components           */
int var[MAXLOCALS+MAXGLOBALS];		/* variables                       */
int incdec;				/* value is being incremented/dec. */
char getaddress = 0;                    /* true when finding &routine      */
char inexpr = 0;                        /* true when in expression         */
char inobj = 0;                         /* true when in object compound    */

int last_precedence;


/* EVALEXPR

	The new-and-improved expression evaluator.  Evaluates the current
	expression (or sub-expression therein) beginning at eval[p].
*/

int EvalExpr(int p)
{
	int n1, n2;
	int oper;
	short result = 0;		/* must be 16 bits */

	/* for precedence stacking/unstacking */
	int next_prec, this_prec, temp_lp;

	if (!evalcount) return 0;       /* no expression */

	do
	{
		if (eval[p]==1)
		{
			if (eval[p+1]==OPEN_BRACKET_T ||
				eval[p+1]==OPEN_SQUARE_T)
			{
				eval[p] = 0;
				eval[p+1] = EvalExpr(p+2);
				TrimExpr(p+2);
			}
			else if (eval[p+1]==MINUS_T)
			{
				TrimExpr(p);
				eval[p+1] = -eval[p+1];
			}
		}

		if (evalcount<=p+2)
		{
			result = eval[p+1];
			TrimExpr(p);
			eval[p] = 0;
			eval[p+1] = result;
			goto ReturnResult;
		}

		n1 = eval[p+1];
		oper = eval[p+3];

		/* At this point, <n1> holds the first value, and <oper> holds
		   the token number of the operator.
		*/
		if (eval[p+4]==1 && (eval[p+5]==OPEN_BRACKET_T
			|| eval[p+5]==OPEN_SQUARE_T))
		{
			eval[p+4] = 0;
			eval[p+5] = EvalExpr(p+6);
			TrimExpr(p+6);
		}

		n2 = eval[p+5];

		if (evalcount > p+7)
		{
			if (eval[p+3]==CLOSE_BRACKET_T && eval[p+2]==1)
			{
				TrimExpr(p+2);
				return eval[p+1];
			}

			/* eval[p+7] holds the next operator, i.e., the "*"
			   in:  "x + y * z"

			   This way, we can check if the upcoming operator
			   takes precedence over the current one.
			*/
			if ((next_prec = Precedence(eval[p+7]))
				< (this_prec = Precedence(oper)))
			{
				if (next_prec >= last_precedence)
				{
#if defined (DEBUG_PRECEDENCE)
sprintf(line, "Not preferring %s to %s because of previous level %d", token[eval[p+7]], token[oper], last_precedence);
Printout(line);
#endif
					goto ReturnResult;
				}

#if defined (DEBUG_PRECEDENCE)
sprintf(line, "Preferring %s to %s", token[eval[p+7]], token[oper]);
Printout(line);
#endif

				temp_lp = last_precedence;
				last_precedence = this_prec;
				n2 = EvalExpr(p+4);
				last_precedence = temp_lp;
			}
		}
		else if (Precedence(oper)>=last_precedence)
		{
			goto ReturnResult;
		}

#if defined (DEBUG_PRECEDENCE)
sprintf(line, "Solving %d %s %d", n1, token[oper], n2);
Printout(line);
#endif

		switch (oper)
		{
			case DECIMAL_T:
			{
				result = GetProp(n1, n2, 1, 0);
				break;
			}

			case EQUALS_T:
			{
				result = (n1==n2);
				break;
			}
			case MINUS_T:
			{
				result = n1 - n2;
				break;
			}
			case PLUS_T:
			{
				result = n1 + n2;
				break;
			}
			case ASTERISK_T:
			{
				result = n1 * n2;
				break;
			}
			case FORWARD_SLASH_T:
			{
				if (n2==0)
#if defined (DEBUGGER)
				{
					RuntimeWarning("Division by zero:  invalid result");
					result = 0;
				}
#else
					FatalError(DIVIDE_E);
#endif
				result = n1 / n2;
				break;
			}
			case PIPE_T:
			{
				result = n1 | n2;
				break;
			}
			case GREATER_EQUAL_T:
			{
				result = (n1>=n2);
				break;
			}
			case LESS_EQUAL_T:
			{
				result = (n1<=n2);
				break;
			}
			case NOT_EQUAL_T:
			{
				result = (n1!=n2);
				break;
			}
			case AMPERSAND_T:
			{
				result = n1 & n2;
				break;
			}
			case GREATER_T:
			{
				result = (n1 > n2);
				break;
			}
			case LESS_T:
			{
				result = (n1 < n2);
				break;
			}
			case AND_T:
			{
				result = (n1 && n2);
				break;
			}
			case OR_T:
			{
				result = (n1 || n2);
				break;
			}

			default:
			{
				result = n1;
			}
		}

#if defined (DEBUGGER)
		if ((debug_eval) && debug_eval_error) return 0;
#endif

		TrimExpr(p+4);          /* second value */
		TrimExpr(p+2);          /* operator */

		eval[p] = 0;
		eval[p+1] = result;


	/* Keep looping while there are expression elements, or until there
	   is a ")", "]", or end of line
	*/
	} while ((evalcount>p+2) && !(eval[p+2]==1 &&
		(eval[p+3]==CLOSE_BRACKET_T || eval[p+3]==CLOSE_SQUARE_T ||
		eval[p+3]==255)));

	result = eval[p+1];

	TrimExpr(p);                    /* first value */

ReturnResult:

#if defined (DEBUG_EXPR_EVAL)
	if (p==0 && exprt)
	{
		sprintf(line, " = %d", result);
		AP(line);
	}
#endif
	return result;
}


/* GETVAL

	Called by GetValue(); does the actual dirty work of returning
	a value from a simple data type.
*/

int GetVal()
{
	char a = 0;
	char tempinexpr, tempgetaddress, tempinobj;
	int i, j;
	int tempret;

	unsigned short routineaddr, arrayaddr;	/* must be 16 bits */
	short val = 0;

	char inctype = 0;
	int preincdec;                  /* pre-increment/decrement */

	defseg = gameseg;

	tempret = ret;
	tempinexpr = inexpr;
	inexpr = 0;

	preincdec = incdec;
	incdec = 0;

	switch (MEM(codeptr))
	{
		case AMPERSAND_T:       /* an address */
			{codeptr++;
			getaddress = true;
			val = GetValue();
			getaddress = false;
			break;}

		case ROUTINE_T:
		case CALL_T:
		{
			if (MEM(codeptr)==ROUTINE_T)
			{
				if (tail_recursion==0 && MEM(codeptr-1)==RETURN_T)
				{
					/* We may be able to tail-recurse this return
					   statement if it's simply 'return Routine(...)'
					*/
					tail_recursion = TAIL_RECURSION_ROUTINE;
				}

				routineaddr = PeekWord(++codeptr);
				codeptr += 2;

				if (getaddress)
					{val = routineaddr;
					getaddress = false;
					break;}
			}
			else
			{
				codeptr++;
				routineaddr = GetValue();
			}

#if defined (DEBUGGER)
			if (debug_eval)
			{
				debug_eval_error = true;
				val = 0;
				break;
			}
#endif
                        val = CallRoutine(routineaddr);

			break;
		}

		case OPEN_BRACKET_T:
		{
			codeptr++;
			inexpr = 1;
			tempgetaddress = getaddress;
			getaddress = false;
			SetupExpr();
			inexpr = 0;
			val = EvalExpr(0);
			getaddress = tempgetaddress;
			break;
		}

		case MINUS_T:
		{
			codeptr++;
			j = inexpr;	/* don't reuse tempinexpr */
			inexpr = 1;
			val = -GetValue();
			inexpr = (char)j;
			break;
		}

		case VALUE_T:                   /* integer 0 - 65535 */
		case OBJECTNUM_T:
		case DICTENTRY_T:
		{
			val = PeekWord(++codeptr);
			codeptr += 2;
			break;
		}

		case ATTR_T:
		case PROP_T:
		{
			val = MEM(++codeptr);
			codeptr++;
			break;
		}

		case VAR_T:                     /* variable */
		{
			val = var[(i=MEM(++codeptr))];

			if (game_version >= 22)
			{
				/* Pre-v2.4 included linelength and pagelength as
				   global variables after objectcount
				*/
				if (i <= ((game_version>=24)?objectcount:objectcount+2))
				{
					if (i==wordcount) val = words;
					else if (i==objectcount) val = objects;

					/* i.e., pre-v2.4 only */
					else if (i==objectcount+1)
					{
#if defined (ACTUAL_LINELENGTH)
						val = ACTUAL_LINELENGTH();
#else
						val = SCREENWIDTH/charwidth;
#endif
					}
					else if (i==objectcount+2)
						val = SCREENHEIGHT/lineheight;
				}
			}
			codeptr++;

			if (!inobj) inctype = IsIncrement(codeptr);

			/* don't operate on, e.g., ++variable.property as
			   (++variable).property
			*/
			if ((incdec || preincdec) && MEM(codeptr)!=DECIMAL_T)
			{
				if (i < MAXGLOBALS) SaveUndo(VAR_T, i, val, 0, 0);

				if (inctype) val = Increment(val, inctype);

				/* still a post-increment hanging around */
				var[i] = (val+=preincdec) + incdec;

				incdec = preincdec = 0;
			}

			break;
		}

		case TRUE_T:
			val = 1;
			codeptr++;
			break;

		case FALSE_T:
			val = 0;
			codeptr++;
			break;

		case TILDE_T:
			codeptr++;
			val = ~GetValue();
			break;

		case NOT_T:
			codeptr++;
			val = !GetValue();
			break;

		case ARRAYDATA_T:
		case ARRAY_T:
		{
			unsigned int element;

			if (MEM(codeptr)==ARRAY_T)
			{
				codeptr++;
				arrayaddr = GetValue();
			}
			else
			{
				arrayaddr = PeekWord(++codeptr);
				codeptr += 2;
			}

			if (MEM(codeptr)!=OPEN_SQUARE_T)
				{val = arrayaddr;
				break;}

			if (game_version>=22)
			{
				/* convert to word value */
				arrayaddr*=2;

				if (game_version>=23)
					/* space for array length */
					a = 2;
			}

			/* check if this is array[] (i.e., array length) */

Lines 1–500 of 1329.