Files
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

1040 lines
27 KiB
C++

//***************************************************************************
//
// ABLEXEC.CPP
//
//***************************************************************************
#include "MW4Headers.hpp"
#include <stdio.h>
#include <string.h>
#include "windows.h"
#include "abl.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
#include <GameOS\ToolOS.hpp>
#include "ablfuncs.hpp"
DECLARE_TIMER(static, AblTime);
//***************************************************************************
namespace ABL
{
//----------
// EXTERNALS
extern long level;
extern long CallStackLevel;
extern long execLineNumber;
extern long execStatementCount;
extern char* codeSegmentPtr;
extern char* statementStartPtr;
extern TokenCodeType codeToken;
extern long NumExecutions;
extern StackItem* stack;
extern StackItemPtr tos;
extern StackItemPtr stackFrameBasePtr;
extern SymTableNodePtr CurRoutineIdPtr;
extern SymTableNodePtr CurModuleIdPtr;
extern TypePtr IntegerTypePtr;
extern TypePtr CharTypePtr;
extern TypePtr RealTypePtr;
extern TypePtr BooleanTypePtr;
extern UserHeapPtr AblStackHeap;
extern bool ExitWithReturn;
extern bool ExitFromTacOrder;
extern bool InOrdersBlock;
extern long MaxLoopIterations;
extern DebuggerPtr debugger;
extern ABLModulePtr CurModule;
extern long CurModuleHandle;
extern bool CallModuleInit;
extern StackItemPtr StaticDataPtr;
extern ModuleEntryPtr ModuleRegistry;
extern ABLModulePtr* ModuleInstanceRegistry;
extern ABLModulePtr CurModule;
extern ABLModulePtr CurLibrary;
extern double ProfileLogFunctionTimeLimit;
extern bool ProfileLog;
TokenCodeType ExitRoutineCodeSegment[2] = {TKN_END_FUNCTION,TKN_SEMICOLON};
TokenCodeType ExitOrderCodeSegment[2] = {TKN_END_FUNCTION,TKN_SEMICOLON};
long dummyCount = 0;
void execOrderReturn (SymTableNodePtr routineIdPtr, long returnVal);
void ABL_AddToProfileLog (char* profileString);
//***************************************************************************
//
//***************************************************************************
void execStatement (void)
{
if (codeToken == TKN_STATEMENT_MARKER)
{
execLineNumber = getCodeStatementMarker();
execStatementCount++;
statementStartPtr = codeSegmentPtr;
if (debugger)
debugger->traceStatementExecution();
getCodeToken();
}
switch (codeToken)
{
case TKN_IDENTIFIER: {
SymTableNodePtr idPtr = getCodeSymTableNodePtr();
ABL_Assert(idPtr != NULL, 0, " oops ");
if (idPtr->defn.key == DFN_FUNCTION)
{
TypePtr returnType = execRoutineCall(idPtr);
if (returnType)
{
//------------------------------------------
// In case this routine returns a value, pop
// the return value off the stack...
pop();
}
}
else
execAssignmentStatement(idPtr);
}
break;
case TKN_CODE: {
bool wasOrdersBlock = InOrdersBlock;
InOrdersBlock = false;
getCodeToken();
TokenCodeType endToken = ((CurLibrary == NULL) ? TKN_END_MODULE : TKN_END_LIBRARY);
while ((codeToken != TKN_END_FUNCTION) && (codeToken != endToken) && (codeToken != TKN_END_STATE))
execStatement();
getCodeToken();
InOrdersBlock = wasOrdersBlock;
}
break;
case TKN_FOR:
execForStatement();
break;
case TKN_IF:
execIfStatement();
break;
case TKN_REPEAT:
execRepeatStatement();
break;
case TKN_WHILE:
execWhileStatement();
break;
case TKN_SWITCH:
execSwitchStatement();
break;
case TKN_TRANS:
Verify (ModuleRegistry[CurModule->getHandle()].moduleIdPtr->defn.key == DFN_FSM);
ModuleRegistry[CurModule->getHandle()].moduleIdPtr->defn.info.routine.state = getCodeSymTableNodePtr();
ABL_Assert(ModuleRegistry[CurModule->getHandle()].moduleIdPtr->defn.info.routine.state != NULL, 0, " oops ");
ExitWithReturn = true;
codeSegmentPtr = (char*)ExitRoutineCodeSegment;
getCodeToken();
break;
case TKN_SEMICOLON:
case TKN_ELSE:
case TKN_UNTIL:
break;
default:
runtimeError(ABL_ERR_RUNTIME_UNIMPLEMENTED_FEATURE);
}
while (codeToken == TKN_SEMICOLON)
getCodeToken();
}
//***************************************************************************
void execAssignmentStatement (SymTableNodePtr idPtr)
{
StackItemPtr targetPtr;
TypePtr targetTypePtr;
TypePtr baseTargetTypePtr;
TypePtr expressionTypePtr;
//--------------------------
// Assignment to variable...
targetTypePtr = execVariable(idPtr, USE_TARGET);
targetPtr = (StackItemPtr)tos->address;
//------------------------------
// Pop off the target address...
pop();
//------------------------
// Pop the size, if nec...
//if (targetTypePtr->form == FRM_ARRAY)
// pop();
baseTargetTypePtr = baseType(targetTypePtr);
//---------------------------------------------------------------
// Routine execExpression() leaves the expression value on top of
// stack...
getCodeToken();
expressionTypePtr = execExpression();
//--------------------------
// Now, do the assignment...
if ((targetTypePtr == RealTypePtr) && (baseType(expressionTypePtr) == IntegerTypePtr))
{
//-------------------------
// integer assigned to real
targetPtr->real = (float)(tos->integer);
}
else if ((targetTypePtr->form == FRM_ARRAY) /*|| (targetTypePtr->form == FRM_RECORD)*/)
{
//-------------------------
// Copy the array/record...
char* dest = (char*)targetPtr;
char* src = tos->address;
long size = targetTypePtr->size;
memcpy(dest, src, size);
}
else if ((baseTargetTypePtr == IntegerTypePtr) || (targetTypePtr->form == FRM_ENUM))
{
//------------------------------------------------------
// Range check assignment to integer or enum subrange...
// NOTE: Need to put this line back in once we implement SUBRANGES...
// if ((targetTypePtr->form == FRM_SUBRANGE) &&
// ((tos->integer < targetTypePtr->info.subrange.min) || (tos->integer > targetTypePtr->info.subrange.max)))
// runtimeError(ABL_ERR_RUNTIME_VALUE_OUT_OF_RANGE);
targetPtr->integer = tos->integer;
}
else if (baseTargetTypePtr == CharTypePtr)
targetPtr->byte = tos->byte;
else
{
//-----------------------
// Assign real to real...
targetPtr->real = tos->real;
}
//-----------------------------
// Grab the expression value...
pop();
if (debugger)
debugger->traceDataStore(idPtr, idPtr->typePtr, targetPtr, targetTypePtr);
}
//***************************************************************************
TypePtr execRoutineCall (SymTableNodePtr routineIdPtr)
{
if (routineIdPtr->defn.info.routine.key == RTN_DECLARED)
return(execDeclaredRoutineCall(routineIdPtr));
else if (routineIdPtr->defn.info.routine.key == RTN_EXTERN)
return (execExternRoutineCall (routineIdPtr));
else
{
Check_Pointer (routineExecFunction);
if (routineExecFunction)
return(routineExecFunction(routineIdPtr));
else
return NULL;
}
}
void _cdecl func1 (int p1,bool p2,char p3,float p4)
{
p1 = 5;
}
void _stdcall func2 (int p1,bool p2,char p3,float p4)
{
p1 = 5;
}
int _cdecl func3 (int p1,bool p2,char p3,float p4)
{
p1 = 5;
return 5;
}
int _stdcall func4 (int p1,bool p2,char p3,float p4)
{
p1 = 5;
return 5;
}
TypePtr execExternRoutineCall (SymTableNodePtr routineIdPtr)
{
long oldLevel = level; // level of caller
long newLevel = routineIdPtr->level + 1; // level of callee
CallStackLevel++;
//-------------------------------------------
// First, set up the stack frame of callee...
StackItemPtr newStackFrameBasePtr = tos + 1;
pushStackFrameHeader(oldLevel, newLevel);
//----------------------------------------
// Push parameter values onto the stack...
getCodeToken();
if (codeToken == TKN_LPAREN)
{
execExternParams(routineIdPtr);
getCodeToken();
}
//-------------------------------------------------
// Set the return address in the new stack frame...
level = newLevel;
stackFrameBasePtr = newStackFrameBasePtr;
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
headerPtr->returnAddress.address = codeSegmentPtr - 1;
{
ABL_LOGIC("Extern");
Start_Timer(AblTime);
if (!routineIdPtr->defn.info.routine.codeSegment)
{
routineIdPtr->defn.info.routine.codeSegment = (char *) GetProcAddress (NULL,routineIdPtr->name);
}
void *funcptr = routineIdPtr->defn.info.routine.codeSegment;
SymTableNodePtr curend = NULL;
SymTableNodePtr formalIdPtr;
TypePtr formalTypePtr;
float pfloat;
int pint;
int pbool;
int pchar;
int space=0;
if (routineIdPtr->defn.info.routine.params)
{
while (curend != (SymTableNodePtr)(routineIdPtr->defn.info.routine.params)) // need to go in reverse order
{
for (formalIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);formalIdPtr->next != curend;formalIdPtr = formalIdPtr->next);
formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
ablREPORT (formalIdPtr->defn.key == DFN_VALPARAM,"can only pass by value for abl extern functions");
ablREPORT (formalTypePtr->form != FRM_ARRAY,"no support of array parameters for abl extern functions");
space += 4;
if (formalTypePtr == RealTypePtr)
{
pfloat = tos->real;
pop ();
_asm
{
push pfloat
}
}
else if (formalTypePtr == IntegerTypePtr)
{
pint = tos->integer;
pop ();
_asm
{
push pint
}
}
else if (formalTypePtr == BooleanTypePtr)
{
pbool = tos->integer;
pop ();
_asm
{
push pbool
}
}
else if (formalTypePtr == CharTypePtr)
{
pchar = tos->byte;
pop ();
_asm
{
push pchar
}
}
else
{
ablREPORT (false,"unknown parameter type for abl extern function");
}
curend = formalIdPtr;
}
}
_asm
{
mov eax,funcptr
call eax
add esp,space
}
// execute(routineIdPtr);
Stop_Timer(AblTime);
}
//-------------------------------------------------------
// Return from the callee, and grab the first token after
// the return...
level = oldLevel;
getCodeToken();
CallStackLevel--;
return((TypePtr)(routineIdPtr->typePtr));
}
//***************************************************************************
TypePtr execDeclaredRoutineCall (SymTableNodePtr routineIdPtr)
{
long oldLevel = level; // level of caller
long newLevel = routineIdPtr->level + 1; // level of callee
CallStackLevel++;
//-------------------------------------------
// First, set up the stack frame of callee...
StackItemPtr newStackFrameBasePtr = tos + 1;
bool isLibraryCall = (routineIdPtr->library && (routineIdPtr->library != CurRoutineIdPtr->library));
if (isLibraryCall)
pushStackFrameHeader(-1, -1);
else
pushStackFrameHeader(oldLevel, newLevel);
//----------------------------------------
// Push parameter values onto the stack...
getCodeToken();
if (codeToken == TKN_LPAREN)
{
execActualParams(routineIdPtr);
getCodeToken();
}
//-------------------------------------------------
// Set the return address in the new stack frame...
level = newLevel;
stackFrameBasePtr = newStackFrameBasePtr;
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
headerPtr->returnAddress.address = codeSegmentPtr - 1;
//---------------------------------------------------------
// If we're calling a library function, we need to set some
// module-specific info...
ABLModulePtr PrevModule = NULL;
if (isLibraryCall)
{
PrevModule = CurModule;
CurModule = routineIdPtr->library;
CurModuleHandle = CurModule->getHandle();
if (debugger)
debugger->setModule(CurModule);
StaticDataPtr = CurModule->getStaticData();
CallModuleInit = !CurModule->getInitCalled();
CurModule->setInitCalled(true);
// routineEntry(ModuleRegistry[CurModule->getHandle()].moduleIdPtr);
}
double functionStartTime = 0.0;
if (ProfileLog)
{
functionStartTime = gos_GetHiResTime();
}
{
ABL_LOGIC("Declared");
Start_Timer(AblTime);
execute(routineIdPtr);
Stop_Timer(AblTime);
}
if (ProfileLog)
{
double functionExecTime = gos_GetHiResTime() - functionStartTime;
if (functionExecTime > ProfileLogFunctionTimeLimit)
{
char s[512];
sprintf(s, "[%08d] ", NumExecutions);
for (long i = 0; i < CallStackLevel; i++)
strcat(s, " ");
char s1[512];
sprintf(s1, "ABL::%s function %s took (%lf)ms\n", CurModule->getName(), routineIdPtr->name, functionExecTime*1000.0f);
strcat(s, s1);
ABL_AddToProfileLog(s);
}
}
//----------------------------------------------------------------
// If we're calling a library function, reset some module-specific
// info...
if (isLibraryCall)
{
// routineExit(ModuleRegistry[CurModule->getHandle()].moduleIdPtr);
CurModule = PrevModule;
CurModuleHandle = CurModule->getHandle();
if (debugger)
debugger->setModule(CurModule);
StaticDataPtr = CurModule->getStaticData();
}
//-------------------------------------------------------
// Return from the callee, and grab the first token after
// the return...
level = oldLevel;
getCodeToken();
CallStackLevel--;
return((TypePtr)(routineIdPtr->typePtr));
}
//***************************************************************************
void setOpenArray (TypePtr arrayTypePtr, long size)
{
long numElements = size / arrayTypePtr->size;
arrayTypePtr->size = size;
while (arrayTypePtr->info.array.elementTypePtr->form == FRM_ARRAY)
arrayTypePtr = arrayTypePtr->info.array.elementTypePtr;
arrayTypePtr->info.array.elementCount = numElements;
}
//***************************************************************************
void execActualParams (SymTableNodePtr routineIdPtr)
{
//--------------------------
// Execute the parameters...
if (debugger)
{
debugger->StartActualParams (routineIdPtr);
}
for (SymTableNodePtr formalIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);
formalIdPtr != NULL;
formalIdPtr = formalIdPtr->next)
{
TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
getCodeToken();
if (formalIdPtr->defn.key == DFN_VALPARAM)
{
//-------------------
// pass by value parameter...
TypePtr actualTypePtr = execExpression();
if ((formalTypePtr == RealTypePtr) && (baseType(actualTypePtr) == IntegerTypePtr))
{
//---------------------------------------------
// Real formal parameter, but integer actual...
tos->real = (float)(tos->integer);
}
//----------------------------------------------------------
// Formal parameter is an array or record, so make a copy...
if ((formalTypePtr->form == FRM_ARRAY)/* || (formalTypePtr->form == FRM_RECORD)*/)
{
//------------------------------------------------------------------------------
// The following is a little inefficient, but is kept this way to keep it clear.
// Once it's verified to work, optimize...
long size = formalTypePtr->size;
char* src = tos->address;
char* dest = (char*)AblStackHeap->Malloc((size_t)size);
if (!dest)
{
char err[255];
sprintf(err, " ABL: Unable to AblStackHeap->malloc actual array param in module %s)", CurModule->getName());
ABL_Fatal(0, err);
}
char* savePtr = dest;
memcpy(dest, src, size);
tos->address = savePtr;
}
}
else
{
//-------------------------------
// pass by reference parameter...
SymTableNodePtr idPtr = getCodeSymTableNodePtr();
execVariable(idPtr, USE_REFPARAM);
}
if (debugger)
debugger->AddActualParam (formalIdPtr,tos);
}
if (debugger)
{
debugger->EndActualParams ();
}
}
void execExternParams (SymTableNodePtr routineIdPtr)
{
//--------------------------
// Execute the parameters...
if (debugger)
{
debugger->StartActualParams (routineIdPtr);
}
for (SymTableNodePtr formalIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);
formalIdPtr != NULL;
formalIdPtr = formalIdPtr->next)
{
TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
getCodeToken();
if (formalIdPtr->defn.key == DFN_VALPARAM)
{
//-------------------
// pass by value parameter...
TypePtr actualTypePtr = execExpression();
if ((formalTypePtr == RealTypePtr) && (baseType(actualTypePtr) == IntegerTypePtr))
{
//---------------------------------------------
// Real formal parameter, but integer actual...
tos->real = (float)(tos->integer);
}
//----------------------------------------------------------
// Formal parameter is an array or record, so make a copy...
if ((formalTypePtr->form == FRM_ARRAY)/* || (formalTypePtr->form == FRM_RECORD)*/)
{
//------------------------------------------------------------------------------
// The following is a little inefficient, but is kept this way to keep it clear.
// Once it's verified to work, optimize...
long size = formalTypePtr->size;
char* src = tos->address;
char* dest = (char*)AblStackHeap->Malloc((size_t)size);
if (!dest)
{
char err[255];
sprintf(err, " ABL: Unable to AblStackHeap->malloc actual array param in module %s)", CurModule->getName());
ABL_Fatal(0, err);
}
char* savePtr = dest;
memcpy(dest, src, size);
tos->address = savePtr;
}
}
else
{
//-------------------------------
// pass by reference parameter...
SymTableNodePtr idPtr = getCodeSymTableNodePtr();
execVariable(idPtr, USE_REFPARAM);
}
if (debugger)
debugger->AddActualParam (formalIdPtr,tos);
}
if (debugger)
{
debugger->EndActualParams ();
}
}
//***************************************************************************
void execSwitchStatement (void)
{
getCodeToken();
char* branchTableLocation = getCodeAddressMarker();
getCodeToken();
TypePtr switchExpressionTypePtr = execExpression();
long switchExpressionValue;
if ((switchExpressionTypePtr == IntegerTypePtr) || (switchExpressionTypePtr->form == FRM_ENUM))
switchExpressionValue = tos->integer;
else
switchExpressionValue = tos->byte;
pop();
//---------------------------------------------------------
// Now, search the branch table for the expression value...
codeSegmentPtr = branchTableLocation;
getCodeToken();
long caseLabelCount = getCodeInteger();
bool done = false;
char* caseBranchLocation = NULL;
while (!done && caseLabelCount--)
{
long caseLabelValue = getCodeInteger();
caseBranchLocation = getCodeAddress();
done = (caseLabelValue == switchExpressionValue);
}
//-----------------------------------------------
// If found, go to the aprropriate branch code...
if (caseLabelCount >= 0)
{
codeSegmentPtr = caseBranchLocation;
getCodeToken();
if (codeToken != TKN_END_CASE)
do
{
execStatement();
if (ExitWithReturn)
return;
} while (codeToken != TKN_END_CASE);
//----------------------------------
// Grab the end case and semi-colon...
getCodeToken();
getCodeToken();
codeSegmentPtr = getCodeAddressMarker();
getCodeToken();
}
else
{
//-----------------------------------------------------------------
// Since the branch table is located at the end of the case blocks,
// the code directly after the switch statement follows our
// current code location, already. Just grab the endswitch
// and semi-colon...
getCodeToken();
getCodeToken();
}
}
//***************************************************************************
void execForStatement (void)
{
getCodeToken();
//---------------------------------------
// Grab address of the end of the loop...
char* loopEndLocation = getCodeAddressMarker();
//--------------------------------------------------------
// Get the address of the control variable's stack item...
getCodeToken();
SymTableNodePtr controlIdPtr = getCodeSymTableNodePtr();
TypePtr controlTypePtr = execVariable(controlIdPtr, USE_TARGET);
StackItemPtr targetPtr = (StackItemPtr)tos->address;
//------------------------------------
// Control variable address...
pop();
//-------------------------------
// Eval the initial expression...
getCodeToken();
execExpression();
long initialValue;
if (controlTypePtr == IntegerTypePtr)
initialValue = tos->integer;
else
initialValue = tos->byte;
//---------------------
// The initial value...
pop();
long deltaValue;
if (codeToken == TKN_TO)
deltaValue = 1;
else
deltaValue = -1;
//----------------------------------
// Now, eval the final expression...
getCodeToken();
execExpression();
long finalValue;
if (controlTypePtr == IntegerTypePtr)
finalValue = tos->integer;
else
finalValue = tos->byte;
//-------------------
// The final value...
pop();
//----------------------------
// Address of start of loop...
char* loopStartLocation = codeSegmentPtr;
long controlValue = initialValue;
//-----------------------------
// Now, execute the FOR loop...
long iterations = 0;
if (deltaValue == 1)
while (controlValue <= finalValue)
{
if (controlTypePtr == IntegerTypePtr)
targetPtr->integer = controlValue;
else
targetPtr->byte = (unsigned char)controlValue;
getCodeToken();
if (codeToken != TKN_END_FOR)
do
{
execStatement();
if (ExitWithReturn)
return;
} while (codeToken != TKN_END_FOR);
//---------------------------
// Check for infinite loop...
if (++iterations == MaxLoopIterations)
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
controlValue++;
codeSegmentPtr = loopStartLocation;
}
else
while (controlValue >= finalValue)
{
if (controlTypePtr == IntegerTypePtr)
targetPtr->integer = controlValue;
else
targetPtr->byte = (unsigned char)controlValue;
getCodeToken();
if (codeToken != TKN_END_FOR)
do
{
execStatement();
if (ExitWithReturn)
return;
} while (codeToken != TKN_END_FOR);
//---------------------------
// Check for infinite loop...
if (++iterations == MaxLoopIterations)
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
controlValue--;
codeSegmentPtr = loopStartLocation;
}
codeSegmentPtr = loopEndLocation;
getCodeToken();
}
//***************************************************************************
void execIfStatement (void)
{
getCodeToken();
char* falseLocation = getCodeAddressMarker();
//-------------------------------
// Eval the boolean expression. Note that, unlike C/C++, the expression
// must be true(1) or false(0). In C/C++, an expression is true if it's
// non-zero. Not the case in ABL using this current implementation. Do we
// want to change this?
getCodeToken();
execExpression();
bool test = (tos->integer == 1);
pop();
if (test)
{
//---------------------------
// execute the TRUE branch...
getCodeToken();
if ((codeToken != TKN_END_IF) && (codeToken != TKN_ELSE))
do
{
execStatement();
if (ExitWithReturn)
return;
} while ((codeToken != TKN_END_IF) && (codeToken != TKN_ELSE));
if (codeToken == TKN_ELSE)
{
getCodeToken();
codeSegmentPtr = getCodeAddressMarker();
getCodeToken();
}
}
else
{
//----------------------------
// Execute the FALSE branch...
codeSegmentPtr = falseLocation;
getCodeToken();
if (codeToken == TKN_ELSE)
{
getCodeToken();
getCodeAddressMarker();
getCodeToken();
if (codeToken != TKN_END_IF)
do
{
execStatement();
if (ExitWithReturn)
return;
} while (codeToken != TKN_END_IF);
}
}
getCodeToken();
}
//***************************************************************************
void execRepeatStatement (void)
{
char* loopStartLocation = codeSegmentPtr;
long iterations = 0;
do
{
getCodeToken();
if (codeToken != TKN_UNTIL)
do
{
execStatement();
if (ExitWithReturn)
return;
} while (codeToken != TKN_UNTIL);
//---------------------------
// Check for infinite loop...
iterations++;
if (iterations == MaxLoopIterations)
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
//-------------------------------
// Eval the boolean expression...
getCodeToken();
execExpression();
if (tos->integer == 0)
codeSegmentPtr = loopStartLocation;
//--------------------------
// Grab the boolean value...
pop();
} while (codeSegmentPtr == loopStartLocation);
}
//***************************************************************************
void execWhileStatement (void)
{
getCodeToken();
char* loopEndLocation = getCodeAddressMarker();
char* testLocation = codeSegmentPtr;
bool loopDone = false;
long iterations = 0;
do
{
//-------------------------------
// Eval the boolean expression...
getCodeToken();
execExpression();
if (tos->integer == 0)
{
codeSegmentPtr = loopEndLocation;
loopDone = true;
}
//-------------------------
// Get the boolean value...
pop();
//----------------------------------
// If TRUE, execute the statement...
if (!loopDone)
{
getCodeToken();
if (codeToken != TKN_END_WHILE)
do
{
execStatement();
if (ExitWithReturn)
return;
} while (codeToken != TKN_END_WHILE);
codeSegmentPtr = testLocation;
//---------------------------
// Check for infinite loop...
iterations++;
if (iterations == MaxLoopIterations)
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
}
} while (!loopDone);
getCodeToken();
}
//***************************************************************************
}