mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-27 14:42:23 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
3252 lines
72 KiB
C++
3252 lines
72 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( 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( 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;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::ExpandBuiltinDefine
|
|
================
|
|
*/
|
|
int idParser::ExpandBuiltinDefine( idToken *deftoken, define_t *define, idToken **firsttoken, idToken **lasttoken ) {
|
|
idToken *token;
|
|
ID_TIME_T t;
|
|
char *curtime;
|
|
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);
|
|
curtime = ctime(&t);
|
|
(*token) = "\"";
|
|
token->Append( curtime+4 );
|
|
token[7] = '\0';
|
|
token->Append( curtime+20 );
|
|
token[10] = '\0';
|
|
token->Append( "\"" );
|
|
free(curtime);
|
|
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);
|
|
curtime = ctime(&t);
|
|
(*token) = "\"";
|
|
token->Append( curtime+11 );
|
|
token[8] = '\0';
|
|
token->Append( "\"" );
|
|
free(curtime);
|
|
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" );
|
|
*firsttoken = NULL;
|
|
*lasttoken = NULL;
|
|
break;
|
|
}
|
|
default: {
|
|
*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
|
|
{
|
|
signed long 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, signed long 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( signed long 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( signed long 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 ) {
|
|
signed long 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 ) {
|
|
signed long 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 ) {
|
|
signed long 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 ) {
|
|
signed long 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 long) 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 "unkown 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 );
|
|
}
|