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