// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnrealBuildTool
{
///
/// Flags indicating the state of each nested conditional block in a stack.
///
[Flags]
enum PreprocessorBranch
{
///
/// The current conditional branch is active.
///
Active = 0x01,
///
/// A branch within the current conditional block has been taken. Any #else/#elif blocks will not be taken.
///
Taken = 0x02,
///
/// The #else directive for this conditional block has been encountered. An #endif directive is expected next.
///
Complete = 0x04,
///
/// The first condition in this branch was an #if directive (as opposed to an #ifdef or #ifndef directive)
///
HasIfDirective = 0x10,
///
/// The first condition in this branch was an #if directive (as opposed to an #ifdef or #ifndef directive)
///
HasIfdefDirective = 0x20,
///
/// The first condition in this branch was an #ifndef directive (as opposed to an #ifdef or #if directive)
///
HasIfndefDirective = 0x40,
///
/// The branch has an #elif directive
///
HasElifDirective = 0x80,
}
///
/// Marshals access to the preprocessor state, ensuring that any changes are tracked within the active transform
///
class PreprocessorState
{
///
/// Stack of conditional branch states
///
List Branches = new List();
///
/// Mapping of name to macro definition
///
Dictionary NameToMacro = new Dictionary();
///
/// The current transform. Any queries or state modifications will be recorded in this.
///
PreprocessorTransform? Transform;
///
/// Enumerates the current branches. Do not use this for decision logic; any dependencies will not be tracked.
///
public IEnumerable CurrentBranches
{
get { return Branches; }
}
///
/// Enumerates the current macros. Do not use this for decision logic; any dependencies will not be tracked.
///
public IEnumerable CurrentMacros
{
get { return NameToMacro.Values; }
}
///
/// Initialize an empty preprocessor state
///
public PreprocessorState()
{
}
///
/// Duplicates another preprocessor state. Throws an exception if a transform is currently being built in the other state.
///
/// The preprocessor state to copy
public PreprocessorState(PreprocessorState Other)
{
if(Other.Transform != null)
{
throw new Exception("Unable to copy another preprocessor state while a transform is being built.");
}
Branches.AddRange(Other.Branches);
foreach(KeyValuePair Pair in Other.NameToMacro)
{
NameToMacro[Pair.Key] = Pair.Value;
}
}
///
/// Create a new transform object, and assign it to be current
///
/// The new transform object
public PreprocessorTransform BeginCapture()
{
Transform = new PreprocessorTransform();
return Transform;
}
///
/// Detach the current transform
///
/// The transform object
public PreprocessorTransform? EndCapture()
{
PreprocessorTransform? PrevTransform = Transform;
Transform = null;
return PrevTransform;
}
///
/// Sets a flag indicating that a #pragma once directive was encountered
///
public void MarkPragmaOnce()
{
if(Transform != null)
{
Transform.bHasPragmaOnce = true;
}
}
///
/// Set a macro to a specific definition
///
/// Macro definition
public void DefineMacro(PreprocessorMacro Macro)
{
NameToMacro[Macro.Name] = Macro;
if(Transform != null)
{
Transform.NewMacros[Macro.Name] = Macro;
}
}
///
/// Checks if a macro with the given name is defined
///
/// The macro name
/// True if the macro is defined
public bool IsMacroDefined(Identifier Name)
{
// Could account for the fact that we don't need the full definition later...
PreprocessorMacro? Macro;
return TryGetMacro(Name, out Macro);
}
///
/// Removes a macro definition
///
/// Name of the macro
public void UndefMacro(Identifier Name)
{
NameToMacro.Remove(Name);
if(Transform != null)
{
Transform.NewMacros[Name] = null;
}
}
///
/// Attemps to get the definition for a macro
///
/// Name of the macro
/// Receives the macro definition, or null if it's not defined
/// True if the macro is defined, otherwise false
public bool TryGetMacro(Identifier Name, [NotNullWhen(true)] out PreprocessorMacro? Macro)
{
bool bResult = NameToMacro.TryGetValue(Name, out Macro);
if(Transform != null && !Transform.NewMacros.ContainsKey(Name))
{
Transform.RequiredMacros[Name] = Macro;
}
return Macro != null;
}
///
/// Pushes the preprocessor branch onto the stack
///
/// The branch state
public void PushBranch(PreprocessorBranch Branch)
{
Branches.Add(Branch);
if(Transform != null)
{
Transform.NewBranches.Add(Branch);
}
}
///
/// Pops a preprocessor branch from the stack
///
/// The popped branch state
public PreprocessorBranch PopBranch()
{
PreprocessorBranch Branch;
if(!TryPopBranch(out Branch))
{
throw new InvalidOperationException("Branch stack is empty");
}
return Branch;
}
///
/// Attempts to pops a preprocessor branch from the stack
///
/// On success, receives the preprocessor branch state
/// True if a branch was active, else false
public bool TryPopBranch(out PreprocessorBranch Branch)
{
if(Branches.Count == 0)
{
Branch = 0;
return false;
}
else
{
Branch = Branches[Branches.Count - 1];
Branches.RemoveAt(Branches.Count - 1);
if(Transform != null)
{
if(Transform.NewBranches.Count > 0)
{
Transform.NewBranches.RemoveAt(Transform.NewBranches.Count - 1);
}
else
{
Transform.RequiredBranches.Add(Branch);
Transform.bRequireTopmostActive = null;
}
}
return true;
}
}
///
/// Determines if the branch that the preprocessor is in is active
///
/// True if the branch is active, false otherwise
public bool IsCurrentBranchActive()
{
bool bActive = (Branches.Count == 0 || Branches[Branches.Count - 1].HasFlag(PreprocessorBranch.Active));
if(Transform != null && Transform.NewBranches.Count == 0)
{
Transform.bRequireTopmostActive = bActive;
}
return bActive;
}
///
/// Determines if the given transform can apply to the current preprocessor state
///
/// The transform to test
/// True if the transform can be applied to the current state
public bool CanApply(PreprocessorTransform Transform)
{
// Check all the required branches match
for(int Idx = 0; Idx < Transform.RequiredBranches.Count; Idx++)
{
if(Branches[Branches.Count - Idx - 1] != Transform.RequiredBranches[Idx])
{
return false;
}
}
// Check the topmost branch is active
if(Transform.bRequireTopmostActive.HasValue)
{
bool bTopmostActive = (Branches.Count == Transform.RequiredBranches.Count || Branches[Branches.Count - Transform.RequiredBranches.Count - 1].HasFlag(PreprocessorBranch.Active));
if(Transform.bRequireTopmostActive.Value != bTopmostActive)
{
return false;
}
}
// Check all the required macros match
foreach(KeyValuePair RequiredPair in Transform.RequiredMacros)
{
PreprocessorMacro? Macro;
if(NameToMacro.TryGetValue(RequiredPair.Key, out Macro))
{
if(RequiredPair.Value == null || !Macro.IsEquivalentTo(RequiredPair.Value))
{
return false;
}
}
else
{
if(RequiredPair.Value != null)
{
return false;
}
}
}
return true;
}
///
/// Apply the given transform to the current preprocessor state
///
/// The transform to apply
/// True if the transform was applied to the current state
public bool TryToApply(PreprocessorTransform Transform)
{
if(!CanApply(Transform))
{
return false;
}
// Update the branch state
Branches.RemoveRange(Branches.Count - Transform.RequiredBranches.Count, Transform.RequiredBranches.Count);
Branches.AddRange(Transform.NewBranches);
// Update the macro definitions
foreach(KeyValuePair NewMacro in Transform.NewMacros)
{
if(NewMacro.Value == null)
{
NameToMacro.Remove(NewMacro.Key);
}
else
{
NameToMacro[NewMacro.Key] = NewMacro.Value;
}
}
return true;
}
}
}