Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1230 lines
28 KiB
C++
1230 lines
28 KiB
C++
//***************************************************************************
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
} |