//***************************************************************************
//
//								ABLSCAN.CPP
//
//***************************************************************************

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

#ifndef ABLGEN_H
#include "ablgen.h"
#endif

#ifndef ABLERR_H
#include "ablerr.h"
#endif

#ifndef ABLSCAN_H
#include "ablscan.h"
#endif

#ifndef ABLPARSE_H
#include "ablparse.h"
#endif

#ifndef ABLENV_H
#include "ablenv.h"
#endif

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

#ifndef ABLFILE_H
#include "ablfile.h"
#endif

//***************************************************************************
namespace ABL
{

//---------------------
// RESERVED WORD tables

#define	MINLEN_RESERVED_WORD	2
#define	MAXLEN_RESERVED_WORD	11

ReservedWord reservedWord2[] = {
	{"if", TKN_IF},
	{"or", TKN_OR},
	{"do", TKN_DO},
	{"to", TKN_TO},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord3[] = {
	{"and", TKN_AND},
	{"for", TKN_FOR},
	{"mod", TKN_MOD},
	{"not", TKN_NOT},
	{"var", TKN_VAR},
	{"fsm", TKN_FSM},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord4[] = {
	{"else", TKN_ELSE},
	{"then", TKN_THEN},
	{"case", TKN_CASE},
	{"code", TKN_CODE},
	{"type", TKN_TYPE},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord5[] = {
	{"const", TKN_CONST},
	{"until", TKN_UNTIL},
	{"while", TKN_WHILE},
	{"endif", TKN_END_IF},
	{"trans", TKN_TRANS},
	{"state", TKN_STATE},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord6[] = {
	{"module", TKN_MODULE},
	{"repeat", TKN_REPEAT},
	{"endfor", TKN_END_FOR},
	{"switch", TKN_SWITCH},
	{"static", TKN_STATIC},
	{"endfsm", TKN_END_FSM},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord7[] = {
	{"endcase", TKN_END_CASE},
	{"eternal", TKN_ETERNAL},
	{"library", TKN_LIBRARY},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord8[] = {
	{"function", TKN_FUNCTION},
	{"endwhile", TKN_END_WHILE},
	{"endstate", TKN_END_STATE},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord9[] = {
	{"endswitch", TKN_END_SWITCH},
	{"endmodule", TKN_END_MODULE},
	{NULL, TKN_NONE}					//This was missing.  Thank You Symantec!!!!		1/24/97 -fs
};

ReservedWord reservedWord10[] = {
	{"endlibrary", TKN_END_LIBRARY},
	{NULL, TKN_NONE}
};

ReservedWord reservedWord11[] = {
	{"endfunction", TKN_END_FUNCTION},
	{NULL, TKN_NONE}
};

ReservedWord* reservedWordTable[] = {
	NULL,
	NULL,
	reservedWord2,
	reservedWord3,
	reservedWord4,
	reservedWord5,
	reservedWord6,
	reservedWord7,
	reservedWord8,
	reservedWord9,
	reservedWord10,
	reservedWord11
};

//------------
// TOKEN lists

TokenCodeType statementStartList[] = {
	TKN_FOR,
	TKN_IF,
	TKN_REPEAT,
	TKN_WHILE,
	TKN_SWITCH,
	TKN_IDENTIFIER,
	TKN_TRANS,
	TKN_STATE,
	TKN_NONE
};

TokenCodeType statementEndList[] = {
	TKN_SEMICOLON,
	TKN_END_IF,
	TKN_END_WHILE,
	TKN_END_FOR,
	TKN_END_SWITCH,
	TKN_END_FUNCTION,
	TKN_ELSE,
	TKN_ELSIF,
	TKN_UNTIL,
	TKN_EOF,
	TKN_NONE
};

TokenCodeType declarationStartList[] = {
	TKN_CONST,
	TKN_VAR,
	TKN_FUNCTION,
	TKN_NONE
};

char* TokenStrings[NUM_TOKENS] = {
	"{BAD TOKEN}",
	"{IDENTIFIER}",
	"{NUMBER}",
	"{TYPE}",
	"{STRING}",
	"*",
	"(",
	")",
	"-",
	"+",
	"=",
	"[",
	"]",
	":",
	";",
	"<",
	">",
	",",
	".",
	"/",
	"==",
	"<=",
	">=",
	"<>",
	"{EOF}",
	"{ERROR}",
	"code",
	"orders",
	"and",
	"switch",
	"case",
	"const",
	"div",
	"do",
	"of",
	"else",
	"endif",
	"endwhile",
	"endfor",
	"endfunction",
	"endmodule",
	"endlibrary",
	"endvar",
	"endcode",
	"endcase",
	"endswitch",
	"for",
	"function",
	"if",
	"mod",
	"not",
	"or",
	"repeat",
	"then",
	"to",
	"until",
	"var",
	"@",
	"while",
	"elsif",
	"return",
	"module",
	"library",
	"eternal",
	"static",
	"fsm",
	"endfsm",
	"state",
	"endstate",
	"trans",
	"#",
	"{UNEXPECTED TOKEN}",
	"{STATEMENT MARKER}",
	"{ADDRESS MARKER}"
};

//--------
// GLOBALS

UserHeapPtr		AblSymTableHeap = NULL;
UserHeapPtr		AblStackHeap = NULL;
UserHeapPtr		AblCodeHeap = NULL;

char			curChar;
TokenCodeType	curToken;
Literal			curLiteral;
long			level = 0;
long				FSMLevel = 0;
long			lineNumber = 0;
long			FileNumber = 0;
FilePtr			sourceFile = NULL;
bool			printFlag = true;
bool			blockFlag = false;
BlockType		blockType = BLOCK_MODULE;
SymTableNodePtr	CurModuleIdPtr = NULL;
SymTableNodePtr	CurRoutineIdPtr = NULL;
bool			DumbGetCharOn = false;

long			NumOpenFiles = 0;
long			NumSourceFiles = 0;
char			SourceFiles[MAX_SOURCE_FILES][MAXLEN_FILENAME];
SourceFile		openFiles[MAX_INCLUDE_DEPTH];
char			sourceBuffer[MAXLEN_SOURCELINE];
char			tokenString[MAXLEN_TOKENSTRING];
char			wordString[MAXLEN_TOKENSTRING];
long			bufferOffset = 0;
char*			bufferp = sourceBuffer;
char*			tokenp = tokenString;

bool			parsingFSM = false;

long			digitCount = 0;
bool			countError = false;

long			pageNumber = 0;
long			lineCount = MAX_LINES_PER_PAGE;

char			sourceName[MAXLEN_FILENAME];
char			date[LEN_DATESTRING];

CharCodeType	charTable[256];

extern bool		AssertEnabled;
extern bool		PrintEnabled;
extern bool		StringFunctionsEnabled;
extern bool		DebugCodeEnabled;

extern ABLModulePtr	CurLibrary;

//***************************************************************************
// MISC routines
//***************************************************************************

inline CharCodeType calcCharCode (long ch) {

	return(charTable[ch]);
}

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

long isReservedWord (void)
{
	long wordLength = strlen(wordString);
	if ((wordLength >= MINLEN_RESERVED_WORD) && (wordLength <= MAXLEN_RESERVED_WORD) && reservedWordTable[wordLength])
	{
		for (ReservedWord* rwPtr = reservedWordTable[wordLength]; rwPtr->string != NULL; rwPtr++)
		{
			if (strcmp(wordString, rwPtr->string) == 0)
			{
				curToken = rwPtr->tokenCode;
				return(1);
			}
		}
	}
	return(0);
}

//***************************************************************************
// INITIALIZATION routines
//***************************************************************************

void getChar();

void initScanner (char* fileName) {

	//----------------------------------
	// 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;

	//---------------------------------------
	// Now, let's open the ABL source file...
	if (fileName)
	{
		sourceFile = new File;
		if (sourceFile->open(fileName) != NO_ERR)
		{
			syntaxError(ABL_ERR_SYNTAX_SOURCE_FILE_OPEN);
			curToken = TKN_ERROR;
		}

		sourceBuffer[0] = NULL;
		bufferp = sourceBuffer;
		getChar();
	}
}

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

void quitScanner (void) {

	sourceFile->close();

	delete sourceFile;
	sourceFile = NULL;
}

//***************************************************************************
// CHARACTER routines
//***************************************************************************

void skipLineComment (void) {

	//-------------------------------------------------------------------
	// Just blow off the rest of the current source line we've read in...
	*bufferp = NULL;
	curChar = ' ';
}

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

void skipBlockComment (void) {

	//----------------------------------------------------
	// Snag the asterisk, then continue until we encounter
	// the closing "*/"...
	DumbGetCharOn = true;
	getChar();
	getChar();
	do {
		while ((curChar != '*') && (curChar != CHR_EOF) && (curChar != CHAR_EOF))
			getChar();
		if ((curChar == CHAR_EOF) || (curChar == CHR_EOF)){
			syntaxError(ABL_ERR_SYNTAX_UNEXPECTED_EOF);
			curToken = TKN_ERROR;
		}
		getChar();
	} while (curChar != '/');
	curChar = ' ';
	DumbGetCharOn = false;
}

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

void skipBlanks (void) {

	while (curChar == ' ')
		getChar();
}

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

void languageDirective (void) {

	DumbGetCharOn = true;
	getChar();

	char directive[32];
	long directiveLength = 0;
	while ((curChar != ' ') && (curChar != '\n')  && (curChar != '\r') && (directiveLength < 31)) {
		directive[directiveLength++] = curChar;
		getChar();
	}
	directive[directiveLength] = NULL;
	_strlwr(directive);

	if (strcmp(directive, "include") == 0) {
		//---------------------
		// INCLUDE directive...
		getChar();
		if (curChar == '"') {
			getChar();
			char fileName[128];
			long fileNameLength = 0;
			while ((curChar != '"') && (fileNameLength < 127)) {
				fileName[fileNameLength++] = curChar;
				getChar();
			}
			fileName[fileNameLength] = NULL;
			//-----------------------------------------------------------------------
			// The language directive should be on a line by itself, since everything
			// after the directive on this line is ignored...
			DumbGetCharOn = false;
			//----------------------------------------------------
			// Now that we have the file name, let's include it...
			long openErr = NO_ERR;
			if ((openErr = openSourceFile(fileName)) != NO_ERR) {
				syntaxError(ABL_ERR_SYNTAX_SOURCE_FILE_OPEN);
				curToken = TKN_ERROR;
			}
			}
		else {
			syntaxError(ABL_ERR_SYNTAX_BAD_LANGUAGE_DIRECTIVE_USAGE);
			curToken = TKN_ERROR;
		}
		}
	else if (strcmp(directive, "include_") == 0) {
		//---------------------
		// INCLUDE directive...
		getChar();
		if (curChar == '"') {
			getChar();
			char fileName[128];
			long fileNameLength = 0;
			while ((curChar != '"') && (fileNameLength < 127)) {
				fileName[fileNameLength++] = curChar;
				getChar();
			}
			fileName[fileNameLength] = NULL;
			//-----------------------------------------------------------------------
			// The language directive should be on a line by itself, since everything
			// after the directive on this line is ignored...
			DumbGetCharOn = false;
			//----------------------------------------------------
			// Now that we have the file name, let's include it...
			long openErr = NO_ERR;

			//---------------------------------------
			// What's the current module's directory?
			char fullPath[255];
			long curChar = strlen(SourceFiles[0]);
			while ((curChar > -1) && (SourceFiles[0][curChar] != '\\'))
				curChar--;
			if (curChar == -1)
				strcpy(fullPath, fileName);
			else {
				strcpy(fullPath, SourceFiles[0]);
				fullPath[curChar + 1] = NULL;
				strcat(fullPath, fileName);
			}
			if ((openErr = openSourceFile(fullPath)) != NO_ERR) {
				syntaxError(ABL_ERR_SYNTAX_SOURCE_FILE_OPEN);
				curToken = TKN_ERROR;
			}
			}
		else if (curChar == '<') {
			getChar();
			char fileName[128];
			long fileNameLength = 0;
			while ((curChar != '>') && (fileNameLength < 127)) {
				fileName[fileNameLength++] = curChar;
				getChar();
			}
			fileName[fileNameLength] = NULL;
			//-----------------------------------------------------------------------
			// The language directive should be on a line by itself, since everything
			// after the directive on this line is ignored...
			DumbGetCharOn = false;
			//----------------------------------------------------
			// Now that we have the file name, let's include it...
			long openErr = NO_ERR;

			//---------------------------------------
			// Ignore the modules directory, since the path should be relative to working directory
			if ((openErr = openSourceFile(fileName)) != NO_ERR) {
				syntaxError(ABL_ERR_SYNTAX_SOURCE_FILE_OPEN);
				curToken = TKN_ERROR;
			}
			}
		else {
			syntaxError(ABL_ERR_SYNTAX_BAD_LANGUAGE_DIRECTIVE_USAGE);
			curToken = TKN_ERROR;
		}
		}
	else if (strcmp(directive, "assert_on") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		AssertEnabled = true;
		}
	else if (strcmp(directive, "assert_off") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		AssertEnabled = false;
		}
	else if (strcmp(directive, "print_on") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		PrintEnabled = true;
		}
	else if (strcmp(directive, "print_off") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		PrintEnabled = false;
		}
	else if (strcmp(directive, "stringfuncs_on") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		StringFunctionsEnabled = true;
		}
	else if (strcmp(directive, "stringfuncs_off") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		StringFunctionsEnabled = false;
		}
	else if (strcmp(directive, "debug_on") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		DebugCodeEnabled = true;
		}
	else if (strcmp(directive, "debug_off") == 0) {
		DumbGetCharOn = false;
		curChar = ' ';
		DebugCodeEnabled = false;
		}
	else if (strcmp(directive, "debug_start") == 0) {
		bool stillCutting = true;
		if (!DebugCodeEnabled) {
			long nestedLevel = 1;
			getChar();
//			getChar();
			do {
				while ((curChar != '#') && (curChar != CHR_EOF))
					getChar();
				if (curChar == '#') {
					char directive2[32];
					long directiveLength = 0;
					while ((curChar != ' ') && (curChar != '\n')  && (curChar != '\r') && (directiveLength < 31)) {
						directive2[directiveLength++] = curChar;
						getChar();
					}
					directive2[directiveLength] = NULL;
					_strlwr(directive2);
					if (strcmp(directive2, "#debug_start") == 0)
						nestedLevel++;
					else if (strcmp(directive2, "#debug_end") == 0) {
						nestedLevel--;
						if (nestedLevel == 0)
							stillCutting = false;
					}
					}
				else if (curChar == CHR_EOF) {
					syntaxError(ABL_ERR_SYNTAX_UNEXPECTED_EOF);
					curToken = TKN_ERROR;
					}
				else
					getChar();
			} while (stillCutting);
		}
		curChar = ' ';
		DumbGetCharOn = false;
		}
	else if (strcmp(directive, "debug_end") == 0) {
		curChar = ' ';
		DumbGetCharOn = false;
		}
	else {
		syntaxError(ABL_ERR_SYNTAX_UNKNOWN_LANGUAGE_DIRECTIVE);
		curToken = TKN_ERROR;
	}
}

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

void getChar (void) {

	if (*bufferp == NULL) {
		if (!getSourceLine()) {
			if (NumOpenFiles > 1) {
				closeSourceFile();
				return;
				}
			else {
				curChar = CHAR_EOF;
				return;
			}
		}
		bufferp = sourceBuffer;
		bufferOffset = 0;
	}

	curChar = *bufferp;
	bufferp++;

	if (DumbGetCharOn) {
		bufferOffset++;
		return;
	}

	//-------------------
	// Special Characters
	switch (curChar) {
		case '\t':
			// TAB
			bufferOffset += TAB_SIZE - bufferOffset % TAB_SIZE;
			curChar = ' ';
			break;
		case '\n':
		case '\r':
			// NEWLINE
			bufferOffset++;
			curChar = ' ';
			break;
		case '/':
			if (*bufferp == '/')
				skipLineComment();
			else if (*bufferp == '*')
				skipBlockComment();
			else
				bufferOffset++;
			break;
		case '#':
			languageDirective();
			break;
		default:
			bufferOffset++;
	}
}

//***************************************************************************
// TOKEN routines
//***************************************************************************
#pragma warning (disable : 4244)
void downShiftWord (void) {

	long offset = 'a' - 'A';
	char* wp = wordString;
	char* tp = tokenString;

	long checkLengthWord = strlen(wordString);
	long checkLengthToken = strlen(tokenString);

	if ((checkLengthWord >= MAXLEN_TOKENSTRING) || (checkLengthToken >= MAXLEN_TOKENSTRING))
		ABL_Fatal(-1," Boy did Glenn screw the pooch here!! ");

	do {
		*wp = (char) ((*tp >= 'A') && (*tp <= 'Z')) ? (*tp + (char)offset) : *tp;
		wp++;
		tp++;
	} while (*tp != NULL);
	*wp = NULL;
}
#pragma warning (default: 4244)
//***************************************************************************

void getToken(void) {

	skipBlanks();
	tokenp = tokenString;

	switch (calcCharCode(curChar)) {
		case CHR_LETTER:
			getWord();
			break;
		case CHR_DIGIT:
			getNumber();
			break;
		case CHR_DQUOTE:
			getString();
			break;
		case CHR_EOF:
			curToken = TKN_EOF;
			break;
		default:
			getSpecial();
			break;
	}

	if (blockFlag)
		crunchToken();
}

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

void getWord (void) {

	while ((calcCharCode(curChar) == CHR_LETTER) || (calcCharCode(curChar) == CHR_DIGIT) || (curChar == '_')) {
		*tokenp = curChar;
		tokenp++;
		getChar();
	}

	*tokenp = NULL;
	downShiftWord();

	TokenCodeType endToken;
	if (parsingFSM)
		endToken = TKN_END_FSM;
	else
		endToken = ((CurLibrary == NULL) ? TKN_END_MODULE : TKN_END_LIBRARY);
	if (curChar == '.') {
		//-------------------------------------------------------------------
		// If we have an "endmodule" string, stop. Otherwise, keep reading...
		if (strcmp(wordString, TokenStrings[endToken])) {
			*tokenp = curChar;
			tokenp++;
			getChar();
			while ((calcCharCode(curChar) == CHR_LETTER) || (calcCharCode(curChar) == CHR_DIGIT) || (curChar == '_')) {
				*tokenp = curChar;
				tokenp++;
				getChar();
			}
			*tokenp = NULL;
			downShiftWord();
		}
	}


	if (!isReservedWord())
		curToken = TKN_IDENTIFIER;
}

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

void accumulateValue (float* valuePtr, SyntaxErrorType errCode) {

	float value = *valuePtr;

	//--------------------------------------------
	// Make sure the first character is a digit...
	if (calcCharCode(curChar) != CHR_DIGIT) {
		syntaxError(errCode);
		curToken = TKN_ERROR;
		return;
	}

	do {
		*tokenp = curChar;
		tokenp++;
		if (++digitCount <= MAX_DIGIT_COUNT)
			value = 10 * value + (curChar - '0');
		else
			countError = true;

		getChar();
	} while (calcCharCode(curChar) == CHR_DIGIT);

	*valuePtr = value;
}

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

void getNumber (void) {

	long wholeCount = 0;
	long decimalOffset = 0;
	char exponentSign = '+';
	long exponent = 0;
	float numberValue = (float)0.0;
	float exponentValue = (float)0.0;
	digitCount = 0;
	countError = false;
	curToken = TKN_NONE;

	//------------------------------------
	// Assume it's an integer, at first...
	curLiteral.type = LIT_INTEGER;

	accumulateValue(&numberValue, ABL_ERR_SYNTAX_INVALID_NUMBER);
	if (curToken == TKN_ERROR)
		return;
	wholeCount = digitCount;

	if (curChar == '.') {
		getChar();
		curLiteral.type = LIT_REAL;
		*tokenp = '.';
		tokenp++;

		accumulateValue(&numberValue, ABL_ERR_SYNTAX_INVALID_FRACTION);
		if (curToken == TKN_ERROR)
			return;
		decimalOffset = wholeCount - digitCount;
	}

	if ((curChar == 'E') || (curChar == 'e')) {
		curLiteral.type = LIT_REAL;
		*tokenp = curChar;
		tokenp++;

		//-------------------
		// Any exponent sign?
		if ((curChar == '+') || (curChar == '-')) {
			*tokenp = exponentSign = curChar;
			tokenp++;
			getChar();
		}

		accumulateValue(&exponentValue, ABL_ERR_SYNTAX_INVALID_EXPONENT);
		if (curToken == TKN_ERROR)
			return;
		if (exponentSign == '-')
			exponentValue = -exponentValue;
	}

	//-----------------
	// Too many digits?
	if (countError) {
		syntaxError(ABL_ERR_SYNTAX_TOO_MANY_DIGITS);
		curToken = TKN_ERROR;
		return;
	}

	//------------------------------------------------------------
	// Adjust number's value using decimal offset and the exponent
	exponent = (long) (exponentValue + ((float) decimalOffset));
	if ((exponent + wholeCount < -MAX_EXPONENT) || (exponent + wholeCount > MAX_EXPONENT)) {
		syntaxError(ABL_ERR_SYNTAX_REAL_OUT_OF_RANGE);
		curToken = TKN_ERROR;
		return;
	}

	if (exponent != 0)
		numberValue *= (float) pow(10.0, (double)exponent);

	//---------------------------
	// Set the literal's value...
	if (curLiteral.type == LIT_INTEGER) {
		if (((long)numberValue < -MAX_INTEGER) || ((long)numberValue > MAX_INTEGER)) {
			syntaxError(ABL_ERR_SYNTAX_INTEGER_OUT_OF_RANGE);
			curToken = TKN_ERROR;
			return;
		}
		curLiteral.value.integer = (long)numberValue;
		}
	else
		curLiteral.value.real = numberValue;

	*tokenp = NULL;
	curToken = TKN_NUMBER;
}

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

//---------------------------------------------------
// NOTE: Currently, ABL does not have STRING types...

void getString (void) {

	char* stringPtr = curLiteral.value.string;
	*tokenp = '\"';
	getChar();

	while (curChar != CHAR_EOF) {
		if (curChar == '\"')
			break;
		*tokenp = curChar;
		tokenp++;
		*stringPtr = curChar;
		stringPtr++;
		getChar();
	}
	curChar = ' ';

	*tokenp = NULL;
	*stringPtr = NULL;
	curToken = TKN_STRING;
	curLiteral.type = LIT_STRING;
}

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

void getSpecial (void) {

	*tokenp = curChar;
	tokenp++;

	switch (curChar) {
//		case '^':
//			curToken = TKN_UPARROW;
//			getChar();
//			break;
		case '*':
			curToken = TKN_STAR;
			getChar();
			break;
		case '(':
			curToken = TKN_LPAREN;
			getChar();
			break;
		case ')':
			curToken = TKN_RPAREN;
			getChar();
			break;
		case '-':
			curToken = TKN_MINUS;
			getChar();
			break;
		case '+':
			curToken = TKN_PLUS;
			getChar();
			break;
		case '[':
			curToken = TKN_LBRACKET;
			getChar();
			break;
		case ']':
			curToken = TKN_RBRACKET;
			getChar();
			break;
		case ';':
			curToken = TKN_SEMICOLON;
			getChar();
			break;
		case '@':
			curToken = TKN_REF;
			getChar();
			break;
		case '#':
			curToken = TKN_POUND;
			getChar();
			break;
// NOTE: For now, let's use the BEGIN and END keywords instead of braces.
// If popular demand is to get rid of the begin/end keywords and switch
// to braces, we can do so...
//		case '{':
//			curToken = TKN_BEGIN;
//			getChar();
//			break;
//		case '}':
//			curToken = TKN_END;
//			getChar();
//			break;
		case ',':
			curToken = TKN_COMMA;
			getChar();
			break;
		case '/':
			//-----------------------------------------------
			// In theory :), we shouldn't have to worry about
			// comments here, as getChar syphons them out...
			curToken = TKN_FSLASH;
			getChar();
			break;
		case '=':
			getChar();
			if (curChar == '=') {
				*tokenp = '=';
				tokenp++;
				curToken = TKN_EQUALEQUAL;
				getChar();
				}
			else
				curToken = TKN_EQUAL;
			break;
		case '<':
			getChar();
			if (curChar == '=') {
				*tokenp = '=';
				tokenp++;
				curToken = TKN_LE;
				getChar();
				}
			else if (curChar == '>') {
				*tokenp = '>';
				tokenp++;
				curToken = TKN_NE;
				getChar();
				}
			else
				curToken = TKN_LT;
			break;
		case '>':
			getChar();
			if (curChar == '=') {
				*tokenp = '=';
				tokenp++;
				curToken = TKN_GE;
				getChar();
				}
			else
				curToken = TKN_GT;
			break;
		case '.':
			curToken = TKN_PERIOD;
			getChar();
			break;
		case ':':
			curToken = TKN_COLON;
			getChar();
			break;
		default:
			curToken = TKN_ERROR;
			getChar();
			break;
	}

	*tokenp = NULL;
}

//***************************************************************************
// TOKEN TESTER routines
//***************************************************************************

bool tokenIn (TokenCodeType* tokenList) {

	if (!tokenList)
		return(false);

	for (TokenCodeType* tp = &tokenList[0]; *tp; tp++)
		if (curToken == *tp)
			return(true);
	return(false);
}

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

void synchronize (TokenCodeType* tokenList1,
				  TokenCodeType* tokenList2,
				  TokenCodeType* tokenList3) {

	bool badLists = (!tokenIn(tokenList1) && !tokenIn(tokenList2) && !tokenIn(tokenList3));

	if (badLists) {
		syntaxError((curToken == TKN_EOF) ? ABL_ERR_SYNTAX_UNEXPECTED_EOF : ABL_ERR_SYNTAX_UNEXPECTED_TOKEN);

		//----------------------------------------------
		// Now, we need to re-sync by skipping tokens...
		while (!tokenIn(tokenList1) &&
			   !tokenIn(tokenList2) &&
			   !tokenIn(tokenList3) &&
			   (curToken != TKN_EOF))
			getToken();
	}
}

//***************************************************************************
// SOURCE FILE routines
//***************************************************************************

bool getSourceLine (void) {

	if (!sourceFile->eof())
	{
		long numChars = sourceFile->readLineEx((MemoryPtr)sourceBuffer, MAXLEN_SOURCELINE);
    if (numChars == 0)
			return(false);
		lineNumber++;
		if (printFlag)
		{
			char printBuffer[MAXLEN_SOURCELINE + 9];
			sprintf(printBuffer, "%4d %d: %s", lineNumber, level, sourceBuffer);
			printLine(printBuffer);
		}
		return(true);
	}
	else
		return(false);
}

//---------------------------------------------------------------------------

long openSourceFile (char* sourceFileName) {

	//---------------------------------------
	// Now, let's open the ABL source file...
	if (!sourceFileName)
		return(-1);

	if (NumOpenFiles == MAX_INCLUDE_DEPTH)
		return(-2);

	if (NumSourceFiles == MAX_SOURCE_FILES)
		return(-3);

	FilePtr sFile = NULL;
	sFile = new File;

	sFile->open(sourceFileName);

	strcpy(SourceFiles[NumSourceFiles], sourceFileName);
	FileNumber = NumSourceFiles++;

	sourceFile = sFile;
	strcpy(openFiles[NumOpenFiles].fileName, sourceFileName);
	openFiles[NumOpenFiles].filePtr = sFile;
	openFiles[NumOpenFiles].fileNumber = (unsigned char) FileNumber;
	openFiles[NumOpenFiles].lineNumber = 0;
	if (NumOpenFiles > 0)
		openFiles[NumOpenFiles - 1].lineNumber = lineNumber;
	NumOpenFiles++;

	lineNumber = 0;
	sourceBuffer[0] = NULL;
	bufferp = sourceBuffer;
	getChar();

	return(NO_ERR);
}

//---------------------------------------------------------------------------

long closeSourceFile (void) {

	if (NumOpenFiles == 0)
		return(-1);

	sourceFile->close();

	delete sourceFile;
	sourceFile = NULL;

	openFiles[NumOpenFiles].filePtr = NULL;
	NumOpenFiles--;

	if (NumOpenFiles > 0) {
		sourceFile = openFiles[NumOpenFiles - 1].filePtr;
		FileNumber = openFiles[NumOpenFiles - 1].fileNumber;
		lineNumber = openFiles[NumOpenFiles - 1].lineNumber;
	}

	return(NO_ERR);
}

//***************************************************************************
// PRINTOUT routines
//***************************************************************************

void printLine (char* line) {

	if (++lineCount > MAX_LINES_PER_PAGE) {
		printPageHeader();
		lineCount = 1;
	}

	char* saveChPtr = NULL;
	char saveCh = ' ';
	if (strlen(line) > MAXLEN_PRINTLINE) {
		saveCh = line[MAXLEN_PRINTLINE];
		saveChPtr = &line[MAXLEN_PRINTLINE];
		*saveChPtr = NULL;
	}
	printf("%s", line);
	if (saveChPtr)
		*saveChPtr = saveCh;
}

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

void initPageHeader (char* fileName) {

	//--------------------------
	// Save the source file name
	strncpy(sourceName, fileName, MAXLEN_FILENAME - 1);

	//-------------------------------
	// Save the current time and date
	time_t timer;
	time(&timer);
	strcpy(date, asctime(localtime(&timer)));
}

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

void printPageHeader (void) {

	//putchar(CHAR_FORMFEED);
	printf("Page %d   %s   %s\n\n", ++pageNumber, sourceName, date);
}

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

}