//***************************************************************************
//
//								ABLRTN.CPP
//
//***************************************************************************

#include "MW4Headers.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>

#pragma warning (disable:4284)
#pragma warning (push)
#include <stlport\string>
#pragma warning (pop)

#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 ABLENV_H
	#include "ablenv.h"
#endif

#ifndef ABL_H
	#include "abl.h"
#endif

#ifndef ABLDBUG_H
	#include "abldbug.h"
#endif

#ifndef ABLHEAP_H
	#include "ablheap.h"
#endif

#include "AblProfiler.hpp"

#include "ablfuncs.hpp"

#include "ABLFile.h"

// MC-specific...
#ifdef USE_OBJECTS
	#ifndef GROUP_H
		#include "group.h"
	#endif
#endif

//#ifndef IFACE_H
//#include "iface.h"
//#endif

//***************************************************************************
namespace ABL
{
	extern ABLError *ablError;
	extern  jmp_buf abl_jmp_buf;

	extern long       MaxBreaks;
	extern long       MaxWatches;
	extern DebuggerPtr    debugger;
	extern long       NumModules;
	extern ModuleEntryPtr ModuleRegistry;
	extern ABLModulePtr*  ModuleInstanceRegistry;
	extern long       MaxModules;
	extern ABLModulePtr*  LibraryInstanceRegistry;
	extern long       MaxLibraries;
	extern long       NumModulesRegistered;
	extern long       NumModuleInstances;
	extern long       MaxWatchesPerModule;
	extern long       MaxBreakPointsPerModule;
	extern long       MaxCodeBufferSize;
	extern ABLModulePtr   CurModule;
	extern ABLModulePtr   CurLibrary;
	extern char*      codeBuffer;
	extern char*      codeBufferPtr;
	extern char*      codeSegmentPtr;
	extern char*      codeSegmentLimit;
	extern char*      statementStartPtr;
	extern StackItem*   stack;
	extern StackItemPtr   tos;
	extern StackItemPtr   stackFrameBasePtr;
	extern StackItemPtr   StaticDataPtr;
	extern long*      StaticVariablesSizes;
extern long*      EternalVariablesSizes;
extern long       MaxEternalVariables;
	extern long       eternalOffset;
	extern long       MaxStaticVariables;
	extern long       NumStaticVariables;
	extern long       CurModuleHandle;
	extern bool       CallModuleInit;
	extern bool       InOrdersBlock;
	extern long       MaxLoopIterations;
	extern bool       AssertEnabled;
	extern bool       IncludeDebugInfo;
	extern bool       ProfileABL;
	extern bool       Crunch;
	extern long       level;
	extern long       FSMLevel;
	extern long       lineNumber;
	extern long       FileNumber;
	extern FilePtr      sourceFile;
	extern bool       printFlag;
	extern bool       blockFlag;
	extern BlockType    blockType;
	extern SymTableNodePtr  CurModuleIdPtr;
	extern SymTableNodePtr  CurRoutineIdPtr;
	extern bool       DumbGetCharOn;
	extern long       NumOpenFiles;
	extern long       NumSourceFiles;
	extern long       NumLibrariesUsed;
	extern ABLModulePtr   LibrariesUsed[MAX_LIBRARIES_USED];
	extern long       bufferOffset;
	extern char*      bufferp;
	extern char*      tokenp;
	extern long       digitCount;
	extern bool       countError;
	extern long       pageNumber;
	extern long       lineCount;
//	std::string		  currentParamList;		// TODO: use this implementation or remove it
//	std::string		  currentScriptSuffix;

#ifdef USE_OBJECTS
	extern bool       IsUnitOrder;
#else
	bool IsUnitOrder = false;
#endif

#ifdef USE_OBJECTS
	extern bool       TacOrderOrigin;
	extern MoverGroupPtr  CurGroup;
	extern GameObjectPtr  CurObject;
	extern long       CurObjectClass;
	extern MechWarriorPtr CurWarrior;
	extern GameObjectPtr  CurContact;
#endif

	extern long       CurAlarm;

	extern bool       eofFlag;
	extern bool       ExitWithReturn;
	extern bool       ExitFromTacOrder;

	extern long       dummyCount;

	extern long       lineNumber;
	extern long       FileNumber;
	extern long       errorCount;
	extern long       execStatementCount;
	extern long       NumSourceFiles;
	extern char       SourceFiles[MAX_SOURCE_FILES][MAXLEN_FILENAME];

	extern TokenCodeType  curToken;
	extern char       wordString[];
	extern SymTableNodePtr  symTableDisplay[];
	extern long       level;
	extern bool       blockFlag;
	extern BlockType    blockType;
	extern bool       printFlag;
	extern SymTableNodePtr  CurRoutineIdPtr;

	extern Type       DummyType;
	extern StackItem*   stack;
//extern StackItem*		eternalStack;
	extern StackItemPtr   tos;
	extern StackItemPtr   stackFrameBasePtr;
	extern long       eternalOffset;

	extern TokenCodeType  statementStartList[];
	extern TokenCodeType  statementEndList[];
	extern TokenCodeType  declarationStartList[];

	extern char       tokenString[MAXLEN_TOKENSTRING];

	extern CharCodeType   charTable[256];

	extern char       sourceBuffer[MAXLEN_SOURCELINE];
	extern long       bufferOffset;
	extern char*      bufferp;
	extern char*      tokenp;

	extern long       digitCount;
	extern bool       countError;

	extern bool       eofFlag;
	extern long       pageNumber;

	extern UserHeapPtr    systemHeap;
	extern UserHeapPtr    AblSymTableHeap;
	extern UserHeapPtr    AblStackHeap;
	extern UserHeapPtr    AblCodeHeap;

	extern SymTableNodePtr  SymTableDisplay[MAX_NESTING_LEVEL];
	extern TypePtr      IntegerTypePtr;
	extern TypePtr      RealTypePtr;
	extern TypePtr      BooleanTypePtr;

	extern StackItemPtr   StaticDataPtr;
	extern StackItem    returnValue;

	extern ModuleEntryPtr ModuleRegistry;
	extern long       MaxModules;
	extern long       NumModulesRegistered;
	extern long*      StaticVariablesSizes;
	extern long       MaxStaticVariables;
	extern long       NumStaticVariables;
	extern long       NumSourceFiles;

	extern bool       IncludeDebugInfo;
	extern bool       AssertEnabled;
	extern bool       PrintEnabled;
	extern bool       StringFunctionsEnabled;
	extern bool       DebugCodeEnabled;
	extern DebuggerPtr    debugger;

	extern bool parsingFSM;


	bool          ABLenabled = false;
	bool          g_ABLCreated = false;
	char          buffer[MAXLEN_PRINTLINE];

	extern long       CallStackLevel;

	long          numLibrariesLoaded = 0;
	long          NumExecutions = 0;

	Profiler*		g_Profiler = 0;

//***************************************************************************

	TokenCodeType followHeaderList[] = {
		TKN_SEMICOLON,
		TKN_EOF,
		TKN_NONE
	};

	TokenCodeType followModuleIdList[] = {
		TKN_LPAREN,
		TKN_COLON,
		TKN_SEMICOLON,
		TKN_EOF,
		TKN_NONE
	};

	TokenCodeType followFunctionIdList[] = {
		TKN_LPAREN,
		TKN_COLON,
		TKN_SEMICOLON,
		TKN_EOF,
		TKN_NONE
	};

	TokenCodeType followParamsList[] = {
		TKN_RPAREN,
		TKN_COMMA, //TKN_SEMICOLON,
		TKN_EOF,
		TKN_NONE
	};

	TokenCodeType followParamList[] = {
		TKN_COMMA,
		TKN_RPAREN,
		TKN_NONE
	};

	TokenCodeType followModuleDeclsList[] = {
		TKN_SEMICOLON,
		TKN_CODE,
		TKN_EOF,
		TKN_NONE
	};

	TokenCodeType followRoutineDeclsList[] = {
		TKN_SEMICOLON,
		TKN_CODE,
		TKN_EOF,
		TKN_NONE
	};

	TokenCodeType followDeclsList[] = {
		TKN_SEMICOLON,
		//TKN_BEGIN,
		TKN_EOF,
		TKN_NONE
	};

	void ABL_CloseProfileLog (void);
	void ABL_OpenProfileLog (void);
	void ABL_AddToProfileLog (char* profileString);

//***************************************************************************
// ABL library interface routines
//***************************************************************************

	void ABLi_init (unsigned long symTableHeapSize,
									unsigned long stackHeapSize,
									unsigned long codeHeapSize,
									unsigned long runtimeStackSize,
									unsigned long maxCodeBufferSize,
									unsigned long maxRegisteredModules,
									unsigned long maxStaticVariables,
									void (*callback)(char* s),
									bool debugInfo,
									bool debug,
									bool profile)
	{

// HACK, for testing...
#ifdef _DEBUG
		profile = true;
#endif
//
		//-------------------------------------------------------------------
		// Let's make sure we have not created too many built-in ABL routines
		// for the lovely user...

		g_ABLFunctions = new stlport::vector<ABLRoutineTableEntry *>;
		g_ABLFunctions->assign (NUM_ABL_ROUTINES,NULL);

//	if (NUM_ABL_ROUTINES > 254)
//		ABL_Fatal(0, " MAJOR ABL ERROR: Too Many ABL Routines ");

		ablError = NULL;

		ABLenabled = true;
		MaxBreaks = 50;
		MaxWatches = 50;
		debugger = NULL;
		NumModules = 0;
		ModuleRegistry = NULL;
		ModuleInstanceRegistry = NULL;
		LibraryInstanceRegistry = NULL;
		MaxModules = 0;
		MaxLibraries = 0;
		NumModulesRegistered = 0;
		NumModuleInstances = 0;
		MaxWatchesPerModule = 20;
		MaxBreakPointsPerModule = 20;
		CurModule = NULL;
		errorCount = 0;
		codeBuffer = NULL;
		codeBufferPtr = NULL;
		codeSegmentPtr = NULL;
		codeSegmentLimit = NULL;
		statementStartPtr = NULL;
		execStatementCount = 0;
		stack = NULL;
		tos = NULL;
		stackFrameBasePtr = NULL;
		StaticDataPtr = NULL;
		StaticVariablesSizes = NULL;
	EternalVariablesSizes = NULL;
	MaxEternalVariables = 5000;
		eternalOffset = 0;
		MaxStaticVariables = 0;
		NumStaticVariables = 0;
		CurModuleHandle = 0;
		CallModuleInit = false;
		InOrdersBlock = false;
		MaxLoopIterations = 100001;
		AssertEnabled = false;
		PrintEnabled = false;
		StringFunctionsEnabled = true;
		IncludeDebugInfo = true;
		ProfileABL = profile;
		Crunch = true;
		AblSymTableHeap = NULL;
		AblStackHeap = NULL;
		AblCodeHeap = NULL;
		level = 0;
		lineNumber = 0;
		FileNumber = 0;
		sourceFile = NULL;
		printFlag = true;
		blockFlag = false;
		blockType = BLOCK_MODULE;
		CurModuleIdPtr = NULL;
		CurRoutineIdPtr = NULL;
		DumbGetCharOn = false;
		NumOpenFiles = 0;
		NumSourceFiles = 0;
		bufferOffset = 0;
		bufferp = sourceBuffer;
		tokenp = tokenString;
		digitCount = 0;
		countError = false;
		pageNumber = 0;
		lineCount = MAX_LINES_PER_PAGE;
		IsUnitOrder = false;

#ifdef USE_OBJECTS
		TacOrderOrigin = ORDER_ORIGIN_COMMANDER;
#endif

		eofFlag = false;
		ExitWithReturn = false;
		ExitFromTacOrder = false;
		dummyCount = 0;
		numLibrariesLoaded = 0;

		AblSymTableHeap = new UserHeap;
		long heapErr = AblSymTableHeap->init(symTableHeapSize,"ABL symbol table");
		if (heapErr != NO_ERR)
		{
			//systemHeap->MemoryDump();
			STOP(("unable to create ABL symbol table heap"));
		}

		AblStackHeap = new UserHeap;
		heapErr = AblStackHeap->init(stackHeapSize,"ABL stack");
		if (heapErr != NO_ERR)
		{
			//systemHeap->MemoryDump();
			STOP(("unable to create ABL stack heap"));
		}

		AblCodeHeap = new UserHeap;
		heapErr = AblCodeHeap->init(codeHeapSize,"ABL code");
		if (heapErr != NO_ERR)
		{
			//systemHeap->MemoryDump();
			STOP(("unable to create ABL code heap"));
		}

		//----------------------------------
		// Initialize the character table...
		for (long curCh = 0; curCh < 256; curCh++)
			charTable[curCh] = CHR_SPECIAL;
		for (curCh = '0'; curCh <= '9'; curCh++)
			charTable[curCh] = CHR_DIGIT;
		for (curCh = 'A'; curCh <= 'Z'; curCh++)
			charTable[curCh] = CHR_LETTER;
		for (curCh = 'a'; curCh <= 'z'; curCh++)
			charTable[curCh] = CHR_LETTER;
		charTable['\"'] = CHR_DQUOTE;
		charTable[CHAR_EOF] = CHR_EOF;

		//--------------------------------------------------------------------
		// Allocate the code buffer used during pre-processing/interpreting...
		MaxCodeBufferSize = maxCodeBufferSize;
		codeBuffer = (char*)AblCodeHeap->Malloc(maxCodeBufferSize);
		if (!codeBuffer)
			ABL_Fatal(0, " ABL: Unable to AblCodeHeap->malloc preprocess code buffer ");

		//----------------------------------------------------------------------
		// Alloc the static variable data block. Ultimately, we may want to just
		// implement the static data construction with a linked list, rather
		// than a set max size...
		MaxStaticVariables = maxStaticVariables;
		NumStaticVariables = 0;
		StaticDataPtr = NULL;
		StaticVariablesSizes = NULL;
		if (MaxStaticVariables > 0)
		{
			StaticVariablesSizes = (long*)AblStackHeap->Malloc(sizeof(long) * MaxStaticVariables);
			if (!StaticVariablesSizes)
				ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc StaticVariablesSizes ");
		}

		//------------------------------
		// Allocate the runtime stack...
		stack = (StackItemPtr)AblStackHeap->Malloc(sizeof(StackItem) * (runtimeStackSize / sizeof(StackItem)));
		if (!stack)
			ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc stack ");

	//-----------------------------------
	// Allocate Eternal Vars Size List...
	if (MaxEternalVariables > 0)
	{
		EternalVariablesSizes = (long*)AblStackHeap->Malloc(sizeof(long) * MaxEternalVariables);
		if (!EternalVariablesSizes)
			ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc EternalVariablesSizes ");
	}

		//-------------------------
		// Prep the symbol table...
		initSymTable();

		initModuleRegistry(maxRegisteredModules);

		initLibraryRegistry(10);

		//--------------------------------
		// Init the debugger, if needed...
		IncludeDebugInfo = debugInfo;
		if (debug)
		{
			IncludeDebugInfo = true;
			debugger = new Debugger;
			if (!debugger)
				ABL_Fatal(0, " Unable to initialize ABL Debugger. ");
			debugger->init(callback, NULL);
		}

//blurt	UserFile::setup();

		if (ProfileABL)
			ABL_OpenProfileLog();

		if (g_Profiler == 0)
		{
			g_Profiler = new Profiler;
		}

		g_ABLCreated = true;
		//---------------------
		// MC-specific stuff...
#ifdef USE_OBJECTS
		CurGroup = NULL;
		CurObject = NULL;
		CurObjectClass = 0;
		CurWarrior = NULL;
		CurContact = NULL;
#endif
	}

//***************************************************************************
	void ABLi_VerifyStates (SymTableNodePtr node)
	{
		if (!node)
			return;
		ABLi_VerifyStates (node->left);

		if ((node->defn.key == DFN_STATE) && (node->defn.info.routine.key==RTN_FORWARD))
			syntaxError (ABL_ERR_SYNTAX_FORWARDED_STATE_NOT_DECLARED);

		ABLi_VerifyStates (node->right);
	}

//***************************************************************************

	long ABLi_preProcess (char* sourceFileName, ABLError *err,long* numErrors, long* numLinesProcessed, long* numFilesProcessed, bool printLines)
	{

		//--------------------------------------------------------------------------------
		// First, check if this module has already been registered into the environment...
		for (long i = 0; i < NumModulesRegistered; i++)
			if (strcmp(strlwr(sourceFileName), ModuleRegistry[i].fileName) == 0)
				return(i);

		ablError = err;

		//---------------------------------
		// Init some important variables...
		parsingFSM = false;
		level = 0;
		lineNumber = 0;
		FileNumber = 0;
		AssertEnabled = (debugger != NULL);
		PrintEnabled = (debugger != NULL);
		StringFunctionsEnabled = true;
		DebugCodeEnabled = (debugger != NULL);
		NumSourceFiles = 0;
		NumLibrariesUsed = 0;
		sourceFile = NULL;
		printFlag = printLines;
		blockFlag = false;
		blockType = BLOCK_MODULE;
		bufferOffset = 0;
		bufferp = sourceBuffer;
		tokenp = tokenString;
		digitCount = 0;
		countError = false;
		pageNumber = 0;
		errorCount = 0;
		execStatementCount = 0;
		eofFlag = false;
		NumStaticVariables = 0;

		if (numErrors)
			*numErrors = 0;

		if (numLinesProcessed)
			*numLinesProcessed = 0;

		//---------------------------------------
		// Now, let's open the ABL source file...	
		long openErr = NO_ERR;
		if ((openErr = openSourceFile(sourceFileName)) != NO_ERR)
		{
			if (ablError)
			{
				ablError->String ("Unable to open file");
				ablError->File (sourceFileName);
				ablError->Line (0);
				ablError->Code (openErr);
			}
			ablError = NULL;
			return(openErr);
		}
#pragma warning (disable:4611)
		if (ablError)
		{
			if (setjmp (abl_jmp_buf))
			{
				closeSourceFile ();
				ablError = NULL;
				return -1;
			}
		}
#pragma warning (default:4611)
		//------------------------
		// Set up the code buffer.
		codeBufferPtr = codeBuffer;

		//------------------
		// Get it rolling...
		getToken();

		SymTableNodePtr moduleIdPtr = moduleHeader();
		CurModuleIdPtr = moduleIdPtr;
		CurRoutineIdPtr = moduleIdPtr;

		//---------------------
		// Error synchronize...
		synchronize(followHeaderList, declarationStartList, statementStartList);
		if (curToken == TKN_SEMICOLON)
			getToken();
		else if (tokenIn(declarationStartList) || tokenIn(statementStartList))
			syntaxError(ABL_ERR_SYNTAX_MISSING_SEMICOLON);

		//-------------------------------------------------------------
		// Parse the module's block. Use to call block(), now we do the
		// stuff right here...
		//block(moduleIdPtr);
		declarations(moduleIdPtr, 1,parsingFSM ? 1: 0);
		if (!parsingFSM)
		{
			synchronize(followModuleDeclsList, NULL, NULL);
			if ((curToken != TKN_CODE) && (parsingFSM == false))
				syntaxError(ABL_ERR_SYNTAX_MISSING_CODE);
			else if ((curToken != TKN_STATE) && (parsingFSM == true))
				syntaxError(ABL_ERR_SYNTAX_CODE_OUTSIDE_STATE_BLOCK);

			crunchToken();
			blockType = BLOCK_MODULE;
			blockFlag = true;

			getToken();
		}
		TokenCodeType endToken;
		if (parsingFSM)
			endToken = TKN_END_FSM;
		else
			endToken = ((CurLibrary == NULL) ? TKN_END_MODULE : TKN_END_LIBRARY);
		if (curToken != endToken)
			do
			{
				statement();
				while (curToken == TKN_SEMICOLON)
					getToken();
				if (curToken == endToken)
					break;

				//------------------------------------
				// Synchronize after possible error...
				synchronize(statementStartList, NULL, NULL);
			} while (tokenIn(statementStartList));

		if (CurLibrary)
			ifTokenGetElseError(TKN_END_LIBRARY, ABL_ERR_SYNTAX_MISSING_END_LIBRARY);
		else if (parsingFSM)
			ifTokenGetElseError(TKN_END_FSM, ABL_ERR_SYNTAX_MISSING_END_MODULE);
		else
			ifTokenGetElseError(TKN_END_MODULE, ABL_ERR_SYNTAX_MISSING_END_MODULE);

		blockFlag = false;

		moduleIdPtr->defn.info.routine.localSymTable = exitScope();
		moduleIdPtr->defn.info.routine.codeSegment = createCodeSegment(moduleIdPtr->defn.info.routine.codeSegmentSize);

		analyzeBlock(moduleIdPtr->defn.info.routine.codeSegment);

		ifTokenGetElseError(TKN_PERIOD, ABL_ERR_SYNTAX_MISSING_PERIOD);

		while (curToken != TKN_EOF)
		{
			syntaxError(ABL_ERR_SYNTAX_UNEXPECTED_TOKEN);
			getToken();
		}

		//------------------------------------------
		// Done with the source file, so close it...
		closeSourceFile();

		//------------------------------------------
		// Verify that all states were declared
		if (parsingFSM)
			ABLi_VerifyStates (moduleIdPtr->defn.info.routine.localSymTable);

		//extractSymTable(&SymTableDisplay[0], moduleIdPtr);

		//--------------------------------------------------
		// Register the new module in the ABL environment...
		ModuleRegistry[NumModulesRegistered].fileName = (char*)AblStackHeap->Malloc(strlen(sourceFileName) + 1);
		if (!ModuleRegistry[NumModulesRegistered].fileName)
			ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc module filename ");
		strcpy(ModuleRegistry[NumModulesRegistered].fileName, strlwr(sourceFileName));
		ModuleRegistry[NumModulesRegistered].moduleIdPtr = moduleIdPtr;

		ModuleRegistry[NumModulesRegistered].numSourceFiles = NumSourceFiles;
		ModuleRegistry[NumModulesRegistered].sourceFiles = (char**)AblStackHeap->Malloc(NumSourceFiles * sizeof(char*));
		if (!ModuleRegistry[NumModulesRegistered].sourceFiles)
			ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc sourceFiles ");
		for (i = 0; i < NumSourceFiles; i++)
		{
			ModuleRegistry[NumModulesRegistered].sourceFiles[i] = (char*)AblStackHeap->Malloc(strlen(SourceFiles[i]) + 1);
			strcpy(ModuleRegistry[NumModulesRegistered].sourceFiles[i], SourceFiles[i]);
		}

		if (NumLibrariesUsed > 0)
		{
			ModuleRegistry[NumModulesRegistered].numLibrariesUsed = NumLibrariesUsed;
			ModuleRegistry[NumModulesRegistered].librariesUsed = (ABLModulePtr*)AblStackHeap->Malloc(NumLibrariesUsed * sizeof(SymTableNodePtr));
			if (!ModuleRegistry[NumModulesRegistered].librariesUsed)
				ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc librariesUsed ");
			for (i = 0; i < NumLibrariesUsed; i++)
				ModuleRegistry[NumModulesRegistered].librariesUsed[i] = LibrariesUsed[i];
		}

		ModuleRegistry[NumModulesRegistered].numStaticVars = NumStaticVariables;
		ModuleRegistry[NumModulesRegistered].sizeStaticVars = NULL;
		ModuleRegistry[NumModulesRegistered].totalSizeStaticVars = 0;
		if (NumStaticVariables)
		{
			ModuleRegistry[NumModulesRegistered].sizeStaticVars = (long*)AblStackHeap->Malloc(sizeof(long) * NumStaticVariables);
			if (!ModuleRegistry[NumModulesRegistered].sizeStaticVars)
				ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc module sizeStaticVars ");
			memcpy(ModuleRegistry[NumModulesRegistered].sizeStaticVars, StaticVariablesSizes, sizeof(long) * NumStaticVariables);
			ModuleRegistry[NumModulesRegistered].totalSizeStaticVars = sizeof(long) * NumStaticVariables;
			for (long i = 0; i < ModuleRegistry[NumModulesRegistered].numStaticVars; i++)
				ModuleRegistry[NumModulesRegistered].totalSizeStaticVars += ModuleRegistry[NumModulesRegistered].sizeStaticVars[i];
		}
		ModuleRegistry[NumModulesRegistered].numInstances = 0;
		NumModulesRegistered++;

		//---------------------------------------------------------------
		// Now, exit with the number of source lines processed, if any...
		if (numLinesProcessed)
			*numLinesProcessed = lineNumber;

		if (numFilesProcessed)
			*numFilesProcessed = FileNumber;

		if (numErrors)
			*numErrors = errorCount;

		ablError = NULL;
		return(NumModulesRegistered - 1);
	}

//***************************************************************************

	/*
	long ABLi_preProcessFromParams(const std::string& params, const std::string& script_suffix)
	{
		ablError = 0;

		//---------------------------------
		// Init some important variables...
		parsingFSM = false;
		level = 0;
		lineNumber = 0;
		FileNumber = 0;
		AssertEnabled = (debugger != NULL);
		PrintEnabled = (debugger != NULL);
		StringFunctionsEnabled = true;
		DebugCodeEnabled = (debugger != NULL);
		NumSourceFiles = 0;
		NumLibrariesUsed = 0;
		sourceFile = NULL;
		blockFlag = false;
		blockType = BLOCK_MODULE;
		bufferOffset = 0;
		bufferp = sourceBuffer;
		tokenp = tokenString;
		digitCount = 0;
		countError = false;
		pageNumber = 0;
		errorCount = 0;
		execStatementCount = 0;
		eofFlag = false;
		NumStaticVariables = 0;

		std::string params_copy(params);
		std::string source_file_name("Content\\ABLScripts\\GenericScripts\\");

		while (params_copy[0] != ',')
		{
			source_file_name += params_copy[0];
			params_copy.erase(0,1);
		}

		params_copy.erase(0,1);

		//---------------------------------------
		// Now, let's open the ABL source file...	
		long openErr = NO_ERR;
		currentParamList = params_copy;
		currentScriptSuffix = script_suffix;
		if ((openErr = openSourceFile((char*)source_file_name.c_str())) != NO_ERR)
		{
			if (ablError)
			{
				ablError->String ("Unable to open file");
				ablError->File (source_file_name.c_str());
				ablError->Line (0);
				ablError->Code (openErr);
			}
			ablError = NULL;
			currentParamList.erase(currentParamList.begin(),currentParamList.end());
			currentScriptSuffix.erase(currentScriptSuffix.begin(),currentScriptSuffix.end());
			return(openErr);
		}
		currentParamList.erase(currentParamList.begin(),currentParamList.end());
		currentScriptSuffix.erase(currentScriptSuffix.begin(),currentScriptSuffix.end());
#pragma warning (disable:4611)
		if (ablError)
		{
			if (setjmp (abl_jmp_buf))
			{
				closeSourceFile ();
				ablError = NULL;
				return -1;
			}
		}
#pragma warning (default:4611)

		//------------------------
		// Set up the code buffer.
		codeBufferPtr = codeBuffer;

		//------------------
		// Get it rolling...
		getToken();

		SymTableNodePtr moduleIdPtr = moduleHeader();
		CurModuleIdPtr = moduleIdPtr;
		CurRoutineIdPtr = moduleIdPtr;

		//---------------------
		// Error synchronize...
		synchronize(followHeaderList, declarationStartList, statementStartList);
		if (curToken == TKN_SEMICOLON)
			getToken();
		else if (tokenIn(declarationStartList) || tokenIn(statementStartList))
			syntaxError(ABL_ERR_SYNTAX_MISSING_SEMICOLON);

		//-------------------------------------------------------------
		// Parse the module's block. Use to call block(), now we do the
		// stuff right here...
		//block(moduleIdPtr);
		declarations(moduleIdPtr, 1,parsingFSM ? 1: 0);
		if (!parsingFSM)
		{
			synchronize(followModuleDeclsList, NULL, NULL);
			if ((curToken != TKN_CODE) && (parsingFSM == false))
				syntaxError(ABL_ERR_SYNTAX_MISSING_CODE);
			else if ((curToken != TKN_STATE) && (parsingFSM == true))
				syntaxError(ABL_ERR_SYNTAX_CODE_OUTSIDE_STATE_BLOCK);

			crunchToken();
			blockType = BLOCK_MODULE;
			blockFlag = true;

			getToken();
		}
		TokenCodeType endToken;
		if (parsingFSM)
			endToken = TKN_END_FSM;
		else
			endToken = ((CurLibrary == NULL) ? TKN_END_MODULE : TKN_END_LIBRARY);
		if (curToken != endToken)
			do
			{
				statement();
				while (curToken == TKN_SEMICOLON)
					getToken();
				if (curToken == endToken)
					break;

				//------------------------------------
				// Synchronize after possible error...
				synchronize(statementStartList, NULL, NULL);
			} while (tokenIn(statementStartList));

		if (CurLibrary)
			ifTokenGetElseError(TKN_END_LIBRARY, ABL_ERR_SYNTAX_MISSING_END_LIBRARY);
		else if (parsingFSM)
			ifTokenGetElseError(TKN_END_FSM, ABL_ERR_SYNTAX_MISSING_END_MODULE);
		else
			ifTokenGetElseError(TKN_END_MODULE, ABL_ERR_SYNTAX_MISSING_END_MODULE);

		blockFlag = false;

		moduleIdPtr->defn.info.routine.localSymTable = exitScope();
		moduleIdPtr->defn.info.routine.codeSegment = createCodeSegment(moduleIdPtr->defn.info.routine.codeSegmentSize);

		analyzeBlock(moduleIdPtr->defn.info.routine.codeSegment);

		ifTokenGetElseError(TKN_PERIOD, ABL_ERR_SYNTAX_MISSING_PERIOD);

//		while (curToken != TKN_EOF)
//		{
//			syntaxError(ABL_ERR_SYNTAX_UNEXPECTED_TOKEN);
//			getToken();
//		}

		//------------------------------------------
		// Done with the source file, so close it...
		closeSourceFile();

		//------------------------------------------
		// Verify that all states were declared
		if (parsingFSM)
			ABLi_VerifyStates (moduleIdPtr->defn.info.routine.localSymTable);

		//extractSymTable(&SymTableDisplay[0], moduleIdPtr);

		//--------------------------------------------------
		// Register the new module in the ABL environment...
		ModuleRegistry[NumModulesRegistered].fileName = (char*)AblStackHeap->Malloc(strlen((char*)source_file_name.c_str()) + 1);
		if (!ModuleRegistry[NumModulesRegistered].fileName)
			ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc module filename ");
		strcpy(ModuleRegistry[NumModulesRegistered].fileName, strlwr((char*)source_file_name.c_str()));
		ModuleRegistry[NumModulesRegistered].moduleIdPtr = moduleIdPtr;

		ModuleRegistry[NumModulesRegistered].numSourceFiles = NumSourceFiles;
		ModuleRegistry[NumModulesRegistered].sourceFiles = (char**)AblStackHeap->Malloc(NumSourceFiles * sizeof(char*));
		if (!ModuleRegistry[NumModulesRegistered].sourceFiles)
			ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc sourceFiles ");
		for (long i = 0; i < NumSourceFiles; i++)
		{
			ModuleRegistry[NumModulesRegistered].sourceFiles[i] = (char*)AblStackHeap->Malloc(strlen(SourceFiles[i]) + 1);
			strcpy(ModuleRegistry[NumModulesRegistered].sourceFiles[i], SourceFiles[i]);
		}

		if (NumLibrariesUsed > 0)
		{
			ModuleRegistry[NumModulesRegistered].numLibrariesUsed = NumLibrariesUsed;
			ModuleRegistry[NumModulesRegistered].librariesUsed = (ABLModulePtr*)AblStackHeap->Malloc(NumLibrariesUsed * sizeof(SymTableNodePtr));
			if (!ModuleRegistry[NumModulesRegistered].librariesUsed)
				ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc librariesUsed ");
			for (i = 0; i < NumLibrariesUsed; i++)
				ModuleRegistry[NumModulesRegistered].librariesUsed[i] = LibrariesUsed[i];
		}

		ModuleRegistry[NumModulesRegistered].numStaticVars = NumStaticVariables;
		ModuleRegistry[NumModulesRegistered].sizeStaticVars = NULL;
		ModuleRegistry[NumModulesRegistered].totalSizeStaticVars = 0;
		if (NumStaticVariables)
		{
			ModuleRegistry[NumModulesRegistered].sizeStaticVars = (long*)AblStackHeap->Malloc(sizeof(long) * NumStaticVariables);
			if (!ModuleRegistry[NumModulesRegistered].sizeStaticVars)
				ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc module sizeStaticVars ");
			memcpy(ModuleRegistry[NumModulesRegistered].sizeStaticVars, StaticVariablesSizes, sizeof(long) * NumStaticVariables);
			ModuleRegistry[NumModulesRegistered].totalSizeStaticVars = sizeof(long) * NumStaticVariables;
			for (long i = 0; i < ModuleRegistry[NumModulesRegistered].numStaticVars; i++)
				ModuleRegistry[NumModulesRegistered].totalSizeStaticVars += ModuleRegistry[NumModulesRegistered].sizeStaticVars[i];
		}
		ModuleRegistry[NumModulesRegistered].numInstances = 0;
		NumModulesRegistered++;

		ablError = NULL;
		return(NumModulesRegistered - 1);
	}
	*/

//***************************************************************************


	long ABLi_execute (SymTableNodePtr moduleIdPtr, SymTableNodePtr functionIdPtr, ABLParamPtr paramList, StackItemPtr returnVal)
	{

		//insertSymTable(&SymTableDisplay[0], moduleIdPtr);

		//--------------------------
		// Execute the ABL module...

		//----------------------------------------------------------------------------------
		// NOTE: Ultimately, we want the ABL virtual machine to be set up once, and then
		// ABL modules/routines can be called multiple times from within a program. For now,
		// to test, let's just assume every time we run a module, it is a self-contained
		// ABL program.

		//---------------------------------
		// Init some important variables...
		//lineNumber = 0;
		//printFlag = true;
		//blockFlag = false;
		//blockType = BLOCK_MODULE;
		CurModuleIdPtr = NULL;
		CurRoutineIdPtr = NULL;
		//bufferOffset = 0;
		//bufferp = sourceBuffer;
		//tokenp = tokenString;
		//digitCount = 0;
		//countError = false;
		errorCount = 0;
		execStatementCount = 0;

		NumExecutions++;

		//------------------
		// Init the stack...
		stackFrameBasePtr = tos = (stack + eternalOffset);

		//---------------------------------------
		// Initialize the module's stack frame...
		level = 1;
		CallStackLevel = 0;
		stackFrameBasePtr = tos + 1;

		//-------------------------
		// Function return value...
		pushInteger(0);
		//---------------
		// Static Link...
		pushAddress(NULL);
		//----------------
		// Dynamic Link...
		pushAddress(NULL);
		//------------------
		// Return Address...
		pushAddress(NULL);

		//initDebugger();

		//----------
		// Run it...

		if (paramList)
		{
			//------------------------------------------------------------------------------
			// NOTE: Currently, parameter passing of arrays is not functioning. This MUST be
			// done...
			long curParam = 0;
			for (SymTableNodePtr formalIdPtr = (SymTableNodePtr)(moduleIdPtr->defn.info.routine.params);
					formalIdPtr != NULL;
					formalIdPtr = formalIdPtr->next)
			{

				TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);

				if (formalIdPtr->defn.key == DFN_VALPARAM)
				{

					if (formalTypePtr == RealTypePtr)
					{
						if (paramList[curParam].type == ABL_PARAM_INTEGER)
						{
							//---------------------------------------------
							// Real formal parameter, but integer actual...
							pushReal((float)(paramList[curParam].integer));
						}
						else if (paramList[curParam].type == ABL_PARAM_REAL)
							pushReal(paramList[curParam].real);
					}
					else if (formalTypePtr == IntegerTypePtr)
					{
						if (paramList[curParam].type== ABL_PARAM_INTEGER)
							pushInteger(paramList[curParam].integer);
						else
							return(0);
					}

					//----------------------------------------------------------
					// 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* dest = (char*)AblStackHeap->Malloc((size_t)size);
						if (!dest)
							ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc module formal array param ");
						char* src = tos->address;
						char* savePtr = dest;
						memcpy(dest, src, size);
						tos->address = savePtr;
					}
				}
				else
				{
					//-------------------------------
					// pass by reference parameter...
					if (formalTypePtr == RealTypePtr)
						pushAddress((Address)&(paramList[curParam].real));
					else if (formalTypePtr == IntegerTypePtr)
						pushAddress((Address)&(paramList[curParam].integer));
					else
						return(0);
					//SymTableNodePtr idPtr = getCodeSymTableNodePtr();
					//execVariable(idPtr, USE_REFPARAM);
				}
				curParam++;
			}
		}

		execute(moduleIdPtr);

		if (returnVal)
			memcpy(returnVal, &returnValue, sizeof(StackItem));

		//extractSymTable(&SymTableDisplay[0], moduleIdPtr);

		//-----------
		// Summary...
		return(execStatementCount);
	}

//***************************************************************************

	long ABLi_deleteModule (SymTableNodePtr moduleIdPtr)
	{

		return(NO_ERR);
	}

//***************************************************************************

	void ABLi_close (void)
	{
		if (!g_ABLCreated)
			return;

		if (g_Profiler != 0)
		{
			delete g_Profiler;
			g_Profiler = 0;
		}

//blurt	UserFile::cleanup();

		destroyModuleRegistry();

		destroyLibraryRegistry();

		stlport::vector<ABLRoutineTableEntry *>::iterator iter;

		for (iter = g_ABLFunctions->begin ();iter < g_ABLFunctions->end ();iter++)
		{
			delete (*iter);
		}
		delete g_ABLFunctions;


		if (StaticVariablesSizes)
		{
			AblStackHeap->Free(StaticVariablesSizes);
			StaticVariablesSizes = NULL;
		}

	if (EternalVariablesSizes)
	{
		AblStackHeap->Free(EternalVariablesSizes);
		EternalVariablesSizes = NULL;
	}

		if (codeBuffer)
		{
			AblCodeHeap->Free(codeBuffer);
			codeBuffer = NULL;
		}

		if (stack)
		{
			AblStackHeap->Free(stack);
			stack = NULL;
		}

		if (debugger)
		{
			delete debugger;
			debugger = NULL;
		}

		if (AblSymTableHeap)
		{
			delete AblSymTableHeap;
			AblSymTableHeap = NULL;
		}
		if (AblStackHeap)
		{
			delete AblStackHeap;
			AblStackHeap = NULL;
		}
		if (AblCodeHeap)
		{
			delete AblCodeHeap;
			AblCodeHeap = NULL;
		}

		ABL_CloseProfileLog();

		ABLenabled = false;
		g_ABLCreated = false;
	}

//***************************************************************************

	long ABLi_loadLibrary (char* sourceFileName, ABLError *ablerr, long* numErrors, long* numLinesProcessed, long* numFilesProcessed, bool printLines)
	{

		//--------------------------------------------------------------------
		// Create an instance of it so it may be used from other modules. Note
		// that we need this when preprocessing since all identifiers in the
		// library should point to this module...
		ABLModulePtr library = new ABLModule;
		if (!library)
			return(-2/*NO_RAM_FOR_ABL*/);

		CurLibrary = library;

		//-------------------------------------------------------------
		// Preprocess the library. Note that a library should be loaded
		// just once.
		long libraryHandle = ABLi_preProcess(sourceFileName, ablerr,numErrors, numLinesProcessed, numFilesProcessed, printLines);
		if (libraryHandle < (NumModulesRegistered - 1))
		{
			//------------------
			// Already loaded...
			delete library;
			library = NULL;
			CurLibrary = NULL;
			return(-1);
		}

		long err = library->init(libraryHandle);
		ABL_Assert(err == NO_ERR, err, " Error Loading ABL Library ");

		library->setName(sourceFileName);

		CurLibrary = NULL;

		LibraryInstanceRegistry[numLibrariesLoaded] = library;
		numLibrariesLoaded++;

		ablError = NULL;
		return(NO_ERR);
	}

//***************************************************************************

	ABLParamPtr ABLi_createParamList (long numParameters)
	{

		if (numParameters)
		{
			ABLParamPtr paramList = (ABLParamPtr)AblStackHeap->Malloc(sizeof(ABLParam) * (numParameters + 1));
			if (!paramList)
				ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc paramList ");
			memset(paramList, 0, sizeof(ABLParam) * (numParameters + 1));
			return(paramList);
		}
		return(NULL);
	}

//***************************************************************************

	void ABLi_setIntegerParam (ABLParamPtr paramList, long index, long value)
	{

		if (paramList)
		{
			paramList[index].type = ABL_PARAM_INTEGER;
			paramList[index].integer = value;
		}
	}

//***************************************************************************

	void ABLi_setRealParam (ABLParamPtr paramList, long index, float value)
	{

		if (paramList)
		{
			paramList[index].type = ABL_PARAM_REAL;
			paramList[index].real = value;
		}
	}

//***************************************************************************

	void ABLi_deleteParamList (ABLParamPtr paramList)
	{

		if (paramList)
			AblStackHeap->Free(paramList);
	}

//***************************************************************************

	ABLModulePtr ABLi_getModule (long id)
	{

		if ((id >= 0) && (id < NumModules))
			return(ModuleInstanceRegistry[id]);
		return(NULL);
	}

//***************************************************************************

	bool ABLi_enabled (void)
	{

		return(ABLenabled);
	}

//***************************************************************************
// MODULE routines
//***************************************************************************

	SymTableNodePtr moduleHeader (void)
	{

		SymTableNodePtr moduleIdPtr = NULL;
		if (CurLibrary)
			ifTokenGetElseError(TKN_LIBRARY, ABL_ERR_SYNTAX_MISSING_LIBRARY);
		else
		{
			if (curToken == TKN_MODULE)
			{
				parsingFSM = false;
				getToken ();
			}
			else if (curToken == TKN_FSM)
			{
				parsingFSM = true;
				getToken ();
			}
			else
			{
				syntaxError(ABL_ERR_SYNTAX_MISSING_MODULE);
			}
		}

		//------------------------------------------------------------
		// NOTE: If a module is a library, its library pointer will be
		// non-NULL.

		if (curToken == TKN_IDENTIFIER)
		{
			searchAndEnterLocalSymTable(moduleIdPtr);
			if (parsingFSM)
				moduleIdPtr->defn.key = DFN_FSM;
			else
				moduleIdPtr->defn.key	= DFN_MODULE;
			moduleIdPtr->defn.info.routine.key = RTN_DECLARED;
			moduleIdPtr->defn.info.routine.paramCount = 0;
			moduleIdPtr->defn.info.routine.totalParamSize = 0;
			moduleIdPtr->defn.info.routine.totalLocalSize = 0;
			moduleIdPtr->defn.info.routine.params = NULL;
			moduleIdPtr->defn.info.routine.locals = NULL;
			moduleIdPtr->defn.info.routine.localSymTable = NULL;
			moduleIdPtr->defn.info.routine.codeSegment = NULL;
			moduleIdPtr->library = CurLibrary;
			moduleIdPtr->typePtr = &DummyType;
			moduleIdPtr->labelIndex = 0;
			getToken();
		}
		else
			syntaxError(ABL_ERR_SYNTAX_MISSING_IDENTIFIER);

		//--------------------------------------------
		// For now, modules do not have param lists...
		synchronize(followModuleIdList, declarationStartList, statementStartList);
		enterScope(NULL);

		if (parsingFSM)
		{
			FSMLevel = level;
		}

		if (curToken == TKN_LPAREN)
		{
			long paramCount;
			long totalParamSize;
			SymTableNodePtr paramListPtr = formalParamList(&paramCount, &totalParamSize);
			//if (forwardFlag)
			//	syntaxError(ABL_ERR_SYNTAX_ALREADY_FORWARDED);
			//else {
			moduleIdPtr->defn.info.routine.paramCount = (unsigned char) paramCount;
			moduleIdPtr->defn.info.routine.totalParamSize = (unsigned char) totalParamSize;
			moduleIdPtr->defn.info.routine.params = paramListPtr;
			//}
		}
		//else if (!forwardFlag) {
		//	functionIdPtr->defn.info.routine.paramCount = 0;
		//	functionIdPtr->defn.info.routine.totalParamSize = 0;
		//	functionIdPtr->defn.info.routine.params = NULL;
		//}

		//-----------------------------
		// Now, check if return type...
		moduleIdPtr->typePtr = NULL;
		SymTableNodePtr typeIdPtr = NULL;
		if (curToken == TKN_COLON)
		{
			getToken();
			if (curToken == TKN_IDENTIFIER)
			{
				searchAndFindAllSymTables(typeIdPtr);

				if (typeIdPtr->defn.key != DFN_TYPE)
					syntaxError(ABL_ERR_SYNTAX_INVALID_TYPE);

				//if (!forwardFlag)
				moduleIdPtr->typePtr = typeIdPtr->typePtr;

				getToken();
			}
			else
			{
				syntaxError(ABL_ERR_SYNTAX_MISSING_IDENTIFIER);
				moduleIdPtr->typePtr = &DummyType;
			}

			//if (forwardFlag)
			//	syntaxError(ABL_ERR_SYNTAX_ALREADY_FORWARDED);
		}

		return(moduleIdPtr);
	}

//***************************************************************************
// ROUTINE/FUNCTION routines
//***************************************************************************

	void routine (void)
	{

		SymTableNodePtr routineIdPtr = NULL;

		//------------------------------------------------------------------------
		// Do we want to have "procedures", or do functions handle both cases (with
		// and without return values)? For now, functions handle both...
		routineIdPtr = functionHeader();

		//------------------------------------------------------------------
		// We need to save a pointer to the current routine we're parsing...
		SymTableNodePtr prevRoutineIdPtr = CurRoutineIdPtr;
		CurRoutineIdPtr = routineIdPtr;

		//---------------------
		// Error synchronize...
		synchronize(followHeaderList, declarationStartList, statementStartList);

		if (curToken == TKN_SEMICOLON)
			getToken();
		else if (tokenIn(declarationStartList) || tokenIn(statementStartList))
			syntaxError(ABL_ERR_SYNTAX_MISSING_SEMICOLON);

		if ((strcmp(wordString, "forward") != 0) && (strcmp(wordString, "extern") != 0))
		{
			routineIdPtr->defn.info.routine.key = RTN_DECLARED;
			//analyzeRoutineHeader(routineIdPtr);

			routineIdPtr->defn.info.routine.locals = NULL;

			//--------------------------------------------------------------
			// Parse the routine's block. Use to call block(), now we do the
			// stuff right here...
			//block(routineIdPtr);
			declarations(routineIdPtr, 0, 0);
			synchronize(followRoutineDeclsList, NULL, NULL);
			if (curToken != TKN_CODE)
				syntaxError(ABL_ERR_SYNTAX_MISSING_CODE);
			crunchToken();

			blockType = BLOCK_ROUTINE;
			blockFlag = true;

			//compoundStatement();
			getToken();
			if (curToken != TKN_END_FUNCTION)
				do
				{
					statement();
					while (curToken == TKN_SEMICOLON)
						getToken();
					if (curToken == TKN_END_FUNCTION)
						break;

					//------------------------------------
					// Synchronize after possible error...
					synchronize(statementStartList, NULL, NULL);
				} while (tokenIn(statementStartList));
			ifTokenGetElseError(TKN_END_FUNCTION, ABL_ERR_SYNTAX_MISSING_END_FUNCTION);

			blockFlag = false;

			ABL::TokenCodeType ttoken = curToken;
			curToken = TKN_NONE;
			crunchToken ();
			curToken = ttoken;

			routineIdPtr->defn.info.routine.codeSegment = createCodeSegment(routineIdPtr->defn.info.routine.codeSegmentSize);
			analyzeBlock(routineIdPtr->defn.info.routine.codeSegment);
		}
		else if (strcmp (wordString,"forward") == 0)
		{
			getToken();
			routineIdPtr->defn.info.routine.key = RTN_FORWARD;
			analyzeRoutineHeader(routineIdPtr);
		}
		else
		{
			getToken();
			routineIdPtr->defn.info.routine.key = RTN_EXTERN;
			analyzeRoutineHeader(routineIdPtr);
		}

		routineIdPtr->defn.info.routine.localSymTable = exitScope();

		CurRoutineIdPtr = prevRoutineIdPtr;
	}

//***************************************************************************

	SymTableNodePtr functionHeader (void)
	{

		getToken();
		SymTableNodePtr functionIdPtr = NULL;
		SymTableNodePtr typeIdPtr = NULL;

		bool forwardFlag = false;

		if (curToken == TKN_IDENTIFIER)
		{
			searchLocalSymTable(functionIdPtr);
			if (!functionIdPtr)
			{
				enterLocalSymTable(functionIdPtr);
				functionIdPtr->defn.key = DFN_FUNCTION;
				functionIdPtr->defn.info.routine.key = RTN_DECLARED;
				functionIdPtr->defn.info.routine.paramCount = 0;
				functionIdPtr->defn.info.routine.totalParamSize = 0;
				functionIdPtr->defn.info.routine.totalLocalSize = 0;
				functionIdPtr->defn.info.routine.params = NULL;
				functionIdPtr->defn.info.routine.locals = NULL;
				functionIdPtr->defn.info.routine.localSymTable = NULL;
				functionIdPtr->defn.info.routine.codeSegment = NULL;
				functionIdPtr->library = CurLibrary;
				functionIdPtr->typePtr = &DummyType;
				functionIdPtr->labelIndex = 0;
			}
			else if ((functionIdPtr->defn.key == DFN_FUNCTION) && (functionIdPtr->defn.info.routine.key == RTN_FORWARD))
				forwardFlag = true;
			else if ((functionIdPtr->defn.key == DFN_FUNCTION) && (functionIdPtr->defn.info.routine.key == RTN_EXTERN))
				syntaxError(ABL_ERR_SYNTAX_REDEFINED_IDENTIFIER);
			else
				syntaxError(ABL_ERR_SYNTAX_REDEFINED_IDENTIFIER);
			getToken();
		}

		//---------------------
		// Error synchronize...
		synchronize(followFunctionIdList, declarationStartList, statementStartList);

		enterScope(NULL);

		if (curToken == TKN_LPAREN)
		{
			long paramCount;
			long totalParamSize;
			SymTableNodePtr paramListPtr = formalParamList(&paramCount, &totalParamSize);
			if (forwardFlag)
				syntaxError(ABL_ERR_SYNTAX_ALREADY_FORWARDED);
			else
			{
				functionIdPtr->defn.info.routine.paramCount = (unsigned char) paramCount;
				functionIdPtr->defn.info.routine.totalParamSize = (unsigned char) totalParamSize;
				functionIdPtr->defn.info.routine.params = paramListPtr;
			}
		}
		else if (!forwardFlag)
		{
			functionIdPtr->defn.info.routine.paramCount = 0;
			functionIdPtr->defn.info.routine.totalParamSize = 0;
			functionIdPtr->defn.info.routine.params = NULL;
		}

		//--------------------------------------------
		// Now, check if return type and/or forward...
		functionIdPtr->typePtr = NULL;
		if (curToken == TKN_COLON)
		{
			getToken();
			if (curToken == TKN_IDENTIFIER)
			{
				searchAndFindAllSymTables(typeIdPtr);

				if (typeIdPtr->defn.key != DFN_TYPE)
					syntaxError(ABL_ERR_SYNTAX_INVALID_TYPE);

				if (!forwardFlag)
					functionIdPtr->typePtr = typeIdPtr->typePtr;

				getToken();
			}
			else
			{
				syntaxError(ABL_ERR_SYNTAX_MISSING_IDENTIFIER);
				functionIdPtr->typePtr = &DummyType;
			}

			if (forwardFlag)
				syntaxError(ABL_ERR_SYNTAX_ALREADY_FORWARDED);
		}

		return(functionIdPtr);
	}

//***************************************************************************

	SymTableNodePtr formalParamList (long* count, long* totalSize)
	{

		SymTableNodePtr paramIdPtr;
		SymTableNodePtr firstIdPtr;
		SymTableNodePtr lastIdPtr = NULL;
		SymTableNodePtr paramListPtr = NULL;
		SymTableNodePtr typeIdPtr;
		TypePtr paramTypePtr;
		DefinitionType paramDefn;
		long paramCount = 0;
		long paramOffset = STACK_FRAME_HEADER_SIZE;

		getToken();

		while ((curToken == TKN_IDENTIFIER) || (curToken == TKN_REF))
		{
			firstIdPtr = NULL;

			if (curToken == TKN_REF)
			{
				paramDefn = DFN_REFPARAM;
				getToken();
			}
			else
				paramDefn	= DFN_VALPARAM;

			//-----------------------------------
			// Grab the type of this parameter...
			if (curToken == TKN_IDENTIFIER)
			{
				searchAndFindAllSymTables(typeIdPtr);
				if (typeIdPtr->defn.key != DFN_TYPE)
					syntaxError(ABL_ERR_SYNTAX_INVALID_TYPE);
				paramTypePtr = (TypePtr)(typeIdPtr->typePtr);
				getToken();
			}
			else
			{
				syntaxError(ABL_ERR_SYNTAX_MISSING_IDENTIFIER);
				paramTypePtr = &DummyType;
			}

			//------------------------------------------------------------
			// Is this an array parameter? If so, parse the dimensions and
			// whether it's an open array...
			if (curToken == TKN_LBRACKET)
			{
			}

			//-----------------------------------------------
			// Now grab the variable id for this parameter...
			if (curToken == TKN_IDENTIFIER)
			{
				searchAndEnterLocalSymTable(paramIdPtr);
				paramIdPtr->defn.key = paramDefn;
				paramIdPtr->labelIndex = 0;

				paramIdPtr->typePtr = paramTypePtr;
				paramIdPtr->defn.info.data.offset = paramOffset++;

				paramCount++;

				if (paramListPtr == NULL)
					paramListPtr = paramIdPtr;
				if (lastIdPtr != NULL)
					lastIdPtr->next = paramIdPtr;
				lastIdPtr = paramIdPtr;
				//if (firstIdPtr == NULL)
				//	firstIdPtr = lastIdPtr = paramIdPtr;
				//else {
				//	lastIdPtr->next = paramIdPtr;
				//	lastIdPtr = paramIdPtr;
				//}

				getToken();
				//ifTokenGet(TKN_COMMA);
			}
			else
			{
				syntaxError(ABL_ERR_SYNTAX_MISSING_IDENTIFIER);
				//--------------------------------
				// Do we need to synchronize here?
			}


			//------------------------------------------------------------------
			// Assign the offset and the type to all param ids in the sublist...
			//for (paramIdPtr = firstIdPtr; paramIdPtr != NULL; paramIdPtr = paramIdPtr->next) {
			//	paramIdPtr->typePtr = paramTypePtr;
			//	paramIdPtr->defn.info.data.offset = paramOffset++;
			//}

			//-----------------------------------------------
			// Link this list to the list of all param ids...
			//if (prevLastIdPtr != NULL)
			//	prevLastIdPtr->next = firstIdPtr;
			//prevLastIdPtr = lastIdPtr;

			//-------------------------------
			// Error synch... should be ; or )
			synchronize(followParamsList, NULL, NULL);
			ifTokenGet(TKN_COMMA);
		}

		ifTokenGetElseError(TKN_RPAREN, ABL_ERR_SYNTAX_MISSING_RPAREN);
		*count = paramCount;
		*totalSize = paramOffset - STACK_FRAME_HEADER_SIZE;

		if (paramCount > 255)
			syntaxError(ABL_ERR_SYNTAX_TOO_MANY_FORMAL_PARAMETERS);

		return(paramListPtr);
	}

//***************************************************************************

	TypePtr routineCall (SymTableNodePtr routineIdPtr, long paramCheckFlag)
	{

		SymTableNodePtr thisRoutineIdPtr = CurRoutineIdPtr;
		TypePtr resultType = NULL;

		if ((routineIdPtr->defn.info.routine.key == RTN_DECLARED) ||
				(routineIdPtr->defn.info.routine.key == RTN_FORWARD) ||
				(routineIdPtr->defn.info.routine.key == RTN_EXTERN) ||
				!paramCheckFlag)
			resultType = declaredRoutineCall(routineIdPtr, paramCheckFlag);
		else
			resultType = standardRoutineCall(routineIdPtr);

		CurRoutineIdPtr = thisRoutineIdPtr;
		return(resultType);
	}

//***************************************************************************

	TypePtr declaredRoutineCall (SymTableNodePtr routineIdPtr, long paramCheckFlag)
	{

		actualParamList(routineIdPtr, paramCheckFlag);
		return(/*routineIdPtr->defn.key == DFN_PROCEDURE ? NULL :*/ (TypePtr)(routineIdPtr->typePtr));
	}

//***************************************************************************

	void actualParamList (SymTableNodePtr routineIdPtr, long paramCheckFlag)
	{

		SymTableNodePtr formalParamIdPtr = NULL;
		DefinitionType formalParamDefn = (DefinitionType)0;
		TypePtr formalParamTypePtr = NULL;
		TypePtr actualParamTypePtr = NULL;

		if (paramCheckFlag)
			formalParamIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);

		if (curToken == TKN_LPAREN)
		{
			//-----------------------------------------------
			// Loop to process actual parameter expression...
			do
			{
				//----------------------------------------------------
				// Obtain info about the corresponding formal param...
				if (paramCheckFlag && (formalParamIdPtr != NULL))
				{
					formalParamDefn = formalParamIdPtr->defn.key;
					formalParamTypePtr = (TypePtr)(formalParamIdPtr->typePtr);
				}

				getToken();

				if ((formalParamIdPtr == NULL) || (formalParamDefn == DFN_VALPARAM) || !paramCheckFlag)
				{
					//--------------
					// VAL Params...
					actualParamTypePtr = expression();
					if (paramCheckFlag && (formalParamIdPtr != NULL) && !isAssignTypeCompatible(formalParamTypePtr, actualParamTypePtr))
						syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
				}
				else
				{
					//--------------
					// VAR params...
					if (curToken == TKN_IDENTIFIER)
					{
						SymTableNodePtr idPtr;
						searchAndFindAllSymTables(idPtr);
						actualParamTypePtr = variable(idPtr);

						if (formalParamTypePtr != actualParamTypePtr)
							syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
					}
					else
					{
						//------------------------------------------------------------------
						// Not a variable--parse the expression anyway for error recovery...
						actualParamTypePtr = expression();
						syntaxError(ABL_ERR_SYNTAX_INVALID_REF_PARAM);
					}
				}

				//---------------------------------------
				// More actual params than formal params?
				if (paramCheckFlag)
				{
					if (formalParamIdPtr == NULL)
						syntaxError(ABL_ERR_SYNTAX_WRONG_NUMBER_OF_PARAMS);
					else
						formalParamIdPtr = formalParamIdPtr->next;
				}

				//---------------
				// Error synch...
				synchronize(followParamList, statementEndList, NULL);
			} while (curToken == TKN_COMMA);

			ifTokenGetElseError(TKN_RPAREN, ABL_ERR_SYNTAX_MISSING_RPAREN);
		}

		//----------------------------------------
		// Fewer actual params than formal params?
		if (paramCheckFlag && (formalParamIdPtr != NULL))
			syntaxError(ABL_ERR_SYNTAX_WRONG_NUMBER_OF_PARAMS);
	}

//***************************************************************************


//***************************************************************************
// State routines
//***************************************************************************

	void state (void)
	{

		SymTableNodePtr routineIdPtr = NULL;

		//------------------------------------------------------------------------
		// Do we want to have "procedures", or do functions handle both cases (with
		// and without return values)? For now, functions handle both...
		routineIdPtr = stateHeader();

		//------------------------------------------------------------------
		// We need to save a pointer to the current routine we're parsing...
		SymTableNodePtr prevRoutineIdPtr = CurRoutineIdPtr;
		CurRoutineIdPtr = routineIdPtr;

		//---------------------
		// Error synchronize...
		synchronize(followHeaderList, declarationStartList, statementStartList);

		if (curToken == TKN_SEMICOLON)
			getToken();
		else if (tokenIn(declarationStartList) || tokenIn(statementStartList))
			syntaxError(ABL_ERR_SYNTAX_MISSING_SEMICOLON);

		if (strcmp(wordString, "forward") != 0)
		{
			routineIdPtr->defn.info.routine.key = RTN_DECLARED;
			//analyzeRoutineHeader(routineIdPtr);

			routineIdPtr->defn.info.routine.locals = NULL;

			//--------------------------------------------------------------
			// Parse the routine's block. Use to call block(), now we do the
			// stuff right here...
			//block(routineIdPtr);
			declarations(routineIdPtr, 0, 0);
			synchronize(followRoutineDeclsList, NULL, NULL);
			if (curToken != TKN_CODE)
				syntaxError(ABL_ERR_SYNTAX_MISSING_CODE);
			crunchToken();

			blockType = BLOCK_ROUTINE;
			blockFlag = true;

			//compoundStatement();
			getToken();
			if (curToken != TKN_END_STATE)
				do
				{
					statement();
					while (curToken == TKN_SEMICOLON)
						getToken();
					if (curToken == TKN_END_STATE)
						break;

					//------------------------------------
					// Synchronize after possible error...
					synchronize(statementStartList, NULL, NULL);
				} while (tokenIn(statementStartList));
			ifTokenGetElseError(TKN_END_STATE, ABL_ERR_SYNTAX_MISSING_END_STATE);

			blockFlag = false;

			ABL::TokenCodeType ttoken = curToken;
			curToken = TKN_NONE;
			crunchToken ();
			curToken = ttoken;

			routineIdPtr->defn.info.routine.codeSegment = createCodeSegment(routineIdPtr->defn.info.routine.codeSegmentSize);
			analyzeBlock(routineIdPtr->defn.info.routine.codeSegment);
		}
		else
		{
			getToken();
			routineIdPtr->defn.info.routine.key = RTN_FORWARD;
			analyzeRoutineHeader(routineIdPtr);
		}

		routineIdPtr->defn.info.routine.localSymTable = exitScope();

		CurRoutineIdPtr = prevRoutineIdPtr;
	}

//***************************************************************************

	SymTableNodePtr stateHeader (void)
	{

		getToken();
		SymTableNodePtr functionIdPtr = NULL;

		if (curToken == TKN_IDENTIFIER)
		{
			searchLocalSymTable(functionIdPtr);
			if (!functionIdPtr)
			{
				enterLocalSymTable(functionIdPtr);
				functionIdPtr->defn.key = DFN_STATE;
				functionIdPtr->defn.info.routine.key = RTN_DECLARED;
				functionIdPtr->defn.info.routine.paramCount = 0;
				functionIdPtr->defn.info.routine.totalParamSize = 0;
				functionIdPtr->defn.info.routine.totalLocalSize = 0;
				functionIdPtr->defn.info.routine.params = NULL;
				functionIdPtr->defn.info.routine.locals = NULL;
				functionIdPtr->defn.info.routine.localSymTable = NULL;
				functionIdPtr->defn.info.routine.codeSegment = NULL;
				functionIdPtr->library = CurLibrary;
				functionIdPtr->typePtr = &DummyType;
				functionIdPtr->labelIndex = 0;
			}
			else if ((functionIdPtr->defn.key == DFN_STATE) && (functionIdPtr->defn.info.routine.key == RTN_FORWARD))
			{
			}
			else
				syntaxError(ABL_ERR_SYNTAX_REDEFINED_IDENTIFIER);
			getToken();
		}

		//---------------------
		// Error synchronize...
		synchronize(followFunctionIdList, declarationStartList, statementStartList);

		enterScope(NULL);

		return(functionIdPtr);
	}

}