diff -r 000000000000 -r 818e61de6cd1 crashanalysercmd/PerfToolsSharedLibraries/Engine/SymBuildParsingLib/Grouper/SymGrouperMastermind.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crashanalysercmd/PerfToolsSharedLibraries/Engine/SymBuildParsingLib/Grouper/SymGrouperMastermind.cs Thu Feb 11 15:50:58 2010 +0200 @@ -0,0 +1,973 @@ +/* +* Copyright (c) 2004-2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + +using System; +using System.Text; +using System.Threading; +using System.Collections; +using SymBuildParsingLib.Token; +using SymBuildParsingLib.Lexer; + +namespace SymBuildParsingLib.Grouper +{ + public class SymGrouperMastermind + { + #region Enumerations + public enum TEvent + { + EEventGroupTokenReady = 0 + }; + #endregion + + #region Observer interface + public delegate void MastermindObserver( TEvent aEvent, SymToken aGroupedToken ); + #endregion + + #region Events + public event MastermindObserver MastermindObservers; + #endregion + + #region Constructors & destructor + public SymGrouperMastermind() + { + } + #endregion + + #region Internal enumerations + private enum TGroupingAction + { + ETokenIgnore = -1, + ETokenMerge = 0, + ETokenEnqueue, + ETokenFlushQueue + }; + + [Flags] + private enum TStateFlag + { + EStateFlagUnspecified = 0, + EStateFlagInQuotation = 1, + EStateFlagInComment = 2, + EStateFlagInPreProcessorDirective = 4 + }; + #endregion + + #region API + public void PerformGrouping() + { + SymToken token = NextInputToken(); + // + while( token != null ) + { + ProcessToken( token ); + token = NextInputToken(); + } + } + + public void EnqueueLexedToken( SymToken aToken ) + { + lock( iLexedTokens ) + { + iLexedTokens.Enqueue( aToken ); + } + } + #endregion + + #region Internal token processors + private void ProcessToken( SymToken aToken ) + { + aToken.RefineTokenClass(); + aToken.RefineTokenType(); + + if ( InQuotation ) + { + ProcessTokenDuringQuotation( aToken ); + } + else if ( InComment ) + { + ProcessTokenDuringComment( aToken ); + } + else if ( InPreProcessorDirective ) + { + ProcessTokenDuringPreProcessorDirective( aToken ); + } + else + { + ProcessTokenDuringNormalOperations( aToken ); + } + } + + private void ProcessTokenDuringNormalOperations( SymToken aToken ) + { + // By default we will just add the input token to the + // pending queue (i.e. no combining/grouping) + TGroupingAction action = TGroupingAction.ETokenEnqueue; + // + if ( iCache.Count == 0 ) + { + #region The cache is empty - enqueue the token. + // Starting a new token batch, so just push the token. If + // its a quotation, then it will be handled during + // the enqueuing. Pragma symbols must appear as the first + // item on a line, and will be picked up similarly to quotes. + if ( aToken.Class == SymToken.TClass.EClassNewLine ) + { + // If we're adding a new blank line as the first + // token, we just want to flush it out immediately. + EnqueueNewOutputToken( aToken ); + action = TGroupingAction.ETokenFlushQueue; + } + else + { + action = TGroupingAction.ETokenEnqueue; + } + #endregion + } + else + { + #region The cache already has some tokens... + SymToken previousToken = PreviousOutputToken; + SymToken.TClass previousTokenClass = previousToken.Class; + // + if ( aToken.Class == SymToken.TClass.EClassNewLine ) + { + #region New line detected... + + // Checking for continuations... + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == @"\" ) + { + // Because of the continuation character, we don't + // flush the cache. + + // Discard new line + previousToken.Class = SymToken.TClass.EClassContinuation; + action = TGroupingAction.ETokenIgnore; + } + else + { + // We never allow new lines to be combined. In fact, + // they are the signal that we should flush whatever we have + // cached so far. We must add the new line token + // first though. + EnqueueNewOutputToken( aToken ); + action = TGroupingAction.ETokenFlushQueue; + } + #endregion + } + else if ( previousTokenClass == aToken.Class ) + { + #region Tokens are the same class - check for combining + // We group almost all tokens, but some are not permitted + // to be combined, for example, brackets. + bool combiningAllowed = previousToken.CombiningAllowed; + if ( combiningAllowed && aToken.CombiningAllowed ) + { + // Merge the two tokens + action = TGroupingAction.ETokenMerge; + } + else + { + // Treat it as a separate token. + action = TGroupingAction.ETokenEnqueue; + } + #endregion + } + else + { + #region Handling some other type of token... + if ( previousTokenClass == SymToken.TClass.EClassSymbol && previousToken.Value == @"\" ) + { + // If the last token was a single escaped character, and this next + // character is not an asterisk or another back slash, then + // we can try to combine the two. + if ( !(aToken.Value == "*" || aToken.Value == @"\" ) ) + { + action = TGroupingAction.ETokenMerge; + } + else + { + System.Diagnostics.Debug.Assert( false ); + } + } + else + { + action = TGroupingAction.ETokenEnqueue; + } + #endregion + } + #endregion + } + + #region Now perform the action + switch( action ) + { + case TGroupingAction.ETokenEnqueue: + EnqueueNewOutputToken( aToken ); + break; + case TGroupingAction.ETokenMerge: + MergeWithPreviousToken( aToken ); + break; + case TGroupingAction.ETokenFlushQueue: + FlushCache(); + break; + default: + case TGroupingAction.ETokenIgnore: + break; + } + #endregion + } + + private void ProcessTokenDuringQuotation( SymToken aToken ) + { +// System.Diagnostics.Debug.Write( "[" + aToken.Value + "] " ); + System.Diagnostics.Debug.Assert( iCache.Count > 0 ); + + #region Quotation examples + // 1) "" + // 2) "\"" + // 3) "\"\"" + // 4) '' + // 5) '\'' + // 6) '\'\'' + // 7) "\'\'\'\"\"" + // 8) "abc def ghi" + // + // 9) #define WIBBLE " this is a test string \ + // This too" " - and this!" + // + // 10) #define WIBBLE2 " this is a test string \\ abc \ + // This too" " - and this!" + // + // 11) #pragma message("Quotation with brackets (;') and other \'nasty\' things! inside it__\\"); + // + #endregion + + if ( aToken.Class == SymToken.TClass.EClassQuotation ) + { + #region Token is a quotation ... + // Quotation symbol whilst already in a quotation. + // We should check whether we have reached + // the closing quotation symbol, or then whether + // this is possibly just an escaped character? + // + // See examples 2,3,5,6,7,10,11 + + SymToken previousToken = PreviousOutputToken; + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == @"\" ) + { + // Combine the \' or \" with any previous token + previousToken.ForceCombine( aToken ); + System.Diagnostics.Debug.Assert( iCache.Count > 0 ); + } + else + { + // The last token was not an escape marker, so this + // is a quotation character all on its own. Since + // we always start a new cache run when we first see + // a quotation (during "normal" state), then the + // first token in the cache forms the basis for the + // search character. + // + // If the number of tokens in the cache with the same + // type (as the first token) is even, then we have + // reached the end of a quotation. If its odd, then + // we're still inside one. + + SymToken initialQuotationToken = iCache.PeekHead; + System.Diagnostics.Debug.Assert( initialQuotationToken.Value.Length == 1 ); + System.Diagnostics.Debug.Assert( initialQuotationToken.Class == SymToken.TClass.EClassQuotation ); + System.Diagnostics.Debug.Assert( initialQuotationToken.Type == SymToken.TType.ETypeQuotationDouble || initialQuotationToken.Type == SymToken.TType.ETypeQuotationSingle ); + + if ( initialQuotationToken.Value == aToken.Value ) + { + // Need to check for a closing quotation. The count in the cache + // should be odd (so that adding aToken makes a balanced set of + // quotation characters). + int count = iCache.CountByType( initialQuotationToken ); + int remainder = count % 2; + if ( remainder == 1 ) + { + // Odd number which means that the quotation is treated as complete + System.Diagnostics.Debug.Assert( aToken.Value == initialQuotationToken.Value ); + EnqueueNewOutputToken( aToken ); + + #region Try to group all of the text into a logical string + + // No sense in doing this unless we have more than 3 tokens + count = iCache.Count; + if ( count > 3 ) + { + // Assume we have the following string: + // "marker.h" + // + // This is actually represented as 5 tokens:- + // + // 0 ["] => EClassQuotation + // 1 [marker] => EClassQuotation + // 2 [.] => EClassQuotation + // 3 [h] => EClassQuotation + // 4 ["] => EClassQuotation + // + // We need to merge tokens at indicies 1, 2 and 3 into a + // single token. + + iCache.MergeAllTokensWithinRange( 1, count - 1, false, true ); + } + #endregion + + FlushCache(); + } + else + { + EnqueueNewOutputToken( aToken ); + } + } + else + { + // It wasn't the closing quotation, so just queue it up + EnqueueNewOutputToken( aToken ); + } + } + #endregion + } + else + { + #region Token is not a quotation... + // We'll try to combine the tokens as much as is possible. + if ( aToken.Class == SymToken.TClass.EClassNewLine ) + { + #region Handle new line during quotation... + // Checking for continuations... + // + // If the last token was not a backshash marker, then + // we should flush the cache (reset state). + SymToken previousToken = PreviousOutputToken; + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == @"\" ) + { + // The last token was an backslash. This means we + // are dealing with a similar case to examples 9 & 10. + + // Discard new line + previousToken.Class = SymToken.TClass.EClassContinuation; + } + else + { + // The last token wasn't a continuation character + // which means this is a "normal" EOL scenario. + // Just add the token and flush the cache. Mind you, this actually + // means the content is invalid. + EnqueueNewOutputToken( aToken ); + FlushCache(); + } + #endregion + } + else if ( aToken.Class == SymToken.TClass.EClassSymbol && aToken.Value == @"\" ) + { + SymToken previousToken = PreviousOutputToken; + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == @"\" ) + { + // Example 10 - an escaped backslash. Combine the + // previous token (a backslash) with the new token + // then join this new combined token with the previous. + // Phew. + MergeWithPreviousToken( aToken ); + PreviousOutputToken.Class = SymToken.TClass.EClassQuotation; + } + else + { + // This should not be combined until we know + // what the next character is. + EnqueueNewOutputToken( aToken ); + } + } + else + { + // Irrespective of what class the token is + // currently, we treat it as part of a quotation. + aToken.Class = SymToken.TClass.EClassQuotation; + + // If the previous character wasn't a quotation, + EnqueueNewOutputToken( aToken ); + } + #endregion + } + } + + private void ProcessTokenDuringComment( SymToken aToken ) + { + #region Comment examples + // // this is a comment + // /* this is also a comment */ + // // "This is another comment" + // // This is a comment with a continuation \ + // and here's the rest. + #endregion + + System.Diagnostics.Debug.Assert( iCache.Count > 0 ); + + if ( aToken.Class == SymToken.TClass.EClassSymbol && aToken.Value == "*" ) + { + #region Ensure asterisk is not merged with other comments + // The asterisk character is separated from + // the rest of the comment in order that we can + // ascertain when the end of a block comment has + // been reached. + EnqueueNewOutputToken( aToken ); + #endregion + } + else if ( aToken.Class == SymToken.TClass.EClassNewLine ) + { + #region New line during comment... + + // Checking for continuations... + SymToken previousToken = PreviousOutputToken; + if ( previousToken.Value == @"\" ) + { + // Discard new line + previousToken.Class = SymToken.TClass.EClassContinuation; + } + else + { + // If we're in a block comment, then we don't flush when we + // see a new line token. + SymToken firstToken = iCache.PeekHead; + EnqueueNewOutputToken( aToken ); + // + if ( firstToken.Type == SymToken.TType.ETypeCommentFullLine ) + { + // Flushing the cache resets the flags... + FlushCache(); + } + else if ( firstToken.Type == SymToken.TType.ETypeCommentBlock ) + { + // Don't end the comment until we see the closing block token. + } + } + #endregion + } + else if ( aToken.Class == SymToken.TClass.EClassSymbol && aToken.Value == "/" ) + { + #region Handle Closing Comment Block [ */ ] + // For ending a comment region, we must have at least one token + // already in the cache. + SymToken previousToken = PreviousOutputToken; + + // Check whether previous token was a "*" - we might be closing a block comment + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == "*" ) + { + // Check whether first token was an opening block + SymToken firstToken = iCache.PeekHead; + if ( firstToken.Type == SymToken.TType.ETypeCommentBlock && firstToken.Value == "/*" ) + { + // End of a block reached. Combine the closing "/" with the asterisk we already + // have in order to form a closing "*/" block token. + previousToken.Combine( aToken ); + previousToken.Class = SymToken.TClass.EClassComment; + previousToken.Type = SymToken.TType.ETypeCommentBlock; + + // No longer in a comment + InComment = false; + } + } + #endregion + } + else if ( aToken.Class == SymToken.TClass.EClassSymbol && aToken.Value == @"\" ) + { + #region Handle possible continuation during comment + // We treat the possible continuation character as a comment. + // If the next character that arrives is really a new line, then we change + // the class to continuation and handle the situation accordingly... + aToken.Class = SymToken.TClass.EClassComment; + EnqueueNewOutputToken( aToken ); + #endregion + } + else + { + aToken.Class = SymToken.TClass.EClassComment; + + if ( PreviousOutputToken.Class == SymToken.TClass.EClassContinuation ) + { + // In this scenario, we don't want to try to merge the specified token with the previous + // new line character, since new lines must be left intact. Just enque it, ensuring + // that the token class is suitably updated. + EnqueueNewOutputToken( aToken ); + } + else if ( iCache.Count == 1 ) + { + // We don't want to merge this token with the first token in the + // cache, or else we won't be able to successfully identify closing + // block comments + EnqueueNewOutputToken( aToken ); + } + else + { + System.Diagnostics.Debug.Assert( PreviousOutputToken.CombiningAllowed ); + ForceMergeWithPreviousToken( aToken ); + } + } + } + + private void ProcessTokenDuringPreProcessorDirective( SymToken aToken ) + { + #region PreProcessor examples + // 1) #_ pragma "This is invalid" + // + // 2) #\ + // pragma message("hello") + // + // 3) #\ + // define TEST + // + // 4) # \ + // define TEST + // + // 5) # \\ + // define INVALID_DEFINE + // + // 6) # pragma "This is a valid \ + // pragma which contains a quotation" + // + // 7) #define LOG_FUNC XLeaveDetector __instrument; \ + // TCleanupItem __cleanupItem(XLeaveDetector::LeaveOccurred, &__instrument); \ + // CleanupStack::PushL(__cleanupItem); + #endregion + + // NB. We only stay in "preprocessor mode" until we've identified + // the preprocessor type,i.e. the first non-whitespace word that + // appears after the initial hash sign. + bool validPreProcessorDirective = true; + int cacheCount = iCache.Count; + System.Diagnostics.Debug.Assert( cacheCount > 0 ); + System.Diagnostics.Debug.Assert( iCache.PeekHead.Class == SymToken.TClass.EClassPreProcessor && iCache.PeekHead.Value == "#" ); + + // Handle case 5 first of all. If the previous token was a possible + // continuation, then this next token must be a new line. If its not, + // then the PP statement is invalid. + SymToken previousToken = PreviousOutputToken; + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == @"\" ) + { + #region Handle new line character - checking for continuations + if ( aToken.Class == SymToken.TClass.EClassNewLine ) + { + previousToken.Class = SymToken.TClass.EClassContinuation; + } + else + { + // Borked. + validPreProcessorDirective = false; + } + #endregion + } + else + { + // The next token HAS to be an alphanumeric or then a whitespace. + // If its not, we're borked. + if ( aToken.Class == SymToken.TClass.EClassAlphaNumeric && aToken.Type == SymToken.TType.ETypeAlphaNumericNormal ) + { + #region Handle identified preprocessor command + // Token was okay - and we can switch back to normal mode + // now as we've grabbed our preprocessor command. + aToken.Class = SymToken.TClass.EClassPreProcessor; + EnqueueNewOutputToken( aToken ); + InPreProcessorDirective = false; + #endregion + } + else if ( aToken.Class == SymToken.TClass.EClassWhiteSpace ) + { + // Token is okay, but don't change mode yet. We still need an alphanumeric word. + } + else if ( aToken.Class == SymToken.TClass.EClassSymbol && aToken.Value == @"\" ) + { + #region Handle possible continuation + // Possibly a valid continuation character prior to seeing the first + // preprocessor command. For this to be really valid, we must only + // have seen whitespace between the first token and now. + bool everythingExceptFirstTokenIsWhiteSpace = iCache.CheckTokensAreOfClass( SymToken.TClass.EClassWhiteSpace, 1 ); + if ( everythingExceptFirstTokenIsWhiteSpace ) + { + // Could be a continuation character, but only if the next char is a new line + EnqueueNewOutputToken( aToken ); + } + else + { + // Borked - we've seen non-whitespace. Actually I don't think we can + // ever come here anyway + System.Diagnostics.Debug.Assert( false ); + validPreProcessorDirective = false; + } + #endregion + } + else + { + // Something else -> borked. + validPreProcessorDirective = false; + } + } + + #region Handle detection of invalid preprocessor line + if ( validPreProcessorDirective == false ) + { + // Token is not valid - this isn't a valid preprocessor directive. + // Reset state, update previous character so that its marked as a symbol + // and bail out. + InPreProcessorDirective = false; + iCache.PeekHead.Class = SymToken.TClass.EClassSymbol; + EnqueueNewOutputToken( aToken ); + } + #endregion + } + #endregion + + #region Internal cache manipulation methods + private void FlushCache() + { +#if SHOW_FLUSHED_TOKENS + StringBuilder debugListing = new StringBuilder(); + foreach( SymToken token in iCache ) + { + if ( token.Class == SymToken.TClass.EClassNewLine ) + { + debugListing.Append( "[NL] " ); + } + else + { + debugListing.Append( "[" + token.Value + "] "); + } + } + if ( debugListing.Length > 0 ) + { + System.Diagnostics.Debug.WriteLine( debugListing.ToString() ); + } +#endif + + foreach( SymToken token in iCache ) + { + if ( MastermindObservers != null ) + { + MastermindObservers( TEvent.EEventGroupTokenReady, token ); + } + } + + iCache.Reset(); + ResetState(); + } + + private SymToken NextInputToken() + { + SymToken ret = null; + // + lock( iLexedTokens ) + { + if ( iLexedTokens.Count > 0 ) + { + ret = iLexedTokens.Dequeue(); + } + } + // + return ret; + } + + private SymToken PreviousOutputToken + { + get + { + SymToken ret = SymToken.NullToken(); + if ( iCache.Count > 0 ) + { + SymToken previousToken = (SymToken) iCache.PeekTail; + ret = previousToken; + } + return ret; + } + } + + private void EnqueueNewOutputToken( SymToken aToken ) + { + if ( CheckIfStateChangeRequiredForEnqueuedToken( aToken ) == false ) + { + //System.Console.WriteLine( "Enqueue [" + aToken.Value + "]" ); + iCache.Append( aToken ); + } + } + + private void MergeWithPreviousTwoTokens( SymToken aNewToken, SymToken.TClass aNewClassType ) + { + System.Diagnostics.Debug.Assert( iCache.Count > 0 ); + + SymToken previousToken = iCache.PopTail(); + + // Combine it with the new token... + previousToken.Combine( aNewToken ); + previousToken.Class = aNewClassType; + + // And combine any previous previous token + MergeWithPreviousToken( previousToken ); + } + + private void MergeWithPreviousToken( SymToken aNewToken ) + { + if ( iCache.Count > 0 ) + { + if ( CheckIfStateChangeRequiredForEnqueuedToken( aNewToken ) == false ) + { + SymToken previousOutputToken = PreviousOutputToken; + previousOutputToken.Combine( aNewToken ); + } + } + else + { + EnqueueNewOutputToken( aNewToken ); + } + } + + private void ForceMergeWithPreviousToken( SymToken aNewToken ) + { + if ( iCache.Count > 0 ) + { + if ( CheckIfStateChangeRequiredForEnqueuedToken( aNewToken ) == false ) + { + SymToken previousOutputToken = PreviousOutputToken; + previousOutputToken.ForceCombine( aNewToken ); + } + } + else + { + EnqueueNewOutputToken( aNewToken ); + } + } + #endregion + + #region Internal state related methods + private void ResetState() + { + iFlags = TStateFlag.EStateFlagUnspecified; + } + + private bool CheckIfStateChangeRequiredForEnqueuedToken( SymToken aToken ) + { + // NB. This method is called before aToken has been enqueued + // or in the case of combining, before the token has been combined + // with any prior token. + bool tokenProcessed = false; + + if ( InQuotation ) + { + } + else if ( InComment ) + { + } + else if ( InPreProcessorDirective ) + { + } + else + { + if ( aToken.Class == SymToken.TClass.EClassQuotation ) + { + #region Handle start of quotation + if ( iCache.Count > 0 ) + { + // Check whether the previous symbol was a backslash. If it was + // then this must be an escaped " or ' character, in which case + // we don't change state. + SymToken previousToken = PreviousOutputToken; + + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == @"\" ) + { + // Last character was an escape marker. Combine it + // with the quotation + previousToken.Combine( aToken ); + + // Already handled the token + tokenProcessed = true; + } + else + { + // Really are starting a quotation. + FlushCache(); + InQuotation = true; + } + } + #endregion + } + else if ( aToken.Class == SymToken.TClass.EClassSymbol ) + { + if ( aToken.Value == "*" ) + { + #region Handle Opening comment block [ /* ] + if ( iCache.Count > 0 ) + { + SymToken previousToken = PreviousOutputToken; + // + if ( previousToken.Class == SymToken.TClass.EClassSymbol && previousToken.Value == "/" ) + { + // "/*" case + // + // In this scenario, in order to ensure that we do not + // flush the first character of our comment marker, we must + // dequeue the tail item, then flush, then enqueue. + SymToken tailToken = iCache.PopTail(); // -> this is the initial "/" that we pop... + FlushCache(); + + // Forward slash and asterisk are combined + tailToken.Combine( aToken ); + + // Mark the token as a full line comment + tailToken.Class = SymToken.TClass.EClassComment; + tailToken.Type = SymToken.TType.ETypeCommentBlock; + + // ...and re-added to the cache + iCache.Append( tailToken ); + + // aToken was already combined so we don't want the caller + // to add it twice. + tokenProcessed = true; + + // We're now in a full line comment. + InComment = true; + } + } + #endregion + } + else if ( aToken.Value == "/" ) + { + #region Handle Full-Line comment [ // ] + if ( iCache.Count > 0 ) + { + SymToken previousToken = PreviousOutputToken; + // + if ( previousToken.Value == aToken.Value ) + { + // "//" case + // + // In this scenario, in order to ensure that we do not + // flush the first character of our comment marker, we must + // dequeue the tail item, then flush, then enqueue. + SymToken tailToken = iCache.PopTail(); // -> this is the initial "/" that we pop... + FlushCache(); + + // Two forward slashes are combined into one. + tailToken.Combine( aToken ); + + // Mark the token as a full line comment + tailToken.Class = SymToken.TClass.EClassComment; + tailToken.Type = SymToken.TType.ETypeCommentFullLine; + + // ...and re-added to the cache + iCache.Append( tailToken ); + + // aToken was already combined so we don't want the caller + // to add it twice. + tokenProcessed = true; + + // We're now in a full line comment. + InComment = true; + } + } + #endregion + } + } + else if ( aToken.Class == SymToken.TClass.EClassPreProcessor ) + { + #region Handle start of preprocessor directive + // Preprocessor directives must only appear on a line + // after whitespace. If there was any non-whitespace + // characters before the preprocessor directive, then its illegal. + bool tokensAreAllWhiteSpace = iCache.CheckTokensAreOfEitherClass( SymToken.TClass.EClassWhiteSpace, SymToken.TClass.EClassNewLine ); + if ( aToken.Value == "#" && tokensAreAllWhiteSpace ) + { + // Starting a preprocess directive + FlushCache(); + InPreProcessorDirective = true; + } + #endregion + } + } + + return tokenProcessed; + } + + #endregion + + #region Internal state properties + private bool InQuotation + { + get + { + bool ret = ( ( iFlags & TStateFlag.EStateFlagInQuotation ) == TStateFlag.EStateFlagInQuotation ); + return ret; + } + set + { + if ( value ) + { + iFlags |= TStateFlag.EStateFlagInQuotation; + } + else + { + iFlags &= ~TStateFlag.EStateFlagInQuotation; + } + } + } + + private bool InComment + { + get + { + bool ret = ( ( iFlags & TStateFlag.EStateFlagInComment ) == TStateFlag.EStateFlagInComment ); + return ret; + } + set + { + if ( value ) + { + iFlags |= TStateFlag.EStateFlagInComment; + } + else + { + iFlags &= ~TStateFlag.EStateFlagInComment; + } + } + } + + private bool InPreProcessorDirective + { + get + { + bool ret = ( ( iFlags & TStateFlag.EStateFlagInPreProcessorDirective ) == TStateFlag.EStateFlagInPreProcessorDirective ); + return ret; + } + set + { + if ( value ) + { + iFlags |= TStateFlag.EStateFlagInPreProcessorDirective; + } + else + { + iFlags &= ~TStateFlag.EStateFlagInPreProcessorDirective; + } + } + } + #endregion + + #region Data members + private SymLexedTokens iLexedTokens = new SymLexedTokens(); + private SymGrouperMastermindCache iCache = new SymGrouperMastermindCache(); + private TStateFlag iFlags = TStateFlag.EStateFlagUnspecified; + #endregion + } +}