mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-11 05:01:25 +00:00
3857 lines
75 KiB
C++
3857 lines
75 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
Copyright (C) 2014 Robert Beckebans
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition 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 BFG Edition 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 "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
//#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()
|
|
{
|
|
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, TAG_IDLIB_PARSER );
|
|
//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( TAG_IDLIB_PARSER ) 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( TAG_IDLIB_PARSER ) 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 ), TAG_IDLIB_PARSER );
|
|
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;
|
|
assert( idParser::tokens != NULL );
|
|
idParser::tokens = idParser::tokens->next;
|
|
delete t;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::UnreadSourceToken
|
|
================
|
|
*/
|
|
int idParser::UnreadSourceToken( idToken* token )
|
|
{
|
|
idToken* t;
|
|
|
|
t = new( TAG_IDLIB_PARSER ) 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( TAG_IDLIB_PARSER ) 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()
|
|
{
|
|
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, TAG_IDLIB_PARSER );
|
|
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()
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < DEFINEHASHSIZE; i++ )
|
|
{
|
|
if( idParser::definehash[i] )
|
|
{
|
|
return CopyDefine( idParser::definehash[i] );
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static idStr PreProcessorDate()
|
|
{
|
|
time_t t = time( NULL );
|
|
char* curtime = ctime( &t );
|
|
if( idStr::Length( curtime ) < 24 )
|
|
{
|
|
return idStr( "*** BAD CURTIME ***" );
|
|
}
|
|
idStr str = "\"";
|
|
// skip DAY, extract MMM DD
|
|
for( int i = 4 ; i < 10 ; i++ )
|
|
{
|
|
str.Append( curtime[i] );
|
|
}
|
|
// skip time, extract space+YYYY
|
|
for( int i = 19 ; i < 24 ; i++ )
|
|
{
|
|
str.Append( curtime[i] );
|
|
}
|
|
str.Append( "\"" );
|
|
return str;
|
|
}
|
|
|
|
static idStr PreProcessorTime()
|
|
{
|
|
time_t t = time( NULL );
|
|
char* curtime = ctime( &t );
|
|
if( idStr::Length( curtime ) < 24 )
|
|
{
|
|
return idStr( "*** BAD CURTIME ***" );
|
|
}
|
|
|
|
idStr str = "\"";
|
|
for( int i = 11 ; i < 19 ; i++ )
|
|
{
|
|
str.Append( curtime[i] );
|
|
}
|
|
str.Append( "\"" );
|
|
return str;
|
|
}
|
|
|
|
CONSOLE_COMMAND( TestPreprocessorMacros, "check analyze warning", 0 )
|
|
{
|
|
idLib::Printf( "%s : %s\n", __DATE__, PreProcessorDate().c_str() );
|
|
idLib::Printf( "%s : %s\n", __TIME__, PreProcessorTime().c_str() );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::ExpandBuiltinDefine
|
|
================
|
|
*/
|
|
int idParser::ExpandBuiltinDefine( idToken* deftoken, define_t* define, idToken** firsttoken, idToken** lasttoken )
|
|
{
|
|
idToken* token;
|
|
char buf[MAX_STRING_CHARS];
|
|
|
|
token = new( TAG_IDLIB_PARSER ) 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:
|
|
{
|
|
*token = PreProcessorDate();
|
|
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:
|
|
{
|
|
*token = PreProcessorTime();
|
|
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( TAG_IDLIB_PARSER ) 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( TAG_IDLIB_PARSER ) idToken( token );
|
|
t->line = deftoken->line;
|
|
}
|
|
else
|
|
{
|
|
idParser::Warning( "stringizing operator without define parameter" );
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t = new( TAG_IDLIB_PARSER ) 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
|
|
================
|
|
*/
|
|
// RB: added token as parameter
|
|
int idParser::Directive_include( idToken* token )
|
|
{
|
|
idLexer* script;
|
|
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( TAG_IDLIB_PARSER ) 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( TAG_IDLIB_PARSER ) 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;
|
|
}
|
|
// RB end
|
|
|
|
/*
|
|
================
|
|
idParser::Directive_undef
|
|
================
|
|
*/
|
|
int idParser::Directive_undef()
|
|
{
|
|
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()
|
|
{
|
|
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, TAG_IDLIB_PARSER );
|
|
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( TAG_IDLIB_PARSER ) 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( TAG_IDLIB_PARSER ) 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()
|
|
{
|
|
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()
|
|
{
|
|
return idParser::Directive_if_def( INDENT_IFDEF );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::Directive_ifndef
|
|
================
|
|
*/
|
|
int idParser::Directive_ifndef()
|
|
{
|
|
return idParser::Directive_if_def( INDENT_IFNDEF );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::Directive_else
|
|
================
|
|
*/
|
|
int idParser::Directive_else()
|
|
{
|
|
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()
|
|
{
|
|
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 int intvalue; // DG: use int instead of long for 64bit compatibility
|
|
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 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;
|
|
int lastoperatortype = 0;
|
|
//
|
|
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;
|
|
lastoperatortype = o->op;
|
|
//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 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( TAG_IDLIB_PARSER ) idToken( token );
|
|
t->next = NULL;
|
|
if( lasttoken ) lasttoken->next = t;
|
|
else firsttoken = t;
|
|
lasttoken = t;
|
|
}
|
|
else if( token == "defined" )
|
|
{
|
|
defined = true;
|
|
t = new( TAG_IDLIB_PARSER ) 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( TAG_IDLIB_PARSER ) 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 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( TAG_IDLIB_PARSER ) idToken( token );
|
|
t->next = NULL;
|
|
if( lasttoken ) lasttoken->next = t;
|
|
else firsttoken = t;
|
|
lasttoken = t;
|
|
}
|
|
else if( token == "defined" )
|
|
{
|
|
defined = true;
|
|
t = new( TAG_IDLIB_PARSER ) 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( TAG_IDLIB_PARSER ) 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()
|
|
{
|
|
signed int value; // DG: use int instead of long for 64bit compatibility
|
|
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()
|
|
{
|
|
signed int value; // DG: use int instead of long for 64bit compatibility
|
|
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()
|
|
{
|
|
idToken token;
|
|
|
|
idParser::Error( "#line directive not supported" );
|
|
while( idParser::ReadLine( &token ) )
|
|
{
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::Directive_error
|
|
================
|
|
*/
|
|
int idParser::Directive_error()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
idToken token;
|
|
|
|
idParser::Warning( "#pragma directive not supported" );
|
|
while( idParser::ReadLine( &token ) )
|
|
{
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::UnreadSignToken
|
|
================
|
|
*/
|
|
void idParser::UnreadSignToken()
|
|
{
|
|
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()
|
|
{
|
|
signed int value; // DG: use int instead of long for 64bit compatibility
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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" )
|
|
{
|
|
// RB lets override for embedded shaders
|
|
idToken filename;
|
|
return Directive_include( &filename );
|
|
// RB end
|
|
}
|
|
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()
|
|
{
|
|
signed int value; // DG: use int instead of long for 64bit compatibility
|
|
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()
|
|
{
|
|
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 ); // DG: use int instead of long for 64bit compatibility
|
|
token.floatvalue = fabs( value );
|
|
idParser::UnreadSourceToken( &token );
|
|
if( value < 0 )
|
|
{
|
|
idParser::UnreadSignToken();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idParser::ReadDollarDirective
|
|
================
|
|
*/
|
|
int idParser::ReadDollarDirective()
|
|
{
|
|
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()
|
|
{
|
|
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, bool parseFirstBrace, char intro, char outro )
|
|
{
|
|
idToken token;
|
|
int i, depth;
|
|
bool doTabs;
|
|
|
|
char temp[ 2 ] = { 0, 0 };
|
|
*temp = intro;
|
|
|
|
out.Empty();
|
|
if( parseFirstBrace )
|
|
{
|
|
if( !ExpectTokenString( temp ) )
|
|
{
|
|
return out.c_str();
|
|
}
|
|
out = temp;
|
|
}
|
|
depth = 1;
|
|
doTabs = ( tabs >= 0 );
|
|
do
|
|
{
|
|
if( !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 ] == outro && i > 0 )
|
|
{
|
|
i--;
|
|
}
|
|
while( i-- > 0 )
|
|
{
|
|
out += "\t";
|
|
}
|
|
}
|
|
if( token.type == TT_STRING )
|
|
{
|
|
out += "\"" + token + "\"";
|
|
}
|
|
else if( token.type == TT_LITERAL )
|
|
{
|
|
out += "\'" + token + "\'";
|
|
}
|
|
else
|
|
{
|
|
if( token[ 0 ] == intro )
|
|
{
|
|
depth++;
|
|
if( doTabs )
|
|
{
|
|
tabs++;
|
|
}
|
|
}
|
|
else if( token[ 0 ] == outro )
|
|
{
|
|
depth--;
|
|
if( doTabs )
|
|
{
|
|
tabs--;
|
|
}
|
|
}
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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 += PATHSEPARATOR_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() 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( TAG_IDLIB_PARSER ) 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* ), TAG_IDLIB_PARSER );
|
|
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( TAG_IDLIB_PARSER ) 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* ), TAG_IDLIB_PARSER );
|
|
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()
|
|
{
|
|
idParser::FreeSource( false );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idParser::EndOfFile
|
|
========================
|
|
*/
|
|
bool idParser::EndOfFile()
|
|
{
|
|
if( scriptstack != NULL )
|
|
{
|
|
return ( bool ) scriptstack->EndOfFile();
|
|
}
|
|
return true;
|
|
}
|
|
|