|
|
//===== Copyright <EFBFBD> 1996-2006, Valve Corporation, All rights reserved. ======// |
|
|
// |
|
|
// Purpose: ExprSimplifier builds a binary tree from an infix expression (in the |
|
|
// form of a character array). Evaluates C style infix parenthetic logical |
|
|
// expressions. Supports !, ||, &&, (). Symbols are resolved via callback. |
|
|
// Syntax is $<name>. $0 evaluates to false. $<number> evaluates to true. |
|
|
// e.g: ( $1 || ( $FOO || $WHATEVER ) && !$BAR ) |
|
|
//===========================================================================// |
|
|
|
|
|
#include <ctype.h> |
|
|
#include <vstdlib/ikeyvaluessystem.h> |
|
|
#include "tier1/exprevaluator.h" |
|
|
#include "tier1/convar.h" |
|
|
#include "tier1/fmtstr.h" |
|
|
#include "tier0/dbg.h" |
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Default conditional symbol handler callback. Symbols are the form $<name>. |
|
|
// Return true or false for the value of the symbol. |
|
|
//----------------------------------------------------------------------------- |
|
|
bool DefaultConditionalSymbolProc( const char *pKey ) |
|
|
{ |
|
|
if ( pKey[0] == '$' ) |
|
|
{ |
|
|
pKey++; |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "WIN32" ) ) |
|
|
{ |
|
|
return IsPC(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "WINDOWS" ) ) |
|
|
{ |
|
|
return IsPlatformWindowsPC(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "X360" ) ) |
|
|
{ |
|
|
return IsX360(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "PS3" ) ) |
|
|
{ |
|
|
return IsPS3(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "OSX" ) ) |
|
|
{ |
|
|
return IsPlatformOSX(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "LINUX" ) ) |
|
|
{ |
|
|
return IsPlatformLinux(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "POSIX" ) ) |
|
|
{ |
|
|
return IsPlatformPosix(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "GAMECONSOLE" ) ) |
|
|
{ |
|
|
return IsGameConsole(); |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "DEMO" ) ) |
|
|
{ |
|
|
#if defined( _DEMO ) |
|
|
return true; |
|
|
#else |
|
|
return false; |
|
|
#endif |
|
|
} |
|
|
|
|
|
if ( !V_stricmp( pKey, "LOWVIOLENCE" ) ) |
|
|
{ |
|
|
#if defined( _LOWVIOLENCE ) |
|
|
return true; |
|
|
#endif |
|
|
// If it is not a LOWVIOLENCE binary build, then fall through |
|
|
// and check if there was a run-time symbol installed for it |
|
|
} |
|
|
|
|
|
// don't know it at compile time, so fall through to installed symbol values |
|
|
return KeyValuesSystem()->GetKeyValuesExpressionSymbol( pKey ); |
|
|
} |
|
|
|
|
|
void DefaultConditionalErrorProc( const char *pReason ) |
|
|
{ |
|
|
Warning( "Conditional Error: %s\n", pReason ); |
|
|
} |
|
|
|
|
|
CExpressionEvaluator::CExpressionEvaluator() |
|
|
{ |
|
|
m_ExprTree = NULL; |
|
|
} |
|
|
|
|
|
CExpressionEvaluator::~CExpressionEvaluator() |
|
|
{ |
|
|
FreeTree( m_ExprTree ); |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Sets mCurToken to the next token in the input string. Skips all whitespace. |
|
|
//----------------------------------------------------------------------------- |
|
|
char CExpressionEvaluator::GetNextToken( void ) |
|
|
{ |
|
|
// while whitespace, Increment CurrentPosition |
|
|
while ( m_pExpression[m_CurPosition] == ' ' ) |
|
|
++m_CurPosition; |
|
|
|
|
|
// CurrentToken = Expression[CurrentPosition] |
|
|
m_CurToken = m_pExpression[m_CurPosition++]; |
|
|
|
|
|
return m_CurToken; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Utility funcs |
|
|
//----------------------------------------------------------------------------- |
|
|
void CExpressionEvaluator::FreeNode( ExprNode *pNode ) |
|
|
{ |
|
|
delete pNode; |
|
|
} |
|
|
|
|
|
ExprNode *CExpressionEvaluator::AllocateNode( void ) |
|
|
{ |
|
|
return new ExprNode; |
|
|
} |
|
|
|
|
|
void CExpressionEvaluator::FreeTree( ExprTree& node ) |
|
|
{ |
|
|
if ( !node ) |
|
|
return; |
|
|
|
|
|
FreeTree( node->left ); |
|
|
FreeTree( node->right ); |
|
|
FreeNode( node ); |
|
|
node = 0; |
|
|
} |
|
|
|
|
|
bool CExpressionEvaluator::IsConditional( bool &bConditional, const char token ) |
|
|
{ |
|
|
char nextchar = ' '; |
|
|
if ( token == OR_OP || token == AND_OP ) |
|
|
{ |
|
|
// expect || or && |
|
|
nextchar = m_pExpression[m_CurPosition++]; |
|
|
if ( (token & nextchar) == token ) |
|
|
{ |
|
|
bConditional = true; |
|
|
} |
|
|
else if ( m_pSyntaxErrorProc ) |
|
|
{ |
|
|
m_pSyntaxErrorProc( CFmtStr( "Bad expression operator: '%c%c', expected C style operator", token, nextchar ) ); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
bConditional = false; |
|
|
} |
|
|
|
|
|
// valid |
|
|
return true; |
|
|
} |
|
|
|
|
|
bool CExpressionEvaluator::IsNotOp( const char token ) |
|
|
{ |
|
|
if ( token == NOT_OP ) |
|
|
return true; |
|
|
else |
|
|
return false; |
|
|
} |
|
|
|
|
|
bool CExpressionEvaluator::IsIdentifierOrConstant( const char token ) |
|
|
{ |
|
|
bool success = false; |
|
|
if ( token == '$' ) |
|
|
{ |
|
|
// store the entire identifier |
|
|
int i = 0; |
|
|
m_Identifier[i++] = token; |
|
|
while( (isalnum( m_pExpression[m_CurPosition] ) || m_pExpression[m_CurPosition] == '_') && i < MAX_IDENTIFIER_LEN ) |
|
|
{ |
|
|
m_Identifier[i] = m_pExpression[m_CurPosition]; |
|
|
++m_CurPosition; |
|
|
++i; |
|
|
} |
|
|
|
|
|
if ( i < MAX_IDENTIFIER_LEN - 1 ) |
|
|
{ |
|
|
m_Identifier[i] = '\0'; |
|
|
success = true; |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
if ( isdigit( token ) ) |
|
|
{ |
|
|
int i = 0; |
|
|
m_Identifier[i++] = token; |
|
|
while( isdigit( m_pExpression[m_CurPosition] ) && ( i < MAX_IDENTIFIER_LEN ) ) |
|
|
{ |
|
|
m_Identifier[i] = m_pExpression[m_CurPosition]; |
|
|
++m_CurPosition; |
|
|
++i; |
|
|
} |
|
|
if ( i < MAX_IDENTIFIER_LEN - 1 ) |
|
|
{ |
|
|
m_Identifier[i] = '\0'; |
|
|
success = true; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return success; |
|
|
} |
|
|
|
|
|
bool CExpressionEvaluator::MakeExprNode( ExprTree &tree, char token, Kind kind, ExprTree left, ExprTree right ) |
|
|
{ |
|
|
tree = AllocateNode(); |
|
|
tree->left = left; |
|
|
tree->right = right; |
|
|
tree->kind = kind; |
|
|
|
|
|
switch ( kind ) |
|
|
{ |
|
|
case CONDITIONAL: |
|
|
tree->data.cond = token; |
|
|
break; |
|
|
|
|
|
case LITERAL: |
|
|
if ( isdigit( m_Identifier[0] ) ) |
|
|
{ |
|
|
tree->data.value = ( atoi( m_Identifier ) != 0 ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
tree->data.value = m_pGetSymbolProc( m_Identifier ); |
|
|
} |
|
|
break; |
|
|
|
|
|
case NOT: |
|
|
break; |
|
|
|
|
|
default: |
|
|
if ( m_pSyntaxErrorProc ) |
|
|
{ |
|
|
Assert( 0 ); |
|
|
m_pSyntaxErrorProc( CFmtStr( "Logic Error in CExpressionEvaluator" ) ); |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Makes a factor :: { <expression> } | <identifier>. |
|
|
//----------------------------------------------------------------------------- |
|
|
bool CExpressionEvaluator::MakeFactor( ExprTree &tree ) |
|
|
{ |
|
|
if ( m_CurToken == '(' ) |
|
|
{ |
|
|
// Get the next token |
|
|
GetNextToken(); |
|
|
|
|
|
// Make an expression, setting Tree to point to it |
|
|
if ( !MakeExpression( tree ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
} |
|
|
else if ( IsIdentifierOrConstant( m_CurToken ) ) |
|
|
{ |
|
|
// Make a literal node, set Tree to point to it, set left/right children to NULL. |
|
|
if ( !MakeExprNode( tree, m_CurToken, LITERAL, NULL, NULL ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
} |
|
|
else if ( IsNotOp( m_CurToken ) ) |
|
|
{ |
|
|
// do nothing |
|
|
return true; |
|
|
} |
|
|
else |
|
|
{ |
|
|
// This must be a bad token |
|
|
if ( m_pSyntaxErrorProc ) |
|
|
{ |
|
|
m_pSyntaxErrorProc( CFmtStr( "Bad expression token: %c", m_CurToken ) ); |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
// Get the next token |
|
|
GetNextToken(); |
|
|
return true; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Makes a term :: <factor> { <not> }. |
|
|
//----------------------------------------------------------------------------- |
|
|
bool CExpressionEvaluator::MakeTerm( ExprTree &tree ) |
|
|
{ |
|
|
// Make a factor, setting Tree to point to it |
|
|
if ( !MakeFactor( tree ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
// while the next token is ! |
|
|
while ( IsNotOp( m_CurToken ) ) |
|
|
{ |
|
|
// Make an operator node, setting left child to Tree and right to NULL. (Tree points to new node) |
|
|
if ( !MakeExprNode( tree, m_CurToken, NOT, tree, NULL ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
// Get the next token. |
|
|
GetNextToken(); |
|
|
|
|
|
// Make a factor, setting the right child of Tree to point to it. |
|
|
if ( !MakeFactor( tree->right ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Makes a complete expression :: <term> { <cond> <term> }. |
|
|
//----------------------------------------------------------------------------- |
|
|
bool CExpressionEvaluator::MakeExpression( ExprTree &tree ) |
|
|
{ |
|
|
// Make a term, setting Tree to point to it |
|
|
if ( !MakeTerm( tree ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
// while the next token is a conditional |
|
|
while ( 1 ) |
|
|
{ |
|
|
bool bConditional = false; |
|
|
bool bValid = IsConditional( bConditional, m_CurToken ); |
|
|
if ( !bValid ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
if ( !bConditional ) |
|
|
{ |
|
|
break; |
|
|
} |
|
|
|
|
|
// Make a conditional node, setting left child to Tree and right to NULL. (Tree points to new node) |
|
|
if ( !MakeExprNode( tree, m_CurToken, CONDITIONAL, tree, NULL ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
// Get the next token. |
|
|
GetNextToken(); |
|
|
|
|
|
// Make a term, setting the right child of Tree to point to it. |
|
|
if ( !MakeTerm( tree->right ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// returns true for success, false for failure |
|
|
//----------------------------------------------------------------------------- |
|
|
bool CExpressionEvaluator::BuildExpression( void ) |
|
|
{ |
|
|
// Get the first token, and build the tree. |
|
|
GetNextToken(); |
|
|
|
|
|
return ( MakeExpression( m_ExprTree ) ); |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// returns the value of the node after resolving all children |
|
|
//----------------------------------------------------------------------------- |
|
|
bool CExpressionEvaluator::SimplifyNode( ExprTree& node ) |
|
|
{ |
|
|
if ( !node ) |
|
|
return false; |
|
|
|
|
|
// Simplify the left and right children of this node |
|
|
bool leftVal = SimplifyNode(node->left); |
|
|
bool rightVal = SimplifyNode(node->right); |
|
|
|
|
|
// Simplify this node |
|
|
switch( node->kind ) |
|
|
{ |
|
|
case NOT: |
|
|
// the child of '!' is always to the right |
|
|
node->data.value = !rightVal; |
|
|
break; |
|
|
|
|
|
case CONDITIONAL: |
|
|
if ( node->data.cond == AND_OP ) |
|
|
{ |
|
|
node->data.value = leftVal && rightVal; |
|
|
} |
|
|
else // OR_OP |
|
|
{ |
|
|
node->data.value = leftVal || rightVal; |
|
|
} |
|
|
break; |
|
|
|
|
|
default: // LITERAL |
|
|
break; |
|
|
} |
|
|
|
|
|
// This node has beed resolved |
|
|
node->kind = LITERAL; |
|
|
return node->data.value; |
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Interface to solve a conditional expression. Returns false on failure, Result is undefined. |
|
|
//----------------------------------------------------------------------------- |
|
|
bool CExpressionEvaluator::Evaluate( bool &bResult, const char *pInfixExpression, GetSymbolProc_t pGetSymbolProc, SyntaxErrorProc_t pSyntaxErrorProc ) |
|
|
{ |
|
|
if ( !pInfixExpression ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
// for caller simplicity, we strip of any enclosing braces |
|
|
// strip the bracketing [] if present |
|
|
char szCleanToken[512]; |
|
|
if ( pInfixExpression[0] == '[' ) |
|
|
{ |
|
|
int len = V_strlen( pInfixExpression ); |
|
|
|
|
|
// SECURITY: Bail on input buffers that are too large, they're used for RCEs and we don't |
|
|
// need to support them. |
|
|
if ( len + 1 > ARRAYSIZE( szCleanToken ) ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
// SECURIY: Because this starts one character late, it picks up the null termination from pInfixExpression. |
|
|
V_strncpy( szCleanToken, pInfixExpression + 1, len ); |
|
|
len--; |
|
|
if ( szCleanToken[len-1] == ']' ) |
|
|
{ |
|
|
szCleanToken[len-1] = '\0'; |
|
|
} |
|
|
pInfixExpression = szCleanToken; |
|
|
} |
|
|
|
|
|
// reset state |
|
|
m_pExpression = pInfixExpression; |
|
|
m_pGetSymbolProc = pGetSymbolProc ? pGetSymbolProc : DefaultConditionalSymbolProc; |
|
|
m_pSyntaxErrorProc = pSyntaxErrorProc ? pSyntaxErrorProc : DefaultConditionalErrorProc; |
|
|
m_ExprTree = 0; |
|
|
m_CurPosition = 0; |
|
|
m_CurToken = 0; |
|
|
|
|
|
// Building the expression tree will fail on bad syntax |
|
|
bool bValid = BuildExpression(); |
|
|
if ( bValid ) |
|
|
{ |
|
|
bResult = SimplifyNode( m_ExprTree ); |
|
|
} |
|
|
|
|
|
// don't leak |
|
|
FreeTree( m_ExprTree ); |
|
|
m_ExprTree = NULL; |
|
|
|
|
|
return bValid; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|