//====================================================================
// Although great care has gone into developing this software,
// it is provided without any guarantee of reliability, accuracy
// of information, or correctness of operation. I am not responsible
// for any damages that may occur as a result of using this software.
// Use this software entirely at your own risk.
// Copyright 2003, Chris Richardson
//
// Description: Simple #include parser.
//
//====================================================================
#include "stdafx.h"
#include "Parser.h"
#include "MMFStream.h"
#include "FILEStream.h"
#include "Lexer.h"
#include "IncludeFinder.h"
#include "DSObjects.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////////////
// Operator precedence stuff.
//////////////////////////////////////////////////////////////////////
// Operator precedence definition (one struct per operator).
typedef struct tagOperatorDef
{
ParseTokens eToken;
int iPrec;
int iOperands;
}OperatorDef;
// Operator precedence table:
static const OperatorDef s_astOpPrecedence[] =
{
{ TOKEN_EQUAL, 0, 2 },
{ TOKEN_BAR_BAR, 1, 2 },
{ TOKEN_AMPERSAND_AMPERSAND, 2, 2 },
{ TOKEN_BAR, 3, 2 },
{ TOKEN_HAT, 4, 2 },
{ TOKEN_AMPERSAND, 5, 2 },
{ TOKEN_EQUAL_EQUAL, 6, 2 },
{ TOKEN_EXCLAMATION_EQUALS, 6, 2 },
{ TOKEN_LEFT, 7, 2 },
{ TOKEN_LEFT_EQUALS, 7, 2 },
{ TOKEN_RIGHT, 7, 2 },
{ TOKEN_RIGHT_EQUALS, 7, 2 },
{ TOKEN_PLUS, 8, 2 },
{ TOKEN_MINUS, 8, 2 },
{ TOKEN_MULTIPLY, 9, 2 },
{ TOKEN_DIVIDE, 9, 2 },
{ TOKEN_MODULUS, 9, 2 },
{ TOKEN_EXCLAMATION, 10, 1 },
{ TOKEN_TILDE, 10, 1 },
{ TOKEN_MINUS, 10, 1 },
{ TOKEN_PLUS, 10, 1 },
};
//////////////////////////////////////////////////////////////////////
// CParser Implementation.
//////////////////////////////////////////////////////////////////////
CParser::CParser( CIncludeFinderInfo * p_poInfo ) :
c_poLexer( NULL ),
c_poLastFile( NULL ),
c_poParentFile( NULL ),
c_poResults( NULL ),
c_hStopEvent( NULL ),
c_poFindInfo( p_poInfo ),
c_lPreprocessorExcluding( 0 )
{
}
//
// ------------------------------------------------------------------
//
CParser::~CParser()
{
c_poLastFile = NULL;
c_poParentFile = NULL;
c_poResults = NULL;
c_poFindInfo = NULL;
}
//
// ------------------------------------------------------------------
//
long CParser::Precedence( ParseTokens p_eOp, int p_iOps )
{
// Look up the precedence of the operator p_eOp, taking care that the unary version of the operator
// may have different precedence than the binary version.
for( int i = 0; i<sizeof( s_astOpPrecedence ) / sizeof( s_astOpPrecedence[0] ); ++i )
{
if( s_astOpPrecedence[i].eToken == p_eOp && s_astOpPrecedence[i].iOperands == p_iOps )
// Found the operator.
return s_astOpPrecedence[i].iPrec;
}
// We couldn't find the operator. The error will be handled by the parser later.
return -1;
}
//
// ------------------------------------------------------------------
//
CParsedFile * CParser::Lookup( const TCHAR * p_pszFile )
{
// Try to lookup an object in the current file's included file list for the specified file.
CParsedFile * a_poFile = NULL;
CParsedFileList::const_iterator a_oIter = c_poResults->begin();
while( a_oIter != c_poResults->end() )
{
a_poFile = *a_oIter;
if( _tcsicmp( a_poFile->GetFileName().c_str(), p_pszFile ) == 0 )
return a_poFile;
a_oIter++;
}
return NULL;
}
//
// ------------------------------------------------------------------
//
void CParser::SetSettings( CDSParsedSettings & p_roSettings )
{
c_oIncludeDirs.clear();
c_oDefinedVars.clear();
int i = 0;
CStdStringArray & a_roIncludes = p_roSettings.GetIncludes();
for( i = 0; i<a_roIncludes.size(); ++i )
{
c_oIncludeDirs.push_back( a_roIncludes[i] );
}
CStdStringArray & a_roDefines = p_roSettings.GetDefines();
for( i = 0; i<a_roDefines.size(); ++i )
{
ParseNode a_stNode;
c_oDefinedVars.insert( CStdMapStringToNode::value_type( a_roDefines[i], a_stNode ) );
}
}
//
// ------------------------------------------------------------------
//
CParsedFile * CParser::LookupInParent( CParsedFile * p_poParent, const TCHAR * p_pszFile )
{
bool a_bRecurse = true;
if( p_poParent->GetAlreadyIncluded() )
{
// The file was already checked. We don't want to recurse because
// we might infinitely recurse (i.e, "rpc.h" includes "windows.h", which includes "rpc.h").
a_bRecurse = false;
}
p_poParent->SetAlreadyIncluded( true );
if( a_bRecurse )
{
// Look in the parent file's included file list.
CParsedFileArray & a_roFiles = p_poParent->GetIncludedFiles();
CParsedFileArray::iterator a_oIter;
for( a_oIter = a_roFiles.begin(); a_oIter != a_roFiles.end(); ++a_oIter )
{
CParsedFile * a_poFile = *a_oIter;
if( a_poFile->GetFileName().compare( p_pszFile ) == 0 )
return a_poFile;
else
{
// Down thru the include tree we go...
a_poFile = LookupInParent( a_poFile, p_pszFile );
if( a_poFile )
return a_poFile;
}
}
}
if( p_poParent->GetFileName().compare( p_pszFile ) == 0 )
// Last but not least, check the parent.
return p_poParent;
return NULL;
}
//
// ------------------------------------------------------------------
//
ParseStatus CParser::Eat( ParseTokens p_eToken )
{
if( c_oToken.c_eType != p_eToken )
return PARSE_STATUS_GENERIC_ERROR;
// Eat the current token.
c_poLexer->Lex( &c_oToken );
return PARSE_STATUS_OK;
}
//
// ------------------------------------------------------------------
//
ParseStatus CParser::Parse( const TCHAR * p_pszFile,
CParsedFileList * p_poResults,
HANDLE p_hStopEvent )
{
c_hStopEvent = p_hStopEvent;
c_poResults = p_poResults;
c_poParentFile = NULL;
c_lPreprocessorExcluding = 0;
c_oPreprocessorExpressionStack.clear();
return ParseInternal( p_pszFile );
}
//
// ------------------------------------------------------------------
//
ParseStatus CParser::ParseInternal( const TCHAR * p_pszFile )
{
if( WaitForSingleObject( c_hStopEvent, 1 ) == WAIT_OBJECT_0 )
return PARSE_STATUS_OK;
ParseStatus a_eStatus = PARSE_STATUS_OK;
CMMFStream a_oStream;
CLexer a_oLexer;
TCHAR a_szFile[MAX_PATH+1] = {0};
_tcscpy( a_szFile, p_pszFile );
_tcslwr( a_szFile );
c_poLastFile = NULL;
if( !c_poFindInfo->c_bPreprocess )
{
// We may have already parsed the file, so let's check.
// If we haven't parsed it yet, we'll add it to our list of parsed files.
c_poLastFile = Lookup( p_pszFile );
if( c_poLastFile )
{
// We've already parsed the file.
return PARSE_STATUS_OK;
}
}
else
if( c_poParentFile )
{
c_poLastFile = LookupInParent( c_poParentFile, p_pszFile );
c_poParentFile->SetAlreadyIncluded( false );
}
if( !c_poLastFile )
{
// Create a new entry for the file.
c_poLastFile = new CParsedFile( p_pszFile );
c_poResults->push_back( c_poLastFile );
}
if( !c_poParentFile )
{
// The parent file represents the first file for each set (the source file in most cases).
c_poParentFile = c_poLastFile;
}
// Notify that we are starting on a file.
c_poFindInfo->c_poStatusNotifier->OnFileStart( c_poLastFile );
// Try to open a stream on the file.
a_eStatus = a_oStream.Open( p_pszFile );
if( a_eStatus == PARSE_STATUS_OK )
{
a_oLexer.SetStream( &a_oStream );
// Push our current lexer pointer and store the new one.
CLexer * a_poPushLexer = c_poLexer;
c_poLexer = &a_oLexer;
// Push the current parsed file.
CParsedFile * a_poPushFile = c_poLastFile;
c_oLexerStack.push_back( &a_oLexer );
// Now parse the file.
a_eStatus = ParseFile();
// Set the line count of the file.
a_poPushFile->SetLineCount( a_oLexer.GetLine() );
// Pop the current parsed file.
c_poLastFile = a_poPushFile;
// Pop the lexer pointer.
c_poLexer = a_poPushLexer;
c_oLexerStack.pop_back();
}
return a_eStatus;
}
//
// ------------------------------------------------------------------
//
ParseStatus CParser::ParseFile()
{
ParseStatus a_eStatus = PARSE_STATUS_OK;
if( !c_poLexer )
return PARSE_STATUS_FILE_NOT_OPEN;
a_eStatus = c_poLexer->Lex( &c_oToken );
if( a_eStatus != PARSE_STATUS_OK )
return a_eStatus;
// Iterate through all the tokens in the file, looking for "#" characters, which are
// the only one's we are interested in.
while( 1 )
{
switch( c_oToken.c_eType )
{
case TOKEN_POUND:
{
// Check that we are at the start of the line, after whitespace.
if( c_poLexer->GetLinePos() == 0 )
{
a_eStatus = ParsePreprocessorStatement();
}
else
Eat( TOKEN_POUND );
break;
}
default:
Eat( c_oToken.c_eType );
break;
}
if( a_eStatus != PARSE_STATUS_OK || c_oToken.c_eType == TOKEN_EOF )
break;
}
return a_eStatus;
}
//
// ------------------------------------------------------------------
//
ParseStatus CParser::ParsePreprocessorStatement()
{
ParseStatus a_eStatus = PARSE_STATUS_OK;
// Eat the pound character.
a_eStatus = Eat( TOKEN_POUND );
if( a_eStatus != PARSE_STATUS_OK )
return a_eStatus;
long a_lProcessCodeInPreprocessorBlock = 1;
if( !_tcscmp( c_oToken.c_szID, _T("include") ) && !c_lPreprocessorExcluding )
{
// Set the lexer to preprocessor mode, so it won't process escape sequences, and will allow <include.h> style strings.
c_poLexer->SetLexMode( CLexer::LEX_MODE_PREPROCESSOR );
Eat( TOKEN_IDENTIFIER );
// Set the lexer back to regular parser mode.
c_poLexer->SetLexMode( CLexer::LEX_MODE_PARSER );
if( c_oToken.c_eType != TOKEN_QUOTED_STRING && c_oToken.c_eType != TOKEN_BRACKET_STRING )
return PARSE_STATUS_OK;
CToken a_oPushToken = c_oToken;
// We have to find the full path to the file, and where we start looking depends on whether
// the string is in brackets or quotes.
CParsedFile * a_poFile = NULL;
if( c_poFindInfo->c_bPreprocess )
{
a_poFile = LookupInParent( c_poParentFile, c_poLexer->GetName() );
c_poParentFile->SetAlreadyIncluded( false );
}
else
a_poFile = Lookup( c_poLexer->GetName() );
TCHAR a_szFullPath[MAX_PATH+1] = {0};
if( FindFullPath( c_oToken.c_szID, a_szFullPath, c_oToken.c_eType == TOKEN_BRACKET_STRING ) )
{
// We found the full path to the file.
CParsedFile * a_poIncludedFile = NULL;
if( c_poFindInfo->c_bPreprocess )
{
a_poIncludedFile = LookupInParent( c_poParentFile, a_szFullPath );
c_poParentFile->SetAlreadyIncluded( false );
if( !a_poIncludedFile )
{
// The file hasn't been included yet, so parse it.
a_poIncludedFile = new CParsedFile( a_szFullPath );
c_poResults->push_back( a_poIncludedFile );
a_poFile->AddIncludedFile( a_poIncludedFile );
a_poIncludedFile->AddIncludedByFile( a_poFile );
// Parse the included file.
a_eStatus = ParseInternal( a_szFullPath );
}
else
{
// The file has already been included by the parent file (or somewhere inbetween),
// so don't parse it again, just add it to the list.
a_poFile->AddIncludedFile( a_poIncludedFile );
a_poIncludedFile->AddIncludedByFile( a_poFile );
}
}
else
{
// Parse the file.
a_eStatus = ParseInternal( a_szFullPath );
a_poIncludedFile = Lookup( a_szFullPath );
if( a_poIncludedFile )
{
a_poIncludedFile->AddIncludedByFile( a_poFile );
a_poFile->AddIncludedFile( a_poIncludedFile );
}
}
}
else
{
// We couldn't find the included file, so we'll add it to the "error" list.
CParsedFile * a_poIncludedFile = Lookup( a_szFullPath );
if( !a_poIncludedFile )
{
// Create a new entry for the file.
a_poIncludedFile = new CParsedFile( a_szFullPath );
c_poResults->push_back( a_poIncludedFile );
}
a_poIncludedFile->SetNotFoundFlag();
a_poFile->AddIncludedFile( a_poIncludedFile );
}
c_oToken = a_oPushToken;
}
else
if( c_poFindInfo->c_bPreprocess ) // Only handle macros and such if the user wants us to.
{
if( !_tcscmp( c_oToken.c_szID, _T("define") ) && !c_lPreprocessorExcluding )
{
// Does not handle complex macro definitions.
// It only handles simple cases like
// #define MY_VAR
// or
// #define MY_VAR SOME_EXPRESSION
unsigned long a_ulLine = c_poLexer->GetLine();
Eat( TOKEN_IDENTIFIER );
if( c_oToken.c_eType == TOKEN_IDENTIFIER )
{
CToken a_oVarName = c_oToken;
Eat( TOKEN_IDENTIFIER );
ParseNode a_stResult;
if( c_poLexer->GetLine() == a_ulLine )
{
// This looks like the "#define MY_VAR SOME_EXPRESSION" case, so we'll parse the associated expression.
a_stResult = ParsePreprocessorExpression( -1 );
}
// Insert the variable into the name to value map.
c_oDefinedVars.insert( CStdMapStringToNode::value_type( a_oVarName.c_szID, a_stResult ) );
}
}
else
if( !_tcscmp( c_oToken.c_szID, _T("undef") ) && !c_lPreprocessorExcluding )
{
Eat( TOKEN_IDENTIFIER );
if( c_oToken.c_eType == TOKEN_IDENTIFIER )
{
// Look up the variable.
CStdMapStringToNode::iterator a_oType = c_oDefinedVars.find( c_oToken.c_szID );
if( a_oType != c_oDefinedVars.end() )
{
// Remove the variable from the name map.
c_oDefinedVars.erase( a_oType );
}
Eat( TOKEN_IDENTIFIER );
}
}
else
if( !_tcscmp( c_oToken.c_szID, _T("ifndef") ) || !_tcscmp( c_oToken.c_szID, _T("ifdef") ) )
{
// Remember if it was an "ifndef" versus an "ifdef".
BOOL a_bIfNDef = !_tcscmp( c_oToken.c_szID, _T("ifndef") );
Eat( TOKEN_IDENTIFIER );
if( c_oToken.c_eType == TOKEN_IDENTIFIER )
{
// Check to see if the variable has been #defined.
CStdMapStringToNode::iterator a_oType = c_oDefinedVars.find( c_oToken.c_szID );
if( a_bIfNDef )
{
// We want to skip the code in the block if the identifier was DEFINED.
if( a_oType != c_oDefinedVars.end() )
a_lProcessCodeInPreprocessorBlock = 0;
}
else
{
// We want to skip the code in the block if the identifier was NOT DEFINED.
if( a_oType == c_oDefinedVars.end() )
a_lProcessCodeInPreprocessorBlock = 0;
}
// Push the latest value onto the top of the expression stack.
c_oPreprocessorExpressionStack.push_back( a_lProcessCodeInPreprocessorBlock );
if( !a_lProcessCodeInPreprocessorBlock || c_lPreprocessorExcluding )
c_lPreprocessorExcluding++;
Eat( TOKEN_IDENTIFIER );
}
}
else
if( !_tcscmp( c_oToken.c_szID, _T("if") ) )
{
Eat( TOKEN_IDENTIFIER );
// Parse the expression following the #if.
ParseNode a_stResult = ParsePreprocessorExpression( -1 );
if( !a_stResult.lValue )
a_lProcessCodeInPreprocessorBlock = 0;
// Push the result of the expression onto the top of the expression stack.
c_oPreprocessorExpressionStack.push_back( a_lProcessCodeInPreprocessorBlock );
if( !a_lProcessCodeInPreprocessorBlock || c_lPreprocessorExcluding )
c_lPreprocessorExcluding++;
}
else
if( !_tcscmp( c_oToken.c_szID, _T("else") ) || !_tcscmp( c_oToken.c_szID, _T("elif") ) )
{
// Remember if it's an "elif" versus an "else".
BOOL a_bIsElif = !_tcscmp( c_oToken.c_szID, _T("elif") );
Eat( TOKEN_IDENTIFIER );
if( c_oPreprocessorExpressionStack.size() == 0 )
{
// We've got a problem (missing #if?).
return PARSE_STATUS_GENERIC_ERROR;
}
else
{
// Check the value at the top of the stack to determine if we should go into the "else" type case.
long a_lLastResult = c_oPreprocessorExpressionStack.back();
if( a_bIsElif )
{
Eat( TOKEN_IDENTIFIER );
// Process the expression part of the elif.
ParseNode a_stResult = ParsePreprocessorExpression( -1 );
// Determine if we are going to be excluding the code block or not.
if( !a_lLastResult )
{
// The last section was not included, so include the next section if the result of the expression was non-zero.
a_lProcessCodeInPreprocessorBlock = a_stResult.lValue;
if( a_stResult.lValue )
c_lPreprocessorExcluding--;
}
else
// The last section was included, so we don't include the next section.
a_lProcessCodeInPreprocessorBlock = 0;
if( !a_lProcessCodeInPreprocessorBlock && !c_lPreprocessorExcluding )
c_lPreprocessorExcluding++;
}
else
{
// Determine if we are going to be excluding the code block or not.
if( a_lLastResult == 1 )
{
// The last section was included, so don't include the next section.
a_lProcessCodeInPreprocessorBlock = 0;
if( !c_lPreprocessorExcluding )
c_lPreprocessorExcluding++;
}
else
if( a_lLastResult == 0 && c_lPreprocessorExcluding == 1 )
// The last section was not included, so include the next section.
c_lPreprocessorExcluding--;
}
if( !a_lLastResult )
{
// Only push a new result of the last result was 0.
c_oPreprocessorExpressionStack.pop_back();
c_oPreprocessorExpressionStack.push_back( a_lProcessCodeInPreprocessorBlock );
}
}
}
else
if( !_tcscmp( c_oToken.c_szID, _T("endif") ) )
{
Eat( TOKEN_IDENTIFIER );
if( c_oPreprocessorExpressionStack.size() == 0 )
{
// We've got a problem (missing #if?).
return PARSE_STATUS_GENERIC_ERROR;
}
else
{
// Decrement the excluding flag.
if( c_lPreprocessorExcluding > 0 )
c_lPreprocessorExcluding--;
// Pop the last value off the top of the expression stack.
c_oPreprocessorExpressionStack.pop_back();
}
}
}
return a_eStatus;
}
//
// ------------------------------------------------------------------
//
ParseNode CParser::ParsePreprocessorUnaryExpression( long p_lPrec )
{
ParseNode a_stLeftSide;
switch( c_oToken.c_eType )
{
case TOKEN_DECIMAL:
{
// Floating point values aren't valid for the preprocessor.
// Get the next token.
Eat( TOKEN_DECIMAL );
return a_stLeftSide;
}
case TOKEN_INTEGER:
{
// Get the value of the integer.
if( c_oToken.c_ulFlags & TOKEN_FLAG_HEX )
// Scan the hex value.
_stscanf( c_oToken.c_szID, _T("%x"), &a_stLeftSide.lValue );
else
if( c_oToken.c_ulFlags & TOKEN_FLAG_OCT )
// Scan the octal value.
_stscanf( c_oToken.c_szID, _T("%o"), &a_stLeftSide.lValue );
else
// It's a regular decimal integer.
a_stLeftSide.lValue = _ttoi( c_oToken.c_szID );
// Get the next token.
Eat( TOKEN_INTEGER );
a_stLeftSide.OK();
break;
}
case TOKEN_IDENTIFIER:
{
if( !_tcscmp( c_oToken.c_szID, _T("defined") ) )
{
// This is a special preprocessor operator.
Eat( TOKEN_IDENTIFIER );
BOOL a_bIsParenthesizedVersion = FALSE;
if( c_oToken.c_eType == TOKEN_LPAREN )
{
Eat( TOKEN_LPAREN );
// Remember it, so we can eat the right parenthesis later.
a_bIsParenthesizedVersion = TRUE;
}
if( c_oToken.c_eType != TOKEN_IDENTIFIER )
return a_stLeftSide;
// Attempt to lookup the value in the defined variable map.
CStdMapStringToNode::iterator a_oType = c_oDefinedVars.find( c_oToken.c_szID );
if( a_oType == c_oDefinedVars.end() )
a_stLeftSide.lValue = 0;
else
a_stLeftSide.lValue = 1;
Eat( TOKEN_IDENTIFIER );
if( a_bIsParenthesizedVersion )
if( Eat( TOKEN_RPAREN ) != PARSE_STATUS_OK )
return a_stLeftSide;
a_stLeftSide.OK();
}
else
{
// Attempt to lookup the value in the defined variable map.
CStdMapStringToNode::iterator a_oType = c_oDefinedVars.find( c_oToken.c_szID );
// Get the next token.
Eat( TOKEN_IDENTIFIER );
if( a_oType != c_oDefinedVars.end() )
a_stLeftSide = a_oType->second;
else
a_stLeftSide.OK();
}
break;
}
case TOKEN_LPAREN:
{
// Skip past the left paren..
Eat( TOKEN_LPAREN );
// Attempt to parse the interior of the parentheses.
a_stLeftSide = ParsePreprocessorExpression( -1 );
// Make sure we are still OK.
if( !a_stLeftSide.bOK || Eat( TOKEN_RPAREN ) != PARSE_STATUS_OK )
return a_stLeftSide;
a_stLeftSide.OK();
break;
}
case TOKEN_EXCLAMATION:
case TOKEN_TILDE:
case TOKEN_MINUS:
case TOKEN_PLUS:
{
// Get the precedence of the current operator.
long a_lPrec = Precedence( c_oToken.c_eType, 1 );
ParseTokens a_eToken = c_oToken.c_eType;
// Get the next token.
Eat( c_oToken.c_eType );
// Parse the operand.
a_stLeftSide = ParsePreprocessorExpression( a_lPrec );
if( !a_stLeftSide.bOK )
return a_stLeftSide;
// Perform the operator.
switch( a_eToken )
{
case TOKEN_EXCLAMATION:
a_stLeftSide.lValue = !a_stLeftSide.lValue;
break;
case TOKEN_TILDE:
a_stLeftSide.lValue = ~a_stLeftSide.lValue;
break;
case TOKEN_MINUS:
a_stLeftSide.lValue = -a_stLeftSide.lValue;
break;
case TOKEN_PLUS:
a_stLeftSide.lValue = +a_stLeftSide.lValue;
break;
}
a_stLeftSide.OK();
break;
}
default:
// Something's wrong with the input.
//Error( ERROR_SYNTAX, &c_oToken );
Eat( c_oToken.c_eType );
break;
}
return a_stLeftSide;
}
//
// ------------------------------------------------------------------
//
ParseNode CParser::ParsePreprocessorExpression( long p_lPrec )
{
ParseNode a_stLeftSide;
ParseNode a_stRightSide;
while( 1 )
{
switch( c_oToken.c_eType )
{
case TOKEN_DECIMAL:
case TOKEN_INTEGER:
case TOKEN_IDENTIFIER:
case TOKEN_LPAREN:
if( !a_stLeftSide.bOK )
{
// Parse the unary node.
a_stLeftSide = ParsePreprocessorUnaryExpression( p_lPrec );
if( !a_stLeftSide.bOK )
return a_stLeftSide;
// Keep going.
continue;
}
else
// We've already parsed a left side of the expression, and didn't find any binary operators,
// so it's possible it's just a unary expression
return a_stLeftSide;
case TOKEN_RPAREN:
// This is potentially the termination of the current expression.
return a_stLeftSide;
case TOKEN_EOF:
// We're done.
return a_stLeftSide;
default:
{
if( !a_stLeftSide.bOK )
{
// This looks like a unary expression.
a_stLeftSide = ParsePreprocessorUnaryExpression( p_lPrec );
if( !a_stLeftSide.bOK )
return a_stLeftSide;
a_stLeftSide.OK();
}
else
{
// Get the precedence of the current operator.
// If it's less than or equal to the precedence of the previous operator, we're done with this portion.
long a_lPrec = Precedence( c_oToken.c_eType, 2 );
if( a_lPrec <= p_lPrec )
return a_stLeftSide;
// While the current precedence is greater than the previous precedence, keep parsing.
while( a_lPrec > p_lPrec )
{
ParseTokens a_eToken = c_oToken.c_eType;
// Get the next token.
Eat( c_oToken.c_eType );
if( a_eToken == TOKEN_EQUAL )
{
// Assignment is not currently handled.
// It would be straightforward, but is it necessary for a preprocessor evaluator?
}
else
{
// Parse the right side of the operator.
a_stRightSide = ParsePreprocessorExpression( a_lPrec );
}
if( !a_stRightSide.bOK )
return a_stRightSide;
// Perform the operator.
switch( a_eToken )
{
case TOKEN_BAR:
a_stLeftSide.lValue = a_stLeftSide.lValue | a_stRightSide.lValue;
break;
case TOKEN_HAT:
a_stLeftSide.lValue = a_stLeftSide.lValue ^ a_stRightSide.lValue;
break;
case TOKEN_AMPERSAND:
a_stLeftSide.lValue = a_stLeftSide.lValue & a_stRightSide.lValue;
break;
case TOKEN_EQUAL_EQUAL:
a_stLeftSide.lValue = a_stLeftSide.lValue == a_stRightSide.lValue;
break;
case TOKEN_EXCLAMATION_EQUALS:
a_stLeftSide.lValue = a_stLeftSide.lValue != a_stRightSide.lValue;
break;
case TOKEN_LEFT:
a_stLeftSide.lValue = a_stLeftSide.lValue < a_stRightSide.lValue;
break;
case TOKEN_LEFT_EQUALS:
a_stLeftSide.lValue = a_stLeftSide.lValue <= a_stRightSide.lValue;
break;
case TOKEN_RIGHT:
a_stLeftSide.lValue = a_stLeftSide.lValue > a_stRightSide.lValue;
break;
case TOKEN_RIGHT_EQUALS:
a_stLeftSide.lValue = a_stLeftSide.lValue >= a_stRightSide.lValue;
break;
case TOKEN_AMPERSAND_AMPERSAND:
a_stLeftSide.lValue = a_stLeftSide.lValue && a_stRightSide.lValue;
break;
case TOKEN_BAR_BAR:
a_stLeftSide.lValue = a_stLeftSide.lValue || a_stRightSide.lValue;
break;
case TOKEN_PLUS:
a_stLeftSide.lValue = a_stLeftSide.lValue + a_stRightSide.lValue;
break;
case TOKEN_MINUS:
a_stLeftSide.lValue = a_stLeftSide.lValue - a_stRightSide.lValue;
break;
case TOKEN_MULTIPLY:
a_stLeftSide.lValue = a_stLeftSide.lValue * a_stRightSide.lValue;
break;
case TOKEN_DIVIDE:
if( a_stRightSide.lValue )
a_stLeftSide.lValue = a_stLeftSide.lValue / a_stRightSide.lValue;
break;
case TOKEN_MODULUS:
if( a_stRightSide.lValue )
a_stLeftSide.lValue = a_stLeftSide.lValue % a_stRightSide.lValue;
break;
}
if( c_oToken.c_eType == TOKEN_EOF )
return a_stLeftSide;
// Store the new precedence of the current portion of the expression.
a_lPrec = Precedence( c_oToken.c_eType, 2 );
}
}
break;
}
}
}
return a_stLeftSide;
}
//
// ------------------------------------------------------------------
//
BOOL CParser::FindFullPath( const TCHAR * p_pszFile,
TCHAR * p_pszFullPath,
BOOL p_bIsBracketed )
{
TCHAR a_szPath[MAX_PATH+1] = {0};
TCHAR a_szCurrentPath[MAX_PATH+1] = {0};
TCHAR a_szFileName[MAX_PATH+1] = {0};
TCHAR a_szExtra[MAX_PATH+1] = {0};
TCHAR * a_pszSep = NULL;
TCHAR * a_pszFile = NULL;
BOOL a_bFoundPath = FALSE;
// Break the complete file name down into the name of the file, and any extra directories specified.
_tcscpy( a_szPath, p_pszFile );
a_pszSep = _tcsrchr( a_szPath, _T('\\') );
if( !a_pszSep )
{
// Search for the alternative separator.
a_pszSep = _tcsrchr( a_szPath, _T('/') );
}
if( a_pszSep )
{
*a_pszSep = 0;
_tcscpy( a_szExtra, a_szPath );
_tcscpy( a_szFileName, a_pszSep+1 );
}
else
_tcscpy( a_szFileName, a_szPath );
if( !p_bIsBracketed )
{
// See if the file is in the same directory as the file currently being parsed,
// and if it's not there, try the grandparent file, then the great grandparent file and so on.
for( int i = c_oLexerStack.size() - 1; i >= 0; --i )
{
CLexer * a_poLexer = c_oLexerStack[i];
_tcscpy( a_szPath, a_poLexer->GetName() );
a_pszSep = _tcsrchr( a_szPath, _T('\\') );
if( a_pszSep )
*(a_pszSep) = 0;
if( i == 0 )
_tcscpy( a_szCurrentPath, a_szPath ); // Store the current file's directory.
// Thanks to "Oliver Wahl" for the following block:
// BEGIN
_tcscat( a_szPath, _T("\\") );
_tcscat( a_szPath, a_szExtra );
_tcscat( a_szPath, _T("\\") );
_tcscat( a_szPath, a_szFileName );
// END
if( GetFileAttributes( a_szPath ) != 0xFFFFFFFF )
{
a_bFoundPath = TRUE;
break;
}
}
}
if( !a_bFoundPath )
{
// Thanks to "EvanKeats" for the following block:
// BEGIN
// Inserted code to set a_szCurentPath
CLexer * a_poLexerParent = c_oLexerStack[0];
_tcscpy( a_szCurrentPath, a_poLexerParent->GetName() );
a_pszSep = _tcsrchr( a_szCurrentPath, _T('\\') );
if( a_pszSep )
*(a_pszSep) = 0;
// END
// Search for the file in the include directories.
for( int i = 0; i < c_oIncludeDirs.size(); ++i )
{
CTCharString & a_rsIncludeDir = c_oIncludeDirs[i];
// We have two tries here. The first try will take the current path and the include directory,
// for cases where we have relative include paths (..\\inc), and the second case will
// take just the include directory, for cases where we have static include paths (C:\\inc).
for( int test = 0; test<2; ++test )
{
if( test == 0 )
// Check relative paths.
_stprintf( a_szPath, _T("%s\\%s"), a_szCurrentPath, a_rsIncludeDir.c_str() );
else
// Check static paths.
_stprintf( a_szPath, _T("%s"), a_rsIncludeDir.c_str() );
// Remove the trailing slash, if it's there.
int a_iLen = _tcslen( a_szPath );
if( a_szPath[a_iLen-1] == _T('\\') || a_szPath[a_iLen-1] == _T('/') )
a_szPath[a_iLen-1] = _T('\0');
// Put in any relative directories that were found in the actual include path.
if( _tcslen( a_szExtra ) )
{
_tcscat( a_szPath, _T("\\") );
_tcscat( a_szPath, a_szExtra );
}
// Add on the file name.
_tcscat( a_szPath, _T("\\") );
_tcscat( a_szPath, a_szFileName );
if( GetFileAttributes( a_szPath ) != 0xFFFFFFFF )
{
a_bFoundPath = TRUE;
break;
}
}
if( a_bFoundPath )
break;
}
}
if( a_bFoundPath )
{
// We might have a strange path here (i.e. "C:\\Wherever\\..\\inc\\file.h"), but it's nicer to have a readable path name.
TCHAR a_szFullPathToFile[MAX_PATH + 1] = {0};
TCHAR * a_pszFile = NULL;
GetFullPathName( a_szPath, MAX_PATH + 1, a_szFullPathToFile, &a_pszFile );
_tcscpy( p_pszFullPath, a_szFullPathToFile );
return TRUE;
}
_tcscpy( p_pszFullPath, p_pszFile );
return FALSE;
}
//
// ------------------------------------------------------------------
//