/// <summary> /// Parse tokens into Modifiers bit flags. /// </summary> public static Modifiers Parse(Parser parser, CodeObject parent) { // Search the parser's unused list for valid modifier tokens Modifiers modifiers = 0; bool hasSandwichedModifiers = false; bool hasConditionalModifiers = false; bool needsElseCondition = true; bool elseIsNotActive = false; bool foundEndIf = false; List <ParsedObject> unusedList = parser.Unused; // First, pre-scan the unused list to check for the special case of conditional directives // being used on the modifiers. Set a flag if we detect this for later processing below. for (int i = unusedList.Count - 1; i >= 0; --i) { ParsedObject parsedObject = unusedList[i]; if (parsedObject is Token) { // Abort if there's a blank line, or if we get a token that isn't a modifier Token token = (Token)parsedObject; if (token.NewLines > 1 || !IsModifier(token.Text)) { break; } if (foundEndIf) { hasSandwichedModifiers = true; } } else { // Abort if we get anything other than the expected #if/#else/#endif chain, or if // we have any comments before we hit the #endif (going backwards). CodeObject codeObject = ((UnusedCodeObject)parsedObject).CodeObject; if (codeObject is EndIfDirective) { if (foundEndIf || parsedObject.HasTrailingComments) { break; } foundEndIf = true; } else if (codeObject is ConditionalDirective) { if (!foundEndIf) { break; } if (codeObject is ElseDirective) { needsElseCondition = false; } else { if (((ConditionalDirective)codeObject).IsActive) { elseIsNotActive = true; } if (codeObject is IfDirective) { if (hasSandwichedModifiers) { hasConditionalModifiers = true; } break; } } } else { break; } } } // Now, reset and do the real parse scan bool postDirective = true; string declarationText = null; string declarationModifiersText = null; Token lastUnrecognizedToken = null; List <ConditionalDirective> inactiveConditions = new List <ConditionalDirective>(); for (int i = unusedList.Count - 1; i >= 0; --i) { ParsedObject parsedObject = unusedList[i]; if (parsedObject is Token) { // Check for modifier tokens Token token = (Token)unusedList[i]; Modifiers value; if (_nameToModifierMap.TryGetValue(token.Text, out value)) { modifiers |= value; parent.MoveFormatting(token); parent.MoveAllComments(token, true); unusedList.RemoveAt(i); // If we've passed the conditionals and have prefixed modifiers, add them // to the skipped text of all of the inactive conditions. if (!hasConditionalModifiers && inactiveConditions.Count > 0) { foreach (ConditionalDirective conditionalDirective in inactiveConditions) { conditionalDirective.SkippedText = AsString(value) + conditionalDirective.SkippedText; } } } else { // If the token isn't recognized, continue processing in case there are other unused tokens // that we do recognize as modifiers (there might be new modifiers added in later C# versions). // Also, keep track of the last unrecognized token so we can transfer any newlines to it. lastUnrecognizedToken = (Token)parsedObject; } } else if (hasConditionalModifiers) { // Check for compiler directives - specifically, we handle an #endif immediately // preceeding the parent declaration, allowing for a chain of conditional directives // used to change the modifiers on the declaration at compile-time. We have already // verified that we have a valid sequence (above), so we process conditional directives // backwards here until we get to the starting #if. UnusedCodeObject unused = (UnusedCodeObject)parsedObject; if (unused.CodeObject is CompilerDirective) { // Store any compiler directives as pre or post annotations on the parent CompilerDirective compilerDirective = (CompilerDirective)unused.CodeObject; if (compilerDirective is EndIfDirective) { declarationText = parent.AsString(); declarationModifiersText = AsString(modifiers); } else if (compilerDirective is ConditionalDirective) { ConditionalDirective conditionalDirective = (ConditionalDirective)compilerDirective; // Create an #else if none existed if (needsElseCondition) { ElseDirective elseDirective = new ElseDirective(); if (elseIsNotActive) { elseDirective.SkippedText = declarationText; inactiveConditions.Add(elseDirective); } else { postDirective = false; } parent.AttachAnnotation(elseDirective, elseIsNotActive ? AnnotationFlags.IsPostfix : AnnotationFlags.None, true); needsElseCondition = false; } // When we find the active condition, switch from post to pre if (conditionalDirective.IsActive) { postDirective = false; } else { // Update any skipped conditions to reflect the entire declaration Modifiers orderedModifiers = Parse(conditionalDirective.SkippedText + " " + declarationModifiersText); conditionalDirective.SkippedText = AsString(orderedModifiers) + declarationText; inactiveConditions.Add(conditionalDirective); } // Stop processing conditionals when we get to the #if if (compilerDirective is IfDirective) { hasConditionalModifiers = false; } } else { break; // For now, stop if we find any other directives } parent.AttachAnnotation(compilerDirective, postDirective ? AnnotationFlags.IsPostfix : AnnotationFlags.None, true); parent.MoveComments(unused.LastToken, true); unusedList.RemoveAt(i); } } else { break; } } // If we had any unrecognized tokens, they'll be emitted before the parent object, so if the last one // doesn't have any newlines, we need to give it the parent's newlines. if (lastUnrecognizedToken != null) { if (lastUnrecognizedToken.NewLines == 0) { lastUnrecognizedToken.NewLines = (ushort)parent.NewLines; } parent.NewLines = 0; } return(modifiers); }