mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-23 04:51:56 +00:00
477252308d
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
3280 lines
73 KiB
C++
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 );
|
|
}
|