acc/token.c
Randy Heit c447410ad8 Don't convert string constants to numbers for #define
- Fixed: You could not #define a string for use in a library, because it
  was converted to a number at definition time and not at use time. This
  is no longer the case. Symbolic constants now store the string in its
  original form, and they now expand into the string when encountered
  instead of their string table index.
2014-07-21 23:32:57 -05:00

1574 lines
33 KiB
C

//**************************************************************************
//**
//** token.c
//**
//**************************************************************************
// HEADER FILES ------------------------------------------------------------
#if defined(_WIN32) && !defined(_MSC_VER)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#ifdef __NeXT__
#include <libc.h>
#else
#ifndef unix
#include <io.h>
#endif
#include <limits.h>
#include <fcntl.h>
#include <stdlib.h>
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "common.h"
#include "token.h"
#include "error.h"
#include "misc.h"
#include "symbol.h"
#include "parse.h"
// MACROS ------------------------------------------------------------------
#define NON_HEX_DIGIT 255
#define MAX_NESTED_SOURCES 16
// TYPES -------------------------------------------------------------------
typedef enum
{
CHR_EOF,
CHR_LETTER,
CHR_NUMBER,
CHR_QUOTE,
CHR_SPECIAL
} chr_t;
typedef struct
{
char *name;
char *start;
char *end;
char *position;
int line;
boolean incLineNumber;
boolean imported;
enum ImportModes prevMode;
char lastChar;
} nestInfo_t;
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static int SortKeywords(const void *a, const void *b);
static void SetLocalIncludePath(char *sourceName);
static int PopNestedSource(enum ImportModes *prevMode);
static void ProcessLetterToken(void);
static void ProcessNumberToken(void);
static void EvalFixedConstant(int whole);
static void EvalHexConstant(void);
static void EvalRadixConstant(void);
static int DigitValue(char digit, int radix);
static void ProcessQuoteToken(void);
static void ProcessSpecialToken(void);
static boolean CheckForKeyword(void);
static boolean CheckForLineSpecial(void);
static boolean CheckForConstant(void);
static void NextChr(void);
static void SkipComment(void);
static void SkipCPPComment(void);
static void BumpMasterSourceLine(char Chr, boolean clear); // master line - Ty 07jan2000
static char *AddFileName(const char *name);
static int OctalChar();
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
tokenType_t tk_Token;
int tk_Line;
int tk_Number;
char *tk_String;
int tk_SpecialValue;
int tk_SpecialArgCount;
char *tk_SourceName;
int tk_IncludedLines;
boolean forSemicolonHack;
char MasterSourceLine[MAX_STATEMENT_LENGTH+1]; // master line - Ty 07jan2000
int MasterSourcePos; // master position - Ty 07jan2000
int PrevMasterSourcePos; // previous master position - RH 09feb2000
boolean ClearMasterSourceLine; // master clear flag - Ty 07jan2000
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static char Chr;
static char *FileStart;
static char *FilePtr;
static char *FileEnd;
static boolean SourceOpen;
static char ASCIIToChrCode[256];
static byte ASCIIToHexDigit[256];
static char TokenStringBuffer[MAX_QUOTED_LENGTH];
static nestInfo_t OpenFiles[MAX_NESTED_SOURCES];
static boolean AlreadyGot;
static int NestDepth;
static boolean IncLineNumber;
static char *FileNames;
static size_t FileNamesLen, FileNamesMax;
// Pascal 12/11/08
// Include paths. Lowest is searched first.
// Include path 0 is always set to the directory of the file being parsed.
static char IncludePaths[MAX_INCLUDE_PATHS][MAX_FILE_NAME_LENGTH];
static int NumIncludePaths;
static struct keyword_s
{
char *name;
tokenType_t token;
} Keywords[] =
{
{ "break", TK_BREAK },
{ "case", TK_CASE },
{ "const", TK_CONST },
{ "continue", TK_CONTINUE },
{ "default", TK_DEFAULT },
{ "define", TK_DEFINE },
{ "do", TK_DO },
{ "else", TK_ELSE },
{ "for", TK_FOR },
{ "goto", TK_GOTO },
{ "if", TK_IF },
{ "include", TK_INCLUDE },
{ "int", TK_INT },
{ "open", TK_OPEN },
{ "print", TK_PRINT },
{ "printbold", TK_PRINTBOLD },
{ "log", TK_LOG },
{ "hudmessage", TK_HUDMESSAGE },
{ "hudmessagebold", TK_HUDMESSAGEBOLD },
{ "restart", TK_RESTART },
{ "script", TK_SCRIPT },
{ "special", TK_SPECIAL },
{ "str", TK_STR },
{ "suspend", TK_SUSPEND },
{ "switch", TK_SWITCH },
{ "terminate", TK_TERMINATE },
{ "until", TK_UNTIL },
{ "void", TK_VOID },
{ "while", TK_WHILE },
{ "world", TK_WORLD },
{ "global", TK_GLOBAL },
// [BC] Start Skulltag tokens.
{ "respawn", TK_RESPAWN },
{ "death", TK_DEATH },
{ "enter", TK_ENTER },
{ "pickup", TK_PICKUP },
{ "bluereturn", TK_BLUERETURN },
{ "redreturn", TK_REDRETURN },
{ "whitereturn", TK_WHITERETURN },
// [BC] End Skulltag tokens.
{ "nocompact", TK_NOCOMPACT },
{ "lightning", TK_LIGHTNING },
{ "createtranslation", TK_CREATETRANSLATION },
{ "function", TK_FUNCTION },
{ "return", TK_RETURN },
{ "wadauthor", TK_WADAUTHOR },
{ "nowadauthor", TK_NOWADAUTHOR },
{ "acs_executewait", TK_ACSEXECUTEWAIT },
{ "acs_namedexecutewait", TK_ACSNAMEDEXECUTEWAIT },
{ "encryptstrings", TK_ENCRYPTSTRINGS },
{ "import", TK_IMPORT },
{ "library", TK_LIBRARY },
{ "libdefine", TK_LIBDEFINE },
{ "bool", TK_BOOL },
{ "net", TK_NET },
{ "clientside", TK_CLIENTSIDE }, // [BB]
{ "disconnect", TK_DISCONNECT },
{ "unloading", TK_UNLOADING },
{ "static", TK_STATIC },
{ "strparam", TK_STRPARAM_EVAL }, // [FDARI]
{ "strcpy", TK_STRCPY }, // [FDARI]
};
#define NUM_KEYWORDS (sizeof(Keywords)/sizeof(Keywords[0]))
// CODE --------------------------------------------------------------------
//==========================================================================
//
// TK_Init
//
//==========================================================================
void TK_Init(void)
{
int i;
for(i = 0; i < 256; i++)
{
ASCIIToChrCode[i] = CHR_SPECIAL;
ASCIIToHexDigit[i] = NON_HEX_DIGIT;
}
for(i = '0'; i <= '9'; i++)
{
ASCIIToChrCode[i] = CHR_NUMBER;
ASCIIToHexDigit[i] = i-'0';
}
for(i = 'A'; i <= 'F'; i++)
{
ASCIIToHexDigit[i] = 10+(i-'A');
}
for(i = 'a'; i <= 'f'; i++)
{
ASCIIToHexDigit[i] = 10+(i-'a');
}
for(i = 'A'; i <= 'Z'; i++)
{
ASCIIToChrCode[i] = CHR_LETTER;
}
for(i = 'a'; i <= 'z'; i++)
{
ASCIIToChrCode[i] = CHR_LETTER;
}
ASCIIToChrCode[ASCII_QUOTE] = CHR_QUOTE;
ASCIIToChrCode[ASCII_UNDERSCORE] = CHR_LETTER;
ASCIIToChrCode[EOF_CHARACTER] = CHR_EOF;
tk_String = TokenStringBuffer;
IncLineNumber = FALSE;
tk_IncludedLines = 0;
NumIncludePaths = 1; // the first path is always the parsed file path - Pascal 12/11/08
SourceOpen = FALSE;
*MasterSourceLine = '\0'; // master line - Ty 07jan2000
MasterSourcePos = 0; // master position - Ty 07jan2000
ClearMasterSourceLine = TRUE; // clear the line to start
qsort (Keywords, NUM_KEYWORDS, sizeof(Keywords[0]), SortKeywords);
FileNames = MS_Alloc(4096, ERR_OUT_OF_MEMORY);
FileNamesLen = 0;
FileNamesMax = 4096;
}
//==========================================================================
//
// SortKeywords
//
//==========================================================================
static int SortKeywords(const void *a, const void *b)
{
return strcmp (((struct keyword_s *)a)->name,
((struct keyword_s *)b)->name);
}
//==========================================================================
//
// TK_OpenSource
//
//==========================================================================
void TK_OpenSource(char *fileName)
{
int size;
TK_CloseSource();
size = MS_LoadFile(fileName, &FileStart);
tk_SourceName = AddFileName(fileName);
SetLocalIncludePath(fileName);
SourceOpen = TRUE;
FileEnd = FileStart+size;
FilePtr = FileStart;
tk_Line = 1;
tk_Token = TK_NONE;
AlreadyGot = FALSE;
NestDepth = 0;
NextChr();
}
//==========================================================================
//
// AddFileName
//
//==========================================================================
static char *AddFileName(const char *name)
{
size_t len = strlen(name) + 1;
char *namespot;
if (FileNamesLen + len > FileNamesMax)
{
FileNames = MS_Alloc(FileNamesMax, ERR_OUT_OF_MEMORY);
FileNamesLen = 0;
}
namespot = FileNames + FileNamesLen;
memcpy(namespot, name, len);
FileNamesLen += len;
return namespot;
}
//==========================================================================
//
// TK_AddIncludePath
// This adds an include path with less priority than the ones already added
//
// Pascal 12/11/08
//
//==========================================================================
void TK_AddIncludePath(char *sourcePath)
{
if(NumIncludePaths < MAX_INCLUDE_PATHS)
{
// Add to list
strcpy(IncludePaths[NumIncludePaths], sourcePath);
// Not ending with directory delimiter?
if(!MS_IsDirectoryDelimiter(*(IncludePaths[NumIncludePaths] + strlen(IncludePaths[NumIncludePaths]) - 1)))
{
// Add a directory delimiter to the include path
strcat(IncludePaths[NumIncludePaths], "/");
}
MS_Message(MSG_DEBUG, "Add include path %d: \"%s\"\n", NumIncludePaths, IncludePaths[NumIncludePaths]);
NumIncludePaths++;
}
}
//==========================================================================
//
// TK_AddProgramIncludePath
// Adds an include path for the directory of the executable.
//
//==========================================================================
void TK_AddProgramIncludePath(char *progname)
{
if(NumIncludePaths < MAX_INCLUDE_PATHS)
{
#ifdef _WIN32
#ifdef _MSC_VER
#if _MSC_VER >= 1300
if (_get_pgmptr(&progname) != 0)
{
return;
}
#else
progname = _pgmptr;
#endif
#else
char progbuff[1024];
GetModuleFileName(0, progbuff, sizeof(progbuff));
progbuff[sizeof(progbuff)-1] = '\0';
progname = progbuff;
#endif
#else
char progbuff[PATH_MAX];
if (realpath(progname, progbuff) != NULL)
{
progname = progbuff;
}
#endif
strcpy(IncludePaths[NumIncludePaths], progname);
if(MS_StripFilename(IncludePaths[NumIncludePaths]))
{
MS_Message(MSG_DEBUG, "Program include path is %d: \"%s\"\n", NumIncludePaths, IncludePaths[NumIncludePaths]);
NumIncludePaths++;
}
}
}
//==========================================================================
//
// SetLocalIncludePath
// This sets the first include path
//
// Pascal 12/11/08
//
//==========================================================================
static void SetLocalIncludePath(char *sourceName)
{
strcpy(IncludePaths[0], sourceName);
if(MS_StripFilename(IncludePaths[0]) == NO)
{
IncludePaths[0][0] = 0;
}
}
//==========================================================================
//
// TK_Include
//
//==========================================================================
void TK_Include(char *fileName)
{
char sourceName[MAX_FILE_NAME_LENGTH];
int size, i;
nestInfo_t *info;
boolean foundfile = FALSE;
MS_Message(MSG_DEBUG, "*Including %s\n", fileName);
if(NestDepth == MAX_NESTED_SOURCES)
{
ERR_Exit(ERR_INCL_NESTING_TOO_DEEP, YES, fileName);
}
info = &OpenFiles[NestDepth++];
info->name = tk_SourceName;
info->start = FileStart;
info->end = FileEnd;
info->position = FilePtr;
info->line = tk_Line;
info->incLineNumber = IncLineNumber;
info->lastChar = Chr;
info->imported = NO;
// Pascal 30/11/08
// Handle absolute paths
if(MS_IsPathAbsolute(fileName))
{
#if defined(_WIN32) || defined(__MSDOS__)
sourceName[0] = '\0';
if(MS_IsDirectoryDelimiter(fileName[0]))
{
// The source file is absolute for the drive, but does not
// specify a drive. Use the path for the current file to
// get the drive letter, if it has one.
if(IncludePaths[0][0] != '\0' && IncludePaths[0][1] == ':')
{
sourceName[0] = IncludePaths[0][0];
sourceName[1] = ':';
sourceName[2] = '\0';
}
}
strcat(sourceName, fileName);
#else
strcpy(sourceName, fileName);
#endif
foundfile = MS_FileExists(sourceName);
}
else
{
// Pascal 12/11/08
// Find the file in the include paths
for(i = 0; i < NumIncludePaths; i++)
{
strcpy(sourceName, IncludePaths[i]);
strcat(sourceName, fileName);
if(MS_FileExists(sourceName))
{
foundfile = TRUE;
break;
}
}
}
if(!foundfile)
{
ERR_ErrorAt(tk_SourceName, tk_Line);
ERR_Exit(ERR_CANT_FIND_INCLUDE, YES, fileName, tk_SourceName, tk_Line);
}
MS_Message(MSG_DEBUG, "*Include file found at %s\n", sourceName);
// Now change the first include path to the file directory
SetLocalIncludePath(sourceName);
tk_SourceName = AddFileName(sourceName);
size = MS_LoadFile(tk_SourceName, &FileStart);
FileEnd = FileStart+size;
FilePtr = FileStart;
tk_Line = 1;
IncLineNumber = FALSE;
tk_Token = TK_NONE;
AlreadyGot = FALSE;
BumpMasterSourceLine('x',TRUE); // dummy x
NextChr();
}
//==========================================================================
//
// TK_Import
//
//==========================================================================
void TK_Import(char *fileName, enum ImportModes prevMode)
{
TK_Include (fileName);
OpenFiles[NestDepth - 1].imported = YES;
OpenFiles[NestDepth - 1].prevMode = prevMode;
ImportMode = IMPORT_Importing;
}
//==========================================================================
//
// PopNestedSource
//
//==========================================================================
static int PopNestedSource(enum ImportModes *prevMode)
{
nestInfo_t *info;
MS_Message(MSG_DEBUG, "*Leaving %s\n", tk_SourceName);
free(FileStart);
SY_FreeConstants(NestDepth);
tk_IncludedLines += tk_Line;
info = &OpenFiles[--NestDepth];
tk_SourceName = info->name;
FileStart = info->start;
FileEnd = info->end;
FilePtr = info->position;
tk_Line = info->line;
IncLineNumber = info->incLineNumber;
Chr = info->lastChar;
tk_Token = TK_NONE;
AlreadyGot = FALSE;
// Pascal 12/11/08
// Set the first include path back to this file directory
SetLocalIncludePath(tk_SourceName);
*prevMode = info->prevMode;
return info->imported ? 2 : 0;
}
//==========================================================================
//
// TK_CloseSource
//
//==========================================================================
void TK_CloseSource(void)
{
int i;
if(SourceOpen)
{
free(FileStart);
for(i = 0; i < NestDepth; i++)
{
free(OpenFiles[i].start);
}
SourceOpen = FALSE;
}
}
//==========================================================================
//
// TK_GetDepth
//
//==========================================================================
int TK_GetDepth(void)
{
return NestDepth;
}
//==========================================================================
//
// TK_NextToken
//
//==========================================================================
tokenType_t TK_NextToken(void)
{
enum ImportModes prevMode;
boolean validToken;
if(AlreadyGot == TRUE)
{
int t = MasterSourcePos;
MasterSourcePos = PrevMasterSourcePos;
PrevMasterSourcePos = t;
AlreadyGot = FALSE;
return tk_Token;
}
tk_String = TokenStringBuffer;
validToken = NO;
PrevMasterSourcePos = MasterSourcePos;
do
{
while(Chr == ASCII_SPACE)
{
NextChr();
}
switch(ASCIIToChrCode[(byte)Chr])
{
case CHR_EOF:
tk_Token = TK_EOF;
break;
case CHR_LETTER:
ProcessLetterToken();
break;
case CHR_NUMBER:
ProcessNumberToken();
break;
case CHR_QUOTE:
ProcessQuoteToken();
break;
default:
ProcessSpecialToken();
break;
}
if(tk_Token == TK_STARTCOMMENT)
{
SkipComment();
}
else if(tk_Token == TK_CPPCOMMENT)
{
SkipCPPComment();
}
else if((tk_Token == TK_EOF) && (NestDepth > 0))
{
if (PopNestedSource(&prevMode))
{
ImportMode = prevMode;
if(!ExporterFlagged)
{
ERR_Exit(ERR_EXPORTER_NOT_FLAGGED, NO);
}
SY_ClearShared();
}
}
else
{
validToken = YES;
}
} while(validToken == NO);
return tk_Token;
}
//==========================================================================
//
// TK_NextCharacter
//
//==========================================================================
int TK_NextCharacter(void)
{
int c;
while(Chr == ASCII_SPACE)
{
NextChr();
}
c = (int)Chr;
if(c == EOF_CHARACTER)
{
c = -1;
}
NextChr();
return c;
}
//==========================================================================
//
// TK_SkipPast
//
//==========================================================================
void TK_SkipPast(tokenType_t token)
{
while (tk_Token != token && tk_Token != TK_EOF)
{
TK_NextToken();
}
TK_NextToken();
}
//==========================================================================
//
// TK_SkipTo
//
//==========================================================================
void TK_SkipTo(tokenType_t token)
{
while (tk_Token != token && tk_Token != TK_EOF)
{
TK_NextToken();
}
}
//==========================================================================
//
// TK_NextTokenMustBe
//
//==========================================================================
boolean TK_NextTokenMustBe(tokenType_t token, error_t error)
{
if(TK_NextToken() != token)
{
ERR_Error(error, YES);
/*
if(skipToken == TK_EOF)
{
ERR_Finish();
}
else if(skipToken != TK_NONE)
{
TK_SkipPast(skipToken);
}
return NO;
*/
ERR_Finish();
}
return YES;
}
//==========================================================================
//
// TK_TokenMustBe
//
//==========================================================================
boolean TK_TokenMustBe(tokenType_t token, error_t error)
{
if (token == TK_SEMICOLON && forSemicolonHack)
{
token = TK_RPAREN;
}
if(tk_Token != token)
{
ERR_Error(error, YES);
/*
if(skipToken == TK_EOF)
{
ERR_Finish();
}
else if(skipToken != TK_NONE)
{
while(tk_Token != skipToken)
{
TK_NextToken();
}
}
return NO;
*/
ERR_Finish();
}
return YES;
}
//==========================================================================
//
// TK_Member
//
//==========================================================================
boolean TK_Member(tokenType_t *list)
{
int i;
for(i = 0; list[i] != TK_NONE; i++)
{
if(tk_Token == list[i])
{
return YES;
}
}
return NO;
}
//==========================================================================
//
// TK_Undo
//
//==========================================================================
void TK_Undo(void)
{
if(tk_Token != TK_NONE)
{
if (AlreadyGot == FALSE)
{
int t = MasterSourcePos;
MasterSourcePos = PrevMasterSourcePos;
PrevMasterSourcePos = t;
AlreadyGot = TRUE;
}
}
}
//==========================================================================
//
// ProcessLetterToken
//
//==========================================================================
static void ProcessLetterToken(void)
{
int i;
char *text;
i = 0;
text = TokenStringBuffer;
while (ASCIIToChrCode[(byte)Chr] == CHR_LETTER
|| ASCIIToChrCode[(byte)Chr] == CHR_NUMBER)
{
if(++i == MAX_IDENTIFIER_LENGTH)
{
ERR_Error(ERR_IDENTIFIER_TOO_LONG, YES);
}
if(i < MAX_IDENTIFIER_LENGTH)
{
*text++ = Chr;
}
NextChr();
}
*text = 0;
MS_StrLwr(TokenStringBuffer);
if(CheckForKeyword() == FALSE
&& CheckForLineSpecial() == FALSE
&& CheckForConstant() == FALSE)
{
tk_Token = TK_IDENTIFIER;
}
}
//==========================================================================
//
// CheckForKeyword
//
//==========================================================================
static boolean CheckForKeyword(void)
{
int min, max, probe, lexx;
// [RH] Use a binary search
min = 0;
max = NUM_KEYWORDS-1;
probe = NUM_KEYWORDS/2;
while (max - min >= 0)
{
lexx = strcmp(tk_String, Keywords[probe].name);
if(lexx == 0)
{
tk_Token = Keywords[probe].token;
return TRUE;
}
else if(lexx < 0)
{
max = probe-1;
}
else
{
min = probe+1;
}
probe = (max-min)/2+min;
}
return FALSE;
}
//==========================================================================
//
// CheckForLineSpecial
//
//==========================================================================
static boolean CheckForLineSpecial(void)
{
symbolNode_t *sym;
sym = SY_FindGlobal(tk_String);
if(sym == NULL)
{
return FALSE;
}
if(sym->type != SY_SPECIAL)
{
return FALSE;
}
tk_Token = TK_LINESPECIAL;
tk_SpecialValue = sym->info.special.value;
tk_SpecialArgCount = sym->info.special.argCount;
return TRUE;
}
//==========================================================================
//
// CheckForConstant
//
//==========================================================================
static boolean CheckForConstant(void)
{
symbolNode_t *sym;
sym = SY_FindGlobal(tk_String);
if(sym == NULL)
{
return FALSE;
}
if(sym->type != SY_CONSTANT)
{
return FALSE;
}
if(sym->info.constant.strValue != NULL)
{
MS_Message(MSG_DEBUG, "Constant string: %s\n", sym->info.constant.strValue);
tk_Token = TK_STRING;
tk_String = sym->info.constant.strValue;
}
else
{
tk_Token = TK_NUMBER;
tk_Number = sym->info.constant.value;
}
return TRUE;
}
//==========================================================================
//
// ProcessNumberToken
//
//==========================================================================
static void ProcessNumberToken(void)
{
char c;
c = Chr;
NextChr();
if(c == '0' && (Chr == 'x' || Chr == 'X'))
{ // Hexadecimal constant
NextChr();
EvalHexConstant();
return;
}
tk_Number = c-'0';
while(ASCIIToChrCode[(byte)Chr] == CHR_NUMBER)
{
tk_Number = 10*tk_Number+(Chr-'0');
NextChr();
}
if(Chr == '.')
{ // Fixed point
NextChr(); // Skip period
EvalFixedConstant(tk_Number);
return;
}
if(Chr == ASCII_UNDERSCORE)
{
NextChr(); // Skip underscore
EvalRadixConstant();
return;
}
tk_Token = TK_NUMBER;
}
//==========================================================================
//
// EvalFixedConstant
//
//==========================================================================
static void EvalFixedConstant(int whole)
{
double frac;
double divisor;
frac = 0;
divisor = 1;
while(ASCIIToChrCode[(byte)Chr] == CHR_NUMBER)
{
frac = 10*frac+(Chr-'0');
divisor *= 10;
NextChr();
}
tk_Number = (whole<<16)+(int)(65536.0*frac/divisor);
tk_Token = TK_NUMBER;
}
//==========================================================================
//
// EvalHexConstant
//
//==========================================================================
static void EvalHexConstant(void)
{
tk_Number = 0;
while(ASCIIToHexDigit[(byte)Chr] != NON_HEX_DIGIT)
{
tk_Number = (tk_Number<<4)+ASCIIToHexDigit[(byte)Chr];
NextChr();
}
tk_Token = TK_NUMBER;
}
//==========================================================================
//
// EvalRadixConstant
//
//==========================================================================
static void EvalRadixConstant(void)
{
int radix;
int digitVal;
radix = tk_Number;
if(radix < 2 || radix > 36)
{
ERR_Error(ERR_BAD_RADIX_CONSTANT, YES, NULL);
radix = 36;
}
tk_Number = 0;
while((digitVal = DigitValue(Chr, radix)) != -1)
{
tk_Number = radix*tk_Number+digitVal;
NextChr();
}
tk_Token = TK_NUMBER;
}
//==========================================================================
//
// DigitValue
//
// Returns -1 if the digit is not allowed in the specified radix.
//
//==========================================================================
static int DigitValue(char digit, int radix)
{
digit = toupper(digit);
if(digit < '0' || (digit > '9' && digit < 'A') || digit > 'Z')
{
return -1;
}
if(digit > '9')
{
digit = 10+digit-'A';
}
else
{
digit -= '0';
}
if(digit >= radix)
{
return -1;
}
return digit;
}
//==========================================================================
//
// ProcessQuoteToken
//
//==========================================================================
static void ProcessQuoteToken(void)
{
int i;
char *text;
boolean escaped;
i = 0;
escaped = FALSE;
text = TokenStringBuffer;
NextChr();
while(Chr != EOF_CHARACTER)
{
if(Chr == ASCII_QUOTE && escaped == 0) // [JB]
{
break;
}
if(++i == MAX_QUOTED_LENGTH)
{
ERR_Error(ERR_STRING_TOO_LONG, YES, NULL);
}
if(i < MAX_QUOTED_LENGTH)
{
*text++ = Chr;
}
// escape the character after a backslash [JB]
if(Chr == '\\')
escaped ^= (Chr == '\\');
else
escaped = FALSE;
NextChr();
}
*text = 0;
if(Chr == ASCII_QUOTE)
{
NextChr();
}
tk_Token = TK_STRING;
}
//==========================================================================
//
// ProcessSpecialToken
//
//==========================================================================
static void ProcessSpecialToken(void)
{
char c;
c = Chr;
NextChr();
switch(c)
{
case '+':
switch(Chr)
{
case '=':
tk_Token = TK_ADDASSIGN;
NextChr();
break;
case '+':
tk_Token = TK_INC;
NextChr();
break;
default:
tk_Token = TK_PLUS;
break;
}
break;
case '-':
switch(Chr)
{
case '=':
tk_Token = TK_SUBASSIGN;
NextChr();
break;
case '-':
tk_Token = TK_DEC;
NextChr();
break;
default:
tk_Token = TK_MINUS;
break;
}
break;
case '*':
switch(Chr)
{
case '=':
tk_Token = TK_MULASSIGN;
NextChr();
break;
case '/':
tk_Token = TK_ENDCOMMENT;
NextChr();
break;
default:
tk_Token = TK_ASTERISK;
break;
}
break;
case '/':
switch(Chr)
{
case '=':
tk_Token = TK_DIVASSIGN;
NextChr();
break;
case '/':
tk_Token = TK_CPPCOMMENT;
break;
case '*':
tk_Token = TK_STARTCOMMENT;
NextChr();
break;
default:
tk_Token = TK_SLASH;
break;
}
break;
case '%':
if(Chr == '=')
{
tk_Token = TK_MODASSIGN;
NextChr();
}
else
{
tk_Token = TK_PERCENT;
}
break;
case '=':
if(Chr == '=')
{
tk_Token = TK_EQ;
NextChr();
}
else
{
tk_Token = TK_ASSIGN;
}
break;
case '<':
if(Chr == '=')
{
tk_Token = TK_LE;
NextChr();
}
else if(Chr == '<')
{
NextChr();
if(Chr == '=')
{
tk_Token = TK_LSASSIGN;
NextChr();
}
else
{
tk_Token = TK_LSHIFT;
}
}
else
{
tk_Token = TK_LT;
}
break;
case '>':
if(Chr == '=')
{
tk_Token = TK_GE;
NextChr();
}
else if(Chr == '>')
{
NextChr();
if(Chr == '=')
{
tk_Token = TK_RSASSIGN;
NextChr();
}
else
{
tk_Token = TK_RSHIFT;
}
}
else
{
tk_Token = TK_GT;
}
break;
case '!':
if(Chr == '=')
{
tk_Token = TK_NE;
NextChr();
}
else
{
tk_Token = TK_NOT;
}
break;
case '&':
if(Chr == '&')
{
tk_Token = TK_ANDLOGICAL;
NextChr();
}
else if(Chr == '=')
{
tk_Token = TK_ANDASSIGN;
NextChr();
}
else
{
tk_Token = TK_ANDBITWISE;
}
break;
case '|':
if(Chr == '|')
{
tk_Token = TK_ORLOGICAL;
NextChr();
}
else if(Chr == '=')
{
tk_Token = TK_ORASSIGN;
NextChr();
}
else
{
tk_Token = TK_ORBITWISE;
}
break;
case '(':
tk_Token = TK_LPAREN;
break;
case ')':
tk_Token = TK_RPAREN;
break;
case '{':
tk_Token = TK_LBRACE;
break;
case '}':
tk_Token = TK_RBRACE;
break;
case '[':
tk_Token = TK_LBRACKET;
break;
case ']':
tk_Token = TK_RBRACKET;
break;
case ':':
tk_Token = TK_COLON;
break;
case ';':
tk_Token = TK_SEMICOLON;
break;
case ',':
tk_Token = TK_COMMA;
break;
case '.':
tk_Token = TK_PERIOD;
break;
case '#':
tk_Token = TK_NUMBERSIGN;
break;
case '^':
if(Chr == '=')
{
tk_Token = TK_EORASSIGN;
NextChr();
}
else
{
tk_Token = TK_EORBITWISE;
}
break;
case '~':
tk_Token = TK_TILDE;
break;
case '\'':
if(Chr == '\\')
{
NextChr();
switch(Chr)
{
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
tk_Number = OctalChar();
break;
case 'x': case 'X':
NextChr();
EvalHexConstant();
if(Chr != '\'')
{
ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL);
}
NextChr();
break;
case 'a':
tk_Number = '\a';
break;
case 'b':
tk_Number = '\b';
break;
case 't':
tk_Number = '\t';
break;
case 'v':
tk_Number = '\v';
break;
case 'n':
tk_Number = '\n';
break;
case 'f':
tk_Number = '\f';
break;
case 'r':
tk_Number = '\r';
break;
case '\'':
case '\\':
tk_Number = Chr;
break;
default:
ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL);
}
tk_Token = TK_NUMBER;
}
else if(Chr == '\'')
{
ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL);
}
else
{
tk_Number = Chr;
tk_Token = TK_NUMBER;
}
NextChr();
if(Chr != '\'')
{
ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL);
}
NextChr();
break;
default:
ERR_Exit(ERR_BAD_CHARACTER, YES, NULL);
break;
}
}
//==========================================================================
//
// NextChr
//
//==========================================================================
static void NextChr(void)
{
if(FilePtr >= FileEnd)
{
Chr = EOF_CHARACTER;
return;
}
if(IncLineNumber == TRUE)
{
tk_Line++;
IncLineNumber = FALSE;
BumpMasterSourceLine('x',TRUE); // dummy x
}
Chr = *FilePtr++;
if(Chr < ASCII_SPACE && Chr >= 0) // Allow high ASCII characters
{
if(Chr == '\n')
{
IncLineNumber = TRUE;
}
Chr = ASCII_SPACE;
}
BumpMasterSourceLine(Chr,FALSE);
}
//==========================================================================
//
// PeekChr // [JB]
//
//==========================================================================
static int PeekChr(void)
{
char ch;
if(FilePtr >= FileEnd)
{
return EOF_CHARACTER;
}
ch = *FilePtr-1;
if(ch < ASCII_SPACE && ch >= 0) // Allow high ASCII characters
{
ch = ASCII_SPACE;
}
return ch;
}
//==========================================================================
//
// OctalChar // [JB]
//
//==========================================================================
static int OctalChar()
{
int digits = 1;
int code = Chr - '0';
while(digits < 4 && PeekChr() >= '0' && PeekChr() <= '7')
{
NextChr();
code = (code << 3) + Chr - '0';
}
return code;
}
//==========================================================================
//
// SkipComment
//
//==========================================================================
void SkipComment(void)
{
boolean first;
first = FALSE;
while(Chr != EOF_CHARACTER)
{
if(first == TRUE && Chr == '/')
{
break;
}
first = (Chr == '*');
NextChr();
}
NextChr();
}
//==========================================================================
//
// SkipCPPComment
//
//==========================================================================
void SkipCPPComment(void)
{
while(FilePtr < FileEnd)
{
if(*FilePtr++ == '\n')
{
tk_Line++;
BumpMasterSourceLine('x',TRUE); // dummy x
break;
}
}
NextChr();
}
//==========================================================================
//
// BumpMasterSourceLine
//
//==========================================================================
void BumpMasterSourceLine(char Chr, boolean clear) // master line - Ty 07jan2000
{
if (ClearMasterSourceLine) // set to clear last time, clear now for first character
{
*MasterSourceLine = '\0';
MasterSourcePos = 0;
ClearMasterSourceLine = FALSE;
}
if (clear)
{
ClearMasterSourceLine = TRUE;
}
else
{
if (MasterSourcePos < MAX_STATEMENT_LENGTH)
MasterSourceLine[MasterSourcePos++] = Chr;
}
}
//==========================================================================
//
// TK_SkipLine
//
//==========================================================================
void TK_SkipLine(void)
{
char *sourcenow = tk_SourceName;
int linenow = tk_Line;
do TK_NextToken(); while (tk_Line == linenow && tk_SourceName == sourcenow);
}