/*
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. Load more
/* 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) */