//***************************************************************************
//
//								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();
	}

//***************************************************************************
}