This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/launch/parselib.c

1469 lines
31 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// parselib.c - script files parser
//=======================================================================
#include "launch.h"
#define MAX_TOKEN_LENGTH MAX_SYSPATH
// number sub-types
enum
{
NT_BINARY = BIT(0),
NT_OCTAL = BIT(1),
NT_DECIMAL = BIT(2),
NT_HEXADECIMAL = BIT(3),
NT_INTEGER = BIT(4),
NT_FLOAT = BIT(5),
NT_UNSIGNED = BIT(6),
NT_LONG = BIT(7),
NT_SINGLE = BIT(8),
NT_DOUBLE = BIT(9),
NT_EXTENDED = BIT(10),
};
// punctuation sub-types
enum
{
PT_RSHIFT_ASSIGN = 1,
PT_LSHIFT_ASSIGN,
PT_PARAMETERS,
PT_PRECOMPILER_MERGE,
PT_LOGIC_AND,
PT_LOGIC_OR,
PT_LOGIC_GEQUAL,
PT_LOGIC_LEQUAL,
PT_LOGIC_EQUAL,
PT_LOGIC_NOTEQUAL,
PT_MUL_ASSIGN,
PT_DIV_ASSIGN,
PT_MOD_ASSIGN,
PT_ADD_ASSIGN,
PT_SUB_ASSIGN,
PT_INCREMENT,
PT_DECREMENT,
PT_BINARY_AND_ASSIGN,
PT_BINARY_OR_ASSIGN,
PT_BINARY_XOR_ASSIGN,
PT_RSHIFT,
PT_LSHIFT,
PT_POINTER_REFERENCE,
PT_CPP_1,
PT_CPP_2,
PT_MUL,
PT_DIV,
PT_MOD,
PT_ADD,
PT_SUB,
PT_ASSIGN,
PT_BINARY_AND,
PT_BINARY_OR,
PT_BINARY_XOR,
PT_BINARY_NOT,
PT_LOGIC_NOT,
PT_LOGIC_GREATER,
PT_LOGIC_LESS,
PT_REFERENCE,
PT_COLON,
PT_COMMA,
PT_SEMICOLON,
PT_QUESTION_MARK,
PT_BRACE_OPEN,
PT_BRACE_CLOSE,
PT_BRACKET_OPEN,
PT_BRACKET_CLOSE,
PT_PARENTHESIS_OPEN,
PT_PARENTHESIS_CLOSE,
PT_PRECOMPILER,
PT_DOLLAR,
PT_BACKSLASH,
PT_MAXCOUNT,
};
// default punctuation table
static punctuation_t ps_punctuationsTable[] =
{
{">>=", PT_RSHIFT_ASSIGN },
{"<<=", PT_LSHIFT_ASSIGN },
{"...", PT_PARAMETERS },
{"##", PT_PRECOMPILER_MERGE},
{"&&", PT_LOGIC_AND },
{"||", PT_LOGIC_OR },
{">=", PT_LOGIC_GEQUAL },
{"<=", PT_LOGIC_LEQUAL },
{"==", PT_LOGIC_EQUAL },
{"!=", PT_LOGIC_NOTEQUAL },
{"*=", PT_MUL_ASSIGN },
{"/=", PT_DIV_ASSIGN },
{"%=", PT_MOD_ASSIGN },
{"+=", PT_ADD_ASSIGN },
{"-=", PT_SUB_ASSIGN },
{"++", PT_INCREMENT },
{"--", PT_DECREMENT },
{"&=", PT_BINARY_AND_ASSIGN},
{"|=", PT_BINARY_OR_ASSIGN },
{"^=", PT_BINARY_XOR_ASSIGN},
{">>", PT_RSHIFT },
{"<<", PT_LSHIFT },
{"->", PT_POINTER_REFERENCE},
{"::", PT_CPP_1 },
{".*", PT_CPP_2 },
{"*", PT_MUL },
{"/", PT_DIV },
{"%", PT_MOD },
{"+", PT_ADD },
{"-", PT_SUB },
{"=", PT_ASSIGN },
{"&", PT_BINARY_AND },
{"|", PT_BINARY_OR },
{"^", PT_BINARY_XOR },
{"~", PT_BINARY_NOT },
{"!", PT_LOGIC_NOT },
{">", PT_LOGIC_GREATER },
{"<", PT_LOGIC_LESS },
{".", PT_REFERENCE },
{":", PT_COLON },
{",", PT_COMMA },
{";", PT_SEMICOLON },
{"?", PT_QUESTION_MARK },
{"{", PT_BRACE_OPEN },
{"}", PT_BRACE_CLOSE },
{"[", PT_BRACKET_OPEN },
{"]", PT_BRACKET_CLOSE },
{"(", PT_PARENTHESIS_OPEN },
{")", PT_PARENTHESIS_CLOSE},
{"#", PT_PRECOMPILER },
{"$", PT_DOLLAR },
{"\\", PT_BACKSLASH },
{NULL, 0}
};
/*
=================
PS_NumberValue
=================
*/
static void PS_NumberValue( token_t *token )
{
char *string = token->string;
double fraction = 0.1, power = 1.0;
bool negative = false;
int i, exponent = 0;
token->floatValue = 0.0;
token->integerValue = 0;
if( token->type != TT_NUMBER )
return;
// if a decimal number
if( token->subType & NT_DECIMAL )
{
// if a floating point number
if( token->subType & NT_FLOAT )
{
while( *string && *string != '.' && *string != 'e' && *string != 'E' )
token->floatValue = token->floatValue * 10.0 + (double)(*string++ - '0');
if( *string == '.' )
{
string++;
while( *string && *string != 'e' && *string != 'E' )
{
token->floatValue = token->floatValue + (double)(*string++ - '0') * fraction;
fraction *= 0.1;
}
}
if( *string == 'e' || *string == 'E' )
{
string++;
if( *string == '+' )
{
string++;
negative = false;
}
else if( *string == '-' )
{
string++;
negative = true;
}
while( *string ) exponent = exponent * 10 + (*string++ - '0');
for( i = 0; i < exponent; i++ ) power *= 10.0;
if( negative ) token->floatValue /= power;
else token->floatValue *= power;
}
token->integerValue = (uint)token->floatValue;
return;
}
// if an integer number
if( token->subType & NT_INTEGER )
{
while( *string ) token->integerValue = token->integerValue * 10 + (*string++ - '0');
token->floatValue = (double)token->integerValue;
return;
}
return;
}
// if a binary number
if( token->subType & NT_BINARY )
{
string += 2;
while( *string ) token->integerValue = (token->integerValue<<1) + (*string++ - '0');
token->floatValue = (double)token->integerValue;
return;
}
// if an octal number
if( token->subType & NT_OCTAL )
{
string += 1;
while( *string ) token->integerValue = (token->integerValue<<3) + (*string++ - '0');
token->floatValue = (double)token->integerValue;
return;
}
// if a hexadecimal number
if( token->subType & NT_HEXADECIMAL )
{
string += 2;
while( *string )
{
if( *string >= 'a' && *string <= 'f' )
token->integerValue = (token->integerValue<<4) + (*string++ - 'a' + 10);
else if( *string >= 'A' && *string <= 'F' )
token->integerValue = (token->integerValue<<4) + (*string++ - 'A' + 10);
else token->integerValue = (token->integerValue<<4) + (*string++ - '0');
}
token->floatValue = (double)token->integerValue;
return;
}
}
/*
=================
PS_ReadWhiteSpace
=================
*/
static bool PS_ReadWhiteSpace( script_t *script, scFlags_t flags )
{
char *text;
int line;
bool hasNewLines = false;
// backup text and line
text = script->text;
line = script->line;
while( 1 )
{
// skip whitespace
while( *script->text <= ' ' )
{
if( !*script->text )
{
script->text = NULL;
return false;
}
if( *script->text == '\n' )
{
script->line++;
hasNewLines = true;
}
script->text++;
}
// if newlines are not allowed, restore text and line
if( hasNewLines && !(flags & SC_ALLOW_NEWLINES))
{
script->text = text;
script->line = line;
return false;
}
// QuArk comments: ;TX1, //TX1, #TX1
if(( flags & SC_COMMENT_SEMICOLON ) && (*script->text == ';' || *script->text == '#' ))
{
if( script->text[1] == 'T' && script->text[2] == 'X' )
script->TXcommand = script->text[3]; // QuArK TX#"-style comment
while( *script->text && *script->text != '\n' )
script->text++;
continue;
}
// skip // comments
if( *script->text == '/' && script->text[1] == '/' )
{
if( *script->text == '/' ) *script->text++;
if( script->text[1] == 'T' && script->text[2] == 'X' )
script->TXcommand = script->text[3]; // QuArK TX#"-style comment
while( *script->text && *script->text != '\n' )
script->text++;
continue;
}
// skip /* */ comments
if( *script->text == '/' && script->text[1] == '*' )
{
script->text += 2;
while( *script->text && (*script->text != '*' || script->text[1] != '/' ))
{
if( *script->text == '\n' )
script->line++;
script->text++;
}
if( *script->text ) script->text += 2;
continue;
}
// an actual token
break;
}
return true;
}
/*
=================
PS_ReadEscapeChar
=================
*/
static bool PS_ReadEscapeChar( script_t *script, scFlags_t flags, char *ch )
{
int value;
script->text++;
switch( *script->text )
{
case 'a':
*ch = '\a';
break;
case 'b':
*ch = '\b';
break;
case 'f':
*ch = '\f';
break;
case 'n':
*ch = '\n';
break;
case 'r':
*ch = '\r';
break;
case 't':
*ch = '\t';
break;
case 'v':
*ch = '\v';
break;
case '\"':
*ch = '\"';
break;
case '\'':
*ch = '\'';
break;
case '\\':
*ch = '\\';
break;
case '\?':
*ch = '\?';
break;
case 'x':
script->text++;
for( value = 0; ; script->text++ )
{
if( *script->text >= 'a' && *script->text <= 'f' )
value = (value<<4) + (*script->text - 'a' + 10);
else if( *script->text >= 'A' && *script->text <= 'F' )
value = (value<<4) + (*script->text - 'A' + 10 );
else if( *script->text >= '0' && *script->text <= '9' )
value = (value<<4) + (*script->text - '0' );
else break;
}
script->text--;
if( value > 0xFF )
{
PS_ScriptError( script, flags, "too large value in escape character" );
return false;
}
*ch = value;
break;
default:
if( *script->text < '0' || *script->text > '9' )
{
PS_ScriptError( script, flags, "unknown escape character" );
return false;
}
for( value = 0; ; script->text++ )
{
if( *script->text >= '0' && *script->text <= '9' )
value = value * 10 + (*script->text - '0');
else break;
}
script->text--;
if( value > 0xFF )
{
PS_ScriptError( script, flags, "too large value in escape character" );
return false;
}
*ch = value;
break;
}
script->text++;
return true;
}
/*
=================
PS_ReadGeneric
=================
*/
static bool PS_ReadGeneric( script_t *script, scFlags_t flags, token_t *token )
{
token->type = TT_GENERIC;
token->subType = 0;
token->line = script->line;
while( 1 )
{
if( *script->text <= ' ' ) break;
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "string longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
token->string[token->length] = 0;
PS_NumberValue( token );
return true;
}
/*
=================
PS_ReadString
=================
*/
static bool PS_ReadString( script_t *script, scFlags_t flags, token_t *token )
{
char *text;
int line;
token->type = TT_STRING;
token->subType = 0;
token->line = script->line;
script->text++;
while( 1 )
{
if( !*script->text )
{
PS_ScriptError( script, flags, "missing trailing quote" );
return false;
}
if( *script->text == '\n' )
{
PS_ScriptError( script, flags, "newline inside string" );
return false;
}
if( *script->text == '\"' )
{
script->text++;
if( !(flags & SC_ALLOW_STRINGCONCAT))
break;
text = script->text;
line = script->line;
if( PS_ReadWhiteSpace( script, flags ))
{
if( *script->text == '\"' )
{
script->text++;
continue;
}
}
script->text = text;
script->line = line;
break;
}
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "string longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
if(( flags & SC_ALLOW_ESCAPECHARS) && *script->text == '\\' )
{
if( !PS_ReadEscapeChar( script, flags, &token->string[token->length] ))
return false;
token->length++;
continue;
}
token->string[token->length++] = *script->text++;
}
token->string[token->length] = 0;
PS_NumberValue( token );
return true;
}
/*
=================
PS_ReadLine
=================
*/
static bool PS_ReadLine( script_t *script, scFlags_t flags, token_t *token )
{
token->type = TT_STRING;
token->subType = 0;
token->line = script->line;
while( 1 )
{
if( !*script->text )
{
PS_ScriptError( script, flags, "missing trailing quote" );
return false;
}
if( *script->text == '\n' )
{
script->line++;
break; // end of line
}
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "string longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
token->string[token->length] = 0;
token->line = script->line;
return true;
}
/*
=================
PS_ReadLiteral
=================
*/
static bool PS_ReadLiteral( script_t *script, scFlags_t flags, token_t *token )
{
char *text;
int line;
token->type = TT_LITERAL;
token->line = script->line;
token->subType = 0;
script->text++;
while( 1 )
{
if( !*script->text )
{
PS_ScriptError( script, flags, "missing trailing quote" );
return false;
}
if( *script->text == '\n' )
{
PS_ScriptError( script, flags, "newline inside literal" );
return false;
}
if( *script->text == '\'' )
{
script->text++;
if(!(flags & SC_ALLOW_STRINGCONCAT))
break;
text = script->text;
line = script->line;
if( PS_ReadWhiteSpace( script, flags ))
{
if( *script->text == '\'' )
{
script->text++;
continue;
}
}
script->text = text;
script->line = line;
break;
}
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "literal longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
if((flags & SC_ALLOW_ESCAPECHARS) && *script->text == '\\' )
{
if( !PS_ReadEscapeChar( script, flags, &token->string[token->length] ))
return false;
token->length++;
continue;
}
token->string[token->length++] = *script->text++;
}
token->string[token->length] = 0;
PS_NumberValue( token );
return true;
}
/*
=================
PS_ReadNumber
=================
*/
static bool PS_ReadNumber( script_t *script, scFlags_t flags, token_t *token )
{
bool hasDot = false;
int c;
token->type = TT_NUMBER;
token->subType = 0;
token->line = script->line;
if( *script->text == '0' && script->text[1] != '.' )
{
if( script->text[1] == 'b' || script->text[1] == 'B' )
{
token->string[token->length++] = *script->text++;
token->string[token->length++] = *script->text++;
while( 1 )
{
c = *script->text;
if( c < '0' || c > '1' )
break;
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "binary number longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
token->subType |= (NT_BINARY|NT_INTEGER);
}
else if( script->text[1] == 'x' || script->text[1] == 'X' )
{
token->string[token->length++] = *script->text++;
token->string[token->length++] = *script->text++;
while( 1 )
{
c = *script->text;
if((c < 'a' || c > 'f') && (c < 'A' || c > 'F') && (c < '0' || c > '9'))
break;
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "hexadecimal number longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
token->subType |= (NT_HEXADECIMAL|NT_INTEGER);
}
else
{
token->string[token->length++] = *script->text++;
while( 1 )
{
c = *script->text;
if( c < '0' || c > '7' )
break;
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "octal number longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
token->subType |= (NT_OCTAL|NT_INTEGER);
}
token->string[token->length] = 0;
PS_NumberValue( token );
return true;
}
while( 1 )
{
c = *script->text;
if( c == '.' )
{
if( hasDot ) break;
hasDot = true;
}
else if( c < '0' || c > '9' )
break;
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "number longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
if( hasDot || (*script->text == 'e' || *script->text == 'E'))
{
token->subType |= (NT_DECIMAL|NT_FLOAT);
if( *script->text == 'e' || *script->text == 'E' )
{
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "number longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
if( *script->text == '+' || *script->text == '-' )
{
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "number longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
while( 1 )
{
c = *script->text;
if( c < '0' || c > '9' ) break;
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "number longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
}
if( *script->text == 'f' || *script->text == 'F' )
{
script->text++;
token->subType |= NT_SINGLE;
}
else if( *script->text == 'l' || *script->text == 'L' )
{
script->text++;
token->subType |= NT_EXTENDED;
}
else token->subType |= NT_DOUBLE;
}
else
{
token->subType |= (NT_DECIMAL|NT_INTEGER);
if( *script->text == 'u' || *script->text == 'U' )
{
script->text++;
token->subType |= NT_UNSIGNED;
if( *script->text == 'l' || *script->text == 'L' )
{
script->text++;
token->subType |= NT_LONG;
}
}
else if( *script->text == 'l' || *script->text == 'L' )
{
script->text++;
token->subType |= NT_LONG;
if( *script->text == 'u' || *script->text == 'U' )
{
script->text++;
token->subType |= NT_UNSIGNED;
}
}
}
token->string[token->length] = 0;
PS_NumberValue( token );
return true;
}
/*
=================
PS_ReadName
=================
*/
static bool PS_ReadName( script_t *script, scFlags_t flags, token_t *token )
{
int c;
token->type = TT_NAME;
token->subType = 0;
token->line = script->line;
while( 1 )
{
c = *script->text;
if( flags & SC_ALLOW_PATHNAMES )
{
if((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '_'
&& c != '/' && c != '\\' && c != ':' && c != '.' && c != '+' && c != '-')
break;
}
else if( flags & SC_ALLOW_PATHNAMES2 )
{
if((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')
&& c != '_' && c != '/' && c != '\\' && c != ':' && c != '.' && c != '+' && c != '@'
&& c != '-' && c != '{' && c != '!' && c != '$' && c != '&' && c != '~' )
break;
}
else
{
if((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '_')
break;
}
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "name longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = *script->text++;
}
token->string[token->length] = 0;
PS_NumberValue( token );
return true;
}
/*
=================
PS_ReadPunctuation
=================
*/
static bool PS_ReadPunctuation( script_t *script, scFlags_t flags, token_t *token )
{
punctuation_t *punctuation;
int i, len;
for( i = 0; script->punctuations[i].name; i++ )
{
punctuation = &script->punctuations[i];
for( len = 0; punctuation->name[len] && script->text[len]; len++ )
{
if( punctuation->name[len] != script->text[len] )
break;
}
if( !punctuation->name[len] )
{
script->text += len;
token->type = TT_PUNCTUATION;
token->subType = punctuation->type;
token->line = script->line;
for( i = 0; i < len; i++ )
{
if( token->length == MAX_TOKEN_LENGTH - 1 )
{
PS_ScriptError( script, flags, "punctuation longer than %i symbols", MAX_TOKEN_LENGTH );
return false;
}
token->string[token->length++] = punctuation->name[i];
}
token->string[token->length] = 0;
PS_NumberValue( token );
return true;
}
}
return false;
}
/*
=================
PS_ReadToken
Reads a token from the script
=================
*/
bool PS_ReadToken( script_t *script, scFlags_t flags, token_t *token )
{
token_t dummy;
// parselib requries token always
if( !token ) token = &dummy;
// if there is a token available (from PS_FreeToken)
if( script->tokenAvailable )
{
script->tokenAvailable = false;
Mem_Copy( token, &script->token, sizeof( token_t ));
return true;
}
// clear token
token->type = TT_EMPTY;
token->subType = 0;
token->line = 1;
token->string[0] = 0;
token->length = 0;
token->floatValue = 0.0;
token->integerValue = 0;
// make sure incoming data is valid
if( !script->text ) return false;
// skip whitespace and comments
if( !PS_ReadWhiteSpace( script, flags ))
return false;
// if we just want to parse a generic string separated by spaces
if( flags & SC_PARSE_GENERIC )
{
// if it is a string
if( flags & SC_PARSE_LINE )
{
if( PS_ReadLine( script, flags, token ))
return true;
}
else if( *script->text == '\"' )
{
if( PS_ReadString( script, flags, token ))
return true;
}
// if it is a literal
else if( *script->text == '\'' )
{
if( PS_ReadLiteral( script, flags, token ))
return true;
}
// check for a generic string
else if( PS_ReadGeneric( script, flags, token ))
return true;
}
// If it is a string
else if( *script->text == '\"' )
{
if( PS_ReadString( script, flags, token ))
return true;
}
// if it is a literal
else if( *script->text == '\'' )
{
if( PS_ReadLiteral( script, flags, token ))
return true;
}
// old pathnames must be grab before numbers because they contains '+', '-' and other unexpected symbols
else if((flags & SC_ALLOW_PATHNAMES2) && (*script->text == '/' || *script->text == '\\' || *script->text == ':' || *script->text == '.'
|| *script->text == '$' || *script->text == '-' || *script->text == '+' || *script->text == '!' || *script->text == '{' )) // damn prefix!
{
if( PS_ReadName( script, flags, token ))
return true;
}
// if it is a number
else if((*script->text >= '0' && *script->text <= '9') || (*script->text == '.' && (script->text[1] >= '0' && script->text[1] <= '9')))
{
if( PS_ReadNumber( script, flags, token ))
return true;
}
// if it is a name
else if((*script->text >= 'a' && *script->text <= 'z') || (*script->text >= 'A' && *script->text <= 'Z') || *script->text == '_')
{
if( PS_ReadName( script, flags, token ))
return true;
}
// check for a path name if needed
else if((flags & SC_ALLOW_PATHNAMES) && (*script->text == '/' || *script->text == '\\' || *script->text == ':' || *script->text == '.'))
{
if( PS_ReadName( script, flags, token ))
return true;
}
// check for a punctuation
else if( PS_ReadPunctuation( script, flags, token ))
return true;
// couldn't parse a token
token->type = TT_EMPTY;
token->subType = 0;
token->line = 1;
token->string[0] = 0;
token->length = 0;
token->floatValue = 0.0;
token->integerValue = 0;
PS_ScriptError( script, flags, "couldn't read token" );
return false;
}
/*
=================
PS_SaveToken
=================
*/
void PS_SaveToken( script_t *script, token_t *token )
{
script->tokenAvailable = true;
Mem_Copy( &script->token, token, sizeof( token_t ));
}
/*
=============================================================================
PARSER SIMPLY USER INTERFACE
=============================================================================
*/
/*
=================
PS_GetString
=================
*/
bool PS_GetString( script_t *script, int flags, char *value, size_t size )
{
token_t token;
if( !PS_ReadToken( script, flags|SC_ALLOW_PATHNAMES, &token ))
return false;
if( value && size ) com.strncpy( value, token.string, size );
return true;
}
/*
=================
PS_GetDouble
=================
*/
bool PS_GetDouble( script_t *script, int flags, double *value )
{
token_t token;
if( !PS_ReadToken( script, flags, &token ))
return false;
if( token.type == TT_PUNCTUATION && !com.stricmp( token.string, "-" ))
{
if( !PS_ReadToken( script, flags, &token ))
return false;
if( token.type != TT_NUMBER )
{
PS_ScriptError( script, flags, "expected float value, found '%s'", token.string );
return false;
}
if( value ) *value = -token.floatValue;
return true;
}
if( token.type != TT_NUMBER )
{
PS_ScriptError( script, flags, "expected float value, found '%s'", token.string );
return false;
}
if( value ) *value = token.floatValue;
return true;
}
/*
=================
PS_GetFloat
=================
*/
bool PS_GetFloat( script_t *script, int flags, float *value )
{
token_t token;
if( !PS_ReadToken( script, flags, &token ))
return false;
if( token.type == TT_PUNCTUATION && !com.stricmp( token.string, "-" ))
{
if( !PS_ReadToken( script, flags, &token ))
return false;
if( token.type != TT_NUMBER )
{
PS_ScriptError( script, flags, "expected float value, found '%s'", token.string );
return false;
}
if( value ) *value = -((float)token.floatValue);
return true;
}
if( token.type != TT_NUMBER )
{
PS_ScriptError( script, flags, "expected float value, found '%s'", token.string );
return false;
}
if( value ) *value = (float)token.floatValue;
return true;
}
/*
=================
PS_GetUnsigned
=================
*/
bool PS_GetUnsigned( script_t *script, int flags, uint *value )
{
token_t token;
if( !PS_ReadToken( script, flags, &token ))
return false;
if( token.type != TT_NUMBER || !(token.subType & NT_INTEGER ))
{
PS_ScriptError( script, flags, "expected integer value, found '%s'", token.string );
return false;
}
if( value ) *value = token.integerValue;
return true;
}
/*
=================
PS_GetInteger
=================
*/
bool PS_GetInteger( script_t *script, int flags, int *value )
{
token_t token;
if( !PS_ReadToken( script, flags, &token ))
return false;
if( token.type == TT_PUNCTUATION && !com.stricmp( token.string, "-" ))
{
if( !PS_ReadToken( script, flags, &token ))
return false;
if( token.type != TT_NUMBER || !(token.subType & NT_INTEGER ))
{
PS_ScriptError( script, flags, "expected integer value, found '%s'", token.string );
return false;
}
if( value ) *value = -((int)token.integerValue);
return true;
}
if( token.type != TT_NUMBER || !(token.subType & NT_INTEGER))
{
PS_ScriptError( script, flags, "expected integer value, found '%s'", token.string );
return false;
}
if( value ) *value = (int)token.integerValue;
return true;
}
/*
=================
PS_SkipWhiteSpace
Skips until a printable character is found
=================
*/
void PS_SkipWhiteSpace( script_t *script )
{
// make sure incoming data is valid
if( !script->text ) return;
while( 1 )
{
// skip whitespace
while( *script->text <= ' ' )
{
if( !*script->text )
{
script->text = NULL;
return;
}
if( *script->text == '\n' )
script->line++;
script->text++;
}
// skip // comments
if( *script->text == '/' && script->text[1] == '/' )
{
while( *script->text && *script->text != '\n' )
script->text++;
continue;
}
// skip /* */ comments
if( *script->text == '/' && script->text[1] == '*' )
{
script->text += 2;
while( *script->text && (*script->text != '*' || script->text[1] != '/' ))
{
if( *script->text == '\n' )
script->line++;
script->text++;
}
if( *script->text ) script->text += 2;
continue;
}
// an actual token
break;
}
}
/*
=================
PS_SkipRestOfLine
Skips until a new line is found
=================
*/
void PS_SkipRestOfLine( script_t *script )
{
token_t token;
while( 1 )
{
if( !PS_ReadToken( script, 0, &token ))
break;
}
}
/*
=================
PS_SkipBracedSection
Skips until a matching close brace is found.
Internal brace depths are properly skipped.
=================
*/
void PS_SkipBracedSection( script_t *script, int depth )
{
token_t token;
do {
if( !PS_ReadToken( script, SC_ALLOW_NEWLINES, &token ))
break;
// HACKHACK: allow pathnames that can contain '{' symbol
// and invoke wrong count of braced sections :(
if( !com.stricmp( "map", token.string ))
if( !PS_ReadToken( script, SC_ALLOW_PATHNAMES2, &token ))
break;
if( token.type == TT_PUNCTUATION )
{
if( !com.stricmp( token.string, "{" ))
depth++;
else if( !com.stricmp( token.string, "}" ))
depth--;
}
} while( depth );
}
/*
=================
PS_ScriptError
=================
*/
void PS_ScriptError( script_t *script, scFlags_t flags, const char *fmt, ... )
{
string errorstring;
va_list argPtr;
if(!(flags & SC_PRINT_ERRORS)) return;
va_start( argPtr, fmt );
com_vsnprintf( errorstring, MAX_STRING, fmt, argPtr );
va_end( argPtr );
MsgDev( D_ERROR, "source '%s', line %i: %s\n", script->name, script->line, errorstring );
}
/*
=================
PS_ScriptWarning
=================
*/
void PS_ScriptWarning( script_t *script, scFlags_t flags, const char *fmt, ... )
{
string warnstring;
va_list argPtr;
if(!(flags & SC_PRINT_WARNINGS)) return;
va_start( argPtr, fmt );
com.vsnprintf( warnstring, MAX_STRING, fmt, argPtr );
va_end( argPtr );
MsgDev( D_WARN, "source '%s', line %i: %s\n", script->name, script->line, warnstring );
}
/*
=================
PS_SetPunctuationsTable
=================
*/
void PS_SetPunctuationsTable( script_t *script, punctuation_t *punctuationsTable )
{
if( punctuationsTable )
script->punctuations = punctuationsTable;
else script->punctuations = ps_punctuationsTable;
}
/*
=================
PS_ResetScript
=================
*/
void PS_ResetScript( script_t *script )
{
script->text = script->buffer;
script->line = 1;
script->tokenAvailable = false;
script->token.type = TT_EMPTY;
script->token.subType = 0;
script->token.line = 1;
script->token.string[0] = 0;
script->token.length = 0;
script->token.floatValue = 0.0;
script->token.integerValue = 0;
}
/*
=================
PS_EndOfScript
=================
*/
bool PS_EndOfScript( script_t *script )
{
if( !script || !script->text )
return true;
return false;
}
static script_t *PS_NewScript( const char *filename, const char *buffer, size_t size )
{
script_t *script;
if( !buffer || size <= 0 ) return NULL;
// allocate the script_t
script = Mem_Alloc( Sys.scriptpool, sizeof( script_t ));
com.strncpy( script->name, filename, sizeof( script->name ));
script->buffer = Mem_Alloc( Sys.scriptpool, size + 1 ); // escape char
Mem_Copy( script->buffer, buffer, size );
script->text = script->buffer;
script->allocated = true;
script->size = size;
script->line = 1;
script->punctuations = ps_punctuationsTable;
script->tokenAvailable = false;
script->token.type = TT_EMPTY;
script->token.subType = 0;
script->token.line = 1;
script->token.string[0] = 0;
script->token.length = 0;
script->token.floatValue = 0.0;
script->token.integerValue = 0;
return script;
}
/*
=================
PS_LoadScriptFile
=================
*/
script_t *PS_LoadScript( const char *filename, const char *buf, size_t size )
{
script_t *script;
if( !buf || size <= 0 )
{
// trying to get script from disk
buf = FS_LoadFile( filename, &size );
script = PS_NewScript( filename, buf, size );
if( buf ) Mem_Free((char *)buf );
}
else script = PS_NewScript( filename, buf, size ); // from memory
return script;
}
/*
=================
PS_FreeScript
=================
*/
void PS_FreeScript( script_t *script )
{
if( !script )
{
MsgDev( D_WARN, "PS_FreeScript: trying to free NULL script\n" );
return;
}
if( script->allocated )
Mem_Free( script->buffer );
Mem_Free( script ); // himself
}