/*
===========================================================================

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, bool supressWarning )
{
	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 )
	{
		if( !supressWarning )
		{
			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;
}

