// 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;
}
}
}