// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using IncludeTool.Support; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace IncludeTool { /// /// Possible types of source file markup /// enum PreprocessorMarkupType { /// /// A span of tokens which are not preprocessor directives /// Text, /// /// An #include directive /// Include, /// /// A #define directive /// Define, /// /// An #undef directive /// Undef, /// /// An #if directive /// If, /// /// An #ifdef directive /// Ifdef, /// /// An #ifndef directive /// Ifndef, /// /// An #elif directive /// Elif, /// /// An #else directive /// Else, /// /// An #endif directive /// Endif, /// /// A #pragma directive /// Pragma, /// /// An #error directive /// Error, /// /// An empty '#' on a line of its own /// Empty, /// /// Macros which decorate an include directive (eg. THIRD_PARTY_INCLUDES_START, THIRD_PARTY_INCLUDES_END) /// IncludeMarkup, /// /// Some other directive /// OtherDirective } /// /// Base class for an annotated section of a source file /// [Serializable] class PreprocessorMarkup { /// /// The directive corresponding to this markup /// public PreprocessorMarkupType Type; /// /// Whether this markup is within an active preprocessor condition /// public bool IsActive; /// /// The start of the annotated text /// public TextLocation Location; /// /// The end of the annotated text /// public TextLocation EndLocation; /// /// The tokens parsed for this markup. Set for directives. /// public List Tokens; /// /// For #include directives, specifies the included file that it resolves to /// public SourceFile IncludedFile; /// /// The output list of included files that this should be replaced with /// public List OutputIncludedFiles; /// /// Construct the annotation with the given range /// /// The type of this directive /// The start location /// The end location /// List of tokens public PreprocessorMarkup(PreprocessorMarkupType Directive, TextLocation Location, TextLocation EndLocation, List Tokens) { this.Type = Directive; this.Location = Location; this.EndLocation = EndLocation; this.Tokens = Tokens; } /// /// Determines if this markup is for an inline include /// /// True if the markup is including an inline file public bool IsInlineInclude() { return Type == PreprocessorMarkupType.Include && IncludedFile != null && (IncludedFile.Flags & SourceFileFlags.Inline) != 0; } /// /// Determines if this markup indicates a preprocessor directive /// /// True if this object marks a preprocessor directive public bool IsPreprocessorDirective() { return Type != PreprocessorMarkupType.Text; } /// /// Determines if this markup indicates a conditional preprocessor directive /// /// True if this object is a conditional preprocessor directive public bool IsConditionalPreprocessorDirective() { switch(Type) { case PreprocessorMarkupType.If: case PreprocessorMarkupType.Ifdef: case PreprocessorMarkupType.Ifndef: case PreprocessorMarkupType.Elif: case PreprocessorMarkupType.Else: case PreprocessorMarkupType.Endif: return true; } return false; } /// /// How this condition modifies the condition depth. Opening "if" statements have a value of +1, "endif" statements have a value of -1, and "else" statements have a value of 0. /// public int GetConditionDepthDelta() { if(Type == PreprocessorMarkupType.If || Type == PreprocessorMarkupType.Ifdef || Type == PreprocessorMarkupType.Ifndef) { return +1; } else if(Type == PreprocessorMarkupType.Endif) { return -1; } else { return 0; } } /// /// Update the target file which an include resolves to /// /// The file being included public bool SetIncludedFile(SourceFile NewIncludedFile) { // Process the included file, and make sure it matches the include directive if(IncludedFile != NewIncludedFile) { SourceFile OldIncludedFile = Interlocked.CompareExchange(ref IncludedFile, NewIncludedFile, null); if(OldIncludedFile == null) { OutputIncludedFiles = new List { NewIncludedFile }; } else if(OldIncludedFile != NewIncludedFile) { return false; } } return true; } /// /// Generate a string describing this annotation /// /// String representation for debugging public override string ToString() { string Result = String.Format("({0})", Location.ToString()); if (Type != PreprocessorMarkupType.Text) { Result += ": #"; if(Type != PreprocessorMarkupType.OtherDirective) { Result += Type.ToString().ToLower() + " "; } if(Tokens != null) { Result += Token.Format(Tokens); } } return Result; } /// /// Create markup for the given text buffer /// /// Reader for token objects /// Array of markup objects which split up the given text buffer public static PreprocessorMarkup[] ParseArray(TextBuffer Text) { TokenReader Reader = new TokenReader(Text, TextLocation.Origin); List Markup = new List(); if(Reader.MoveNext()) { bool bMoveNext = true; while(bMoveNext) { TextLocation StartLocation = Reader.TokenWhitespaceLocation; if (Reader.Current.Text == "#") { // Create the appropriate markup object for the directive PreprocessorMarkupType Type = PreprocessorMarkupType.OtherDirective; if(Reader.MoveNext()) { switch (Reader.Current.Text) { case "include": Type = PreprocessorMarkupType.Include; break; case "define": Type = PreprocessorMarkupType.Define; break; case "undef": Type = PreprocessorMarkupType.Undef; break; case "if": Type = PreprocessorMarkupType.If; break; case "ifdef": Type = PreprocessorMarkupType.Ifdef; break; case "ifndef": Type = PreprocessorMarkupType.Ifndef; break; case "elif": Type = PreprocessorMarkupType.Elif; break; case "else": Type = PreprocessorMarkupType.Else; break; case "endif": Type = PreprocessorMarkupType.Endif; break; case "pragma": Type = PreprocessorMarkupType.Pragma; break; case "error": Type = PreprocessorMarkupType.Error; break; case "\n": Type = PreprocessorMarkupType.Empty; break; } } // Create the token list List Tokens = new List(); if(Type == PreprocessorMarkupType.OtherDirective) { Tokens.Add(Reader.Current); } // Read the first token if(Type == PreprocessorMarkupType.Empty) { bMoveNext = true; } else if(Type == PreprocessorMarkupType.Include) { bMoveNext = Reader.MoveNext(TokenReaderContext.IncludeDirective); } else if(Type == PreprocessorMarkupType.Error) { bMoveNext = Reader.MoveNext(TokenReaderContext.TokenString); } else { bMoveNext = Reader.MoveNext(); } // Read the rest of the tokens while(bMoveNext && Reader.Current.Text != "\n") { Tokens.Add(Reader.Current); bMoveNext = Reader.MoveNext(); } // Create the markup Markup.Add(new PreprocessorMarkup(Type, StartLocation, Reader.TokenEndLocation, Tokens)); // Move to the next token bMoveNext = Reader.MoveNext(); } else if (Reader.Current.Text != "\n") { // Skip over as many parser tokens as possible before the next directive (or EOF) bMoveNext = Reader.MoveToNextLine(); while(bMoveNext && Reader.Current.Text != "#") { bMoveNext = Reader.MoveToNextLine(); } // Create the new fragment PreprocessorMarkupType Type = IsIncludeMarkup(Text, StartLocation, Reader.TokenLocation)? PreprocessorMarkupType.IncludeMarkup : PreprocessorMarkupType.Text; Markup.Add(new PreprocessorMarkup(Type, StartLocation, Reader.TokenLocation, null)); } else { // Skip the empty line bMoveNext = Reader.MoveNext(); } } } return Markup.ToArray(); } /// /// Checks whether the given block of text is an include decoration /// /// The text buffer to read from /// Start of the block of text to check /// End of the block of text to check /// True if the region just consists of #include markup macros public static bool IsIncludeMarkup(TextBuffer Text, TextLocation StartLocation, TextLocation EndLocation) { TokenReader Reader = new TokenReader(Text, StartLocation, EndLocation); while(Reader.MoveNext(TokenReaderContext.IgnoreNewlines)) { if(Reader.Current.Text == "MONOLITHIC_HEADER_BOILERPLATE" && Reader.MoveNext() && Reader.Current.Text == "(" && Reader.MoveNext() && Reader.Current.Text == ")") { continue; } if(Reader.Current.Text == "THIRD_PARTY_INCLUDES_START" || Reader.Current.Text == "THIRD_PARTY_INCLUDES_END") { continue; } if(Reader.Current.Text == "OPENCV_INCLUDES_START" || Reader.Current.Text == "OPENCV_INCLUDES_END") { continue; } if(Reader.Current.Text == "PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS" || Reader.Current.Text == "PRAGMA_ENABLE_SHADOW_VARIABLE_WARNINGS") { continue; } if(Reader.Current.Text == "PRAGMA_DISABLE_DEPRECATION_WARNINGS" || Reader.Current.Text == "PRAGMA_ENABLE_DEPRECATION_WARNINGS") { continue; } return false; } return true; } } }