2072 lines
46 KiB
C++
2072 lines
46 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
#include "../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#define PUNCTABLE
|
|
|
|
// Auto-close files on destruction
|
|
// Prefer this to using idFile* in most cases, since this is exception-safe
|
|
struct sdCleanupPolicy_CloseIdLibMemoryFile {
|
|
static void Free( idFile_Memory* fp ) {
|
|
idLib::fileSystem->CloseFile( fp );
|
|
}
|
|
};
|
|
|
|
//longer punctuations first
|
|
static punctuation_t default_punctuations[] = {
|
|
// binary operators
|
|
{">>=",P_RSHIFT_ASSIGN},
|
|
{"<<=",P_LSHIFT_ASSIGN},
|
|
//
|
|
{"...",P_PARMS},
|
|
{"->*",P_POINTER_TO_MEMBER_POINTER},
|
|
// define merge operator
|
|
{"##",P_PRECOMPMERGE}, // pre-compiler
|
|
// logic operators
|
|
{"&&",P_LOGIC_AND}, // pre-compiler
|
|
{"||",P_LOGIC_OR}, // pre-compiler
|
|
{">=",P_LOGIC_GEQ}, // pre-compiler
|
|
{"<=",P_LOGIC_LEQ}, // pre-compiler
|
|
{"==",P_LOGIC_EQ}, // pre-compiler
|
|
{"!=",P_LOGIC_UNEQ}, // pre-compiler
|
|
// arithmetic operators
|
|
{"*=",P_MUL_ASSIGN},
|
|
{"/=",P_DIV_ASSIGN},
|
|
{"%=",P_MOD_ASSIGN},
|
|
{"+=",P_ADD_ASSIGN},
|
|
{"-=",P_SUB_ASSIGN},
|
|
{"++",P_INC},
|
|
{"--",P_DEC},
|
|
// binary operators
|
|
{"&=",P_BIN_AND_ASSIGN},
|
|
{"|=",P_BIN_OR_ASSIGN},
|
|
{"^=",P_BIN_XOR_ASSIGN},
|
|
{">>",P_RSHIFT}, // pre-compiler
|
|
{"<<",P_LSHIFT}, // pre-compiler
|
|
// member selection
|
|
{"->",P_MEMBER_SELECTION_POINTER},
|
|
{"::",P_SCOPE_RESOLUTION},
|
|
{".*",P_POINTER_TO_MEMBER_OBJECT},
|
|
// arithmetic operators
|
|
{"*",P_MUL}, // pre-compiler
|
|
{"/",P_DIV}, // pre-compiler
|
|
{"%",P_MOD}, // pre-compiler
|
|
{"+",P_ADD}, // pre-compiler
|
|
{"-",P_SUB}, // pre-compiler
|
|
{"=",P_ASSIGN},
|
|
// binary operators
|
|
{"&",P_BIN_AND}, // pre-compiler
|
|
{"|",P_BIN_OR}, // pre-compiler
|
|
{"^",P_BIN_XOR}, // pre-compiler
|
|
{"~",P_BIN_NOT}, // pre-compiler
|
|
// logic operators
|
|
{"!",P_LOGIC_NOT}, // pre-compiler
|
|
{">",P_LOGIC_GREATER}, // pre-compiler
|
|
{"<",P_LOGIC_LESS}, // pre-compiler
|
|
// member selection
|
|
{".",P_MEMBER_SELECTION_OBJECT},
|
|
// separators
|
|
{",",P_COMMA}, // pre-compiler
|
|
{";",P_SEMICOLON},
|
|
// label indication
|
|
{":",P_COLON}, // pre-compiler
|
|
// if statement
|
|
{"?",P_QUESTIONMARK}, // pre-compiler
|
|
// embracements
|
|
{"(",P_PARENTHESESOPEN}, // pre-compiler
|
|
{")",P_PARENTHESESCLOSE}, // pre-compiler
|
|
{"{",P_BRACEOPEN}, // pre-compiler
|
|
{"}",P_BRACECLOSE}, // pre-compiler
|
|
{"[",P_SQBRACKETOPEN},
|
|
{"]",P_SQBRACKETCLOSE},
|
|
//
|
|
{"\\",P_BACKSLASH},
|
|
// precompiler operator
|
|
{"#",P_PRECOMP}, // pre-compiler
|
|
{"$",P_DOLLAR},
|
|
{NULL, 0}
|
|
};
|
|
|
|
int default_punctuationtable[256];
|
|
int default_nextpunctuation[sizeof(default_punctuations) / sizeof(punctuation_t)];
|
|
int default_setup;
|
|
|
|
char idLexer::baseFolder[ 256 ];
|
|
|
|
/*
|
|
================
|
|
idLexer::CreatePunctuationTable
|
|
================
|
|
*/
|
|
void idLexer::CreatePunctuationTable( const punctuation_t *punctuations ) {
|
|
int i, n, lastp;
|
|
const punctuation_t *p, *newp;
|
|
|
|
//get memory for the table
|
|
if ( punctuations == default_punctuations ) {
|
|
idLexer::punctuationtable = default_punctuationtable;
|
|
idLexer::nextpunctuation = default_nextpunctuation;
|
|
if ( default_setup ) {
|
|
return;
|
|
}
|
|
default_setup = true;
|
|
i = sizeof(default_punctuations) / sizeof(punctuation_t);
|
|
}
|
|
else {
|
|
if ( !idLexer::punctuationtable || idLexer::punctuationtable == default_punctuationtable ) {
|
|
idLexer::punctuationtable = (int *) Mem_Alloc(256 * sizeof(int));
|
|
}
|
|
if ( idLexer::nextpunctuation && idLexer::nextpunctuation != default_nextpunctuation ) {
|
|
Mem_Free( idLexer::nextpunctuation );
|
|
}
|
|
for (i = 0; punctuations[i].p; i++) {
|
|
}
|
|
idLexer::nextpunctuation = (int *) Mem_Alloc(i * sizeof(int));
|
|
}
|
|
memset(idLexer::punctuationtable, 0xFF, 256 * sizeof(int));
|
|
memset(idLexer::nextpunctuation, 0xFF, i * sizeof(int));
|
|
//add the punctuations in the list to the punctuation table
|
|
for (i = 0; punctuations[i].p; i++) {
|
|
newp = &punctuations[i];
|
|
lastp = -1;
|
|
//sort the punctuations in this table entry on length (longer punctuations first)
|
|
for (n = idLexer::punctuationtable[(unsigned int) newp->p[0]]; n >= 0; n = idLexer::nextpunctuation[n] ) {
|
|
p = &punctuations[n];
|
|
if (idStr::Length(p->p) < idStr::Length(newp->p)) {
|
|
idLexer::nextpunctuation[i] = n;
|
|
if (lastp >= 0) {
|
|
idLexer::nextpunctuation[lastp] = i;
|
|
}
|
|
else {
|
|
idLexer::punctuationtable[(unsigned int) newp->p[0]] = i;
|
|
}
|
|
break;
|
|
}
|
|
lastp = n;
|
|
}
|
|
if (n < 0) {
|
|
idLexer::nextpunctuation[i] = -1;
|
|
if (lastp >= 0) {
|
|
idLexer::nextpunctuation[lastp] = i;
|
|
}
|
|
else {
|
|
idLexer::punctuationtable[(unsigned int) newp->p[0]] = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetPunctuationFromId
|
|
================
|
|
*/
|
|
const char *idLexer::GetPunctuationFromId( int id ) const {
|
|
int i;
|
|
|
|
for (i = 0; idLexer::punctuations[i].p; i++) {
|
|
if ( idLexer::punctuations[i].n == id ) {
|
|
return idLexer::punctuations[i].p;
|
|
}
|
|
}
|
|
return "unkown punctuation";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetPunctuationId
|
|
================
|
|
*/
|
|
int idLexer::GetPunctuationId( const char *p ) const {
|
|
int i;
|
|
|
|
for (i = 0; idLexer::punctuations[i].p; i++) {
|
|
if ( !idStr::Cmp(idLexer::punctuations[i].p, p) ) {
|
|
return idLexer::punctuations[i].n;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Error
|
|
================
|
|
*/
|
|
void idLexer::Error( const char *str, ... ) {
|
|
char text[MAX_STRING_CHARS];
|
|
va_list ap;
|
|
|
|
hadError = true;
|
|
|
|
if ( idLexer::flags & LEXFL_NOERRORS ) {
|
|
return;
|
|
}
|
|
|
|
va_start(ap, str);
|
|
vsprintf(text, str, ap);
|
|
va_end(ap);
|
|
|
|
if ( idLexer::flags & LEXFL_VCSTYLEREPORTS ) {
|
|
idStr path = idLexer::filename;
|
|
path.SlashesToBackSlashes();
|
|
idLib::common->Printf( "%s(%d) : error : %s\n", path.c_str(), idLexer::line, text );
|
|
} else if ( idLexer::flags & LEXFL_NOFATALERRORS ) {
|
|
idLib::common->Warning( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
|
|
} else {
|
|
idLib::common->Error( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Warning
|
|
================
|
|
*/
|
|
void idLexer::Warning( const char *str, ... ) {
|
|
char text[4096];
|
|
va_list ap;
|
|
|
|
hadWarning = true;
|
|
|
|
if ( idLexer::flags & LEXFL_NOWARNINGS ) {
|
|
return;
|
|
}
|
|
|
|
va_start( ap, str );
|
|
vsprintf( text, str, ap );
|
|
va_end( ap );
|
|
|
|
if ( idLexer::flags & LEXFL_VCSTYLEREPORTS ) {
|
|
idStr path = idLexer::filename;
|
|
path.SlashesToBackSlashes();
|
|
idLib::common->Printf( "%s(%d) : warning : %s\n", path.c_str(), idLexer::line, text );
|
|
} else {
|
|
idLib::common->Warning( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SetPunctuations
|
|
================
|
|
*/
|
|
void idLexer::SetPunctuations( const punctuation_t *p ) {
|
|
assert( !loaded ); // punctuations must be set before loading the file
|
|
#ifdef PUNCTABLE
|
|
if ( p != NULL ) {
|
|
CreatePunctuationTable( p );
|
|
} else {
|
|
CreatePunctuationTable( default_punctuations );
|
|
}
|
|
#endif //PUNCTABLE
|
|
if ( p != NULL ) {
|
|
punctuations = p;
|
|
} else {
|
|
punctuations = default_punctuations;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SkipWhiteSpace
|
|
|
|
Reads spaces, tabs, C-like comments etc.
|
|
When a newline character is found the scripts line counter is increased.
|
|
Returns 0 if there is no token left to be read.
|
|
================
|
|
*/
|
|
int idLexer::SkipWhiteSpace( bool currentLine ) {
|
|
while( 1 ) {
|
|
// skip white space
|
|
while( *idLexer::script_p <= ' ' ) {
|
|
if ( !*idLexer::script_p ) {
|
|
return 0;
|
|
}
|
|
if ( *idLexer::script_p == '\n' ) {
|
|
idLexer::line++;
|
|
if ( currentLine ) {
|
|
idLexer::script_p++;
|
|
return 1;
|
|
}
|
|
}
|
|
idLexer::script_p++;
|
|
}
|
|
// skip comments
|
|
if ( *idLexer::script_p == '/' ) {
|
|
// comments //
|
|
if ( *(idLexer::script_p+1) == '/' ) {
|
|
idLexer::script_p++;
|
|
do {
|
|
idLexer::script_p++;
|
|
if ( !*idLexer::script_p ) {
|
|
return 0;
|
|
}
|
|
}
|
|
while( *idLexer::script_p != '\n' );
|
|
idLexer::line++;
|
|
idLexer::script_p++;
|
|
if ( currentLine ) {
|
|
return 1;
|
|
}
|
|
if ( !*idLexer::script_p ) {
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
// comments /* */
|
|
else if ( *(idLexer::script_p+1) == '*' ) {
|
|
idLexer::script_p++;
|
|
while( 1 ) {
|
|
idLexer::script_p++;
|
|
if ( !*idLexer::script_p ) {
|
|
return 0;
|
|
}
|
|
if ( *idLexer::script_p == '\n' ) {
|
|
idLexer::line++;
|
|
}
|
|
else if ( *idLexer::script_p == '/' ) {
|
|
if ( *(idLexer::script_p-1) == '*' ) {
|
|
break;
|
|
}
|
|
if ( *(idLexer::script_p+1) == '*' ) {
|
|
idLexer::Warning( "nested comment" );
|
|
}
|
|
}
|
|
}
|
|
idLexer::script_p++;
|
|
if ( !*idLexer::script_p ) {
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadEscapeCharacter
|
|
================
|
|
*/
|
|
int idLexer::ReadEscapeCharacter( char *ch ) {
|
|
int c, val, i;
|
|
|
|
// step over the leading '\\'
|
|
idLexer::script_p++;
|
|
// determine the escape character
|
|
if( !( flags & LEXFL_NOEMITSTRINGESCAPECHARS )) {
|
|
switch(*idLexer::script_p) {
|
|
case '\\': c = '\\'; break;
|
|
case 'n': c = '\n'; break;
|
|
case 'r': c = '\r'; break;
|
|
case 't': c = '\t'; break;
|
|
case 'v': c = '\v'; break;
|
|
case 'b': c = '\b'; break;
|
|
case 'f': c = '\f'; break;
|
|
case 'a': c = '\a'; break;
|
|
case '\'': c = '\''; break;
|
|
case '\"': c = '\"'; break;
|
|
case '\?': c = '\?'; break;
|
|
case 'x':
|
|
{
|
|
idLexer::script_p++;
|
|
for (i = 0, val = 0; ; i++, idLexer::script_p++) {
|
|
c = *idLexer::script_p;
|
|
if (c >= '0' && c <= '9')
|
|
c = c - '0';
|
|
else if (c >= 'A' && c <= 'Z')
|
|
c = c - 'A' + 10;
|
|
else if (c >= 'a' && c <= 'z')
|
|
c = c - 'a' + 10;
|
|
else
|
|
break;
|
|
val = (val << 4) + c;
|
|
}
|
|
idLexer::script_p--;
|
|
if (val > 0xFF) {
|
|
idLexer::Warning( "too large value in escape character" );
|
|
val = 0xFF;
|
|
}
|
|
c = val;
|
|
break;
|
|
}
|
|
default: //NOTE: decimal ASCII code, NOT octal
|
|
{
|
|
if (*idLexer::script_p < '0' || *idLexer::script_p > '9') {
|
|
idLexer::Error("unknown escape char");
|
|
}
|
|
for (i = 0, val = 0; ; i++, idLexer::script_p++) {
|
|
c = *idLexer::script_p;
|
|
if (c >= '0' && c <= '9')
|
|
c = c - '0';
|
|
else
|
|
break;
|
|
val = val * 10 + c;
|
|
}
|
|
idLexer::script_p--;
|
|
if (val > 0xFF) {
|
|
idLexer::Warning( "too large value in escape character" );
|
|
val = 0xFF;
|
|
}
|
|
c = val;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
c = *script_p;
|
|
}
|
|
|
|
// step over the escape character or the last digit of the number
|
|
idLexer::script_p++;
|
|
// store the escape character
|
|
*ch = c;
|
|
// succesfully read escape character
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadString
|
|
|
|
Escape characters are interpretted.
|
|
Reads two strings with only a white space between them as one string.
|
|
================
|
|
*/
|
|
int idLexer::ReadString( idToken *token, int quote ) {
|
|
int tmpline;
|
|
const char *tmpscript_p;
|
|
char ch;
|
|
|
|
if ( quote == '\"' ) {
|
|
token->type = TT_STRING;
|
|
} else {
|
|
token->type = TT_LITERAL;
|
|
}
|
|
|
|
// leading quote
|
|
idLexer::script_p++;
|
|
|
|
while(1) {
|
|
// if there is an escape character and escape characters are allowed
|
|
if (*idLexer::script_p == '\\' && !(idLexer::flags & LEXFL_NOSTRINGESCAPECHARS)) {
|
|
if(( flags & LEXFL_NOEMITSTRINGESCAPECHARS )) {
|
|
token->AppendDirty( '\\' );
|
|
}
|
|
|
|
if ( !idLexer::ReadEscapeCharacter( &ch ) ) {
|
|
return 0;
|
|
}
|
|
token->AppendDirty( ch );
|
|
}
|
|
// if a trailing quote
|
|
else if (*idLexer::script_p == quote) {
|
|
// step over the quote
|
|
idLexer::script_p++;
|
|
// if consecutive strings should not be concatenated
|
|
if ( (idLexer::flags & LEXFL_NOSTRINGCONCAT) &&
|
|
(!(idLexer::flags & LEXFL_ALLOWBACKSLASHSTRINGCONCAT) || (quote != '\"')) ) {
|
|
break;
|
|
}
|
|
|
|
tmpscript_p = idLexer::script_p;
|
|
tmpline = idLexer::line;
|
|
// read white space between possible two consecutive strings
|
|
if ( !idLexer::SkipWhiteSpace( false ) ) {
|
|
idLexer::script_p = tmpscript_p;
|
|
idLexer::line = tmpline;
|
|
break;
|
|
}
|
|
|
|
if ( idLexer::flags & LEXFL_NOSTRINGCONCAT ) {
|
|
if ( *idLexer::script_p != '\\' ) {
|
|
idLexer::script_p = tmpscript_p;
|
|
idLexer::line = tmpline;
|
|
break;
|
|
}
|
|
// step over the '\\'
|
|
idLexer::script_p++;
|
|
if ( !idLexer::SkipWhiteSpace( false ) || ( *idLexer::script_p != quote ) ) {
|
|
idLexer::Error( "expecting string after '\\' terminated line" );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// if there's no leading qoute
|
|
if ( *idLexer::script_p != quote ) {
|
|
idLexer::script_p = tmpscript_p;
|
|
idLexer::line = tmpline;
|
|
break;
|
|
}
|
|
// step over the new leading quote
|
|
idLexer::script_p++;
|
|
}
|
|
else {
|
|
if (*idLexer::script_p == '\0') {
|
|
idLexer::Error( "missing trailing quote" );
|
|
return 0;
|
|
}
|
|
if (*idLexer::script_p == '\n') {
|
|
idLexer::Error( "newline inside string" );
|
|
return 0;
|
|
}
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
}
|
|
}
|
|
token->data[token->len] = '\0';
|
|
|
|
if ( token->type == TT_LITERAL ) {
|
|
if ( !(idLexer::flags & LEXFL_ALLOWMULTICHARLITERALS) ) {
|
|
if ( token->Length() != 1 ) {
|
|
idLexer::Warning( "literal is not one character long" );
|
|
}
|
|
}
|
|
token->subtype = (*token)[0];
|
|
}
|
|
else {
|
|
// the sub type is the length of the string
|
|
token->subtype = token->Length();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadName
|
|
================
|
|
*/
|
|
int idLexer::ReadName( idToken *token ) {
|
|
char c;
|
|
|
|
token->type = TT_NAME;
|
|
do {
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
} while ((c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
c == '_' ||
|
|
// if treating all tokens as strings, don't parse '-' as a separate token
|
|
((idLexer::flags & LEXFL_ONLYSTRINGS) && (c == '-')) ||
|
|
// if special path name characters are allowed
|
|
((idLexer::flags & LEXFL_ALLOWPATHNAMES) && (c == '/' || c == '\\' || c == ':' || c == '.')) );
|
|
token->data[token->len] = '\0';
|
|
//the sub type is the length of the name
|
|
token->subtype = token->Length();
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::CheckString
|
|
================
|
|
*/
|
|
ID_INLINE int idLexer::CheckString( const char *str ) const {
|
|
int i;
|
|
|
|
for ( i = 0; str[i]; i++ ) {
|
|
if ( idLexer::script_p[i] != str[i] ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadNumber
|
|
================
|
|
*/
|
|
int idLexer::ReadNumber( idToken *token ) {
|
|
int i;
|
|
int dot;
|
|
char c, c2;
|
|
|
|
token->type = TT_NUMBER;
|
|
token->subtype = 0;
|
|
token->intvalue = 0;
|
|
token->floatvalue = 0;
|
|
|
|
c = *idLexer::script_p;
|
|
c2 = *(idLexer::script_p + 1);
|
|
|
|
if ( c == '0' && c2 != '.' ) {
|
|
// check for a hexadecimal number
|
|
if ( c2 == 'x' || c2 == 'X' ) {
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
while((c >= '0' && c <= '9') ||
|
|
(c >= 'a' && c <= 'f') ||
|
|
(c >= 'A' && c <= 'F')) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
token->subtype = TT_HEX | TT_INTEGER;
|
|
}
|
|
// check for a binary number
|
|
else if ( c2 == 'b' || c2 == 'B' ) {
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
while( c == '0' || c == '1' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
token->subtype = TT_BINARY | TT_INTEGER;
|
|
}
|
|
// its an octal number
|
|
else {
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
while( c >= '0' && c <= '7' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
token->subtype = TT_OCTAL | TT_INTEGER;
|
|
}
|
|
}
|
|
else {
|
|
// decimal integer or floating point number or ip address
|
|
dot = 0;
|
|
while( 1 ) {
|
|
if ( c >= '0' && c <= '9' ) {
|
|
} else if ( c == '.' ) {
|
|
dot++;
|
|
} else {
|
|
break;
|
|
}
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
// if a floating point number
|
|
if ( dot == 1 || c == 'e' ) {
|
|
token->subtype = TT_DECIMAL | TT_FLOAT;
|
|
// check for floating point exponent
|
|
if ( c == 'e' ) {
|
|
c = *(++idLexer::script_p);
|
|
if ( c == '-' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
} else if ( c == '+' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
while( c >= '0' && c <= '9' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
}
|
|
// check for floating point exception infinite 1.#INF or indefinite 1.#IND or NaN
|
|
else if ( c == '#' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
if ( CheckString( "INF" ) ) {
|
|
token->subtype |= TT_INFINITE;
|
|
c2 = 3;
|
|
} else if ( CheckString( "IND" ) ) {
|
|
token->subtype |= TT_INDEFINITE;
|
|
c2 = 3;
|
|
} else if ( CheckString( "NAN" ) ) {
|
|
token->subtype |= TT_NAN;
|
|
c2 = 3;
|
|
} else if ( CheckString( "QNAN" ) ) {
|
|
token->subtype |= TT_NAN;
|
|
c2 = 4;
|
|
} else if ( CheckString( "SNAN" ) ) {
|
|
token->subtype |= TT_NAN;
|
|
c2 = 4;
|
|
}
|
|
for ( i = 0; i < c2; i++ ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
while( c >= '0' && c <= '9' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
if ( !(idLexer::flags & LEXFL_ALLOWFLOATEXCEPTIONS) ) {
|
|
token->AppendDirty( 0 ); // zero terminate for c_str
|
|
idLexer::Error( "parsed %s", token->c_str() );
|
|
}
|
|
}
|
|
}
|
|
else if ( dot > 1 ) {
|
|
if ( !( idLexer::flags & LEXFL_ALLOWIPADDRESSES ) ) {
|
|
idLexer::Error( "more than one dot in number" );
|
|
return 0;
|
|
}
|
|
if ( dot != 3 ) {
|
|
idLexer::Error( "ip address should have three dots" );
|
|
return 0;
|
|
}
|
|
token->subtype = TT_IPADDRESS;
|
|
}
|
|
else {
|
|
token->subtype = TT_DECIMAL | TT_INTEGER;
|
|
}
|
|
}
|
|
|
|
if ( token->subtype & TT_FLOAT ) {
|
|
if ( c > ' ' ) {
|
|
// single-precision: float
|
|
if ( c == 'f' || c == 'F' ) {
|
|
token->subtype |= TT_SINGLE_PRECISION;
|
|
idLexer::script_p++;
|
|
}
|
|
// extended-precision: long double
|
|
else if ( c == 'l' || c == 'L' ) {
|
|
token->subtype |= TT_EXTENDED_PRECISION;
|
|
idLexer::script_p++;
|
|
}
|
|
// default is double-precision: double
|
|
else {
|
|
token->subtype |= TT_DOUBLE_PRECISION;
|
|
}
|
|
}
|
|
else {
|
|
token->subtype |= TT_DOUBLE_PRECISION;
|
|
}
|
|
}
|
|
else if ( token->subtype & TT_INTEGER ) {
|
|
if ( c > ' ' ) {
|
|
// default: signed long
|
|
for ( i = 0; i < 2; i++ ) {
|
|
// long integer
|
|
if ( c == 'l' || c == 'L' ) {
|
|
token->subtype |= TT_LONG;
|
|
}
|
|
// unsigned integer
|
|
else if ( c == 'u' || c == 'U' ) {
|
|
token->subtype |= TT_UNSIGNED;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
}
|
|
}
|
|
else if ( token->subtype & TT_IPADDRESS ) {
|
|
if ( c == ':' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
while( c >= '0' && c <= '9' ) {
|
|
token->AppendDirty( c );
|
|
c = *(++idLexer::script_p);
|
|
}
|
|
token->subtype |= TT_IPPORT;
|
|
}
|
|
}
|
|
token->data[token->len] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadRawStringBlock
|
|
================
|
|
*/
|
|
int idLexer::ReadRawStringBlock( idToken *token ) {
|
|
token->type = TT_STRING;
|
|
|
|
// leading identifier
|
|
idLexer::script_p += 2;
|
|
|
|
while ( true ) {
|
|
// if a trailing identifier
|
|
if ( idLexer::script_p[0] == '%' && idLexer::script_p[1] == '>' ) {
|
|
// step over the identifier
|
|
idLexer::script_p += 2;
|
|
|
|
break;
|
|
} else {
|
|
if ( *idLexer::script_p == '\0' ) {
|
|
idLexer::Error( "missing trailing identifier" );
|
|
return 0;
|
|
}
|
|
if ( *idLexer::script_p == '\n' ) {
|
|
line++;
|
|
}
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
}
|
|
}
|
|
token->data[token->len] = '\0';
|
|
|
|
// the sub type is the length of the string
|
|
token->subtype = token->Length();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadPunctuation
|
|
================
|
|
*/
|
|
int idLexer::ReadPunctuation( idToken *token ) {
|
|
int l, n, i;
|
|
char *p;
|
|
const punctuation_t *punc;
|
|
|
|
#ifdef PUNCTABLE
|
|
for (n = idLexer::punctuationtable[(unsigned int)*(idLexer::script_p)]; n >= 0; n = idLexer::nextpunctuation[n])
|
|
{
|
|
punc = &(idLexer::punctuations[n]);
|
|
#else
|
|
for (n = 0; idLexer::punctuations[n].p; n++) {
|
|
punc = &idLexer::punctuations[n];
|
|
#endif
|
|
p = punc->p;
|
|
// check for this punctuation in the script
|
|
for ( l = 0; p[l] && idLexer::script_p[l]; l++ ) {
|
|
if ( idLexer::script_p[l] != p[l] ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( !p[l] ) {
|
|
//
|
|
token->EnsureAlloced( l+1, false );
|
|
for ( i = 0; i <= l; i++ ) {
|
|
token->data[i] = p[i];
|
|
}
|
|
token->len = l;
|
|
//
|
|
idLexer::script_p += l;
|
|
token->type = TT_PUNCTUATION;
|
|
// sub type is the punctuation id
|
|
token->subtype = punc->n;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadToken
|
|
================
|
|
*/
|
|
int idLexer::ReadToken( idToken *token ) {
|
|
|
|
if ( !loaded ) {
|
|
idLib::common->Error( "idLexer::ReadToken: no file loaded" );
|
|
return 0;
|
|
}
|
|
|
|
// if there is a token available (from unreadToken)
|
|
if ( tokens.Num() ) {
|
|
*token = tokens[ tokens.Num() - 1 ];
|
|
tokens.RemoveIndex( tokens.Num() - 1 );
|
|
return 1;
|
|
}
|
|
|
|
// read from binary file if loaded
|
|
if ( binary.IsLoaded() ) {
|
|
return binary.ReadToken( token );
|
|
}
|
|
|
|
// save script pointer
|
|
lastScript_p = script_p;
|
|
// save line counter
|
|
lastline = line;
|
|
// clear the token stuff
|
|
token->data[0] = '\0';
|
|
token->len = 0;
|
|
// start of the white space
|
|
whiteSpaceStart_p = script_p;
|
|
token->whiteSpaceStart_p = script_p;
|
|
// read white space before token
|
|
if ( !SkipWhiteSpace( false ) ) {
|
|
return 0;
|
|
}
|
|
// end of the white space
|
|
idLexer::whiteSpaceEnd_p = script_p;
|
|
token->whiteSpaceEnd_p = script_p;
|
|
// line the token is on
|
|
token->line = line;
|
|
// number of lines crossed before token
|
|
token->linesCrossed = line - lastline;
|
|
// clear token flags
|
|
token->flags = 0;
|
|
|
|
int c = *idLexer::script_p;
|
|
|
|
// if we're keeping everything as whitespace deliminated strings
|
|
if ( idLexer::flags & LEXFL_ONLYSTRINGS ) {
|
|
// if there is a leading quote
|
|
if ( c == '\"' || c == '\'' ) {
|
|
if (!idLexer::ReadString( token, c )) {
|
|
return 0;
|
|
}
|
|
} else if ( !idLexer::ReadName( token ) ) {
|
|
return 0;
|
|
}
|
|
}
|
|
// if there is a number
|
|
else if ( (c >= '0' && c <= '9') ||
|
|
(c == '.' && (*(idLexer::script_p + 1) >= '0' && *(idLexer::script_p + 1) <= '9')) ) {
|
|
if ( !idLexer::ReadNumber( token ) ) {
|
|
return 0;
|
|
}
|
|
// if names are allowed to start with a number
|
|
if ( idLexer::flags & LEXFL_ALLOWNUMBERNAMES ) {
|
|
c = *idLexer::script_p;
|
|
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ) {
|
|
if ( !idLexer::ReadName( token ) ) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// if there is a leading quote
|
|
else if ( c == '\"' || c == '\'' ) {
|
|
if (!idLexer::ReadString( token, c )) {
|
|
return 0;
|
|
}
|
|
}
|
|
// if there is a name
|
|
else if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ) {
|
|
if ( !idLexer::ReadName( token ) ) {
|
|
return 0;
|
|
}
|
|
}
|
|
// names may also start with a slash when pathnames are allowed
|
|
else if ( ( idLexer::flags & LEXFL_ALLOWPATHNAMES ) && ( (c == '/' || c == '\\') || c == '.' ) ) {
|
|
if ( !idLexer::ReadName( token ) ) {
|
|
return 0;
|
|
}
|
|
}
|
|
// if there is a raw string block
|
|
else if ( ( idLexer::flags & LEXFL_ALLOWRAWSTRINGBLOCKS ) && ( idLexer::script_p[0] == '<' && idLexer::script_p[1] == '%' ) ) {
|
|
if ( !idLexer::ReadRawStringBlock( token ) ) {
|
|
return 0;
|
|
}
|
|
}
|
|
// check for punctuations
|
|
else if ( !idLexer::ReadPunctuation( token ) ) {
|
|
idLexer::Error( "unknown punctuation %c", c );
|
|
return 0;
|
|
}
|
|
// succesfully read a token
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ExpectTokenString
|
|
================
|
|
*/
|
|
bool idLexer::ExpectTokenString( const char *string ) {
|
|
idToken token;
|
|
|
|
if (!idLexer::ReadToken( &token )) {
|
|
idLexer::Error( "couldn't find expected '%s'", string );
|
|
return false;
|
|
}
|
|
if ( token != string ) {
|
|
idLexer::Error( "expected '%s' but found '%s'", string, token.c_str() );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ExpectTokenType
|
|
================
|
|
*/
|
|
bool idLexer::ExpectTokenType( int type, int subtype, idToken *token ) {
|
|
idStr str;
|
|
|
|
if ( !idLexer::ReadToken( token ) ) {
|
|
idLexer::Error( "couldn't read expected token" );
|
|
return false;
|
|
}
|
|
|
|
if ( token->type != type ) {
|
|
switch( type ) {
|
|
case TT_STRING: str = "string"; break;
|
|
case TT_LITERAL: str = "literal"; break;
|
|
case TT_NUMBER: str = "number"; break;
|
|
case TT_NAME: str = "name"; break;
|
|
case TT_PUNCTUATION: str = "punctuation"; break;
|
|
default: str = "unknown type"; break;
|
|
}
|
|
idLexer::Error( "expected a %s but found '%s'", str.c_str(), token->c_str() );
|
|
return false;
|
|
}
|
|
if ( token->type == TT_NUMBER ) {
|
|
if ( (token->subtype & subtype) != subtype ) {
|
|
str.Clear();
|
|
if ( subtype & TT_DECIMAL ) str = "decimal ";
|
|
if ( subtype & TT_HEX ) str = "hex ";
|
|
if ( subtype & TT_OCTAL ) str = "octal ";
|
|
if ( subtype & TT_BINARY ) str = "binary ";
|
|
if ( subtype & TT_UNSIGNED ) str += "unsigned ";
|
|
if ( subtype & TT_LONG ) str += "long ";
|
|
if ( subtype & TT_FLOAT ) str += "float ";
|
|
if ( subtype & TT_INTEGER ) str += "integer ";
|
|
str.StripTrailing( ' ' );
|
|
idLexer::Error( "expected %s but found '%s'", str.c_str(), token->c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
else if ( token->type == TT_PUNCTUATION ) {
|
|
if ( subtype < 0 ) {
|
|
idLexer::Error( "BUG: wrong punctuation subtype" );
|
|
return false;
|
|
}
|
|
if ( token->subtype != subtype ) {
|
|
idLexer::Error( "expected '%s' but found '%s'", GetPunctuationFromId( subtype ), token->c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ExpectAnyToken
|
|
================
|
|
*/
|
|
bool idLexer::ExpectAnyToken( idToken *token ) {
|
|
if (!idLexer::ReadToken( token )) {
|
|
idLexer::Error( "couldn't read expected token" );
|
|
return false;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::CheckTokenString
|
|
================
|
|
*/
|
|
bool idLexer::CheckTokenString( const char *string ) {
|
|
idToken tok;
|
|
|
|
if ( !ReadToken( &tok ) ) {
|
|
return false;
|
|
}
|
|
// if the given string is available
|
|
if ( tok == string ) {
|
|
return true;
|
|
}
|
|
// unread token
|
|
UnreadToken( &tok );
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::CheckTokenType
|
|
================
|
|
*/
|
|
bool idLexer::CheckTokenType( int type, int subtype, idToken *token ) {
|
|
idToken tok;
|
|
|
|
if ( !ReadToken( &tok ) ) {
|
|
return false;
|
|
}
|
|
// if the type matches
|
|
if ( tok.type == type && ( tok.subtype & subtype ) == subtype ) {
|
|
*token = tok;
|
|
return true;
|
|
}
|
|
// unread token
|
|
UnreadToken( &tok );
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::PeekTokenString
|
|
================
|
|
*/
|
|
int idLexer::PeekTokenString( const char *string ) {
|
|
idToken tok;
|
|
|
|
if ( !ReadToken( &tok ) ) {
|
|
return 0;
|
|
}
|
|
|
|
// unread token
|
|
UnreadToken( &tok );
|
|
|
|
// if the given string is available
|
|
if ( tok == string ) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::PeekTokenType
|
|
================
|
|
*/
|
|
int idLexer::PeekTokenType( int type, int subtype, idToken *token ) {
|
|
idToken tok;
|
|
|
|
if ( !ReadToken( &tok ) ) {
|
|
return 0;
|
|
}
|
|
|
|
// unread token
|
|
UnreadToken( &tok );
|
|
|
|
// if the type matches
|
|
if ( tok.type == type && ( tok.subtype & subtype ) == subtype ) {
|
|
*token = tok;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SkipUntilString
|
|
================
|
|
*/
|
|
bool idLexer::SkipUntilString( const char *string, idToken* token ) {
|
|
idToken _token;
|
|
if ( !token ) {
|
|
token = &_token;
|
|
}
|
|
|
|
while( idLexer::ReadToken( token ) ) {
|
|
if ( *token == string ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SkipRestOfLine
|
|
================
|
|
*/
|
|
int idLexer::SkipRestOfLine( void ) {
|
|
idToken token;
|
|
|
|
while( ReadToken( &token ) ) {
|
|
if ( token.linesCrossed ) {
|
|
UnreadToken( &token );
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::SkipBracedSection
|
|
|
|
Skips until a matching close brace is found.
|
|
Internal brace depths are properly skipped.
|
|
=================
|
|
*/
|
|
int idLexer::SkipBracedSection( bool parseFirstBrace ) {
|
|
idToken token;
|
|
int depth;
|
|
|
|
depth = parseFirstBrace ? 0 : 1;
|
|
do {
|
|
if ( !ReadToken( &token ) ) {
|
|
return false;
|
|
}
|
|
if ( token.type == TT_PUNCTUATION ) {
|
|
if ( token == "{" ) {
|
|
depth++;
|
|
} else if ( token == "}" ) {
|
|
depth--;
|
|
}
|
|
}
|
|
} while( depth );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::SkipBracedSectionExact
|
|
|
|
Skips until a matching close brace is found.
|
|
Internal brace depths are properly skipped.
|
|
Reads exact characters between braces.
|
|
=================
|
|
*/
|
|
bool idLexer::SkipBracedSectionExact( int tabs, bool parseFirstBrace ) {
|
|
int depth;
|
|
bool doTabs;
|
|
bool skipWhite;
|
|
|
|
if ( parseFirstBrace ) {
|
|
if ( !idLexer::ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
depth = 1;
|
|
skipWhite = false;
|
|
doTabs = ( tabs >= 0 );
|
|
|
|
while( depth ) {
|
|
if ( !*idLexer::script_p ) {
|
|
return false;
|
|
}
|
|
|
|
char c = *(idLexer::script_p++);
|
|
|
|
switch ( c ) {
|
|
case '\t':
|
|
case ' ': {
|
|
if ( skipWhite ) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case '\n': {
|
|
if ( doTabs ) {
|
|
skipWhite = true;
|
|
continue;
|
|
}
|
|
line++;
|
|
break;
|
|
}
|
|
case '{': {
|
|
depth++;
|
|
tabs++;
|
|
break;
|
|
}
|
|
case '}': {
|
|
depth--;
|
|
tabs--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( skipWhite ) {
|
|
skipWhite = false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::UnreadToken
|
|
================
|
|
*/
|
|
void idLexer::UnreadToken( const idToken *token ) {
|
|
idToken& tokenLocal = tokens.Alloc();
|
|
tokenLocal = *token;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadTokenOnLine
|
|
================
|
|
*/
|
|
int idLexer::ReadTokenOnLine( idToken *token ) {
|
|
idToken tok;
|
|
|
|
if ( !ReadToken( &tok ) ) {
|
|
return false;
|
|
}
|
|
|
|
// if no lines were crossed before this token
|
|
if ( !tok.linesCrossed ) {
|
|
*token = tok;
|
|
return true;
|
|
}
|
|
|
|
// restore our position
|
|
UnreadToken( &tok );
|
|
|
|
token->Clear();
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ParseInt
|
|
================
|
|
*/
|
|
int idLexer::ParseInt( void ) {
|
|
idToken token;
|
|
|
|
if ( !idLexer::ReadToken( &token ) ) {
|
|
idLexer::Error( "couldn't read expected integer" );
|
|
return 0;
|
|
}
|
|
if ( token.type == TT_PUNCTUATION && token == "-" ) {
|
|
ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
|
|
return -((signed int) token.GetIntValue());
|
|
}
|
|
else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) {
|
|
idLexer::Error( "expected integer value, found '%s'", token.c_str() );
|
|
}
|
|
return token.GetIntValue();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ParseBool
|
|
================
|
|
*/
|
|
bool idLexer::ParseBool( void ) {
|
|
idToken token;
|
|
|
|
if ( !idLexer::ExpectTokenType( TT_NUMBER, 0, &token ) ) {
|
|
idLexer::Error( "couldn't read expected boolean" );
|
|
return false;
|
|
}
|
|
return ( token.GetIntValue() != 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ParseFloat
|
|
================
|
|
*/
|
|
float idLexer::ParseFloat( bool *errorFlag ) {
|
|
idToken token;
|
|
|
|
if ( errorFlag ) {
|
|
*errorFlag = false;
|
|
}
|
|
|
|
if ( !idLexer::ReadToken( &token ) ) {
|
|
if ( errorFlag ) {
|
|
idLexer::Warning( "couldn't read expected floating point number" );
|
|
*errorFlag = true;
|
|
} else {
|
|
idLexer::Error( "couldn't read expected floating point number" );
|
|
}
|
|
return 0;
|
|
}
|
|
if ( token.type == TT_PUNCTUATION && token == "-" ) {
|
|
idLexer::ExpectTokenType( TT_NUMBER, 0, &token );
|
|
return -token.GetFloatValue();
|
|
}
|
|
else if ( token.type != TT_NUMBER ) {
|
|
if ( errorFlag ) {
|
|
idLexer::Warning( "expected float value, found '%s'", token.c_str() );
|
|
*errorFlag = true;
|
|
} else {
|
|
idLexer::Error( "expected float value, found '%s'", token.c_str() );
|
|
}
|
|
}
|
|
return token.GetFloatValue();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Parse1DMatrix
|
|
================
|
|
*/
|
|
int idLexer::Parse1DMatrix( int x, float *m, bool expectCommas ) {
|
|
int i;
|
|
|
|
if ( !idLexer::ExpectTokenString( "(" ) ) {
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0; i < x; i++ ) {
|
|
m[i] = idLexer::ParseFloat();
|
|
if ( expectCommas && i != x - 1 ) {
|
|
if ( !idLexer::ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !idLexer::ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Parse2DMatrix
|
|
================
|
|
*/
|
|
int idLexer::Parse2DMatrix( int y, int x, float *m ) {
|
|
int i;
|
|
|
|
if ( !idLexer::ExpectTokenString( "(" ) ) {
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0; i < y; i++ ) {
|
|
if ( !idLexer::Parse1DMatrix( x, m + i * x ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( !idLexer::ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Parse3DMatrix
|
|
================
|
|
*/
|
|
int idLexer::Parse3DMatrix( int z, int y, int x, float *m ) {
|
|
int i;
|
|
|
|
if ( !idLexer::ExpectTokenString( "(" ) ) {
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0 ; i < z; i++ ) {
|
|
if ( !idLexer::Parse2DMatrix( y, x, m + i * x*y ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( !idLexer::ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::ParseBracedSectionExact
|
|
|
|
The next token should be an open brace.
|
|
Parses until a matching close brace is found.
|
|
Maintains exact characters between braces.
|
|
|
|
FIXME: this should use ReadToken and replace the token white space with correct indents and newlines
|
|
=================
|
|
*/
|
|
bool idLexer::ParseBracedSectionExact( idStr &out, int tabs, bool parseFirstBrace ) {
|
|
int depth;
|
|
bool doTabs;
|
|
bool skipWhite;
|
|
|
|
sdStringBuilder_Heap builder;
|
|
|
|
out.Empty();
|
|
|
|
if ( parseFirstBrace ) {
|
|
if ( !idLexer::ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
builder = "{";
|
|
}
|
|
|
|
depth = 1;
|
|
skipWhite = false;
|
|
doTabs = ( tabs >= 0 );
|
|
|
|
while( depth ) {
|
|
if ( !*idLexer::script_p ) {
|
|
return false;
|
|
}
|
|
|
|
char c = *(idLexer::script_p++);
|
|
|
|
switch ( c ) {
|
|
case '\t':
|
|
case ' ': {
|
|
if ( skipWhite ) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case '\n': {
|
|
if ( doTabs ) {
|
|
skipWhite = true;
|
|
builder += c;
|
|
continue;
|
|
}
|
|
line++;
|
|
break;
|
|
}
|
|
case '{': {
|
|
depth++;
|
|
tabs++;
|
|
break;
|
|
}
|
|
case '}': {
|
|
depth--;
|
|
tabs--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( skipWhite ) {
|
|
int i = tabs;
|
|
if ( c == '{' ) {
|
|
i--;
|
|
}
|
|
skipWhite = false;
|
|
for ( ; i > 0; i-- ) {
|
|
builder += '\t';
|
|
}
|
|
}
|
|
builder += c;
|
|
}
|
|
builder.ToString( out );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::ParseBracedSection
|
|
|
|
The next token should be an open brace.
|
|
Parses until a matching close brace is found.
|
|
Internal brace depths are properly skipped.
|
|
=================
|
|
*/
|
|
const char *idLexer::ParseBracedSection( idStr &out, int tabs, bool parseFirstBrace, char intro, char outro ) {
|
|
idToken token;
|
|
int i, depth;
|
|
bool doTabs;
|
|
|
|
sdStringBuilder_Heap builder;
|
|
|
|
char temp[ 2 ] = { '\0', '\0' };
|
|
temp[ 0 ] = intro;
|
|
|
|
out.Empty();
|
|
if ( parseFirstBrace ) {
|
|
if ( !idLexer::ExpectTokenString( temp ) ) {
|
|
return out.c_str();
|
|
}
|
|
builder = temp;
|
|
}
|
|
depth = 1;
|
|
doTabs = ( tabs >= 0 );
|
|
do {
|
|
if ( !idLexer::ReadToken( &token ) ) {
|
|
Error( "missing closing brace" );
|
|
builder.ToString( out );
|
|
return out.c_str();
|
|
}
|
|
|
|
// if the token is on a new line
|
|
for ( i = 0; i < token.linesCrossed; i++ ) {
|
|
builder += "\r\n";
|
|
}
|
|
|
|
if ( doTabs && token.linesCrossed ) {
|
|
i = tabs;
|
|
if ( token[0] == '}' && i > 0 ) {
|
|
i--;
|
|
}
|
|
while( i-- > 0 ) {
|
|
builder += "\t";
|
|
}
|
|
}
|
|
if ( token.type != TT_STRING ) {
|
|
if ( token[ 0 ] == intro ) {
|
|
depth++;
|
|
if ( doTabs ) {
|
|
tabs++;
|
|
}
|
|
} else if ( token[ 0 ] == outro ) {
|
|
depth--;
|
|
if ( doTabs ) {
|
|
tabs--;
|
|
}
|
|
}
|
|
if ( builder.Length() ) {
|
|
builder += " ";
|
|
}
|
|
builder += token;
|
|
} else {
|
|
if ( builder.Length() ) {
|
|
builder += " ";
|
|
}
|
|
builder += "\"" + token + "\"";
|
|
}
|
|
} while( depth );
|
|
|
|
builder.ToString( out );
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::ParseRestOfLine
|
|
|
|
Like ParseCompleteLine, but doesn't advance to next line and doesn't return \n
|
|
It does, however, trim leading and trailing white space
|
|
=================
|
|
*/
|
|
const char *idLexer::ParseRestOfLine( idStr &out ) {
|
|
idToken token;
|
|
const char *start;
|
|
|
|
start = idLexer::script_p;
|
|
|
|
while ( 1 ) {
|
|
// end of buffer
|
|
if ( *idLexer::script_p == 0 ) {
|
|
break;
|
|
}
|
|
if ( *idLexer::script_p == '\n' ) {
|
|
break;
|
|
}
|
|
idLexer::script_p++;
|
|
}
|
|
|
|
// Trim leading white space
|
|
while( *start <= ' ' && start < idLexer::script_p ) {
|
|
start++;
|
|
}
|
|
// Trim trailing white space
|
|
while( *(idLexer::script_p-1) <= ' ' && idLexer::script_p > start ) {
|
|
idLexer::script_p--;
|
|
}
|
|
|
|
out.Empty();
|
|
out.Append( start, idLexer::script_p - start );
|
|
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::ParseRestOfLine
|
|
|
|
Returns a string up to the \n, but doesn't eat any whitespace at the beginning of the next line
|
|
=================
|
|
*/
|
|
const char *idLexer::ParseCompleteLine( idStr &out ) {
|
|
idToken token;
|
|
const char *start;
|
|
|
|
start = idLexer::script_p;
|
|
|
|
while ( 1 ) {
|
|
// end of buffer
|
|
if ( *idLexer::script_p == 0 ) {
|
|
break;
|
|
}
|
|
if ( *idLexer::script_p == '\n' ) {
|
|
idLexer::line++;
|
|
idLexer::script_p++;
|
|
break;
|
|
}
|
|
idLexer::script_p++;
|
|
}
|
|
|
|
out.Empty();
|
|
out.Append( start, idLexer::script_p - start );
|
|
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetNextWhiteSpace
|
|
================
|
|
*/
|
|
int idLexer::GetNextWhiteSpace( idStr &whiteSpace, bool currentLine ) {
|
|
whiteSpaceStart_p = script_p;
|
|
SkipWhiteSpace( currentLine );
|
|
whiteSpaceEnd_p = script_p;
|
|
return GetLastWhiteSpace( whiteSpace );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetLastWhiteSpace
|
|
================
|
|
*/
|
|
int idLexer::GetLastWhiteSpace( idStr &whiteSpace ) const {
|
|
whiteSpace.Clear();
|
|
for ( const char *p = whiteSpaceStart_p; p < whiteSpaceEnd_p; p++ ) {
|
|
whiteSpace.Append( *p );
|
|
}
|
|
return whiteSpace.Length();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetLastWhiteSpaceStart
|
|
================
|
|
*/
|
|
int idLexer::GetLastWhiteSpaceStart( void ) const {
|
|
return whiteSpaceStart_p - buffer;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetLastWhiteSpaceEnd
|
|
================
|
|
*/
|
|
int idLexer::GetLastWhiteSpaceEnd( void ) const {
|
|
return whiteSpaceEnd_p - buffer;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Reset
|
|
================
|
|
*/
|
|
void idLexer::Reset( void ) {
|
|
// pointer in script buffer
|
|
idLexer::script_p = idLexer::buffer;
|
|
// pointer in script buffer before reading token
|
|
idLexer::lastScript_p = idLexer::buffer;
|
|
// begin of white space
|
|
idLexer::whiteSpaceStart_p = NULL;
|
|
// end of white space
|
|
idLexer::whiteSpaceEnd_p = NULL;
|
|
|
|
tokens.Clear();
|
|
|
|
idLexer::line = 1;
|
|
idLexer::lastline = 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::EndOfFile
|
|
================
|
|
*/
|
|
int idLexer::EndOfFile( void ) const {
|
|
if( binary.IsLoaded() ) {
|
|
return binary.EndOfFile();
|
|
}
|
|
return idLexer::script_p >= idLexer::end_p;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::NumLinesCrossed
|
|
================
|
|
*/
|
|
int idLexer::NumLinesCrossed( void ) const {
|
|
return idLexer::line - idLexer::lastline;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::LoadFile
|
|
================
|
|
*/
|
|
bool idLexer::LoadFile( const char *filename, bool OSPath, int startLine ) {
|
|
idFile *fp;
|
|
idStr pathname;
|
|
int length;
|
|
char *buf;
|
|
|
|
if ( idLexer::loaded ) {
|
|
idLib::common->Error("idLexer::LoadFile: another script already loaded");
|
|
return false;
|
|
}
|
|
/*
|
|
if ( !OSPath ) {
|
|
if ( binary.ReadFile( filename ) ) {
|
|
this->loaded = true;
|
|
this->filename = fileSystem->RelativePathToOSPath( filename, "fs_basepath" );
|
|
this->filename.CollapsePath();
|
|
return true;
|
|
}
|
|
}
|
|
*/
|
|
if ( !OSPath && ( baseFolder[0] != '\0' ) ) {
|
|
pathname = va( "%s/%s", baseFolder, filename );
|
|
} else {
|
|
pathname = filename;
|
|
}
|
|
if ( OSPath ) {
|
|
fp = idLib::fileSystem->OpenExplicitFileRead( pathname );
|
|
} else {
|
|
fp = idLib::fileSystem->OpenFileRead( pathname );
|
|
}
|
|
if ( fp == NULL ) {
|
|
return false;
|
|
}
|
|
length = fp->Length();
|
|
buf = (char *) Mem_Alloc( length + 1 );
|
|
buf[length] = '\0';
|
|
fp->Read( buf, length );
|
|
this->fileTime = fp->Timestamp();
|
|
if ( OSPath ) {
|
|
this->filename = fp->GetFullPath();
|
|
} else {
|
|
this->filename = pathname;
|
|
}
|
|
this->filename.CollapsePath();
|
|
idLib::fileSystem->CloseFile( fp );
|
|
|
|
this->buffer = buf;
|
|
this->length = length;
|
|
// pointer in script buffer
|
|
this->script_p = this->buffer;
|
|
// pointer in script buffer before reading token
|
|
this->lastScript_p = this->buffer;
|
|
// pointer to end of script buffer
|
|
this->end_p = &(this->buffer[length]);
|
|
|
|
tokens.Clear();
|
|
|
|
this->line = startLine;
|
|
this->lastline = startLine;
|
|
this->allocated = true;
|
|
this->loaded = true;
|
|
/*
|
|
if ( !OSPath ) {
|
|
binary.WriteFile( *this, pathname );
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::LoadMemory
|
|
================
|
|
*/
|
|
bool idLexer::LoadMemory( const char *ptr, int length, const char *name, int startLine ) {
|
|
if ( idLexer::loaded ) {
|
|
idLib::common->Error("idLexer::LoadMemory: another script already loaded");
|
|
return false;
|
|
}
|
|
idLexer::filename = name;
|
|
idLexer::filename.CollapsePath();
|
|
idLexer::buffer = ptr;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = length;
|
|
// pointer in script buffer
|
|
idLexer::script_p = idLexer::buffer;
|
|
// pointer in script buffer before reading token
|
|
idLexer::lastScript_p = idLexer::buffer;
|
|
// pointer to end of script buffer
|
|
idLexer::end_p = &(idLexer::buffer[length]);
|
|
|
|
tokens.Clear();
|
|
idLexer::line = startLine;
|
|
idLexer::lastline = startLine;
|
|
idLexer::allocated = false;
|
|
idLexer::loaded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idLexer::LoadMemoryBinary
|
|
================
|
|
*/
|
|
bool idLexer::LoadMemoryBinary( const byte* ptr, int length, const char *name, idTokenCache* globals ) {
|
|
if( ptr == NULL || length == 0 ) {
|
|
return false;
|
|
}
|
|
idLexer::filename = name;
|
|
idLexer::filename.CollapsePath();
|
|
idLexer::buffer = NULL;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = 0;
|
|
// pointer in script buffer
|
|
idLexer::script_p = NULL;
|
|
|
|
idLexer::lastScript_p = NULL;
|
|
// pointer to end of script buffer
|
|
idLexer::end_p = NULL;
|
|
|
|
tokens.Clear();
|
|
idLexer::line = 0;
|
|
idLexer::lastline = 0;
|
|
idLexer::allocated = false;
|
|
idLexer::loaded = true;
|
|
|
|
sdLibFileMemoryPtr memFile( idLib::fileSystem->OpenMemoryFile( name ) );
|
|
|
|
memFile->SetData( reinterpret_cast< const char* >( ptr ), length );
|
|
|
|
bool retVal = binary.Read( memFile.Get() );
|
|
binary.SetData( NULL, globals );
|
|
return retVal;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idLexer::LoadTokenStream
|
|
============
|
|
*/
|
|
bool idLexer::LoadTokenStream( const idList<unsigned short>& indices, const idTokenCache& tokens, const char* name ) {
|
|
idLexer::filename = name;
|
|
idLexer::filename.CollapsePath();
|
|
idLexer::buffer = NULL;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = 0;
|
|
// pointer in script buffer
|
|
idLexer::script_p = NULL;
|
|
|
|
idLexer::lastScript_p = NULL;
|
|
// pointer to end of script buffer
|
|
idLexer::end_p = NULL;
|
|
|
|
idLexer::tokens.Clear();
|
|
idLexer::line = 0;
|
|
idLexer::lastline = 0;
|
|
idLexer::allocated = false;
|
|
idLexer::loaded = true;
|
|
|
|
binary.Clear();
|
|
binary.SetData( &indices, &tokens );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::FreeSource
|
|
================
|
|
*/
|
|
void idLexer::FreeSource( void ) {
|
|
#ifdef PUNCTABLE
|
|
if ( idLexer::punctuationtable && idLexer::punctuationtable != default_punctuationtable ) {
|
|
Mem_Free( (void *) idLexer::punctuationtable );
|
|
idLexer::punctuationtable = NULL;
|
|
}
|
|
if ( idLexer::nextpunctuation && idLexer::nextpunctuation != default_nextpunctuation ) {
|
|
Mem_Free( (void *) idLexer::nextpunctuation );
|
|
idLexer::nextpunctuation = NULL;
|
|
}
|
|
#endif //PUNCTABLE
|
|
if ( idLexer::allocated ) {
|
|
Mem_Free( (void *) idLexer::buffer );
|
|
idLexer::buffer = NULL;
|
|
idLexer::allocated = false;
|
|
}
|
|
idLexer::filename.Clear();
|
|
idLexer::tokens.Clear();
|
|
idLexer::loaded = false;
|
|
binary.Clear();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer( void ) {
|
|
idLexer::loaded = false;
|
|
idLexer::filename = "";
|
|
idLexer::flags = 0;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = 0;
|
|
idLexer::line = 0;
|
|
idLexer::lastline = 0;
|
|
tokens.Clear();
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
idLexer::hadWarning = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer( int flags ) {
|
|
idLexer::loaded = false;
|
|
idLexer::filename = "";
|
|
this->flags = flags;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = 0;
|
|
idLexer::line = 0;
|
|
idLexer::lastline = 0;
|
|
tokens.Clear();
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
idLexer::hadWarning = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer( const char *filename, int flags, bool OSPath, int startLine ) {
|
|
idLexer::loaded = false;
|
|
this->flags = flags;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
tokens.Clear();
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
idLexer::hadWarning = false;
|
|
idLexer::LoadFile( filename, OSPath, startLine );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer( const char *ptr, int length, const char *name, int flags, int startLine ) {
|
|
idLexer::loaded = false;
|
|
this->flags = flags;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
tokens.Clear();
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
idLexer::hadWarning = false;
|
|
idLexer::LoadMemory( ptr, length, name, startLine );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::~idLexer
|
|
================
|
|
*/
|
|
idLexer::~idLexer( void ) {
|
|
idLexer::FreeSource();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SetBaseFolder
|
|
================
|
|
*/
|
|
void idLexer::SetBaseFolder( const char *path ) {
|
|
idStr::Copynz( baseFolder, path, sizeof( baseFolder ) );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::HadError
|
|
================
|
|
*/
|
|
bool idLexer::HadError( void ) const {
|
|
return hadError;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::HadWarning
|
|
================
|
|
*/
|
|
bool idLexer::HadWarning( void ) const {
|
|
return hadWarning;
|
|
}
|