dhewm3-sdk/idlib/Parser.cpp
Daniel Gibson af8e8bda20 Fix date/time handling in idParser::ExpandBuiltinDefine()
this was so broken, I think if anyone ever tried to use __DATE__ or
__TIME__ it must've crashed, either from free(curtime) (which was the
return value of ctime()) or from things like token[7] = NULL when token
was a pointer (to a single element!).

I think it could work now.

Also fixed potential memory leaks in cases that don't "return" anything
2024-03-18 23:21:41 +01:00

3280 lines
73 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "idlib/Lib.h"
#include "framework/Common.h"
#include "idlib/Parser.h"
//#define DEBUG_EVAL
#define MAX_DEFINEPARMS 128
#define DEFINEHASHSIZE 2048
#define TOKEN_FL_RECURSIVE_DEFINE 1
define_t * idParser::globaldefines;
/*
================
idParser::SetBaseFolder
================
*/
void idParser::SetBaseFolder( const char *path) {
idLexer::SetBaseFolder(path);
}
/*
================
idParser::AddGlobalDefine
================
*/
int idParser::AddGlobalDefine( const char *string ) {
define_t *define;
define = idParser::DefineFromString(string);
if (!define) {
return false;
}
define->next = globaldefines;
globaldefines = define;
return true;
}
/*
================
idParser::RemoveGlobalDefine
================
*/
int idParser::RemoveGlobalDefine( const char *name ) {
define_t *d, *prev;
for ( prev = NULL, d = idParser::globaldefines; d; prev = d, d = d->next ) {
if ( !strcmp( d->name, name ) ) {
break;
}
}
if ( d ) {
if ( prev ) {
prev->next = d->next;
}
else {
idParser::globaldefines = d->next;
}
idParser::FreeDefine( d );
return true;
}
return false;
}
/*
================
idParser::RemoveAllGlobalDefines
================
*/
void idParser::RemoveAllGlobalDefines( void ) {
define_t *define;
for ( define = globaldefines; define; define = globaldefines ) {
globaldefines = globaldefines->next;
idParser::FreeDefine(define);
}
}
/*
===============================================================================
idParser
===============================================================================
*/
/*
================
idParser::PrintDefine
================
*/
void idParser::PrintDefine( define_t *define ) {
idLib::common->Printf("define->name = %s\n", define->name);
idLib::common->Printf("define->flags = %d\n", define->flags);
idLib::common->Printf("define->builtin = %d\n", define->builtin);
idLib::common->Printf("define->numparms = %d\n", define->numparms);
}
/*
================
PC_PrintDefineHashTable
================
* /
static void PC_PrintDefineHashTable(define_t **definehash) {
int i;
define_t *d;
for (i = 0; i < DEFINEHASHSIZE; i++) {
Log_Write("%4d:", i);
for (d = definehash[i]; d; d = d->hashnext) {
Log_Write(" %s", d->name);
}
Log_Write("\n");
}
}
*/
/*
================
PC_NameHash
================
*/
ID_INLINE int PC_NameHash( const char *name ) {
int hash, i;
hash = 0;
for ( i = 0; name[i] != '\0'; i++ ) {
hash += name[i] * (119 + i);
}
hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1);
return hash;
}
/*
================
idParser::AddDefineToHash
================
*/
void idParser::AddDefineToHash( define_t *define, define_t **definehash ) {
int hash;
hash = PC_NameHash(define->name);
define->hashnext = definehash[hash];
definehash[hash] = define;
}
/*
================
FindHashedDefine
================
*/
define_t *idParser::FindHashedDefine( define_t **definehash, const char *name ) {
define_t *d;
int hash;
hash = PC_NameHash(name);
for ( d = definehash[hash]; d; d = d->hashnext ) {
if ( !strcmp(d->name, name) ) {
return d;
}
}
return NULL;
}
/*
================
idParser::FindDefine
================
*/
define_t *idParser::FindDefine( define_t *defines, const char *name ) {
define_t *d;
for ( d = defines; d; d = d->next ) {
if ( !strcmp(d->name, name) ) {
return d;
}
}
return NULL;
}
/*
================
idParser::FindDefineParm
================
*/
int idParser::FindDefineParm( define_t *define, const char *name ) {
idToken *p;
int i;
i = 0;
for ( p = define->parms; p; p = p->next ) {
if ( (*p) == name ) {
return i;
}
i++;
}
return -1;
}
/*
================
idParser::CopyDefine
================
*/
define_t *idParser::CopyDefine( define_t *define ) {
define_t *newdefine;
idToken *token, *newtoken, *lasttoken;
newdefine = (define_t *) Mem_Alloc(sizeof(define_t) + strlen(define->name) + 1);
//copy the define name
newdefine->name = (char *) newdefine + sizeof(define_t);
strcpy(newdefine->name, define->name);
newdefine->flags = define->flags;
newdefine->builtin = define->builtin;
newdefine->numparms = define->numparms;
//the define is not linked
newdefine->next = NULL;
newdefine->hashnext = NULL;
//copy the define tokens
newdefine->tokens = NULL;
for (lasttoken = NULL, token = define->tokens; token; token = token->next) {
newtoken = new idToken(token);
newtoken->next = NULL;
if (lasttoken) lasttoken->next = newtoken;
else newdefine->tokens = newtoken;
lasttoken = newtoken;
}
//copy the define parameters
newdefine->parms = NULL;
for (lasttoken = NULL, token = define->parms; token; token = token->next) {
newtoken = new idToken(token);
newtoken->next = NULL;
if (lasttoken) lasttoken->next = newtoken;
else newdefine->parms = newtoken;
lasttoken = newtoken;
}
return newdefine;
}
/*
================
idParser::FreeDefine
================
*/
void idParser::FreeDefine( define_t *define ) {
idToken *t, *next;
//free the define parameters
for (t = define->parms; t; t = next) {
next = t->next;
delete t;
}
//free the define tokens
for (t = define->tokens; t; t = next) {
next = t->next;
delete t;
}
//free the define
Mem_Free( define );
}
/*
================
idParser::DefineFromString
================
*/
define_t *idParser::DefineFromString( const char *string ) {
idParser src;
define_t *def;
if ( !src.LoadMemory(string, strlen(string), "*defineString") ) {
return NULL;
}
// create a define from the source
if ( !src.Directive_define() ) {
src.FreeSource();
return NULL;
}
def = src.CopyFirstDefine();
src.FreeSource();
//if the define was created succesfully
return def;
}
/*
================
idParser::Error
================
*/
void idParser::Error( const char *str, ... ) const {
char text[MAX_STRING_CHARS];
va_list ap;
va_start(ap, str);
vsprintf(text, str, ap);
va_end(ap);
if ( idParser::scriptstack ) {
idParser::scriptstack->Error( "%s", text );
}
}
/*
================
idParser::Warning
================
*/
void idParser::Warning( const char *str, ... ) const {
char text[MAX_STRING_CHARS];
va_list ap;
va_start(ap, str);
vsprintf(text, str, ap);
va_end(ap);
if ( idParser::scriptstack ) {
idParser::scriptstack->Warning( "%s", text );
}
}
/*
================
idParser::PushIndent
================
*/
void idParser::PushIndent( int type, int skip ) {
indent_t *indent;
indent = (indent_t *) Mem_Alloc(sizeof(indent_t));
indent->type = type;
indent->script = idParser::scriptstack;
indent->skip = (skip != 0);
idParser::skip += indent->skip;
indent->next = idParser::indentstack;
idParser::indentstack = indent;
}
/*
================
idParser::PopIndent
================
*/
void idParser::PopIndent( int *type, int *skip ) {
indent_t *indent;
*type = 0;
*skip = 0;
indent = idParser::indentstack;
if (!indent) return;
// must be an indent from the current script
if (idParser::indentstack->script != idParser::scriptstack) {
return;
}
*type = indent->type;
*skip = indent->skip;
idParser::indentstack = idParser::indentstack->next;
idParser::skip -= indent->skip;
Mem_Free( indent );
}
/*
================
idParser::PushScript
================
*/
void idParser::PushScript( idLexer *script ) {
idLexer *s;
for ( s = idParser::scriptstack; s; s = s->next ) {
if ( !idStr::Icmp(s->GetFileName(), script->GetFileName()) ) {
idParser::Warning( "'%s' recursively included", script->GetFileName() );
return;
}
}
//push the script on the script stack
script->next = idParser::scriptstack;
idParser::scriptstack = script;
}
/*
================
idParser::ReadSourceToken
================
*/
int idParser::ReadSourceToken( idToken *token ) {
idToken *t;
idLexer *script;
int type, skip, changedScript;
if ( !idParser::scriptstack ) {
idLib::common->FatalError( "idParser::ReadSourceToken: not loaded" );
return false;
}
changedScript = 0;
// if there's no token already available
while( !idParser::tokens ) {
// if there's a token to read from the script
if ( idParser::scriptstack->ReadToken( token ) ) {
token->linesCrossed += changedScript;
// set the marker based on the start of the token read in
if ( !marker_p ) {
marker_p = token->whiteSpaceEnd_p;
}
return true;
}
// if at the end of the script
if ( idParser::scriptstack->EndOfFile() ) {
// remove all indents of the script
while( idParser::indentstack && idParser::indentstack->script == idParser::scriptstack ) {
idParser::Warning( "missing #endif" );
idParser::PopIndent( &type, &skip );
}
changedScript = 1;
}
// if this was the initial script
if ( !idParser::scriptstack->next ) {
return false;
}
// remove the script and return to the previous one
script = idParser::scriptstack;
idParser::scriptstack = idParser::scriptstack->next;
delete script;
}
// copy the already available token
*token = idParser::tokens;
// remove the token from the source
t = idParser::tokens;
idParser::tokens = idParser::tokens->next;
delete t;
return true;
}
/*
================
idParser::UnreadSourceToken
================
*/
int idParser::UnreadSourceToken( idToken *token ) {
idToken *t;
t = new idToken(token);
t->next = idParser::tokens;
idParser::tokens = t;
return true;
}
/*
================
idParser::ReadDefineParms
================
*/
int idParser::ReadDefineParms( define_t *define, idToken **parms, int maxparms ) {
define_t *newdefine;
idToken token, *t, *last;
int i, done, lastcomma, numparms, indent;
if ( !idParser::ReadSourceToken( &token ) ) {
idParser::Error( "define '%s' missing parameters", define->name );
return false;
}
if ( define->numparms > maxparms ) {
idParser::Error( "define with more than %d parameters", maxparms );
return false;
}
for ( i = 0; i < define->numparms; i++ ) {
parms[i] = NULL;
}
// if no leading "("
if ( token != "(" ) {
idParser::UnreadSourceToken( &token );
idParser::Error( "define '%s' missing parameters", define->name );
return false;
}
// read the define parameters
for ( done = 0, numparms = 0, indent = 1; !done; ) {
if ( numparms >= maxparms ) {
idParser::Error( "define '%s' with too many parameters", define->name );
return false;
}
parms[numparms] = NULL;
lastcomma = 1;
last = NULL;
while( !done ) {
if ( !idParser::ReadSourceToken( &token ) ) {
idParser::Error( "define '%s' incomplete", define->name );
return false;
}
if ( token == "," ) {
if ( indent <= 1 ) {
if ( lastcomma ) {
idParser::Warning( "too many comma's" );
}
if ( numparms >= define->numparms ) {
idParser::Warning( "too many define parameters" );
}
lastcomma = 1;
break;
}
}
else if ( token == "(" ) {
indent++;
}
else if ( token == ")" ) {
indent--;
if ( indent <= 0 ) {
if ( !parms[define->numparms-1] ) {
idParser::Warning( "too few define parameters" );
}
done = 1;
break;
}
}
else if ( token.type == TT_NAME ) {
newdefine = FindHashedDefine( idParser::definehash, token.c_str() );
if ( newdefine ) {
if ( !idParser::ExpandDefineIntoSource( &token, newdefine ) ) {
return false;
}
continue;
}
}
lastcomma = 0;
if ( numparms < define->numparms ) {
t = new idToken( token );
t->next = NULL;
if (last) last->next = t;
else parms[numparms] = t;
last = t;
}
}
numparms++;
}
return true;
}
/*
================
idParser::StringizeTokens
================
*/
int idParser::StringizeTokens( idToken *tokens, idToken *token ) {
idToken *t;
token->type = TT_STRING;
token->whiteSpaceStart_p = NULL;
token->whiteSpaceEnd_p = NULL;
(*token) = "";
for ( t = tokens; t; t = t->next ) {
token->Append( t->c_str() );
}
return true;
}
/*
================
idParser::MergeTokens
================
*/
int idParser::MergeTokens( idToken *t1, idToken *t2 ) {
// merging of a name with a name or number
if ( t1->type == TT_NAME && (t2->type == TT_NAME || (t2->type == TT_NUMBER && !(t2->subtype & TT_FLOAT))) ) {
t1->Append( t2->c_str() );
return true;
}
// merging of two strings
if (t1->type == TT_STRING && t2->type == TT_STRING) {
t1->Append( t2->c_str() );
return true;
}
// merging of two numbers
if ( t1->type == TT_NUMBER && t2->type == TT_NUMBER &&
!(t1->subtype & (TT_HEX|TT_BINARY)) && !(t2->subtype & (TT_HEX|TT_BINARY)) &&
(!(t1->subtype & TT_FLOAT) || !(t2->subtype & TT_FLOAT)) ) {
t1->Append( t2->c_str() );
return true;
}
return false;
}
/*
================
idParser::AddBuiltinDefines
================
*/
void idParser::AddBuiltinDefines( void ) {
int i;
define_t *define;
struct builtin
{
const char *string;
int id;
} builtin[] = {
{ "__LINE__", BUILTIN_LINE },
{ "__FILE__", BUILTIN_FILE },
{ "__DATE__", BUILTIN_DATE },
{ "__TIME__", BUILTIN_TIME },
{ "__STDC__", BUILTIN_STDC },
{ NULL, 0 }
};
for (i = 0; builtin[i].string; i++) {
define = (define_t *) Mem_Alloc(sizeof(define_t) + strlen(builtin[i].string) + 1);
define->name = (char *) define + sizeof(define_t);
strcpy(define->name, builtin[i].string);
define->flags = DEFINE_FIXED;
define->builtin = builtin[i].id;
define->numparms = 0;
define->parms = NULL;
define->tokens = NULL;
// add the define to the source
AddDefineToHash(define, idParser::definehash);
}
}
/*
================
idParser::CopyFirstDefine
================
*/
define_t *idParser::CopyFirstDefine( void ) {
int i;
for ( i = 0; i < DEFINEHASHSIZE; i++ ) {
if ( idParser::definehash[i] ) {
return CopyDefine(idParser::definehash[i]);
}
}
return NULL;
}
// simple abstraction around localtime()/localtime_r() or whatever
static bool my_localtime(const time_t* t, struct tm* ts)
{
// TODO: does any non-windows platform not support localtime_r()?
// then add them here (or handle them specially with their own #elif)
#ifdef _WIN32
// localtime() is C89 so it should be available everywhere, but it *may* not be threadsafe.
// It *is* threadsafe on Windows though (there it returns a thread-local variable).
// (yes, Windows has localtime_s(), but it's unclear if MinGW supports that
// or localtime_r() and in the end, *on Windows* localtime() is safe so use it)
struct tm* tmp = localtime(t);
if(tmp != NULL) {
*ts = *tmp;
return true;
}
#else // localtime_r() assumed available, use it (all Unix-likes incl. macOS support it, AROS as well)
if(localtime_r(t, ts) != NULL)
return true;
#endif
// if we get here, the localtime() (or localtime_r() or whatever) call failed
memset(ts, 0, sizeof(*ts)); // make sure ts has deterministic content
return false;
}
/*
================
idParser::ExpandBuiltinDefine
================
*/
int idParser::ExpandBuiltinDefine( idToken *deftoken, define_t *define, idToken **firsttoken, idToken **lasttoken ) {
idToken *token;
ID_TIME_T t;
char buf[MAX_STRING_CHARS];
token = new idToken(deftoken);
switch( define->builtin ) {
case BUILTIN_LINE: {
sprintf( buf, "%d", deftoken->line );
(*token) = buf;
token->intvalue = deftoken->line;
token->floatvalue = deftoken->line;
token->type = TT_NUMBER;
token->subtype = TT_DECIMAL | TT_INTEGER | TT_VALUESVALID;
token->line = deftoken->line;
token->linesCrossed = deftoken->linesCrossed;
token->flags = 0;
*firsttoken = token;
*lasttoken = token;
break;
}
case BUILTIN_FILE: {
(*token) = idParser::scriptstack->GetFileName();
token->type = TT_NAME;
token->subtype = token->Length();
token->line = deftoken->line;
token->linesCrossed = deftoken->linesCrossed;
token->flags = 0;
*firsttoken = token;
*lasttoken = token;
break;
}
case BUILTIN_DATE: {
t = time(NULL);
// DG: apparently this was supposed to extract the date part of "Wed Jun 30 21:49:08 1993\n"
// (like "Jun 30 1993") - it was both ugly and broken before I changed it, though
// Originally it copied stuff out of ctime(), which is ugly but ok, but also
// free'd the returned value (wrong) and tried to modify token in ways that should've crashed
// (like token[7] = NULL; which should've been (*token)[7] = '\0';)
struct tm ts;
my_localtime(&t, &ts);
strftime(buf, sizeof(buf), "\"%b %d %Y\"", &ts);
(*token) = buf;
token->type = TT_STRING;
token->subtype = token->Length();
token->line = deftoken->line;
token->linesCrossed = deftoken->linesCrossed;
token->flags = 0;
*firsttoken = token;
*lasttoken = token;
break;
}
case BUILTIN_TIME: {
t = time(NULL);
// DG: apparently this was supposed to extract the time part of "Wed Jun 30 21:49:08 1993\n"
// (like "21:49:08") - it was both ugly and broken before I changed it, though
// (had basicaly the same problems as BUILTIN_DATE)
struct tm ts;
my_localtime(&t, &ts);
strftime(buf, sizeof(buf), "\"%H:%M:%S\"", &ts);
(*token) = buf;
token->type = TT_STRING;
token->subtype = token->Length();
token->line = deftoken->line;
token->linesCrossed = deftoken->linesCrossed;
token->flags = 0;
*firsttoken = token;
*lasttoken = token;
break;
}
case BUILTIN_STDC: {
idParser::Warning( "__STDC__ not supported\n" );
delete token; // DG: we probably shouldn't leak it, right?
*firsttoken = NULL;
*lasttoken = NULL;
break;
}
default: {
delete token; // DG: we probably shouldn't leak it, right?
*firsttoken = NULL;
*lasttoken = NULL;
break;
}
}
return true;
}
/*
================
idParser::ExpandDefine
================
*/
int idParser::ExpandDefine( idToken *deftoken, define_t *define, idToken **firsttoken, idToken **lasttoken ) {
idToken *parms[MAX_DEFINEPARMS], *dt, *pt, *t;
idToken *t1, *t2, *first, *last, *nextpt, token;
int parmnum, i;
// if it is a builtin define
if ( define->builtin ) {
return idParser::ExpandBuiltinDefine( deftoken, define, firsttoken, lasttoken );
}
// if the define has parameters
if ( define->numparms ) {
if ( !idParser::ReadDefineParms( define, parms, MAX_DEFINEPARMS ) ) {
return false;
}
#ifdef DEBUG_EVAL
for ( i = 0; i < define->numparms; i++ ) {
Log_Write("define parms %d:", i);
for ( pt = parms[i]; pt; pt = pt->next ) {
Log_Write( "%s", pt->c_str() );
}
}
#endif //DEBUG_EVAL
}
// empty list at first
first = NULL;
last = NULL;
// create a list with tokens of the expanded define
for ( dt = define->tokens; dt; dt = dt->next ) {
parmnum = -1;
// if the token is a name, it could be a define parameter
if ( dt->type == TT_NAME ) {
parmnum = FindDefineParm( define, dt->c_str() );
}
// if it is a define parameter
if ( parmnum >= 0 ) {
for ( pt = parms[parmnum]; pt; pt = pt->next ) {
t = new idToken(pt);
//add the token to the list
t->next = NULL;
if (last) last->next = t;
else first = t;
last = t;
}
}
else {
// if stringizing operator
if ( (*dt) == "#" ) {
// the stringizing operator must be followed by a define parameter
if ( dt->next ) {
parmnum = FindDefineParm( define, dt->next->c_str() );
}
else {
parmnum = -1;
}
if ( parmnum >= 0 ) {
// step over the stringizing operator
dt = dt->next;
// stringize the define parameter tokens
if ( !idParser::StringizeTokens( parms[parmnum], &token ) ) {
idParser::Error( "can't stringize tokens" );
return false;
}
t = new idToken(token);
t->line = deftoken->line;
}
else {
idParser::Warning( "stringizing operator without define parameter" );
continue;
}
}
else {
t = new idToken(dt);
t->line = deftoken->line;
}
// add the token to the list
t->next = NULL;
// the token being read from the define list should use the line number of
// the original file, not the header file
t->line = deftoken->line;
if ( last ) last->next = t;
else first = t;
last = t;
}
}
// check for the merging operator
for ( t = first; t; ) {
if ( t->next ) {
// if the merging operator
if ( (*t->next) == "##" ) {
t1 = t;
t2 = t->next->next;
if ( t2 ) {
if ( !idParser::MergeTokens( t1, t2 ) ) {
idParser::Error( "can't merge '%s' with '%s'", t1->c_str(), t2->c_str() );
return false;
}
delete t1->next;
t1->next = t2->next;
if ( t2 == last ) last = t1;
delete t2;
continue;
}
}
}
t = t->next;
}
// store the first and last token of the list
*firsttoken = first;
*lasttoken = last;
// free all the parameter tokens
for ( i = 0; i < define->numparms; i++ ) {
for ( pt = parms[i]; pt; pt = nextpt ) {
nextpt = pt->next;
delete pt;
}
}
return true;
}
/*
================
idParser::ExpandDefineIntoSource
================
*/
int idParser::ExpandDefineIntoSource( idToken *deftoken, define_t *define ) {
idToken *firsttoken, *lasttoken;
if ( !idParser::ExpandDefine( deftoken, define, &firsttoken, &lasttoken ) ) {
return false;
}
// if the define is not empty
if ( firsttoken && lasttoken ) {
firsttoken->linesCrossed += deftoken->linesCrossed;
lasttoken->next = idParser::tokens;
idParser::tokens = firsttoken;
}
return true;
}
/*
================
idParser::ReadLine
reads a token from the current line, continues reading on the next
line only if a backslash '\' is found
================
*/
int idParser::ReadLine( idToken *token ) {
int crossline;
crossline = 0;
do {
if (!idParser::ReadSourceToken( token )) {
return false;
}
if (token->linesCrossed > crossline) {
idParser::UnreadSourceToken( token );
return false;
}
crossline = 1;
} while( (*token) == "\\" );
return true;
}
/*
================
idParser::Directive_include
================
*/
int idParser::Directive_include( void ) {
idLexer *script;
idToken token;
idStr path;
if ( !idParser::ReadSourceToken( &token ) ) {
idParser::Error( "#include without file name" );
return false;
}
if ( token.linesCrossed > 0 ) {
idParser::Error( "#include without file name" );
return false;
}
if ( token.type == TT_STRING ) {
script = new idLexer;
// try relative to the current file
path = scriptstack->GetFileName();
path.StripFilename();
path += "/";
path += token;
if ( !script->LoadFile( path, OSPath ) ) {
// try absolute path
path = token;
if ( !script->LoadFile( path, OSPath ) ) {
// try from the include path
path = includepath + token;
if ( !script->LoadFile( path, OSPath ) ) {
delete script;
script = NULL;
}
}
}
}
else if ( token.type == TT_PUNCTUATION && token == "<" ) {
path = idParser::includepath;
while( idParser::ReadSourceToken( &token ) ) {
if ( token.linesCrossed > 0 ) {
idParser::UnreadSourceToken( &token );
break;
}
if ( token.type == TT_PUNCTUATION && token == ">" ) {
break;
}
path += token;
}
if ( token != ">" ) {
idParser::Warning( "#include missing trailing >" );
}
if ( !path.Length() ) {
idParser::Error( "#include without file name between < >" );
return false;
}
if ( idParser::flags & LEXFL_NOBASEINCLUDES ) {
return true;
}
script = new idLexer;
if ( !script->LoadFile( includepath + path, OSPath ) ) {
delete script;
script = NULL;
}
}
else {
idParser::Error( "#include without file name" );
return false;
}
if (!script) {
idParser::Error( "file '%s' not found", path.c_str() );
return false;
}
script->SetFlags( idParser::flags );
script->SetPunctuations( idParser::punctuations );
idParser::PushScript( script );
return true;
}
/*
================
idParser::Directive_undef
================
*/
int idParser::Directive_undef( void ) {
idToken token;
define_t *define, *lastdefine;
int hash;
//
if (!idParser::ReadLine( &token )) {
idParser::Error( "undef without name" );
return false;
}
if (token.type != TT_NAME) {
idParser::UnreadSourceToken( &token );
idParser::Error( "expected name but found '%s'", token.c_str() );
return false;
}
hash = PC_NameHash( token.c_str() );
for (lastdefine = NULL, define = idParser::definehash[hash]; define; define = define->hashnext) {
if (!strcmp(define->name, token.c_str()))
{
if (define->flags & DEFINE_FIXED) {
idParser::Warning( "can't undef '%s'", token.c_str() );
}
else {
if (lastdefine) {
lastdefine->hashnext = define->hashnext;
}
else {
idParser::definehash[hash] = define->hashnext;
}
FreeDefine(define);
}
break;
}
lastdefine = define;
}
return true;
}
/*
================
idParser::Directive_define
================
*/
int idParser::Directive_define( void ) {
idToken token, *t, *last;
define_t *define;
if (!idParser::ReadLine( &token )) {
idParser::Error( "#define without name" );
return false;
}
if (token.type != TT_NAME) {
idParser::UnreadSourceToken( &token );
idParser::Error( "expected name after #define, found '%s'", token.c_str() );
return false;
}
// check if the define already exists
define = FindHashedDefine(idParser::definehash, token.c_str());
if (define) {
if (define->flags & DEFINE_FIXED) {
idParser::Error( "can't redefine '%s'", token.c_str() );
return false;
}
idParser::Warning( "redefinition of '%s'", token.c_str() );
// unread the define name before executing the #undef directive
idParser::UnreadSourceToken( &token );
if (!idParser::Directive_undef())
return false;
// if the define was not removed (define->flags & DEFINE_FIXED)
define = FindHashedDefine(idParser::definehash, token.c_str());
}
// allocate define
define = (define_t *) Mem_ClearedAlloc(sizeof(define_t) + token.Length() + 1);
define->name = (char *) define + sizeof(define_t);
strcpy(define->name, token.c_str());
// add the define to the source
AddDefineToHash(define, idParser::definehash);
// if nothing is defined, just return
if ( !idParser::ReadLine( &token ) ) {
return true;
}
// if it is a define with parameters
if ( token.WhiteSpaceBeforeToken() == 0 && token == "(" ) {
// read the define parameters
last = NULL;
if ( !idParser::CheckTokenString(")") ) {
while(1) {
if ( !idParser::ReadLine( &token ) ) {
idParser::Error( "expected define parameter" );
return false;
}
// if it isn't a name
if (token.type != TT_NAME) {
idParser::Error( "invalid define parameter" );
return false;
}
if (FindDefineParm(define, token.c_str()) >= 0) {
idParser::Error( "two the same define parameters" );
return false;
}
// add the define parm
t = new idToken(token);
t->ClearTokenWhiteSpace();
t->next = NULL;
if (last) last->next = t;
else define->parms = t;
last = t;
define->numparms++;
// read next token
if (!idParser::ReadLine( &token )) {
idParser::Error( "define parameters not terminated" );
return false;
}
if ( token == ")" ) {
break;
}
// then it must be a comma
if ( token != "," ) {
idParser::Error( "define not terminated" );
return false;
}
}
}
if ( !idParser::ReadLine( &token ) ) {
return true;
}
}
// read the defined stuff
last = NULL;
do
{
t = new idToken(token);
if ( t->type == TT_NAME && !strcmp( t->c_str(), define->name ) ) {
t->flags |= TOKEN_FL_RECURSIVE_DEFINE;
idParser::Warning( "recursive define (removed recursion)" );
}
t->ClearTokenWhiteSpace();
t->next = NULL;
if ( last ) last->next = t;
else define->tokens = t;
last = t;
} while( idParser::ReadLine( &token ) );
if ( last ) {
// check for merge operators at the beginning or end
if ( (*define->tokens) == "##" || (*last) == "##" ) {
idParser::Error( "define with misplaced ##" );
return false;
}
}
return true;
}
/*
================
idParser::AddDefine
================
*/
int idParser::AddDefine( const char *string ) {
define_t *define;
define = DefineFromString( string );
if (!define) {
return false;
}
AddDefineToHash(define, idParser::definehash);
return true;
}
/*
================
idParser::AddGlobalDefinesToSource
================
*/
void idParser::AddGlobalDefinesToSource( void ) {
define_t *define, *newdefine;
for (define = globaldefines; define; define = define->next) {
newdefine = CopyDefine( define );
AddDefineToHash(newdefine, idParser::definehash);
}
}
/*
================
idParser::Directive_if_def
================
*/
int idParser::Directive_if_def( int type ) {
idToken token;
define_t *d;
int skip;
if ( !idParser::ReadLine( &token ) ) {
idParser::Error( "#ifdef without name" );
return false;
}
if (token.type != TT_NAME) {
idParser::UnreadSourceToken( &token );
idParser::Error( "expected name after #ifdef, found '%s'", token.c_str() );
return false;
}
d = FindHashedDefine(idParser::definehash, token.c_str());
skip = (type == INDENT_IFDEF) == (d == NULL);
idParser::PushIndent( type, skip );
return true;
}
/*
================
idParser::Directive_ifdef
================
*/
int idParser::Directive_ifdef( void ) {
return idParser::Directive_if_def( INDENT_IFDEF );
}
/*
================
idParser::Directive_ifndef
================
*/
int idParser::Directive_ifndef( void ) {
return idParser::Directive_if_def( INDENT_IFNDEF );
}
/*
================
idParser::Directive_else
================
*/
int idParser::Directive_else( void ) {
int type, skip;
idParser::PopIndent( &type, &skip );
if (!type) {
idParser::Error( "misplaced #else" );
return false;
}
if (type == INDENT_ELSE) {
idParser::Error( "#else after #else" );
return false;
}
idParser::PushIndent( INDENT_ELSE, !skip );
return true;
}
/*
================
idParser::Directive_endif
================
*/
int idParser::Directive_endif( void ) {
int type, skip;
idParser::PopIndent( &type, &skip );
if (!type) {
idParser::Error( "misplaced #endif" );
return false;
}
return true;
}
/*
================
idParser::EvaluateTokens
================
*/
typedef struct operator_s
{
int op;
int priority;
int parentheses;
struct operator_s *prev, *next;
} operator_t;
typedef struct value_s
{
int intvalue;
double floatvalue;
int parentheses;
struct value_s *prev, *next;
} value_t;
int PC_OperatorPriority(int op) {
switch(op) {
case P_MUL: return 15;
case P_DIV: return 15;
case P_MOD: return 15;
case P_ADD: return 14;
case P_SUB: return 14;
case P_LOGIC_AND: return 7;
case P_LOGIC_OR: return 6;
case P_LOGIC_GEQ: return 12;
case P_LOGIC_LEQ: return 12;
case P_LOGIC_EQ: return 11;
case P_LOGIC_UNEQ: return 11;
case P_LOGIC_NOT: return 16;
case P_LOGIC_GREATER: return 12;
case P_LOGIC_LESS: return 12;
case P_RSHIFT: return 13;
case P_LSHIFT: return 13;
case P_BIN_AND: return 10;
case P_BIN_OR: return 8;
case P_BIN_XOR: return 9;
case P_BIN_NOT: return 16;
case P_COLON: return 5;
case P_QUESTIONMARK: return 5;
}
return false;
}
//#define AllocValue() GetClearedMemory(sizeof(value_t));
//#define FreeValue(val) FreeMemory(val)
//#define AllocOperator(op) op = (operator_t *) GetClearedMemory(sizeof(operator_t));
//#define FreeOperator(op) FreeMemory(op);
#define MAX_VALUES 64
#define MAX_OPERATORS 64
#define AllocValue(val) \
if ( numvalues >= MAX_VALUES ) { \
idParser::Error( "out of value space\n" ); \
error = 1; \
break; \
} \
else { \
val = &value_heap[numvalues++]; \
}
#define FreeValue(val)
#define AllocOperator(op) \
if ( numoperators >= MAX_OPERATORS ) { \
idParser::Error( "out of operator space\n" ); \
error = 1; \
break; \
} \
else { \
op = &operator_heap[numoperators++]; \
}
#define FreeOperator(op)
int idParser::EvaluateTokens( idToken *tokens, int *intvalue, double *floatvalue, int integer ) {
operator_t *o, *firstoperator, *lastoperator;
value_t *v, *firstvalue, *lastvalue, *v1, *v2;
idToken *t;
int brace = 0;
int parentheses = 0;
int error = 0;
int lastwasvalue = 0;
int negativevalue = 0;
int questmarkintvalue = 0;
double questmarkfloatvalue = 0;
int gotquestmarkvalue = false;
//
operator_t operator_heap[MAX_OPERATORS];
int numoperators = 0;
value_t value_heap[MAX_VALUES];
int numvalues = 0;
firstoperator = lastoperator = NULL;
firstvalue = lastvalue = NULL;
if (intvalue) *intvalue = 0;
if (floatvalue) *floatvalue = 0;
for ( t = tokens; t; t = t->next ) {
switch( t->type ) {
case TT_NAME:
{
if ( lastwasvalue || negativevalue ) {
idParser::Error( "syntax error in #if/#elif" );
error = 1;
break;
}
if ( (*t) != "defined" ) {
idParser::Error( "undefined name '%s' in #if/#elif", t->c_str() );
error = 1;
break;
}
t = t->next;
if ( (*t) == "(" ) {
brace = true;
t = t->next;
}
if (!t || t->type != TT_NAME) {
idParser::Error( "defined() without name in #if/#elif" );
error = 1;
break;
}
//v = (value_t *) GetClearedMemory(sizeof(value_t));
AllocValue(v);
if (FindHashedDefine(idParser::definehash, t->c_str())) {
v->intvalue = 1;
v->floatvalue = 1;
}
else {
v->intvalue = 0;
v->floatvalue = 0;
}
v->parentheses = parentheses;
v->next = NULL;
v->prev = lastvalue;
if (lastvalue) lastvalue->next = v;
else firstvalue = v;
lastvalue = v;
if (brace) {
t = t->next;
if (!t || (*t) != ")" ) {
idParser::Error( "defined missing ) in #if/#elif" );
error = 1;
break;
}
}
brace = false;
// defined() creates a value
lastwasvalue = 1;
break;
}
case TT_NUMBER:
{
if (lastwasvalue) {
idParser::Error( "syntax error in #if/#elif" );
error = 1;
break;
}
//v = (value_t *) GetClearedMemory(sizeof(value_t));
AllocValue(v);
if (negativevalue) {
v->intvalue = - t->GetIntValue();
v->floatvalue = - t->GetFloatValue();
}
else {
v->intvalue = t->GetIntValue();
v->floatvalue = t->GetFloatValue();
}
v->parentheses = parentheses;
v->next = NULL;
v->prev = lastvalue;
if (lastvalue) lastvalue->next = v;
else firstvalue = v;
lastvalue = v;
//last token was a value
lastwasvalue = 1;
//
negativevalue = 0;
break;
}
case TT_PUNCTUATION:
{
if (negativevalue) {
idParser::Error( "misplaced minus sign in #if/#elif" );
error = 1;
break;
}
if (t->subtype == P_PARENTHESESOPEN) {
parentheses++;
break;
}
else if (t->subtype == P_PARENTHESESCLOSE) {
parentheses--;
if (parentheses < 0) {
idParser::Error( "too many ) in #if/#elsif" );
error = 1;
}
break;
}
//check for invalid operators on floating point values
if ( !integer ) {
if (t->subtype == P_BIN_NOT || t->subtype == P_MOD ||
t->subtype == P_RSHIFT || t->subtype == P_LSHIFT ||
t->subtype == P_BIN_AND || t->subtype == P_BIN_OR ||
t->subtype == P_BIN_XOR) {
idParser::Error( "illigal operator '%s' on floating point operands\n", t->c_str() );
error = 1;
break;
}
}
switch( t->subtype ) {
case P_LOGIC_NOT:
case P_BIN_NOT:
{
if (lastwasvalue) {
idParser::Error( "! or ~ after value in #if/#elif" );
error = 1;
break;
}
break;
}
case P_INC:
case P_DEC:
{
idParser::Error( "++ or -- used in #if/#elif" );
break;
}
case P_SUB:
{
if (!lastwasvalue) {
negativevalue = 1;
break;
}
}
case P_MUL:
case P_DIV:
case P_MOD:
case P_ADD:
case P_LOGIC_AND:
case P_LOGIC_OR:
case P_LOGIC_GEQ:
case P_LOGIC_LEQ:
case P_LOGIC_EQ:
case P_LOGIC_UNEQ:
case P_LOGIC_GREATER:
case P_LOGIC_LESS:
case P_RSHIFT:
case P_LSHIFT:
case P_BIN_AND:
case P_BIN_OR:
case P_BIN_XOR:
case P_COLON:
case P_QUESTIONMARK:
{
if (!lastwasvalue) {
idParser::Error( "operator '%s' after operator in #if/#elif", t->c_str() );
error = 1;
break;
}
break;
}
default:
{
idParser::Error( "invalid operator '%s' in #if/#elif", t->c_str() );
error = 1;
break;
}
}
if (!error && !negativevalue) {
//o = (operator_t *) GetClearedMemory(sizeof(operator_t));
AllocOperator(o);
o->op = t->subtype;
o->priority = PC_OperatorPriority(t->subtype);
o->parentheses = parentheses;
o->next = NULL;
o->prev = lastoperator;
if (lastoperator) lastoperator->next = o;
else firstoperator = o;
lastoperator = o;
lastwasvalue = 0;
}
break;
}
default:
{
idParser::Error( "unknown '%s' in #if/#elif", t->c_str() );
error = 1;
break;
}
}
if (error) {
break;
}
}
if (!error) {
if (!lastwasvalue) {
idParser::Error( "trailing operator in #if/#elif" );
error = 1;
}
else if (parentheses) {
idParser::Error( "too many ( in #if/#elif" );
error = 1;
}
}
//
gotquestmarkvalue = false;
questmarkintvalue = 0;
questmarkfloatvalue = 0;
//while there are operators
while( !error && firstoperator ) {
v = firstvalue;
for (o = firstoperator; o->next; o = o->next) {
//if the current operator is nested deeper in parentheses
//than the next operator
if (o->parentheses > o->next->parentheses) {
break;
}
//if the current and next operator are nested equally deep in parentheses
if (o->parentheses == o->next->parentheses) {
//if the priority of the current operator is equal or higher
//than the priority of the next operator
if (o->priority >= o->next->priority) {
break;
}
}
//if the arity of the operator isn't equal to 1
if (o->op != P_LOGIC_NOT && o->op != P_BIN_NOT) {
v = v->next;
}
//if there's no value or no next value
if (!v) {
idParser::Error( "mising values in #if/#elif" );
error = 1;
break;
}
}
if (error) {
break;
}
v1 = v;
v2 = v->next;
#ifdef DEBUG_EVAL
if (integer) {
Log_Write("operator %s, value1 = %d", idParser::scriptstack->getPunctuationFromId(o->op), v1->intvalue);
if (v2) Log_Write("value2 = %d", v2->intvalue);
}
else {
Log_Write("operator %s, value1 = %f", idParser::scriptstack->getPunctuationFromId(o->op), v1->floatvalue);
if (v2) Log_Write("value2 = %f", v2->floatvalue);
}
#endif //DEBUG_EVAL
switch(o->op) {
case P_LOGIC_NOT: v1->intvalue = !v1->intvalue;
v1->floatvalue = !v1->floatvalue; break;
case P_BIN_NOT: v1->intvalue = ~v1->intvalue;
break;
case P_MUL: v1->intvalue *= v2->intvalue;
v1->floatvalue *= v2->floatvalue; break;
case P_DIV: if (!v2->intvalue || !v2->floatvalue)
{
idParser::Error( "divide by zero in #if/#elif\n" );
error = 1;
break;
}
v1->intvalue /= v2->intvalue;
v1->floatvalue /= v2->floatvalue; break;
case P_MOD: if (!v2->intvalue)
{
idParser::Error( "divide by zero in #if/#elif\n" );
error = 1;
break;
}
v1->intvalue %= v2->intvalue; break;
case P_ADD: v1->intvalue += v2->intvalue;
v1->floatvalue += v2->floatvalue; break;
case P_SUB: v1->intvalue -= v2->intvalue;
v1->floatvalue -= v2->floatvalue; break;
case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue;
v1->floatvalue = v1->floatvalue && v2->floatvalue; break;
case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue;
v1->floatvalue = v1->floatvalue || v2->floatvalue; break;
case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue;
v1->floatvalue = v1->floatvalue >= v2->floatvalue; break;
case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue;
v1->floatvalue = v1->floatvalue <= v2->floatvalue; break;
case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue;
v1->floatvalue = v1->floatvalue == v2->floatvalue; break;
case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue;
v1->floatvalue = v1->floatvalue != v2->floatvalue; break;
case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue;
v1->floatvalue = v1->floatvalue > v2->floatvalue; break;
case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue;
v1->floatvalue = v1->floatvalue < v2->floatvalue; break;
case P_RSHIFT: v1->intvalue >>= v2->intvalue;
break;
case P_LSHIFT: v1->intvalue <<= v2->intvalue;
break;
case P_BIN_AND: v1->intvalue &= v2->intvalue;
break;
case P_BIN_OR: v1->intvalue |= v2->intvalue;
break;
case P_BIN_XOR: v1->intvalue ^= v2->intvalue;
break;
case P_COLON:
{
if (!gotquestmarkvalue) {
idParser::Error( ": without ? in #if/#elif" );
error = 1;
break;
}
if (integer) {
if (!questmarkintvalue)
v1->intvalue = v2->intvalue;
}
else {
if (!questmarkfloatvalue)
v1->floatvalue = v2->floatvalue;
}
gotquestmarkvalue = false;
break;
}
case P_QUESTIONMARK:
{
if (gotquestmarkvalue) {
idParser::Error( "? after ? in #if/#elif" );
error = 1;
break;
}
questmarkintvalue = v1->intvalue;
questmarkfloatvalue = v1->floatvalue;
gotquestmarkvalue = true;
break;
}
}
#ifdef DEBUG_EVAL
if (integer) Log_Write("result value = %d", v1->intvalue);
else Log_Write("result value = %f", v1->floatvalue);
#endif //DEBUG_EVAL
if (error)
break;
//if not an operator with arity 1
if (o->op != P_LOGIC_NOT && o->op != P_BIN_NOT) {
//remove the second value if not question mark operator
if (o->op != P_QUESTIONMARK) {
v = v->next;
}
//
if (v->prev) v->prev->next = v->next;
else firstvalue = v->next;
if (v->next) v->next->prev = v->prev;
else lastvalue = v->prev;
//FreeMemory(v);
FreeValue(v);
}
//remove the operator
if (o->prev) o->prev->next = o->next;
else firstoperator = o->next;
if (o->next) o->next->prev = o->prev;
else lastoperator = o->prev;
//FreeMemory(o);
FreeOperator(o);
}
if (firstvalue) {
if (intvalue) *intvalue = firstvalue->intvalue;
if (floatvalue) *floatvalue = firstvalue->floatvalue;
}
for (o = firstoperator; o; o = lastoperator) {
lastoperator = o->next;
//FreeMemory(o);
FreeOperator(o);
}
for (v = firstvalue; v; v = lastvalue) {
lastvalue = v->next;
//FreeMemory(v);
FreeValue(v);
}
if (!error) {
return true;
}
if (intvalue) {
*intvalue = 0;
}
if (floatvalue) {
*floatvalue = 0;
}
return false;
}
/*
================
idParser::Evaluate
================
*/
int idParser::Evaluate( int *intvalue, double *floatvalue, int integer ) {
idToken token, *firsttoken, *lasttoken;
idToken *t, *nexttoken;
define_t *define;
int defined = false;
if (intvalue) {
*intvalue = 0;
}
if (floatvalue) {
*floatvalue = 0;
}
//
if ( !idParser::ReadLine( &token ) ) {
idParser::Error( "no value after #if/#elif" );
return false;
}
firsttoken = NULL;
lasttoken = NULL;
do {
//if the token is a name
if (token.type == TT_NAME) {
if (defined) {
defined = false;
t = new idToken(token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
}
else if ( token == "defined" ) {
defined = true;
t = new idToken(token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
}
else {
//then it must be a define
define = FindHashedDefine(idParser::definehash, token.c_str());
if (!define) {
idParser::Error( "can't Evaluate '%s', not defined", token.c_str() );
return false;
}
if ( !idParser::ExpandDefineIntoSource( &token, define ) ) {
return false;
}
}
}
//if the token is a number or a punctuation
else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) {
t = new idToken(token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
}
else {
idParser::Error( "can't Evaluate '%s'", token.c_str() );
return false;
}
} while(idParser::ReadLine( &token ));
//
if ( !idParser::EvaluateTokens( firsttoken, intvalue, floatvalue, integer ) ) {
return false;
}
//
#ifdef DEBUG_EVAL
Log_Write("eval:");
#endif //DEBUG_EVAL
for (t = firsttoken; t; t = nexttoken) {
#ifdef DEBUG_EVAL
Log_Write(" %s", t->c_str());
#endif //DEBUG_EVAL
nexttoken = t->next;
delete t;
} //end for
#ifdef DEBUG_EVAL
if (integer) Log_Write("eval result: %d", *intvalue);
else Log_Write("eval result: %f", *floatvalue);
#endif //DEBUG_EVAL
//
return true;
}
/*
================
idParser::DollarEvaluate
================
*/
int idParser::DollarEvaluate( int *intvalue, double *floatvalue, int integer) {
int indent, defined = false;
idToken token, *firsttoken, *lasttoken;
idToken *t, *nexttoken;
define_t *define;
if (intvalue) {
*intvalue = 0;
}
if (floatvalue) {
*floatvalue = 0;
}
//
if ( !idParser::ReadSourceToken( &token ) ) {
idParser::Error( "no leading ( after $evalint/$evalfloat" );
return false;
}
if ( !idParser::ReadSourceToken( &token ) ) {
idParser::Error( "nothing to Evaluate" );
return false;
}
indent = 1;
firsttoken = NULL;
lasttoken = NULL;
do {
//if the token is a name
if (token.type == TT_NAME) {
if (defined) {
defined = false;
t = new idToken(token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
}
else if ( token == "defined" ) {
defined = true;
t = new idToken(token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
}
else {
//then it must be a define
define = FindHashedDefine(idParser::definehash, token.c_str());
if (!define) {
idParser::Warning( "can't Evaluate '%s', not defined", token.c_str() );
return false;
}
if ( !idParser::ExpandDefineIntoSource( &token, define ) ) {
return false;
}
}
}
//if the token is a number or a punctuation
else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) {
if ( token[0] == '(' ) indent++;
else if ( token[0] == ')' ) indent--;
if (indent <= 0) {
break;
}
t = new idToken(token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
}
else {
idParser::Error( "can't Evaluate '%s'", token.c_str() );
return false;
}
} while(idParser::ReadSourceToken( &token ));
//
if (!idParser::EvaluateTokens( firsttoken, intvalue, floatvalue, integer)) {
return false;
}
//
#ifdef DEBUG_EVAL
Log_Write("$eval:");
#endif //DEBUG_EVAL
for (t = firsttoken; t; t = nexttoken) {
#ifdef DEBUG_EVAL
Log_Write(" %s", t->c_str());
#endif //DEBUG_EVAL
nexttoken = t->next;
delete t;
} //end for
#ifdef DEBUG_EVAL
if (integer) Log_Write("$eval result: %d", *intvalue);
else Log_Write("$eval result: %f", *floatvalue);
#endif //DEBUG_EVAL
//
return true;
}
/*
================
idParser::Directive_elif
================
*/
int idParser::Directive_elif( void ) {
int value;
int type, skip;
idParser::PopIndent( &type, &skip );
if (!type || type == INDENT_ELSE) {
idParser::Error( "misplaced #elif" );
return false;
}
if ( !idParser::Evaluate( &value, NULL, true ) ) {
return false;
}
skip = (value == 0);
idParser::PushIndent( INDENT_ELIF, skip );
return true;
}
/*
================
idParser::Directive_if
================
*/
int idParser::Directive_if( void ) {
int value;
int skip;
if ( !idParser::Evaluate( &value, NULL, true ) ) {
return false;
}
skip = (value == 0);
idParser::PushIndent( INDENT_IF, skip );
return true;
}
/*
================
idParser::Directive_line
================
*/
int idParser::Directive_line( void ) {
idToken token;
idParser::Error( "#line directive not supported" );
while( idParser::ReadLine( &token ) ) {
}
return true;
}
/*
================
idParser::Directive_error
================
*/
int idParser::Directive_error( void ) {
idToken token;
if ( !idParser::ReadLine( &token) || token.type != TT_STRING ) {
idParser::Error( "#error without string" );
return false;
}
idParser::Error( "#error: %s", token.c_str() );
return true;
}
/*
================
idParser::Directive_warning
================
*/
int idParser::Directive_warning( void ) {
idToken token;
if ( !idParser::ReadLine( &token) || token.type != TT_STRING ) {
idParser::Warning( "#warning without string" );
return false;
}
idParser::Warning( "#warning: %s", token.c_str() );
return true;
}
/*
================
idParser::Directive_pragma
================
*/
int idParser::Directive_pragma( void ) {
idToken token;
idParser::Warning( "#pragma directive not supported" );
while( idParser::ReadLine( &token ) ) {
}
return true;
}
/*
================
idParser::UnreadSignToken
================
*/
void idParser::UnreadSignToken( void ) {
idToken token;
token.line = idParser::scriptstack->GetLineNum();
token.whiteSpaceStart_p = NULL;
token.whiteSpaceEnd_p = NULL;
token.linesCrossed = 0;
token.flags = 0;
token = "-";
token.type = TT_PUNCTUATION;
token.subtype = P_SUB;
idParser::UnreadSourceToken( &token );
}
/*
================
idParser::Directive_eval
================
*/
int idParser::Directive_eval( void ) {
int value;
idToken token;
char buf[128];
if ( !idParser::Evaluate( &value, NULL, true ) ) {
return false;
}
token.line = idParser::scriptstack->GetLineNum();
token.whiteSpaceStart_p = NULL;
token.whiteSpaceEnd_p = NULL;
token.linesCrossed = 0;
token.flags = 0;
sprintf(buf, "%d", abs(value));
token = buf;
token.type = TT_NUMBER;
token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;
idParser::UnreadSourceToken( &token );
if ( value < 0 ) {
idParser::UnreadSignToken();
}
return true;
}
/*
================
idParser::Directive_evalfloat
================
*/
int idParser::Directive_evalfloat( void ) {
double value;
idToken token;
char buf[128];
if ( !idParser::Evaluate( NULL, &value, false ) ) {
return false;
}
token.line = idParser::scriptstack->GetLineNum();
token.whiteSpaceStart_p = NULL;
token.whiteSpaceEnd_p = NULL;
token.linesCrossed = 0;
token.flags = 0;
sprintf(buf, "%1.2f", idMath::Fabs(value));
token = buf;
token.type = TT_NUMBER;
token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;
idParser::UnreadSourceToken( &token );
if (value < 0) {
idParser::UnreadSignToken();
}
return true;
}
/*
================
idParser::ReadDirective
================
*/
int idParser::ReadDirective( void ) {
idToken token;
//read the directive name
if ( !idParser::ReadSourceToken( &token ) ) {
idParser::Error( "found '#' without name" );
return false;
}
//directive name must be on the same line
if (token.linesCrossed > 0) {
idParser::UnreadSourceToken( &token );
idParser::Error( "found '#' at end of line" );
return false;
}
//if if is a name
if (token.type == TT_NAME) {
if ( token == "if" ) {
return idParser::Directive_if();
}
else if ( token == "ifdef" ) {
return idParser::Directive_ifdef();
}
else if ( token == "ifndef" ) {
return idParser::Directive_ifndef();
}
else if ( token == "elif" ) {
return idParser::Directive_elif();
}
else if ( token == "else" ) {
return idParser::Directive_else();
}
else if ( token == "endif" ) {
return idParser::Directive_endif();
}
else if (idParser::skip > 0) {
// skip the rest of the line
while( idParser::ReadLine( &token ) ) {
}
return true;
}
else {
if ( token == "include" ) {
return idParser::Directive_include();
}
else if ( token == "define" ) {
return idParser::Directive_define();
}
else if ( token == "undef" ) {
return idParser::Directive_undef();
}
else if ( token == "line" ) {
return idParser::Directive_line();
}
else if ( token == "error" ) {
return idParser::Directive_error();
}
else if ( token == "warning" ) {
return idParser::Directive_warning();
}
else if ( token == "pragma" ) {
return idParser::Directive_pragma();
}
else if ( token == "eval" ) {
return idParser::Directive_eval();
}
else if ( token == "evalfloat" ) {
return idParser::Directive_evalfloat();
}
}
}
idParser::Error( "unknown precompiler directive '%s'", token.c_str() );
return false;
}
/*
================
idParser::DollarDirective_evalint
================
*/
int idParser::DollarDirective_evalint( void ) {
int value;
idToken token;
char buf[128];
if ( !idParser::DollarEvaluate( &value, NULL, true ) ) {
return false;
}
token.line = idParser::scriptstack->GetLineNum();
token.whiteSpaceStart_p = NULL;
token.whiteSpaceEnd_p = NULL;
token.linesCrossed = 0;
token.flags = 0;
sprintf( buf, "%d", abs( value ) );
token = buf;
token.type = TT_NUMBER;
token.subtype = TT_INTEGER | TT_LONG | TT_DECIMAL | TT_VALUESVALID;
token.intvalue = abs( value );
token.floatvalue = abs( value );
idParser::UnreadSourceToken( &token );
if ( value < 0 ) {
idParser::UnreadSignToken();
}
return true;
}
/*
================
idParser::DollarDirective_evalfloat
================
*/
int idParser::DollarDirective_evalfloat( void ) {
double value;
idToken token;
char buf[128];
if ( !idParser::DollarEvaluate( NULL, &value, false ) ) {
return false;
}
token.line = idParser::scriptstack->GetLineNum();
token.whiteSpaceStart_p = NULL;
token.whiteSpaceEnd_p = NULL;
token.linesCrossed = 0;
token.flags = 0;
sprintf( buf, "%1.2f", fabs( value ) );
token = buf;
token.type = TT_NUMBER;
token.subtype = TT_FLOAT | TT_LONG | TT_DECIMAL | TT_VALUESVALID;
token.intvalue = (unsigned int) fabs( value );
token.floatvalue = fabs( value );
idParser::UnreadSourceToken( &token );
if ( value < 0 ) {
idParser::UnreadSignToken();
}
return true;
}
/*
================
idParser::ReadDollarDirective
================
*/
int idParser::ReadDollarDirective( void ) {
idToken token;
// read the directive name
if ( !idParser::ReadSourceToken( &token ) ) {
idParser::Error( "found '$' without name" );
return false;
}
// directive name must be on the same line
if ( token.linesCrossed > 0 ) {
idParser::UnreadSourceToken( &token );
idParser::Error( "found '$' at end of line" );
return false;
}
// if if is a name
if (token.type == TT_NAME) {
if ( token == "evalint" ) {
return idParser::DollarDirective_evalint();
}
else if ( token == "evalfloat" ) {
return idParser::DollarDirective_evalfloat();
}
}
idParser::UnreadSourceToken( &token );
return false;
}
/*
================
idParser::ReadToken
================
*/
int idParser::ReadToken( idToken *token ) {
define_t *define;
while(1) {
if ( !idParser::ReadSourceToken( token ) ) {
return false;
}
// check for precompiler directives
if ( token->type == TT_PUNCTUATION && (*token)[0] == '#' && (*token)[1] == '\0' ) {
// read the precompiler directive
if ( !idParser::ReadDirective() ) {
return false;
}
continue;
}
// if skipping source because of conditional compilation
if ( idParser::skip ) {
continue;
}
// recursively concatenate strings that are behind each other still resolving defines
if ( token->type == TT_STRING && !(idParser::scriptstack->GetFlags() & LEXFL_NOSTRINGCONCAT) ) {
idToken newtoken;
if ( idParser::ReadToken( &newtoken ) ) {
if ( newtoken.type == TT_STRING ) {
token->Append( newtoken.c_str() );
}
else {
idParser::UnreadSourceToken( &newtoken );
}
}
}
//
if ( !(idParser::scriptstack->GetFlags() & LEXFL_NODOLLARPRECOMPILE) ) {
// check for special precompiler directives
if ( token->type == TT_PUNCTUATION && (*token)[0] == '$' && (*token)[1] == '\0' ) {
// read the precompiler directive
if ( idParser::ReadDollarDirective() ) {
continue;
}
}
}
// if the token is a name
if ( token->type == TT_NAME && !( token->flags & TOKEN_FL_RECURSIVE_DEFINE ) ) {
// check if the name is a define macro
define = FindHashedDefine( idParser::definehash, token->c_str() );
// if it is a define macro
if ( define ) {
// expand the defined macro
if ( !idParser::ExpandDefineIntoSource( token, define ) ) {
return false;
}
continue;
}
}
// found a token
return true;
}
}
/*
================
idParser::ExpectTokenString
================
*/
int idParser::ExpectTokenString( const char *string ) {
idToken token;
if ( !idParser::ReadToken( &token ) ) {
idParser::Error( "couldn't find expected '%s'", string );
return false;
}
if ( token != string ) {
idParser::Error( "expected '%s' but found '%s'", string, token.c_str() );
return false;
}
return true;
}
/*
================
idParser::ExpectTokenType
================
*/
int idParser::ExpectTokenType( int type, int subtype, idToken *token ) {
idStr str;
if ( !idParser::ReadToken( token ) ) {
idParser::Error( "couldn't read expected token" );
return 0;
}
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;
}
idParser::Error( "expected a %s but found '%s'", str.c_str(), token->c_str() );
return 0;
}
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( ' ' );
idParser::Error( "expected %s but found '%s'", str.c_str(), token->c_str() );
return 0;
}
}
else if ( token->type == TT_PUNCTUATION ) {
if ( subtype < 0 ) {
idParser::Error( "BUG: wrong punctuation subtype" );
return 0;
}
if ( token->subtype != subtype ) {
idParser::Error( "expected '%s' but found '%s'", scriptstack->GetPunctuationFromId( subtype ), token->c_str() );
return 0;
}
}
return 1;
}
/*
================
idParser::ExpectAnyToken
================
*/
int idParser::ExpectAnyToken( idToken *token ) {
if (!idParser::ReadToken( token )) {
idParser::Error( "couldn't read expected token" );
return false;
}
else {
return true;
}
}
/*
================
idParser::CheckTokenString
================
*/
int idParser::CheckTokenString( const char *string ) {
idToken tok;
if ( !ReadToken( &tok ) ) {
return false;
}
//if the token is available
if ( tok == string ) {
return true;
}
UnreadSourceToken( &tok );
return false;
}
/*
================
idParser::CheckTokenType
================
*/
int idParser::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;
}
UnreadSourceToken( &tok );
return false;
}
/*
================
idParser::PeekTokenString
================
*/
int idParser::PeekTokenString( const char *string ) {
idToken tok;
if ( !ReadToken( &tok ) ) {
return false;
}
UnreadSourceToken( &tok );
// if the token is available
if ( tok == string ) {
return true;
}
return false;
}
/*
================
idParser::PeekTokenType
================
*/
int idParser::PeekTokenType( int type, int subtype, idToken *token ) {
idToken tok;
if ( !ReadToken( &tok ) ) {
return false;
}
UnreadSourceToken( &tok );
// if the type matches
if ( tok.type == type && ( tok.subtype & subtype ) == subtype ) {
*token = tok;
return true;
}
return false;
}
/*
================
idParser::SkipUntilString
================
*/
int idParser::SkipUntilString( const char *string ) {
idToken token;
while(idParser::ReadToken( &token )) {
if ( token == string ) {
return true;
}
}
return false;
}
/*
================
idParser::SkipRestOfLine
================
*/
int idParser::SkipRestOfLine( void ) {
idToken token;
while(idParser::ReadToken( &token )) {
if ( token.linesCrossed ) {
idParser::UnreadSourceToken( &token );
return true;
}
}
return false;
}
/*
=================
idParser::SkipBracedSection
Skips until a matching close brace is found.
Internal brace depths are properly skipped.
=================
*/
int idParser::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;
}
/*
=================
idParser::ParseBracedSectionExact
The next token should be an open brace.
Parses until a matching close brace is found.
Maintains the exact formating of the braced section
FIXME: what about precompilation ?
=================
*/
const char *idParser::ParseBracedSectionExact( idStr &out, int tabs ) {
return scriptstack->ParseBracedSectionExact( out, tabs );
}
/*
=================
idParser::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 *idParser::ParseBracedSection( idStr &out, int tabs ) {
idToken token;
int i, depth;
bool doTabs = false;
if (tabs >= 0) {
doTabs = true;
}
out.Empty();
if ( !idParser::ExpectTokenString( "{" ) ) {
return out.c_str();
}
out = "{";
depth = 1;
do {
if ( !idParser::ReadToken( &token ) ) {
Error( "missing closing brace" );
return out.c_str();
}
// if the token is on a new line
for ( i = 0; i < token.linesCrossed; i++ ) {
out += "\r\n";
}
if (doTabs && token.linesCrossed) {
i = tabs;
if (token[0] == '}' && i > 0) {
i--;
}
while (i-- > 0) {
out += "\t";
}
}
if ( token.type == TT_PUNCTUATION ) {
if ( token[0] == '{' ) {
depth++;
if (doTabs) {
tabs++;
}
}
else if ( token[0] == '}' ) {
depth--;
if (doTabs) {
tabs--;
}
}
}
if ( token.type == TT_STRING ) {
out += "\"" + token + "\"";
}
else {
out += token;
}
out += " ";
} while( depth );
return out.c_str();
}
/*
=================
idParser::ParseRestOfLine
parse the rest of the line
=================
*/
const char *idParser::ParseRestOfLine( idStr &out ) {
idToken token;
out.Empty();
while(idParser::ReadToken( &token )) {
if ( token.linesCrossed ) {
idParser::UnreadSourceToken( &token );
break;
}
if ( out.Length() ) {
out += " ";
}
out += token;
}
return out.c_str();
}
/*
================
idParser::UnreadToken
================
*/
void idParser::UnreadToken( idToken *token ) {
idParser::UnreadSourceToken( token );
}
/*
================
idParser::ReadTokenOnLine
================
*/
int idParser::ReadTokenOnLine( idToken *token ) {
idToken tok;
if (!idParser::ReadToken( &tok )) {
return false;
}
// if no lines were crossed before this token
if ( !tok.linesCrossed ) {
*token = tok;
return true;
}
//
idParser::UnreadSourceToken( &tok );
return false;
}
/*
================
idParser::ParseInt
================
*/
int idParser::ParseInt( void ) {
idToken token;
if ( !idParser::ReadToken( &token ) ) {
idParser::Error( "couldn't read expected integer" );
return 0;
}
if ( token.type == TT_PUNCTUATION && token == "-" ) {
idParser::ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
return -((signed int) token.GetIntValue());
}
else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) {
idParser::Error( "expected integer value, found '%s'", token.c_str() );
}
return token.GetIntValue();
}
/*
================
idParser::ParseBool
================
*/
bool idParser::ParseBool( void ) {
idToken token;
if ( !idParser::ExpectTokenType( TT_NUMBER, 0, &token ) ) {
idParser::Error( "couldn't read expected boolean" );
return false;
}
return ( token.GetIntValue() != 0 );
}
/*
================
idParser::ParseFloat
================
*/
float idParser::ParseFloat( void ) {
idToken token;
if ( !idParser::ReadToken( &token ) ) {
idParser::Error( "couldn't read expected floating point number" );
return 0.0f;
}
if ( token.type == TT_PUNCTUATION && token == "-" ) {
idParser::ExpectTokenType( TT_NUMBER, 0, &token );
return -token.GetFloatValue();
}
else if ( token.type != TT_NUMBER ) {
idParser::Error( "expected float value, found '%s'", token.c_str() );
}
return token.GetFloatValue();
}
/*
================
idParser::Parse1DMatrix
================
*/
int idParser::Parse1DMatrix( int x, float *m ) {
int i;
if ( !idParser::ExpectTokenString( "(" ) ) {
return false;
}
for ( i = 0; i < x; i++ ) {
m[i] = idParser::ParseFloat();
}
if ( !idParser::ExpectTokenString( ")" ) ) {
return false;
}
return true;
}
/*
================
idParser::Parse2DMatrix
================
*/
int idParser::Parse2DMatrix( int y, int x, float *m ) {
int i;
if ( !idParser::ExpectTokenString( "(" ) ) {
return false;
}
for ( i = 0; i < y; i++ ) {
if ( !idParser::Parse1DMatrix( x, m + i * x ) ) {
return false;
}
}
if ( !idParser::ExpectTokenString( ")" ) ) {
return false;
}
return true;
}
/*
================
idParser::Parse3DMatrix
================
*/
int idParser::Parse3DMatrix( int z, int y, int x, float *m ) {
int i;
if ( !idParser::ExpectTokenString( "(" ) ) {
return false;
}
for ( i = 0 ; i < z; i++ ) {
if ( !idParser::Parse2DMatrix( y, x, m + i * x*y ) ) {
return false;
}
}
if ( !idParser::ExpectTokenString( ")" ) ) {
return false;
}
return true;
}
/*
================
idParser::GetLastWhiteSpace
================
*/
int idParser::GetLastWhiteSpace( idStr &whiteSpace ) const {
if ( scriptstack ) {
scriptstack->GetLastWhiteSpace( whiteSpace );
} else {
whiteSpace.Clear();
}
return whiteSpace.Length();
}
/*
================
idParser::SetMarker
================
*/
void idParser::SetMarker( void ) {
marker_p = NULL;
}
/*
================
idParser::GetStringFromMarker
FIXME: this is very bad code, the script isn't even garrenteed to still be around
================
*/
void idParser::GetStringFromMarker( idStr& out, bool clean ) {
char* p;
char save;
if ( marker_p == NULL ) {
marker_p = scriptstack->buffer;
}
if ( tokens ) {
p = (char*)tokens->whiteSpaceStart_p;
} else {
p = (char*)scriptstack->script_p;
}
// Set the end character to NULL to give us a complete string
save = *p;
*p = 0;
// If cleaning then reparse
if ( clean ) {
idParser temp( marker_p, strlen( marker_p ), "temp", flags );
idToken token;
while ( temp.ReadToken ( &token ) ) {
out += token;
}
} else {
out = marker_p;
}
// restore the character we set to NULL
*p = save;
}
/*
================
idParser::SetIncludePath
================
*/
void idParser::SetIncludePath( const char *path ) {
idParser::includepath = path;
// add trailing path seperator
if (idParser::includepath[idParser::includepath.Length()-1] != '\\' &&
idParser::includepath[idParser::includepath.Length()-1] != '/') {
idParser::includepath += PATHSEPERATOR_STR;
}
}
/*
================
idParser::SetPunctuations
================
*/
void idParser::SetPunctuations( const punctuation_t *p ) {
idParser::punctuations = p;
}
/*
================
idParser::SetFlags
================
*/
void idParser::SetFlags( int flags ) {
idLexer *s;
idParser::flags = flags;
for ( s = idParser::scriptstack; s; s = s->next ) {
s->SetFlags( flags );
}
}
/*
================
idParser::GetFlags
================
*/
int idParser::GetFlags( void ) const {
return idParser::flags;
}
/*
================
idParser::LoadFile
================
*/
int idParser::LoadFile( const char *filename, bool OSPath ) {
idLexer *script;
if ( idParser::loaded ) {
idLib::common->FatalError("idParser::loadFile: another source already loaded");
return false;
}
script = new idLexer( filename, 0, OSPath );
if ( !script->IsLoaded() ) {
delete script;
return false;
}
script->SetFlags( idParser::flags );
script->SetPunctuations( idParser::punctuations );
script->next = NULL;
idParser::OSPath = OSPath;
idParser::filename = filename;
idParser::scriptstack = script;
idParser::tokens = NULL;
idParser::indentstack = NULL;
idParser::skip = 0;
idParser::loaded = true;
if ( !idParser::definehash ) {
idParser::defines = NULL;
idParser::definehash = (define_t **) Mem_ClearedAlloc( DEFINEHASHSIZE * sizeof(define_t *) );
idParser::AddGlobalDefinesToSource();
}
return true;
}
/*
================
idParser::LoadMemory
================
*/
int idParser::LoadMemory(const char *ptr, int length, const char *name ) {
idLexer *script;
if ( idParser::loaded ) {
idLib::common->FatalError("idParser::loadMemory: another source already loaded");
return false;
}
script = new idLexer( ptr, length, name );
if ( !script->IsLoaded() ) {
delete script;
return false;
}
script->SetFlags( idParser::flags );
script->SetPunctuations( idParser::punctuations );
script->next = NULL;
idParser::filename = name;
idParser::scriptstack = script;
idParser::tokens = NULL;
idParser::indentstack = NULL;
idParser::skip = 0;
idParser::loaded = true;
if ( !idParser::definehash ) {
idParser::defines = NULL;
idParser::definehash = (define_t **) Mem_ClearedAlloc( DEFINEHASHSIZE * sizeof(define_t *) );
idParser::AddGlobalDefinesToSource();
}
return true;
}
/*
================
idParser::FreeSource
================
*/
void idParser::FreeSource( bool keepDefines ) {
idLexer *script;
idToken *token;
define_t *define;
indent_t *indent;
int i;
// free all the scripts
while( scriptstack ) {
script = scriptstack;
scriptstack = scriptstack->next;
delete script;
}
// free all the tokens
while( tokens ) {
token = tokens;
tokens = tokens->next;
delete token;
}
// free all indents
while( indentstack ) {
indent = indentstack;
indentstack = indentstack->next;
Mem_Free( indent );
}
if ( !keepDefines ) {
// free hash table
if ( definehash ) {
// free defines
for ( i = 0; i < DEFINEHASHSIZE; i++ ) {
while( definehash[i] ) {
define = definehash[i];
definehash[i] = definehash[i]->hashnext;
FreeDefine(define);
}
}
defines = NULL;
Mem_Free( idParser::definehash );
definehash = NULL;
}
}
loaded = false;
}
/*
================
idParser::GetPunctuationFromId
================
*/
const char *idParser::GetPunctuationFromId( int id ) {
int i;
if ( !idParser::punctuations ) {
idLexer lex;
return lex.GetPunctuationFromId( id );
}
for (i = 0; idParser::punctuations[i].p; i++) {
if ( idParser::punctuations[i].n == id ) {
return idParser::punctuations[i].p;
}
}
return "unknown punctuation";
}
/*
================
idParser::GetPunctuationId
================
*/
int idParser::GetPunctuationId( const char *p ) {
int i;
if ( !idParser::punctuations ) {
idLexer lex;
return lex.GetPunctuationId( p );
}
for (i = 0; idParser::punctuations[i].p; i++) {
if ( !strcmp(idParser::punctuations[i].p, p) ) {
return idParser::punctuations[i].n;
}
}
return 0;
}
/*
================
idParser::idParser
================
*/
idParser::idParser() {
this->loaded = false;
this->OSPath = false;
this->punctuations = 0;
this->flags = 0;
this->scriptstack = NULL;
this->indentstack = NULL;
this->definehash = NULL;
this->defines = NULL;
this->tokens = NULL;
this->marker_p = NULL;
}
/*
================
idParser::idParser
================
*/
idParser::idParser( int flags ) {
this->loaded = false;
this->OSPath = false;
this->punctuations = 0;
this->flags = flags;
this->scriptstack = NULL;
this->indentstack = NULL;
this->definehash = NULL;
this->defines = NULL;
this->tokens = NULL;
this->marker_p = NULL;
}
/*
================
idParser::idParser
================
*/
idParser::idParser( const char *filename, int flags, bool OSPath ) {
this->loaded = false;
this->OSPath = true;
this->punctuations = 0;
this->flags = flags;
this->scriptstack = NULL;
this->indentstack = NULL;
this->definehash = NULL;
this->defines = NULL;
this->tokens = NULL;
this->marker_p = NULL;
LoadFile( filename, OSPath );
}
/*
================
idParser::idParser
================
*/
idParser::idParser( const char *ptr, int length, const char *name, int flags ) {
this->loaded = false;
this->OSPath = false;
this->punctuations = 0;
this->flags = flags;
this->scriptstack = NULL;
this->indentstack = NULL;
this->definehash = NULL;
this->defines = NULL;
this->tokens = NULL;
this->marker_p = NULL;
LoadMemory( ptr, length, name );
}
/*
================
idParser::~idParser
================
*/
idParser::~idParser( void ) {
idParser::FreeSource( false );
}