Files
firestorm/Gameleap/code/mw4/Code/MW4/Ablxexpr.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

663 lines
19 KiB
C++

//***************************************************************************
//
// ABLXEXPR.CPP
//
//***************************************************************************
#include "MW4Headers.hpp"
#include <string.h>
#ifndef ABLGEN_H
#include "ablgen.h"
#endif
#ifndef ABLERR_H
#include "ablerr.h"
#endif
#ifndef ABLSCAN_H
#include "ablscan.h"
#endif
#ifndef ABLSYMT_H
#include "ablsymt.h"
#endif
#ifndef ABLPARSE_H
#include "ablparse.h"
#endif
#ifndef ABLEXEC_H
#include "ablexec.h"
#endif
#ifndef ABLDBUG_H
#include "abldbug.h"
#endif
#ifndef ABLHEAP_H
#include "ablheap.h"
#endif
namespace ABL
{
//***************************************************************************
//----------
// EXTERNALS
extern long level;
extern char* codeSegmentPtr;
extern TokenCodeType codeToken;
extern StackItem* stack;
extern StackItemPtr tos;
extern StackItemPtr stackFrameBasePtr;
extern StackItemPtr StaticDataPtr;
extern SymTableNodePtr CurRoutineIdPtr;
extern ABLModulePtr CurModule;
extern TypePtr IntegerTypePtr;
extern TypePtr CharTypePtr;
extern TypePtr RealTypePtr;
extern TypePtr BooleanTypePtr;
extern UserHeapPtr AblStackHeap;
extern DebuggerPtr debugger;
//***************************************************************************
inline void promoteOperandsToReal (StackItemPtr operand1Ptr, TypePtr type1Ptr,
StackItemPtr operand2Ptr, TypePtr type2Ptr)
{
if (type1Ptr == IntegerTypePtr)
operand1Ptr->real = (float)(operand1Ptr->integer);
if (type2Ptr == IntegerTypePtr)
operand2Ptr->real = (float)(operand2Ptr->integer);
}
//***************************************************************************
TypePtr execField (void)
{
//------------------------------------------------------------------
// NOTE: this routine has been included should we use records/fields
// in ABL...
getCodeToken();
SymTableNodePtr fieldIdPtr = getCodeSymTableNodePtr();
tos->address += fieldIdPtr->defn.info.data.offset;
getCodeToken();
return((TypePtr)(fieldIdPtr->typePtr));
}
//***************************************************************************
TypePtr execSubscripts (TypePtr typePtr)
{
//----------------------------------------
// Loop to execute bracketed subscripts...
while (codeToken == TKN_LBRACKET)
{
do
{
getCodeToken();
execExpression();
long subscriptValue = tos->integer;
pop();
//-------------------------
// Range check the index...
if ((subscriptValue < 0) || (subscriptValue >= typePtr->info.array.elementCount))
runtimeError(ABL_ERR_RUNTIME_VALUE_OUT_OF_RANGE);
tos->address += (subscriptValue * typePtr->info.array.elementTypePtr->size);
if (codeToken == TKN_COMMA)
typePtr = typePtr->info.array.elementTypePtr;
} while (codeToken == TKN_COMMA);
getCodeToken();
if (codeToken == TKN_LBRACKET)
typePtr = typePtr->info.array.elementTypePtr;
}
return(typePtr->info.array.elementTypePtr);
}
//***************************************************************************
TypePtr execConstant (SymTableNodePtr idPtr)
{
TypePtr typePtr = (TypePtr)(idPtr->typePtr);
if ((baseType(typePtr) == IntegerTypePtr) || (typePtr->form == FRM_ENUM))
pushInteger(idPtr->defn.info.constant.value.integer);
else if (typePtr == RealTypePtr)
pushReal(idPtr->defn.info.constant.value.real);
else if (typePtr == CharTypePtr)
pushInteger(idPtr->defn.info.constant.value.character);
else if (typePtr->form == FRM_ARRAY)
pushAddress(idPtr->defn.info.constant.value.stringPtr);
if (debugger)
debugger->traceDataFetch(idPtr, typePtr, tos);
getCodeToken();
return(typePtr);
}
//***************************************************************************
TypePtr execVariable (SymTableNodePtr idPtr, UseType use)
{
TypePtr typePtr = (TypePtr)(idPtr->typePtr);
// First, point to the variable's stack item. If the variable's scope
// level is less than the current scope level, follow the static links
// to the proper stack frame base...
StackItemPtr dataPtr = NULL;
switch (idPtr->defn.info.data.varType)
{
case VAR_TYPE_NORMAL: {
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
long delta = level - idPtr->level;
while (delta-- > 0)
headerPtr = (StackFrameHeaderPtr)headerPtr->staticLink.address;
dataPtr = (StackItemPtr)headerPtr + idPtr->defn.info.data.offset;
}
break;
case VAR_TYPE_ETERNAL:
dataPtr = (StackItemPtr)stack + idPtr->defn.info.data.offset;
break;
case VAR_TYPE_STATIC:
//---------------------------------------------------------
// If we're referencing a library's static variable, we may
// need to shift to its static data space temporarily...
if (idPtr->library && (idPtr->library != CurModule))
StaticDataPtr = idPtr->library->getStaticData();
dataPtr = (StackItemPtr)StaticDataPtr + idPtr->defn.info.data.offset;
if (idPtr->library && (idPtr->library != CurModule))
StaticDataPtr = CurModule->getStaticData();
break;
}
//---------------------------------------------------------------
// If it's a scalar or enumeration reference parameter, that item
// points to the actual item...
if ((idPtr->defn.key == DFN_REFPARAM) && (typePtr->form != FRM_ARRAY)/* && (typePtr->form != FRM_RECORD)*/)
dataPtr = (StackItemPtr)dataPtr->address;
ABL_Assert(dataPtr != NULL, 0, " ABL.execVariable(): dataPtr is NULL ");
//-----------------------------------------------------
// Now, push the address of the variable's data area...
if ((typePtr->form == FRM_ARRAY) /*|| (typePtr->form == FRM_RECORD)*/)
{
//pushInteger(typePtr->size);
pushAddress((Address)dataPtr->address);
}
else
pushAddress((Address)dataPtr);
//-----------------------------------------------------------------------------------
// If there is a subscript (or field identifier, if records are being used in ABL)
// then modify the address to point to the proper element of the array (or record)...
getCodeToken();
while ((codeToken == TKN_LBRACKET) /*|| (codeTOken == TKN_PERIOD)*/)
{
if (codeToken == TKN_LBRACKET)
typePtr = execSubscripts(typePtr);
//else if (codeToken == TKN_PERIOD)
// typePtr = execField(typePtr);
}
TypePtr baseTypePtr = baseType(typePtr);
//------------------------------------------------------------
// Leave the modified address on the top of the stack if:
// a) it's an assignment target;
// b) it reresents a parameter passed by reference;
// c) it's the address of an array or record;
// Otherwise, replace the address with the value it points to.
if ((use != USE_TARGET) && (use != USE_REFPARAM) && (typePtr->form != FRM_ARRAY) /*&& (typePtr->form != FRM_RECORD)*/)
{
if ((baseTypePtr == IntegerTypePtr) || (typePtr->form == FRM_ENUM))
{
tos->integer = *((long*)tos->address);
}
else if (baseTypePtr == CharTypePtr)
tos->byte = *((char*)tos->address);
else
tos->real = *((float*)tos->address);
}
if (debugger)
{
if ((use != USE_TARGET) && (use != USE_REFPARAM))
{
if ((typePtr->form == FRM_ARRAY) /*|| (typePtr->form == FRM_RECORD)*/)
debugger->traceDataFetch(idPtr, typePtr, (StackItemPtr)tos->address);
else
debugger->traceDataFetch(idPtr, typePtr, tos);
}
}
return(typePtr);
}
//***************************************************************************
TypePtr execFactor (void)
{
TypePtr resultTypePtr = NULL;
switch (codeToken)
{
case TKN_IDENTIFIER: {
SymTableNodePtr idPtr = getCodeSymTableNodePtr();
if (idPtr->defn.key == DFN_FUNCTION)
{
SymTableNodePtr thisRoutineIdPtr = CurRoutineIdPtr;
resultTypePtr = execRoutineCall(idPtr);
CurRoutineIdPtr = thisRoutineIdPtr;
}
else if (idPtr->defn.key == DFN_CONST)
resultTypePtr = execConstant(idPtr);
else
resultTypePtr = execVariable(idPtr, USE_EXPR);
}
break;
case TKN_NUMBER: {
SymTableNodePtr numberPtr = getCodeSymTableNodePtr();
if (numberPtr->typePtr == IntegerTypePtr)
{
pushInteger(numberPtr->defn.info.constant.value.integer);
resultTypePtr = IntegerTypePtr;
}
else
{
pushReal(numberPtr->defn.info.constant.value.real);
resultTypePtr = RealTypePtr;
}
getCodeToken();
}
break;
case TKN_STRING: {
SymTableNodePtr nodePtr = getCodeSymTableNodePtr();
long length = strlen(nodePtr->name);
if (length > 1)
{
//-----------------------------------------------------------------------
// Remember, the double quotes are on the back and front of the string...
pushAddress(nodePtr->info);
resultTypePtr = nodePtr->typePtr;
}
else
{
//----------------------------------------------
// Just push the one character in this string...
pushByte(nodePtr->name[0]);
resultTypePtr = CharTypePtr;
}
getCodeToken();
}
break;
case TKN_NOT:
getCodeToken();
resultTypePtr = execFactor();
//--------------------------------------
// Following flips 1 to 0, and 0 to 1...
tos->integer = 1 - tos->integer;
break;
case TKN_LPAREN:
getCodeToken();
resultTypePtr = execExpression();
getCodeToken();
break;
}
return(resultTypePtr);
}
//***************************************************************************
TypePtr execTerm (void)
{
StackItemPtr operand1Ptr;
StackItemPtr operand2Ptr;
TypePtr type2Ptr;
TokenCodeType op;
TypePtr resultTypePtr = execFactor();
//----------------------------------------------
// Process the factors separated by operators...
while ((codeToken == TKN_STAR) || (codeToken == TKN_FSLASH) ||
(codeToken == TKN_DIV) || (codeToken == TKN_MOD) ||
(codeToken == TKN_AND))
{
op = codeToken;
resultTypePtr = baseType(resultTypePtr);
getCodeToken();
type2Ptr = baseType(execFactor());
operand1Ptr = tos - 1;
operand2Ptr = tos;
if (op == TKN_AND)
{
operand1Ptr->integer = operand1Ptr->integer && operand2Ptr->integer;
resultTypePtr = BooleanTypePtr;
}
else
switch (op)
{
case TKN_STAR:
if ((resultTypePtr == IntegerTypePtr) && (type2Ptr == IntegerTypePtr))
{
//-----------------------------
// Both operands are integer...
operand1Ptr->integer = operand1Ptr->integer * operand2Ptr->integer;
resultTypePtr = IntegerTypePtr;
}
else
{
//----------------------------------------------------------------
// Both operands are real, or one is real and the other integer...
promoteOperandsToReal(operand1Ptr, resultTypePtr, operand2Ptr, type2Ptr);
operand1Ptr->real = operand1Ptr->real * operand2Ptr->real;
resultTypePtr = RealTypePtr;
}
break;
case TKN_FSLASH:
//--------------------------------------------------------------------
// Both operands are real, or one is real and the other an integer. We
// probably want this same token to be used for integers, as opposed
// to using the DIV token...
if ((resultTypePtr == IntegerTypePtr) && (type2Ptr == IntegerTypePtr))
{
//-----------------------------
// Both operands are integer...
if (operand2Ptr->integer == 0)
{
#ifdef _DEBUG
runtimeError(ABL_ERR_RUNTIME_DIVISION_BY_ZERO);
#else
//HACK!!!!!!!!!!!!
operand1Ptr->integer = 0;
#endif
}
else
operand1Ptr->integer = operand1Ptr->integer / operand2Ptr->integer;
resultTypePtr = IntegerTypePtr;
}
else
{
//----------------------------------------------------------------
// Both operands are real, or one is real and the other integer...
promoteOperandsToReal(operand1Ptr, resultTypePtr, operand2Ptr, type2Ptr);
if (operand2Ptr->real == 0.0)
#ifdef _DEBUG
runtimeError(ABL_ERR_RUNTIME_DIVISION_BY_ZERO);
#else
//HACK!!!!!!!!!!!!
operand1Ptr->real = 0.0;
#endif
else
operand1Ptr->real = operand1Ptr->real / operand2Ptr->real;
resultTypePtr = RealTypePtr;
}
break;
case TKN_DIV:
case TKN_MOD:
//-----------------------------
// Both operands are integer...
if (operand2Ptr->integer == 0)
#ifdef _DEBUG
runtimeError(ABL_ERR_RUNTIME_DIVISION_BY_ZERO);
#else
//HACK!!!!!!!!!!!!
operand1Ptr->integer = 0;
#endif
else
{
if (op == TKN_DIV)
operand1Ptr->integer = operand1Ptr->integer / operand2Ptr->integer;
else
operand1Ptr->integer = operand1Ptr->integer % operand2Ptr->integer;
}
resultTypePtr = IntegerTypePtr;
break;
}
//------------------------------
// Pop off the second operand...
pop();
}
return(resultTypePtr);
}
//***************************************************************************
TypePtr execSimpleExpression (void)
{
TokenCodeType unaryOp = TKN_PLUS;
//------------------------------------------------------
// If there's a + or - before the expression, save it...
if ((codeToken == TKN_PLUS) || (codeToken == TKN_MINUS))
{
unaryOp = codeToken;
getCodeToken();
}
TypePtr resultTypePtr = execTerm();
//-------------------------------------------------------
// If there was a unary -, negate the top of the stack...
if (unaryOp == TKN_MINUS)
{
if (resultTypePtr == IntegerTypePtr)
tos->integer = -(tos->integer);
else
tos->real = -(tos->real);
}
while ((codeToken == TKN_PLUS) || (codeToken == TKN_MINUS) || (codeToken == TKN_OR))
{
TokenCodeType op = codeToken;
resultTypePtr = baseType(resultTypePtr);
getCodeToken();
TypePtr type2Ptr = baseType(execTerm());
StackItemPtr operand1Ptr = tos - 1;
StackItemPtr operand2Ptr = tos;
if (op == TKN_OR)
{
operand1Ptr->integer = operand1Ptr->integer || operand2Ptr->integer;
resultTypePtr = BooleanTypePtr;
}
else if ((resultTypePtr == IntegerTypePtr) && (type2Ptr == IntegerTypePtr))
{
if (op == TKN_PLUS)
operand1Ptr->integer = operand1Ptr->integer + operand2Ptr->integer;
else
operand1Ptr->integer = operand1Ptr->integer - operand2Ptr->integer;
resultTypePtr = IntegerTypePtr;
}
else
{
//-------------------------------------------------------------------
// Both operands are real, or one is real and the other is integer...
promoteOperandsToReal(operand1Ptr, resultTypePtr, operand2Ptr, type2Ptr);
if (op == TKN_PLUS)
operand1Ptr->real = operand1Ptr->real + operand2Ptr->real;
else
operand1Ptr->real = operand1Ptr->real - operand2Ptr->real;
resultTypePtr = RealTypePtr;
}
//--------------------------
// Pop the second operand...
pop();
}
return(resultTypePtr);
}
//***************************************************************************
TypePtr execExpression (void)
{
StackItemPtr operand1Ptr;
StackItemPtr operand2Ptr;
TokenCodeType op;
bool result = false;
//-----------------------------------
// Get the first simple expression...
TypePtr resultTypePtr = execSimpleExpression();
TypePtr type2Ptr = NULL;
//-----------------------------------------------------------
// If there is a relational operator, save it and process the
// second simple expression...
if ((codeToken == TKN_EQUALEQUAL) || (codeToken == TKN_LT) ||
(codeToken == TKN_GT) || (codeToken == TKN_NE) ||
(codeToken == TKN_LE) || (codeToken == TKN_GE))
{
op = codeToken;
resultTypePtr = baseType(resultTypePtr);
getCodeToken();
//---------------------------------
// Get the 2nd simple expression...
type2Ptr = baseType(execSimpleExpression());
operand1Ptr = tos - 1;
operand2Ptr = tos;
//-----------------------------------------------------
// Both operands are integer, boolean or enumeration...
if (((resultTypePtr == IntegerTypePtr) && (type2Ptr == IntegerTypePtr)) ||
(resultTypePtr->form == FRM_ENUM))
{
switch (op)
{
case TKN_EQUALEQUAL:
result = operand1Ptr->integer == operand2Ptr->integer;
break;
case TKN_LT:
result = operand1Ptr->integer < operand2Ptr->integer;
break;
case TKN_GT:
result = operand1Ptr->integer > operand2Ptr->integer;
break;
case TKN_NE:
result = operand1Ptr->integer != operand2Ptr->integer;
break;
case TKN_LE:
result = operand1Ptr->integer <= operand2Ptr->integer;
break;
case TKN_GE:
result = operand1Ptr->integer >= operand2Ptr->integer;
break;
}
}
else if (resultTypePtr == CharTypePtr)
{
switch (op)
{
case TKN_EQUALEQUAL:
result = operand1Ptr->byte == operand2Ptr->byte;
break;
case TKN_LT:
result = operand1Ptr->byte < operand2Ptr->byte;
break;
case TKN_GT:
result = operand1Ptr->byte > operand2Ptr->byte;
break;
case TKN_NE:
result = operand1Ptr->byte != operand2Ptr->byte;
break;
case TKN_LE:
result = operand1Ptr->byte <= operand2Ptr->byte;
break;
case TKN_GE:
result = operand1Ptr->byte >= operand2Ptr->byte;
break;
}
}
else if ((resultTypePtr->form == FRM_ARRAY) && (resultTypePtr->info.array.elementTypePtr == CharTypePtr))
{
//----------------------------------------
// Strings. For now, always return true...
result = true;
}
else if ((resultTypePtr == RealTypePtr) || (type2Ptr == RealTypePtr))
{
promoteOperandsToReal(operand1Ptr, resultTypePtr, operand2Ptr, type2Ptr);
switch (op)
{
case TKN_EQUALEQUAL:
result = operand1Ptr->real == operand2Ptr->real;
break;
case TKN_LT:
result = operand1Ptr->real < operand2Ptr->real;
break;
case TKN_GT:
result = operand1Ptr->real > operand2Ptr->real;
break;
case TKN_NE:
result = operand1Ptr->real != operand2Ptr->real;
break;
case TKN_LE:
result = operand1Ptr->real <= operand2Ptr->real;
break;
case TKN_GE:
result = operand1Ptr->real >= operand2Ptr->real;
break;
}
}
//---------------------------------------------------------
// Replace the two operands with the result on the stack...
if (result)
operand1Ptr->integer = 1;
else
operand1Ptr->integer = 0;
pop();
resultTypePtr = BooleanTypePtr;
}
return(resultTypePtr);
}
//***************************************************************************
}