mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-14 06:21:31 +00:00
9eeea7adf3
mostly -Wreorder, use const char* instead of char* for "static strings", fix inappropriate usage of NULL (e.g. instead of '\0' or (int)0)
2281 lines
42 KiB
C++
2281 lines
42 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
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 PUNCTABLE
|
|
|
|
//longer punctuations first
|
|
punctuation_t default_punctuations[] =
|
|
{
|
|
//binary operators
|
|
{">>=", P_RSHIFT_ASSIGN},
|
|
{"<<=", P_LSHIFT_ASSIGN},
|
|
//
|
|
{"...", P_PARMS},
|
|
//define merge operator
|
|
{"##", P_PRECOMPMERGE}, // pre-compiler
|
|
//logic operators
|
|
{"&&", P_LOGIC_AND}, // pre-compiler
|
|
{"||", P_LOGIC_OR}, // pre-compiler
|
|
{">=", P_LOGIC_GEQ}, // pre-compiler
|
|
{"<=", P_LOGIC_LEQ}, // pre-compiler
|
|
{"==", P_LOGIC_EQ}, // pre-compiler
|
|
{"!=", P_LOGIC_UNEQ}, // pre-compiler
|
|
//arithmatic operators
|
|
{"*=", P_MUL_ASSIGN},
|
|
{"/=", P_DIV_ASSIGN},
|
|
{"%=", P_MOD_ASSIGN},
|
|
{"+=", P_ADD_ASSIGN},
|
|
{"-=", P_SUB_ASSIGN},
|
|
{"++", P_INC},
|
|
{"--", P_DEC},
|
|
//binary operators
|
|
{"&=", P_BIN_AND_ASSIGN},
|
|
{"|=", P_BIN_OR_ASSIGN},
|
|
{"^=", P_BIN_XOR_ASSIGN},
|
|
{">>", P_RSHIFT}, // pre-compiler
|
|
{"<<", P_LSHIFT}, // pre-compiler
|
|
//reference operators
|
|
{"->", P_POINTERREF},
|
|
//C++
|
|
{"::", P_CPP1},
|
|
{".*", P_CPP2},
|
|
//arithmatic operators
|
|
{"*", P_MUL}, // pre-compiler
|
|
{"/", P_DIV}, // pre-compiler
|
|
{"%", P_MOD}, // pre-compiler
|
|
{"+", P_ADD}, // pre-compiler
|
|
{"-", P_SUB}, // pre-compiler
|
|
{"=", P_ASSIGN},
|
|
//binary operators
|
|
{"&", P_BIN_AND}, // pre-compiler
|
|
{"|", P_BIN_OR}, // pre-compiler
|
|
{"^", P_BIN_XOR}, // pre-compiler
|
|
{"~", P_BIN_NOT}, // pre-compiler
|
|
//logic operators
|
|
{"!", P_LOGIC_NOT}, // pre-compiler
|
|
{">", P_LOGIC_GREATER}, // pre-compiler
|
|
{"<", P_LOGIC_LESS}, // pre-compiler
|
|
//reference operator
|
|
{".", P_REF},
|
|
//seperators
|
|
{",", P_COMMA}, // pre-compiler
|
|
{";", P_SEMICOLON},
|
|
//label indication
|
|
{":", P_COLON}, // pre-compiler
|
|
//if statement
|
|
{"?", P_QUESTIONMARK}, // pre-compiler
|
|
//embracements
|
|
{"(", P_PARENTHESESOPEN}, // pre-compiler
|
|
{")", P_PARENTHESESCLOSE}, // pre-compiler
|
|
{"{", P_BRACEOPEN}, // pre-compiler
|
|
{"}", P_BRACECLOSE}, // pre-compiler
|
|
{"[", P_SQBRACKETOPEN},
|
|
{"]", P_SQBRACKETCLOSE},
|
|
//
|
|
{"\\", P_BACKSLASH},
|
|
//precompiler operator
|
|
{"#", P_PRECOMP}, // pre-compiler
|
|
{"$", P_DOLLAR},
|
|
{NULL, 0}
|
|
};
|
|
|
|
int default_punctuationtable[256];
|
|
int default_nextpunctuation[sizeof( default_punctuations ) / sizeof( punctuation_t )];
|
|
int default_setup;
|
|
|
|
char idLexer::baseFolder[ 256 ];
|
|
|
|
/*
|
|
================
|
|
idLexer::CreatePunctuationTable
|
|
================
|
|
*/
|
|
void idLexer::CreatePunctuationTable( const punctuation_t* punctuations )
|
|
{
|
|
int i, n, lastp;
|
|
const punctuation_t* p, *newp;
|
|
|
|
//get memory for the table
|
|
if( punctuations == default_punctuations )
|
|
{
|
|
idLexer::punctuationtable = default_punctuationtable;
|
|
idLexer::nextpunctuation = default_nextpunctuation;
|
|
if( default_setup )
|
|
{
|
|
return;
|
|
}
|
|
default_setup = true;
|
|
i = sizeof( default_punctuations ) / sizeof( punctuation_t );
|
|
}
|
|
else
|
|
{
|
|
if( !idLexer::punctuationtable || idLexer::punctuationtable == default_punctuationtable )
|
|
{
|
|
idLexer::punctuationtable = ( int* ) Mem_Alloc( 256 * sizeof( int ), TAG_IDLIB_LEXER );
|
|
}
|
|
if( idLexer::nextpunctuation && idLexer::nextpunctuation != default_nextpunctuation )
|
|
{
|
|
Mem_Free( idLexer::nextpunctuation );
|
|
}
|
|
for( i = 0; punctuations[i].p; i++ )
|
|
{
|
|
}
|
|
idLexer::nextpunctuation = ( int* ) Mem_Alloc( i * sizeof( int ), TAG_IDLIB_LEXER );
|
|
}
|
|
memset( idLexer::punctuationtable, 0xFF, 256 * sizeof( int ) );
|
|
memset( idLexer::nextpunctuation, 0xFF, i * sizeof( int ) );
|
|
//add the punctuations in the list to the punctuation table
|
|
for( i = 0; punctuations[i].p; i++ )
|
|
{
|
|
newp = &punctuations[i];
|
|
lastp = -1;
|
|
//sort the punctuations in this table entry on length (longer punctuations first)
|
|
for( n = idLexer::punctuationtable[( unsigned int ) newp->p[0]]; n >= 0; n = idLexer::nextpunctuation[n] )
|
|
{
|
|
p = &punctuations[n];
|
|
if( strlen( p->p ) < strlen( newp->p ) )
|
|
{
|
|
idLexer::nextpunctuation[i] = n;
|
|
if( lastp >= 0 )
|
|
{
|
|
idLexer::nextpunctuation[lastp] = i;
|
|
}
|
|
else
|
|
{
|
|
idLexer::punctuationtable[( unsigned int ) newp->p[0]] = i;
|
|
}
|
|
break;
|
|
}
|
|
lastp = n;
|
|
}
|
|
if( n < 0 )
|
|
{
|
|
idLexer::nextpunctuation[i] = -1;
|
|
if( lastp >= 0 )
|
|
{
|
|
idLexer::nextpunctuation[lastp] = i;
|
|
}
|
|
else
|
|
{
|
|
idLexer::punctuationtable[( unsigned int ) newp->p[0]] = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetPunctuationFromId
|
|
================
|
|
*/
|
|
const char* idLexer::GetPunctuationFromId( int id )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; idLexer::punctuations[i].p; i++ )
|
|
{
|
|
if( idLexer::punctuations[i].n == id )
|
|
{
|
|
return idLexer::punctuations[i].p;
|
|
}
|
|
}
|
|
return "unkown punctuation";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetPunctuationId
|
|
================
|
|
*/
|
|
int idLexer::GetPunctuationId( const char* p )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; idLexer::punctuations[i].p; i++ )
|
|
{
|
|
if( !strcmp( idLexer::punctuations[i].p, p ) )
|
|
{
|
|
return idLexer::punctuations[i].n;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Error
|
|
================
|
|
*/
|
|
void idLexer::Error( const char* str, ... )
|
|
{
|
|
char text[MAX_STRING_CHARS];
|
|
va_list ap;
|
|
|
|
hadError = true;
|
|
|
|
if( idLexer::flags & LEXFL_NOERRORS )
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start( ap, str );
|
|
vsprintf( text, str, ap );
|
|
va_end( ap );
|
|
|
|
if( idLexer::flags & LEXFL_NOFATALERRORS )
|
|
{
|
|
idLib::common->Warning( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
|
|
}
|
|
else
|
|
{
|
|
idLib::common->Error( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Warning
|
|
================
|
|
*/
|
|
void idLexer::Warning( const char* str, ... )
|
|
{
|
|
char text[MAX_STRING_CHARS];
|
|
va_list ap;
|
|
|
|
if( idLexer::flags & LEXFL_NOWARNINGS )
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start( ap, str );
|
|
vsprintf( text, str, ap );
|
|
va_end( ap );
|
|
idLib::common->Warning( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SetPunctuations
|
|
================
|
|
*/
|
|
void idLexer::SetPunctuations( const punctuation_t* p )
|
|
{
|
|
#ifdef PUNCTABLE
|
|
if( p )
|
|
{
|
|
idLexer::CreatePunctuationTable( p );
|
|
}
|
|
else
|
|
{
|
|
idLexer::CreatePunctuationTable( default_punctuations );
|
|
}
|
|
#endif //PUNCTABLE
|
|
if( p )
|
|
{
|
|
idLexer::punctuations = p;
|
|
}
|
|
else
|
|
{
|
|
idLexer::punctuations = default_punctuations;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadWhiteSpace
|
|
|
|
Reads spaces, tabs, C-like comments etc.
|
|
When a newline character is found the scripts line counter is increased.
|
|
================
|
|
*/
|
|
int idLexer::ReadWhiteSpace()
|
|
{
|
|
while( 1 )
|
|
{
|
|
// skip white space
|
|
while( *idLexer::script_p <= ' ' )
|
|
{
|
|
if( !*idLexer::script_p )
|
|
{
|
|
return 0;
|
|
}
|
|
if( *idLexer::script_p == '\n' )
|
|
{
|
|
idLexer::line++;
|
|
}
|
|
idLexer::script_p++;
|
|
}
|
|
// skip comments
|
|
if( *idLexer::script_p == '/' )
|
|
{
|
|
// comments //
|
|
if( *( idLexer::script_p + 1 ) == '/' )
|
|
{
|
|
idLexer::script_p++;
|
|
do
|
|
{
|
|
idLexer::script_p++;
|
|
if( !*idLexer::script_p )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
while( *idLexer::script_p != '\n' );
|
|
idLexer::line++;
|
|
idLexer::script_p++;
|
|
if( !*idLexer::script_p )
|
|
{
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
// comments /* */
|
|
else if( *( idLexer::script_p + 1 ) == '*' )
|
|
{
|
|
idLexer::script_p++;
|
|
while( 1 )
|
|
{
|
|
idLexer::script_p++;
|
|
if( !*idLexer::script_p )
|
|
{
|
|
return 0;
|
|
}
|
|
if( *idLexer::script_p == '\n' )
|
|
{
|
|
idLexer::line++;
|
|
}
|
|
else if( *idLexer::script_p == '/' )
|
|
{
|
|
if( *( idLexer::script_p - 1 ) == '*' )
|
|
{
|
|
break;
|
|
}
|
|
if( *( idLexer::script_p + 1 ) == '*' )
|
|
{
|
|
idLexer::Warning( "nested comment" );
|
|
}
|
|
}
|
|
}
|
|
idLexer::script_p++;
|
|
if( !*idLexer::script_p )
|
|
{
|
|
return 0;
|
|
}
|
|
idLexer::script_p++;
|
|
if( !*idLexer::script_p )
|
|
{
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idLexer::SkipWhiteSpace
|
|
|
|
Reads spaces, tabs, C-like comments etc. When a newline character is found, the scripts line
|
|
counter is increased. Returns false if there is no token left to be read.
|
|
========================
|
|
*/
|
|
bool idLexer::SkipWhiteSpace( bool currentLine )
|
|
{
|
|
while( 1 )
|
|
{
|
|
assert( script_p <= end_p );
|
|
if( script_p == end_p )
|
|
{
|
|
return false;
|
|
}
|
|
// skip white space
|
|
while( *script_p <= ' ' )
|
|
{
|
|
if( script_p == end_p )
|
|
{
|
|
return false;
|
|
}
|
|
if( !*script_p )
|
|
{
|
|
return false;
|
|
}
|
|
if( *script_p == '\n' )
|
|
{
|
|
line++;
|
|
if( currentLine )
|
|
{
|
|
script_p++;
|
|
return true;
|
|
}
|
|
}
|
|
script_p++;
|
|
}
|
|
// skip comments
|
|
if( *script_p == '/' )
|
|
{
|
|
// comments //
|
|
if( *( script_p + 1 ) == '/' )
|
|
{
|
|
script_p++;
|
|
do
|
|
{
|
|
script_p++;
|
|
if( !*script_p )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
while( *script_p != '\n' );
|
|
line++;
|
|
script_p++;
|
|
if( currentLine )
|
|
{
|
|
return true;
|
|
}
|
|
if( !*script_p )
|
|
{
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
// comments /* */
|
|
else if( *( script_p + 1 ) == '*' )
|
|
{
|
|
script_p++;
|
|
while( 1 )
|
|
{
|
|
script_p++;
|
|
if( !*script_p )
|
|
{
|
|
return false;
|
|
}
|
|
if( *script_p == '\n' )
|
|
{
|
|
line++;
|
|
}
|
|
else if( *script_p == '/' )
|
|
{
|
|
if( *( script_p - 1 ) == '*' )
|
|
{
|
|
break;
|
|
}
|
|
if( *( script_p + 1 ) == '*' )
|
|
{
|
|
Warning( "nested comment" );
|
|
}
|
|
}
|
|
}
|
|
script_p++;
|
|
if( !*script_p )
|
|
{
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadEscapeCharacter
|
|
================
|
|
*/
|
|
int idLexer::ReadEscapeCharacter( char* ch )
|
|
{
|
|
int c, val, i;
|
|
|
|
// step over the leading '\\'
|
|
idLexer::script_p++;
|
|
// determine the escape character
|
|
switch( *idLexer::script_p )
|
|
{
|
|
case '\\':
|
|
c = '\\';
|
|
break;
|
|
case 'n':
|
|
c = '\n';
|
|
break;
|
|
case 'r':
|
|
c = '\r';
|
|
break;
|
|
case 't':
|
|
c = '\t';
|
|
break;
|
|
case 'v':
|
|
c = '\v';
|
|
break;
|
|
case 'b':
|
|
c = '\b';
|
|
break;
|
|
case 'f':
|
|
c = '\f';
|
|
break;
|
|
case 'a':
|
|
c = '\a';
|
|
break;
|
|
case '\'':
|
|
c = '\'';
|
|
break;
|
|
case '\"':
|
|
c = '\"';
|
|
break;
|
|
case '\?':
|
|
c = '\?';
|
|
break;
|
|
case 'x':
|
|
{
|
|
idLexer::script_p++;
|
|
for( i = 0, val = 0; ; i++, idLexer::script_p++ )
|
|
{
|
|
c = *idLexer::script_p;
|
|
if( c >= '0' && c <= '9' )
|
|
c = c - '0';
|
|
else if( c >= 'A' && c <= 'Z' )
|
|
c = c - 'A' + 10;
|
|
else if( c >= 'a' && c <= 'z' )
|
|
c = c - 'a' + 10;
|
|
else
|
|
break;
|
|
val = ( val << 4 ) + c;
|
|
}
|
|
idLexer::script_p--;
|
|
if( val > 0xFF )
|
|
{
|
|
idLexer::Warning( "too large value in escape character" );
|
|
val = 0xFF;
|
|
}
|
|
c = val;
|
|
break;
|
|
}
|
|
default: //NOTE: decimal ASCII code, NOT octal
|
|
{
|
|
if( *idLexer::script_p < '0' || *idLexer::script_p > '9' )
|
|
{
|
|
idLexer::Error( "unknown escape char" );
|
|
}
|
|
for( i = 0, val = 0; ; i++, idLexer::script_p++ )
|
|
{
|
|
c = *idLexer::script_p;
|
|
if( c >= '0' && c <= '9' )
|
|
c = c - '0';
|
|
else
|
|
break;
|
|
val = val * 10 + c;
|
|
}
|
|
idLexer::script_p--;
|
|
if( val > 0xFF )
|
|
{
|
|
idLexer::Warning( "too large value in escape character" );
|
|
val = 0xFF;
|
|
}
|
|
c = val;
|
|
break;
|
|
}
|
|
}
|
|
// step over the escape character or the last digit of the number
|
|
idLexer::script_p++;
|
|
// store the escape character
|
|
*ch = c;
|
|
// succesfully read escape character
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadString
|
|
|
|
Escape characters are interpretted.
|
|
Reads two strings with only a white space between them as one string.
|
|
================
|
|
*/
|
|
int idLexer::ReadString( idToken* token, int quote )
|
|
{
|
|
int tmpline;
|
|
const char* tmpscript_p;
|
|
char ch;
|
|
|
|
if( quote == '\"' )
|
|
{
|
|
token->type = TT_STRING;
|
|
}
|
|
else
|
|
{
|
|
token->type = TT_LITERAL;
|
|
}
|
|
|
|
// leading quote
|
|
idLexer::script_p++;
|
|
|
|
while( 1 )
|
|
{
|
|
// if there is an escape character and escape characters are allowed
|
|
if( *idLexer::script_p == '\\' && !( idLexer::flags & LEXFL_NOSTRINGESCAPECHARS ) )
|
|
{
|
|
if( !idLexer::ReadEscapeCharacter( &ch ) )
|
|
{
|
|
return 0;
|
|
}
|
|
token->AppendDirty( ch );
|
|
}
|
|
// if a trailing quote
|
|
else if( *idLexer::script_p == quote )
|
|
{
|
|
// step over the quote
|
|
idLexer::script_p++;
|
|
// if consecutive strings should not be concatenated
|
|
if( ( idLexer::flags & LEXFL_NOSTRINGCONCAT ) &&
|
|
( !( idLexer::flags & LEXFL_ALLOWBACKSLASHSTRINGCONCAT ) || ( quote != '\"' ) ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
tmpscript_p = idLexer::script_p;
|
|
tmpline = idLexer::line;
|
|
// read white space between possible two consecutive strings
|
|
if( !idLexer::ReadWhiteSpace() )
|
|
{
|
|
idLexer::script_p = tmpscript_p;
|
|
idLexer::line = tmpline;
|
|
break;
|
|
}
|
|
|
|
if( idLexer::flags & LEXFL_NOSTRINGCONCAT )
|
|
{
|
|
if( *idLexer::script_p != '\\' )
|
|
{
|
|
idLexer::script_p = tmpscript_p;
|
|
idLexer::line = tmpline;
|
|
break;
|
|
}
|
|
// step over the '\\'
|
|
idLexer::script_p++;
|
|
if( !idLexer::ReadWhiteSpace() || ( *idLexer::script_p != quote ) )
|
|
{
|
|
idLexer::Error( "expecting string after '\' terminated line" );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// if there's no leading qoute
|
|
if( *idLexer::script_p != quote )
|
|
{
|
|
idLexer::script_p = tmpscript_p;
|
|
idLexer::line = tmpline;
|
|
break;
|
|
}
|
|
// step over the new leading quote
|
|
idLexer::script_p++;
|
|
}
|
|
else
|
|
{
|
|
if( *idLexer::script_p == '\0' )
|
|
{
|
|
idLexer::Error( "missing trailing quote" );
|
|
return 0;
|
|
}
|
|
if( *idLexer::script_p == '\n' )
|
|
{
|
|
idLexer::Error( "newline inside string" );
|
|
return 0;
|
|
}
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
}
|
|
}
|
|
token->data[token->len] = '\0';
|
|
|
|
if( token->type == TT_LITERAL )
|
|
{
|
|
if( !( idLexer::flags & LEXFL_ALLOWMULTICHARLITERALS ) )
|
|
{
|
|
if( token->Length() != 1 )
|
|
{
|
|
idLexer::Warning( "literal is not one character long" );
|
|
}
|
|
}
|
|
token->subtype = ( *token )[0];
|
|
}
|
|
else
|
|
{
|
|
// the sub type is the length of the string
|
|
token->subtype = token->Length();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadName
|
|
================
|
|
*/
|
|
int idLexer::ReadName( idToken* token )
|
|
{
|
|
char c;
|
|
|
|
token->type = TT_NAME;
|
|
do
|
|
{
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
}
|
|
while( ( c >= 'a' && c <= 'z' ) ||
|
|
( c >= 'A' && c <= 'Z' ) ||
|
|
( c >= '0' && c <= '9' ) ||
|
|
c == '_' ||
|
|
// if treating all tokens as strings, don't parse '-' as a seperate token
|
|
( ( idLexer::flags & LEXFL_ONLYSTRINGS ) && ( c == '-' ) ) ||
|
|
// if special path name characters are allowed
|
|
( ( idLexer::flags & LEXFL_ALLOWPATHNAMES ) && ( c == '/' || c == '\\' || c == ':' || c == '.' ) ) );
|
|
token->data[token->len] = '\0';
|
|
//the sub type is the length of the name
|
|
token->subtype = token->Length();
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::CheckString
|
|
================
|
|
*/
|
|
ID_INLINE int idLexer::CheckString( const char* str ) const
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; str[i]; i++ )
|
|
{
|
|
if( idLexer::script_p[i] != str[i] )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadNumber
|
|
================
|
|
*/
|
|
int idLexer::ReadNumber( idToken* token )
|
|
{
|
|
int i;
|
|
int dot;
|
|
char c, c2;
|
|
|
|
token->type = TT_NUMBER;
|
|
token->subtype = 0;
|
|
token->intvalue = 0;
|
|
token->floatvalue = 0;
|
|
|
|
c = *idLexer::script_p;
|
|
c2 = *( idLexer::script_p + 1 );
|
|
|
|
if( c == '0' && c2 != '.' )
|
|
{
|
|
// check for a hexadecimal number
|
|
if( c2 == 'x' || c2 == 'X' )
|
|
{
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
while( ( c >= '0' && c <= '9' ) ||
|
|
( c >= 'a' && c <= 'f' ) ||
|
|
( c >= 'A' && c <= 'F' ) )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
token->subtype = TT_HEX | TT_INTEGER;
|
|
}
|
|
// check for a binary number
|
|
else if( c2 == 'b' || c2 == 'B' )
|
|
{
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
while( c == '0' || c == '1' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
token->subtype = TT_BINARY | TT_INTEGER;
|
|
}
|
|
// its an octal number
|
|
else
|
|
{
|
|
token->AppendDirty( *idLexer::script_p++ );
|
|
c = *idLexer::script_p;
|
|
while( c >= '0' && c <= '7' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
token->subtype = TT_OCTAL | TT_INTEGER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// decimal integer or floating point number or ip address
|
|
dot = 0;
|
|
while( 1 )
|
|
{
|
|
if( c >= '0' && c <= '9' )
|
|
{
|
|
}
|
|
else if( c == '.' )
|
|
{
|
|
dot++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
if( c == 'e' && dot == 0 )
|
|
{
|
|
//We have scientific notation without a decimal point
|
|
dot++;
|
|
}
|
|
// if a floating point number
|
|
if( dot == 1 )
|
|
{
|
|
token->subtype = TT_DECIMAL | TT_FLOAT;
|
|
// check for floating point exponent
|
|
if( c == 'e' )
|
|
{
|
|
//Append the e so that GetFloatValue code works
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
if( c == '-' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
else if( c == '+' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
while( c >= '0' && c <= '9' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
}
|
|
// check for floating point exception infinite 1.#INF or indefinite 1.#IND or NaN
|
|
else if( c == '#' )
|
|
{
|
|
c2 = 4;
|
|
if( CheckString( "INF" ) )
|
|
{
|
|
token->subtype |= TT_INFINITE;
|
|
}
|
|
else if( CheckString( "IND" ) )
|
|
{
|
|
token->subtype |= TT_INDEFINITE;
|
|
}
|
|
else if( CheckString( "NAN" ) )
|
|
{
|
|
token->subtype |= TT_NAN;
|
|
}
|
|
else if( CheckString( "QNAN" ) )
|
|
{
|
|
token->subtype |= TT_NAN;
|
|
c2++;
|
|
}
|
|
else if( CheckString( "SNAN" ) )
|
|
{
|
|
token->subtype |= TT_NAN;
|
|
c2++;
|
|
}
|
|
for( i = 0; i < c2; i++ )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
while( c >= '0' && c <= '9' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
if( !( idLexer::flags & LEXFL_ALLOWFLOATEXCEPTIONS ) )
|
|
{
|
|
token->AppendDirty( 0 ); // zero terminate for c_str
|
|
idLexer::Error( "parsed %s", token->c_str() );
|
|
}
|
|
}
|
|
}
|
|
else if( dot > 1 )
|
|
{
|
|
if( !( idLexer::flags & LEXFL_ALLOWIPADDRESSES ) )
|
|
{
|
|
idLexer::Error( "more than one dot in number" );
|
|
return 0;
|
|
}
|
|
if( dot != 3 )
|
|
{
|
|
idLexer::Error( "ip address should have three dots" );
|
|
return 0;
|
|
}
|
|
token->subtype = TT_IPADDRESS;
|
|
}
|
|
else
|
|
{
|
|
token->subtype = TT_DECIMAL | TT_INTEGER;
|
|
}
|
|
}
|
|
|
|
if( token->subtype & TT_FLOAT )
|
|
{
|
|
if( c > ' ' )
|
|
{
|
|
// single-precision: float
|
|
if( c == 'f' || c == 'F' )
|
|
{
|
|
token->subtype |= TT_SINGLE_PRECISION;
|
|
idLexer::script_p++;
|
|
}
|
|
// extended-precision: long double
|
|
else if( c == 'l' || c == 'L' )
|
|
{
|
|
token->subtype |= TT_EXTENDED_PRECISION;
|
|
idLexer::script_p++;
|
|
}
|
|
// default is double-precision: double
|
|
else
|
|
{
|
|
token->subtype |= TT_DOUBLE_PRECISION;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
token->subtype |= TT_DOUBLE_PRECISION;
|
|
}
|
|
}
|
|
else if( token->subtype & TT_INTEGER )
|
|
{
|
|
if( c > ' ' )
|
|
{
|
|
// default: signed long
|
|
for( i = 0; i < 2; i++ )
|
|
{
|
|
// long integer
|
|
if( c == 'l' || c == 'L' )
|
|
{
|
|
token->subtype |= TT_LONG;
|
|
}
|
|
// unsigned integer
|
|
else if( c == 'u' || c == 'U' )
|
|
{
|
|
token->subtype |= TT_UNSIGNED;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
}
|
|
}
|
|
else if( token->subtype & TT_IPADDRESS )
|
|
{
|
|
if( c == ':' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
while( c >= '0' && c <= '9' )
|
|
{
|
|
token->AppendDirty( c );
|
|
c = *( ++idLexer::script_p );
|
|
}
|
|
token->subtype |= TT_IPPORT;
|
|
}
|
|
}
|
|
token->data[token->len] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadPunctuation
|
|
================
|
|
*/
|
|
int idLexer::ReadPunctuation( idToken* token )
|
|
{
|
|
int l, n, i;
|
|
const char* p;
|
|
const punctuation_t* punc;
|
|
|
|
#ifdef PUNCTABLE
|
|
for( n = idLexer::punctuationtable[( unsigned int ) * ( idLexer::script_p )]; n >= 0; n = idLexer::nextpunctuation[n] )
|
|
{
|
|
punc = &( idLexer::punctuations[n] );
|
|
#else
|
|
int i;
|
|
|
|
for( i = 0; idLexer::punctuations[i].p; i++ )
|
|
{
|
|
punc = &idLexer::punctuations[i];
|
|
#endif
|
|
p = punc->p;
|
|
// check for this punctuation in the script
|
|
for( l = 0; p[l] && idLexer::script_p[l]; l++ )
|
|
{
|
|
if( idLexer::script_p[l] != p[l] )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( !p[l] )
|
|
{
|
|
//
|
|
token->EnsureAlloced( l + 1, false );
|
|
for( i = 0; i <= l; i++ )
|
|
{
|
|
token->data[i] = p[i];
|
|
}
|
|
token->len = l;
|
|
//
|
|
idLexer::script_p += l;
|
|
token->type = TT_PUNCTUATION;
|
|
// sub type is the punctuation id
|
|
token->subtype = punc->n;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadToken
|
|
================
|
|
*/
|
|
int idLexer::ReadToken( idToken* token )
|
|
{
|
|
int c;
|
|
|
|
if( !loaded )
|
|
{
|
|
idLib::common->Error( "idLexer::ReadToken: no file loaded" );
|
|
return 0;
|
|
}
|
|
|
|
if( script_p == NULL )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// if there is a token available (from unreadToken)
|
|
if( tokenavailable )
|
|
{
|
|
tokenavailable = 0;
|
|
*token = idLexer::token;
|
|
return 1;
|
|
}
|
|
// save script pointer
|
|
lastScript_p = script_p;
|
|
// save line counter
|
|
lastline = line;
|
|
// clear the token stuff
|
|
token->data[0] = '\0';
|
|
token->len = 0;
|
|
// start of the white space
|
|
whiteSpaceStart_p = script_p;
|
|
token->whiteSpaceStart_p = script_p;
|
|
// read white space before token
|
|
if( !ReadWhiteSpace() )
|
|
{
|
|
return 0;
|
|
}
|
|
// end of the white space
|
|
idLexer::whiteSpaceEnd_p = script_p;
|
|
token->whiteSpaceEnd_p = script_p;
|
|
// line the token is on
|
|
token->line = line;
|
|
// number of lines crossed before token
|
|
token->linesCrossed = line - lastline;
|
|
// clear token flags
|
|
token->flags = 0;
|
|
|
|
c = *idLexer::script_p;
|
|
|
|
// if we're keeping everything as whitespace deliminated strings
|
|
if( idLexer::flags & LEXFL_ONLYSTRINGS )
|
|
{
|
|
// if there is a leading quote
|
|
if( c == '\"' || c == '\'' )
|
|
{
|
|
if( !idLexer::ReadString( token, c ) )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else if( !idLexer::ReadName( token ) )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
// if there is a number
|
|
else if( ( c >= '0' && c <= '9' ) ||
|
|
( c == '.' && ( *( idLexer::script_p + 1 ) >= '0' && *( idLexer::script_p + 1 ) <= '9' ) ) )
|
|
{
|
|
if( !idLexer::ReadNumber( token ) )
|
|
{
|
|
return 0;
|
|
}
|
|
// if names are allowed to start with a number
|
|
if( idLexer::flags & LEXFL_ALLOWNUMBERNAMES )
|
|
{
|
|
c = *idLexer::script_p;
|
|
if( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' )
|
|
{
|
|
if( !idLexer::ReadName( token ) )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// if there is a leading quote
|
|
else if( c == '\"' || c == '\'' )
|
|
{
|
|
if( !idLexer::ReadString( token, c ) )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
// if there is a name
|
|
else if( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' )
|
|
{
|
|
if( !idLexer::ReadName( token ) )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
// names may also start with a slash when pathnames are allowed
|
|
else if( ( idLexer::flags & LEXFL_ALLOWPATHNAMES ) && ( ( c == '/' || c == '\\' ) || c == '.' ) )
|
|
{
|
|
if( !idLexer::ReadName( token ) )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
// check for punctuations
|
|
else if( !idLexer::ReadPunctuation( token ) )
|
|
{
|
|
idLexer::Error( "unknown punctuation %c", c );
|
|
return 0;
|
|
}
|
|
// succesfully read a token
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ExpectTokenString
|
|
================
|
|
*/
|
|
int idLexer::ExpectTokenString( const char* string )
|
|
{
|
|
idToken token;
|
|
|
|
if( !idLexer::ReadToken( &token ) )
|
|
{
|
|
idLexer::Error( "couldn't find expected '%s'", string );
|
|
return 0;
|
|
}
|
|
if( token != string )
|
|
{
|
|
idLexer::Error( "expected '%s' but found '%s'", string, token.c_str() );
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ExpectTokenType
|
|
================
|
|
*/
|
|
int idLexer::ExpectTokenType( int type, int subtype, idToken* token )
|
|
{
|
|
idStr str;
|
|
|
|
if( !idLexer::ReadToken( token ) )
|
|
{
|
|
idLexer::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;
|
|
}
|
|
idLexer::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( ' ' );
|
|
idLexer::Error( "expected %s but found '%s'", str.c_str(), token->c_str() );
|
|
return 0;
|
|
}
|
|
}
|
|
else if( token->type == TT_PUNCTUATION )
|
|
{
|
|
if( subtype < 0 )
|
|
{
|
|
idLexer::Error( "BUG: wrong punctuation subtype" );
|
|
return 0;
|
|
}
|
|
if( token->subtype != subtype )
|
|
{
|
|
idLexer::Error( "expected '%s' but found '%s'", GetPunctuationFromId( subtype ), token->c_str() );
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ExpectAnyToken
|
|
================
|
|
*/
|
|
int idLexer::ExpectAnyToken( idToken* token )
|
|
{
|
|
if( !idLexer::ReadToken( token ) )
|
|
{
|
|
idLexer::Error( "couldn't read expected token" );
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::CheckTokenString
|
|
================
|
|
*/
|
|
int idLexer::CheckTokenString( const char* string )
|
|
{
|
|
idToken tok;
|
|
|
|
if( !ReadToken( &tok ) )
|
|
{
|
|
return 0;
|
|
}
|
|
// if the given string is available
|
|
if( tok == string )
|
|
{
|
|
return 1;
|
|
}
|
|
// unread token
|
|
script_p = lastScript_p;
|
|
line = lastline;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::CheckTokenType
|
|
================
|
|
*/
|
|
int idLexer::CheckTokenType( int type, int subtype, idToken* token )
|
|
{
|
|
idToken tok;
|
|
|
|
if( !ReadToken( &tok ) )
|
|
{
|
|
return 0;
|
|
}
|
|
// if the type matches
|
|
if( tok.type == type && ( tok.subtype & subtype ) == subtype )
|
|
{
|
|
*token = tok;
|
|
return 1;
|
|
}
|
|
// unread token
|
|
script_p = lastScript_p;
|
|
line = lastline;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::PeekTokenString
|
|
================
|
|
*/
|
|
int idLexer::PeekTokenString( const char* string )
|
|
{
|
|
idToken tok;
|
|
|
|
if( !ReadToken( &tok ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// unread token
|
|
script_p = lastScript_p;
|
|
line = lastline;
|
|
|
|
// if the given string is available
|
|
if( tok == string )
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::PeekTokenType
|
|
================
|
|
*/
|
|
int idLexer::PeekTokenType( int type, int subtype, idToken* token )
|
|
{
|
|
idToken tok;
|
|
|
|
if( !ReadToken( &tok ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// unread token
|
|
script_p = lastScript_p;
|
|
line = lastline;
|
|
|
|
// if the type matches
|
|
if( tok.type == type && ( tok.subtype & subtype ) == subtype )
|
|
{
|
|
*token = tok;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SkipUntilString
|
|
================
|
|
*/
|
|
int idLexer::SkipUntilString( const char* string )
|
|
{
|
|
idToken token;
|
|
|
|
while( idLexer::ReadToken( &token ) )
|
|
{
|
|
if( token == string )
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SkipRestOfLine
|
|
================
|
|
*/
|
|
int idLexer::SkipRestOfLine()
|
|
{
|
|
idToken token;
|
|
|
|
while( idLexer::ReadToken( &token ) )
|
|
{
|
|
if( token.linesCrossed )
|
|
{
|
|
idLexer::script_p = lastScript_p;
|
|
idLexer::line = lastline;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::SkipBracedSection
|
|
|
|
Skips until a matching close brace is found.
|
|
Internal brace depths are properly skipped.
|
|
=================
|
|
*/
|
|
int idLexer::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;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::UnreadToken
|
|
================
|
|
*/
|
|
void idLexer::UnreadToken( const idToken* token )
|
|
{
|
|
if( idLexer::tokenavailable )
|
|
{
|
|
idLib::common->FatalError( "idLexer::unreadToken, unread token twice\n" );
|
|
}
|
|
idLexer::token = *token;
|
|
idLexer::tokenavailable = 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadTokenOnLine
|
|
================
|
|
*/
|
|
int idLexer::ReadTokenOnLine( idToken* token )
|
|
{
|
|
idToken tok;
|
|
|
|
if( !idLexer::ReadToken( &tok ) )
|
|
{
|
|
idLexer::script_p = lastScript_p;
|
|
idLexer::line = lastline;
|
|
return false;
|
|
}
|
|
// if no lines were crossed before this token
|
|
if( !tok.linesCrossed )
|
|
{
|
|
*token = tok;
|
|
return true;
|
|
}
|
|
// restore our position
|
|
idLexer::script_p = lastScript_p;
|
|
idLexer::line = lastline;
|
|
token->Clear();
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ReadRestOfLine
|
|
================
|
|
*/
|
|
const char* idLexer::ReadRestOfLine( idStr& out )
|
|
{
|
|
while( 1 )
|
|
{
|
|
|
|
if( *idLexer::script_p == '\n' )
|
|
{
|
|
idLexer::line++;
|
|
break;
|
|
}
|
|
|
|
if( !*idLexer::script_p )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( *idLexer::script_p <= ' ' )
|
|
{
|
|
out += " ";
|
|
}
|
|
else
|
|
{
|
|
out += *idLexer::script_p;
|
|
}
|
|
idLexer::script_p++;
|
|
|
|
}
|
|
|
|
out.Strip( ' ' );
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ParseInt
|
|
================
|
|
*/
|
|
int idLexer::ParseInt()
|
|
{
|
|
idToken token;
|
|
|
|
if( !idLexer::ReadToken( &token ) )
|
|
{
|
|
idLexer::Error( "couldn't read expected integer" );
|
|
return 0;
|
|
}
|
|
if( token.type == TT_PUNCTUATION && token == "-" )
|
|
{
|
|
idLexer::ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
|
|
return -( ( signed int ) token.GetIntValue() );
|
|
}
|
|
else if( token.type != TT_NUMBER || token.subtype == TT_FLOAT )
|
|
{
|
|
idLexer::Error( "expected integer value, found '%s'", token.c_str() );
|
|
}
|
|
return token.GetIntValue();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ParseBool
|
|
================
|
|
*/
|
|
bool idLexer::ParseBool()
|
|
{
|
|
idToken token;
|
|
|
|
if( !idLexer::ExpectTokenType( TT_NUMBER, 0, &token ) )
|
|
{
|
|
idLexer::Error( "couldn't read expected boolean" );
|
|
return false;
|
|
}
|
|
return ( token.GetIntValue() != 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::ParseFloat
|
|
================
|
|
*/
|
|
float idLexer::ParseFloat( bool* errorFlag )
|
|
{
|
|
idToken token;
|
|
|
|
if( errorFlag )
|
|
{
|
|
*errorFlag = false;
|
|
}
|
|
|
|
if( !idLexer::ReadToken( &token ) )
|
|
{
|
|
if( errorFlag )
|
|
{
|
|
idLexer::Warning( "couldn't read expected floating point number" );
|
|
*errorFlag = true;
|
|
}
|
|
else
|
|
{
|
|
idLexer::Error( "couldn't read expected floating point number" );
|
|
}
|
|
return 0;
|
|
}
|
|
if( token.type == TT_PUNCTUATION && token == "-" )
|
|
{
|
|
idLexer::ExpectTokenType( TT_NUMBER, 0, &token );
|
|
return -token.GetFloatValue();
|
|
}
|
|
else if( token.type != TT_NUMBER )
|
|
{
|
|
if( errorFlag )
|
|
{
|
|
idLexer::Warning( "expected float value, found '%s'", token.c_str() );
|
|
*errorFlag = true;
|
|
}
|
|
else
|
|
{
|
|
idLexer::Error( "expected float value, found '%s'", token.c_str() );
|
|
}
|
|
}
|
|
return token.GetFloatValue();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Parse1DMatrix
|
|
================
|
|
*/
|
|
int idLexer::Parse1DMatrix( int x, float* m )
|
|
{
|
|
int i;
|
|
|
|
if( !idLexer::ExpectTokenString( "(" ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for( i = 0; i < x; i++ )
|
|
{
|
|
m[i] = idLexer::ParseFloat();
|
|
}
|
|
|
|
if( !idLexer::ExpectTokenString( ")" ) )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Parse2DMatrix
|
|
================
|
|
*/
|
|
int idLexer::Parse2DMatrix( int y, int x, float* m )
|
|
{
|
|
int i;
|
|
|
|
if( !idLexer::ExpectTokenString( "(" ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for( i = 0; i < y; i++ )
|
|
{
|
|
if( !idLexer::Parse1DMatrix( x, m + i * x ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( !idLexer::ExpectTokenString( ")" ) )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Parse3DMatrix
|
|
================
|
|
*/
|
|
int idLexer::Parse3DMatrix( int z, int y, int x, float* m )
|
|
{
|
|
int i;
|
|
|
|
if( !idLexer::ExpectTokenString( "(" ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for( i = 0 ; i < z; i++ )
|
|
{
|
|
if( !idLexer::Parse2DMatrix( y, x, m + i * x * y ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( !idLexer::ExpectTokenString( ")" ) )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idParser::ParseBracedSection
|
|
|
|
The next token should be an open brace.
|
|
Parses until a matching close brace is found.
|
|
Maintains exact characters between braces.
|
|
|
|
FIXME: this should use ReadToken and replace the token white space with correct indents and newlines
|
|
=================
|
|
*/
|
|
const char* idLexer::ParseBracedSectionExact( idStr& out, int tabs )
|
|
{
|
|
int depth;
|
|
bool doTabs;
|
|
bool skipWhite;
|
|
|
|
out.Empty();
|
|
|
|
if( !idLexer::ExpectTokenString( "{" ) )
|
|
{
|
|
return out.c_str( );
|
|
}
|
|
|
|
out = "{";
|
|
depth = 1;
|
|
skipWhite = false;
|
|
doTabs = tabs >= 0;
|
|
|
|
while( depth && *idLexer::script_p )
|
|
{
|
|
char c = *( idLexer::script_p++ );
|
|
|
|
switch( c )
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
{
|
|
if( skipWhite )
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case '\n':
|
|
{
|
|
if( doTabs )
|
|
{
|
|
skipWhite = true;
|
|
out += c;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case '{':
|
|
{
|
|
depth++;
|
|
tabs++;
|
|
break;
|
|
}
|
|
case '}':
|
|
{
|
|
depth--;
|
|
tabs--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( skipWhite )
|
|
{
|
|
int i = tabs;
|
|
if( c == '{' )
|
|
{
|
|
i--;
|
|
}
|
|
skipWhite = false;
|
|
for( ; i > 0; i-- )
|
|
{
|
|
out += '\t';
|
|
}
|
|
}
|
|
out += c;
|
|
}
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::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* idLexer::ParseBracedSection( idStr& out )
|
|
{
|
|
idToken token;
|
|
int i, depth;
|
|
|
|
out.Empty();
|
|
if( !idLexer::ExpectTokenString( "{" ) )
|
|
{
|
|
return out.c_str();
|
|
}
|
|
out = "{";
|
|
depth = 1;
|
|
do
|
|
{
|
|
if( !idLexer::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( token.type == TT_PUNCTUATION )
|
|
{
|
|
if( token[0] == '{' )
|
|
{
|
|
depth++;
|
|
}
|
|
else if( token[0] == '}' )
|
|
{
|
|
depth--;
|
|
}
|
|
}
|
|
|
|
if( token.type == TT_STRING )
|
|
{
|
|
out += "\"" + token + "\"";
|
|
}
|
|
else
|
|
{
|
|
out += token;
|
|
}
|
|
out += " ";
|
|
}
|
|
while( depth );
|
|
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idLexer::ParseRestOfLine
|
|
|
|
parse the rest of the line
|
|
=================
|
|
*/
|
|
const char* idLexer::ParseRestOfLine( idStr& out )
|
|
{
|
|
idToken token;
|
|
|
|
out.Empty();
|
|
while( idLexer::ReadToken( &token ) )
|
|
{
|
|
if( token.linesCrossed )
|
|
{
|
|
idLexer::script_p = lastScript_p;
|
|
idLexer::line = lastline;
|
|
break;
|
|
}
|
|
if( out.Length() )
|
|
{
|
|
out += " ";
|
|
}
|
|
out += token;
|
|
}
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idLexer::ParseCompleteLine
|
|
|
|
Returns a string up to the \n, but doesn't eat any whitespace at the beginning of the next line.
|
|
========================
|
|
*/
|
|
const char* idLexer::ParseCompleteLine( idStr& out )
|
|
{
|
|
idToken token;
|
|
const char* start;
|
|
|
|
start = script_p;
|
|
|
|
while( 1 )
|
|
{
|
|
// end of buffer
|
|
if( *script_p == 0 )
|
|
{
|
|
break;
|
|
}
|
|
if( *script_p == '\n' )
|
|
{
|
|
line++;
|
|
script_p++;
|
|
break;
|
|
}
|
|
script_p++;
|
|
}
|
|
|
|
out.Empty();
|
|
out.Append( start, script_p - start );
|
|
|
|
return out.c_str();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetLastWhiteSpace
|
|
================
|
|
*/
|
|
int idLexer::GetLastWhiteSpace( idStr& whiteSpace ) const
|
|
{
|
|
whiteSpace.Clear();
|
|
for( const char* p = whiteSpaceStart_p; p < whiteSpaceEnd_p; p++ )
|
|
{
|
|
whiteSpace.Append( *p );
|
|
}
|
|
return whiteSpace.Length();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetLastWhiteSpaceStart
|
|
================
|
|
*/
|
|
int idLexer::GetLastWhiteSpaceStart() const
|
|
{
|
|
return whiteSpaceStart_p - buffer;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::GetLastWhiteSpaceEnd
|
|
================
|
|
*/
|
|
int idLexer::GetLastWhiteSpaceEnd() const
|
|
{
|
|
return whiteSpaceEnd_p - buffer;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::Reset
|
|
================
|
|
*/
|
|
void idLexer::Reset()
|
|
{
|
|
// pointer in script buffer
|
|
idLexer::script_p = idLexer::buffer;
|
|
// pointer in script buffer before reading token
|
|
idLexer::lastScript_p = idLexer::buffer;
|
|
// begin of white space
|
|
idLexer::whiteSpaceStart_p = NULL;
|
|
// end of white space
|
|
idLexer::whiteSpaceEnd_p = NULL;
|
|
// set if there's a token available in idLexer::token
|
|
idLexer::tokenavailable = 0;
|
|
|
|
idLexer::line = 1;
|
|
idLexer::lastline = 1;
|
|
// clear the saved token
|
|
idLexer::token = "";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::EndOfFile
|
|
================
|
|
*/
|
|
bool idLexer::EndOfFile()
|
|
{
|
|
return idLexer::script_p >= idLexer::end_p;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::NumLinesCrossed
|
|
================
|
|
*/
|
|
int idLexer::NumLinesCrossed()
|
|
{
|
|
return idLexer::line - idLexer::lastline;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::LoadFile
|
|
================
|
|
*/
|
|
int idLexer::LoadFile( const char* filename, bool OSPath )
|
|
{
|
|
idFile* fp;
|
|
idStr pathname;
|
|
int length;
|
|
char* buf;
|
|
|
|
if( idLexer::loaded )
|
|
{
|
|
idLib::common->Error( "idLexer::LoadFile: another script already loaded" );
|
|
return false;
|
|
}
|
|
|
|
if( !OSPath && ( baseFolder[0] != '\0' ) )
|
|
{
|
|
pathname = va( "%s/%s", baseFolder, filename );
|
|
}
|
|
else
|
|
{
|
|
pathname = filename;
|
|
}
|
|
if( OSPath )
|
|
{
|
|
fp = idLib::fileSystem->OpenExplicitFileRead( pathname );
|
|
}
|
|
else
|
|
{
|
|
fp = idLib::fileSystem->OpenFileRead( pathname );
|
|
}
|
|
if( !fp )
|
|
{
|
|
return false;
|
|
}
|
|
length = fp->Length();
|
|
buf = ( char* ) Mem_Alloc( length + 1, TAG_IDLIB_LEXER );
|
|
buf[length] = '\0';
|
|
fp->Read( buf, length );
|
|
idLexer::fileTime = fp->Timestamp();
|
|
idLexer::filename = fp->GetFullPath();
|
|
idLib::fileSystem->CloseFile( fp );
|
|
|
|
idLexer::buffer = buf;
|
|
idLexer::length = length;
|
|
// pointer in script buffer
|
|
idLexer::script_p = idLexer::buffer;
|
|
// pointer in script buffer before reading token
|
|
idLexer::lastScript_p = idLexer::buffer;
|
|
// pointer to end of script buffer
|
|
idLexer::end_p = &( idLexer::buffer[length] );
|
|
|
|
idLexer::tokenavailable = 0;
|
|
idLexer::line = 1;
|
|
idLexer::lastline = 1;
|
|
idLexer::allocated = true;
|
|
idLexer::loaded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::LoadMemory
|
|
================
|
|
*/
|
|
int idLexer::LoadMemory( const char* ptr, int length, const char* name, int startLine )
|
|
{
|
|
if( idLexer::loaded )
|
|
{
|
|
idLib::common->Error( "idLexer::LoadMemory: another script already loaded" );
|
|
return false;
|
|
}
|
|
idLexer::filename = name;
|
|
idLexer::buffer = ptr;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = length;
|
|
// pointer in script buffer
|
|
idLexer::script_p = idLexer::buffer;
|
|
// pointer in script buffer before reading token
|
|
idLexer::lastScript_p = idLexer::buffer;
|
|
// pointer to end of script buffer
|
|
idLexer::end_p = &( idLexer::buffer[length] );
|
|
|
|
idLexer::tokenavailable = 0;
|
|
idLexer::line = startLine;
|
|
idLexer::lastline = startLine;
|
|
idLexer::allocated = false;
|
|
idLexer::loaded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::FreeSource
|
|
================
|
|
*/
|
|
void idLexer::FreeSource()
|
|
{
|
|
#ifdef PUNCTABLE
|
|
if( idLexer::punctuationtable && idLexer::punctuationtable != default_punctuationtable )
|
|
{
|
|
Mem_Free( ( void* ) idLexer::punctuationtable );
|
|
idLexer::punctuationtable = NULL;
|
|
}
|
|
if( idLexer::nextpunctuation && idLexer::nextpunctuation != default_nextpunctuation )
|
|
{
|
|
Mem_Free( ( void* ) idLexer::nextpunctuation );
|
|
idLexer::nextpunctuation = NULL;
|
|
}
|
|
#endif //PUNCTABLE
|
|
if( idLexer::allocated )
|
|
{
|
|
Mem_Free( ( void* ) idLexer::buffer );
|
|
idLexer::buffer = NULL;
|
|
idLexer::allocated = false;
|
|
}
|
|
idLexer::tokenavailable = 0;
|
|
idLexer::token = "";
|
|
idLexer::loaded = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer()
|
|
{
|
|
idLexer::loaded = false;
|
|
idLexer::filename = "";
|
|
idLexer::flags = 0;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = 0;
|
|
idLexer::line = 0;
|
|
idLexer::lastline = 0;
|
|
idLexer::tokenavailable = 0;
|
|
idLexer::token = "";
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer( int flags )
|
|
{
|
|
idLexer::loaded = false;
|
|
idLexer::filename = "";
|
|
idLexer::flags = flags;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
idLexer::fileTime = 0;
|
|
idLexer::length = 0;
|
|
idLexer::line = 0;
|
|
idLexer::lastline = 0;
|
|
idLexer::tokenavailable = 0;
|
|
idLexer::token = "";
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer( const char* filename, int flags, bool OSPath )
|
|
{
|
|
idLexer::loaded = false;
|
|
idLexer::flags = flags;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
idLexer::token = "";
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
idLexer::LoadFile( filename, OSPath );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::idLexer
|
|
================
|
|
*/
|
|
idLexer::idLexer( const char* ptr, int length, const char* name, int flags )
|
|
{
|
|
idLexer::loaded = false;
|
|
idLexer::flags = flags;
|
|
idLexer::SetPunctuations( NULL );
|
|
idLexer::allocated = false;
|
|
idLexer::token = "";
|
|
idLexer::next = NULL;
|
|
idLexer::hadError = false;
|
|
idLexer::LoadMemory( ptr, length, name );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::~idLexer
|
|
================
|
|
*/
|
|
idLexer::~idLexer()
|
|
{
|
|
idLexer::FreeSource();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::SetBaseFolder
|
|
================
|
|
*/
|
|
void idLexer::SetBaseFolder( const char* path )
|
|
{
|
|
idStr::Copynz( baseFolder, path, sizeof( baseFolder ) );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idLexer::HadError
|
|
================
|
|
*/
|
|
bool idLexer::HadError() const
|
|
{
|
|
return hadError;
|
|
}
|
|
|