/* =========================================================================== 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 . 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; }