2020-11-23 13:33:12 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
using System.Text ;
2023-05-30 18:38:07 -04:00
using EpicGames.Core ;
2020-11-23 13:33:12 -04:00
namespace UnrealBuildTool
{
/// <summary>
/// Possible types of source file markup
/// </summary>
enum SourceFileMarkupType
{
/// <summary>
/// A span of tokens which are not preprocessor directives
/// </summary>
Text ,
/// <summary>
/// An #include directive
/// </summary>
Include ,
/// <summary>
/// A #define directive
/// </summary>
Define ,
/// <summary>
/// An #undef directive
/// </summary>
Undef ,
/// <summary>
/// An #if directive
/// </summary>
If ,
/// <summary>
/// An #ifdef directive
/// </summary>
Ifdef ,
/// <summary>
/// An #ifndef directive
/// </summary>
Ifndef ,
/// <summary>
/// An #elif directive
/// </summary>
Elif ,
/// <summary>
/// An #else directive
/// </summary>
Else ,
/// <summary>
/// An #endif directive
/// </summary>
Endif ,
/// <summary>
/// A #pragma directive
/// </summary>
Pragma ,
/// <summary>
/// An #error directive
/// </summary>
Error ,
/// <summary>
/// A #warning directive
/// </summary>
Warning ,
/// <summary>
/// An empty '#' on a line of its own
/// </summary>
Empty ,
/// <summary>
/// Some other directive
/// </summary>
OtherDirective
}
/// <summary>
/// Base class for an annotated section of a source file
/// </summary>
[Serializable]
class SourceFileMarkup
{
/// <summary>
/// The directive corresponding to this markup
/// </summary>
2023-03-10 12:35:47 -05:00
public readonly SourceFileMarkupType Type ;
2020-11-23 13:33:12 -04:00
/// <summary>
/// The one-based line number of this markup
/// </summary>
2023-03-10 12:35:47 -05:00
public readonly int LineNumber ;
2020-11-23 13:33:12 -04:00
/// <summary>
/// The tokens parsed for this markup. Set for directives.
/// </summary>
2023-03-10 12:35:47 -05:00
public readonly List < Token > ? Tokens ;
2020-11-23 13:33:12 -04:00
/// <summary>
/// Construct the annotation with the given range
/// </summary>
2023-03-10 12:35:47 -05:00
/// <param name="type">The type of this directive</param>
/// <param name="lineNumber">The line number of this markup</param>
/// <param name="tokens">List of tokens</param>
public SourceFileMarkup ( SourceFileMarkupType type , int lineNumber , List < Token > ? tokens )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
Type = type ;
LineNumber = lineNumber ;
Tokens = tokens ;
2020-11-23 13:33:12 -04:00
}
/// <summary>
/// Constructs a markup object using data read from an archive
/// </summary>
2023-03-10 12:35:47 -05:00
/// <param name="reader">The reader to deserialize from</param>
public SourceFileMarkup ( BinaryArchiveReader reader )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
Type = ( SourceFileMarkupType ) reader . ReadByte ( ) ;
LineNumber = reader . ReadInt ( ) ;
Tokens = reader . ReadList ( ( ) = > reader . ReadToken ( ) ) ;
2020-11-23 13:33:12 -04:00
}
/// <summary>
/// Serializes this object to a binary archive
/// </summary>
2023-03-10 12:35:47 -05:00
/// <param name="writer">Writer to serialize to</param>
public void Write ( BinaryArchiveWriter writer )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
writer . WriteByte ( ( byte ) Type ) ;
writer . WriteInt ( LineNumber ) ;
writer . WriteList ( Tokens , x = > writer . WriteToken ( x ) ) ;
2020-11-23 13:33:12 -04:00
}
/// <summary>
/// Determines if this markup indicates a conditional preprocessor directive
/// </summary>
/// <returns>True if this object is a conditional preprocessor directive</returns>
public bool IsConditionalPreprocessorDirective ( )
{
2023-03-10 12:35:47 -05:00
return Type switch
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
SourceFileMarkupType . If or SourceFileMarkupType . Ifdef or SourceFileMarkupType . Ifndef or SourceFileMarkupType . Elif or SourceFileMarkupType . Else or SourceFileMarkupType . Endif = > true ,
_ = > false ,
} ;
2020-11-23 13:33:12 -04:00
}
/// <summary>
/// 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.
/// </summary>
public int GetConditionDepthDelta ( )
{
2023-03-10 12:35:47 -05:00
if ( Type = = SourceFileMarkupType . If | | Type = = SourceFileMarkupType . Ifdef | | Type = = SourceFileMarkupType . Ifndef )
2020-11-23 13:33:12 -04:00
{
return + 1 ;
}
2023-03-10 12:35:47 -05:00
else if ( Type = = SourceFileMarkupType . Endif )
2020-11-23 13:33:12 -04:00
{
return - 1 ;
}
else
{
return 0 ;
}
}
/// <summary>
/// Generate a string describing this annotation
/// </summary>
/// <returns>String representation for debugging</returns>
public override string ToString ( )
{
2023-03-10 12:35:47 -05:00
StringBuilder result = new ( ) ;
result . AppendFormat ( "[{0}] " , LineNumber ) ;
2020-11-23 13:33:12 -04:00
2023-03-10 12:35:47 -05:00
if ( Type = = SourceFileMarkupType . Text )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
result . Append ( "..." ) ;
2020-11-23 13:33:12 -04:00
}
else
{
2023-03-10 12:35:47 -05:00
result . Append ( '#' ) ;
if ( Type ! = SourceFileMarkupType . OtherDirective )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
result . Append ( Type . ToString ( ) . ToLowerInvariant ( ) ) ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
if ( Tokens ! = null & & Tokens . Count > 0 )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
result . Append ( ' ' ) ;
Token . Format ( Tokens , result ) ;
2020-11-23 13:33:12 -04:00
}
}
2023-03-10 12:35:47 -05:00
return result . ToString ( ) ;
2020-11-23 13:33:12 -04:00
}
/// <summary>
/// Create markup for the given file
/// </summary>
2023-03-10 12:35:47 -05:00
/// <param name="reader">Reader for tokens in the file</param>
2020-11-23 13:33:12 -04:00
/// <returns>Array of markup objects which split up the given text buffer</returns>
2023-03-10 12:35:47 -05:00
public static SourceFileMarkup [ ] Parse ( TokenReader reader )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
List < SourceFileMarkup > markup = new ( ) ;
if ( reader . MoveNext ( ) )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
bool moveNext = true ;
while ( moveNext )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
int startLineNumber = reader . LineNumber ;
if ( reader . Current . Type = = TokenType . Hash )
2020-11-23 13:33:12 -04:00
{
// Create the appropriate markup object for the directive
2023-03-10 12:35:47 -05:00
SourceFileMarkupType type = SourceFileMarkupType . OtherDirective ;
if ( reader . MoveNext ( ) )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
if ( reader . Current . Type = = TokenType . Identifier )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
Identifier directive = reader . Current . Identifier ! ;
if ( directive = = Identifiers . Include )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Include ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Define )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Define ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Undef )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Undef ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . If )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . If ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Ifdef )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Ifdef ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Ifndef )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Ifndef ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Elif )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Elif ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Else )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Else ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Endif )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Endif ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Pragma )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Pragma ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Error )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Error ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( directive = = Identifiers . Warning )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Warning ;
2020-11-23 13:33:12 -04:00
}
}
2023-03-10 12:35:47 -05:00
else if ( reader . Current . Type = = TokenType . Newline )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
type = SourceFileMarkupType . Empty ;
2020-11-23 13:33:12 -04:00
}
}
// Create the token list
2023-03-10 12:35:47 -05:00
List < Token > tokens = new ( ) ;
if ( type = = SourceFileMarkupType . OtherDirective )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
tokens . Add ( reader . Current ) ;
2020-11-23 13:33:12 -04:00
}
// Read the first token
2023-03-10 12:35:47 -05:00
if ( type = = SourceFileMarkupType . Empty )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
moveNext = true ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( type = = SourceFileMarkupType . Include )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
moveNext = reader . MoveNextIncludePath ( ) ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( type = = SourceFileMarkupType . Error | | type = = SourceFileMarkupType . Warning )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
moveNext = reader . MoveNextTokenString ( ) ;
2020-11-23 13:33:12 -04:00
}
else
{
2023-03-10 12:35:47 -05:00
moveNext = reader . MoveNext ( ) ;
2020-11-23 13:33:12 -04:00
}
// Read the rest of the tokens
2023-03-10 12:35:47 -05:00
while ( moveNext & & reader . Current . Type ! = TokenType . Newline )
2020-11-23 13:33:12 -04:00
{
2023-03-10 12:35:47 -05:00
tokens . Add ( reader . Current ) ;
moveNext = reader . MoveNext ( ) ;
2020-11-23 13:33:12 -04:00
}
// Create the markup
2023-03-10 12:35:47 -05:00
markup . Add ( new SourceFileMarkup ( type , startLineNumber , tokens ) ) ;
2020-11-23 13:33:12 -04:00
// Move to the next token
2023-03-10 12:35:47 -05:00
moveNext = reader . MoveNext ( ) ;
2020-11-23 13:33:12 -04:00
}
2023-03-10 12:35:47 -05:00
else if ( reader . Current . Type ! = TokenType . Newline )
2020-11-23 13:33:12 -04:00
{
// Create the new fragment
2023-03-10 12:35:47 -05:00
markup . Add ( new SourceFileMarkup ( SourceFileMarkupType . Text , startLineNumber , null ) ) ;
2020-11-23 13:33:12 -04:00
// Move to the next directive
2023-03-10 12:35:47 -05:00
moveNext = reader . MoveToNextDirective ( ) ;
2020-11-23 13:33:12 -04:00
}
else
{
// Skip the empty line
2023-03-10 12:35:47 -05:00
moveNext = reader . MoveNext ( ) ;
2020-11-23 13:33:12 -04:00
}
}
}
2023-03-10 12:35:47 -05:00
return markup . ToArray ( ) ;
2020-11-23 13:33:12 -04:00
}
}
}