// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Runtime.CompilerServices; using EpicGames.BuildGraph.Expressions; namespace EpicGames.BuildGraph { /// /// Flags for an expression /// [Flags] public enum BgExprFlags { /// /// No flags set /// None = 0, /// /// Indicates that the expression should never be interned (ie. encoded to a separate fragment), and will always be duplicated in the bytecode. /// Trivial constants with short encodings typically have this flag set. /// NotInterned = 1, /// /// Always eagerly evaluate this expression /// Eager = 2, /// /// Force this expression to be stored in a separate fragment. Can help improves readability of the disassembly output. /// ForceFragment = 4, } /// /// Base class for computable expressions /// public abstract class BgExpr { /// /// Null value /// public static BgExpr Null { get; } = new BgNullExpr(); /// /// Flags for this expression /// public BgExprFlags Flags { get; } /// /// Constructor /// /// protected BgExpr(BgExprFlags flags) { Flags = flags; } /// /// Throws an exception /// /// Type of the expression to masquerade as /// Message to display /// Path to the source file declaring this diagnostic. Automatically set by the runtime. /// Line number in the source file declaring this diagnostic. Automatically set by the runtime. public static T Throw(BgString message, [CallerFilePath] string sourcePath = "", [CallerLineNumber] int sourceLine = 0) where T : BgExpr { return BgType.Wrap(new BgThrowExpr(sourcePath, sourceLine, message)); } /// /// Chooses between two values based on a condition /// /// Type of the expression to choose between /// Condition to check /// Value to return if the condition is true /// Value to return if the condition is false /// The chosen value public static T Choose(BgBool condition, T valueIfTrue, T valueIfFalse) where T : BgExpr { return BgType.Wrap(new BgChooseExpr(condition, valueIfTrue, valueIfFalse)); } /// /// Serialize the expression to an output stream /// /// Writer for output data public abstract void Write(BgBytecodeWriter writer); /// /// Convert the value of the expression to a string /// public abstract BgString ToBgString(); } class BgChooseExpr : BgExpr where T : BgExpr { public BgBool Condition { get; } public T ValueIfTrue { get; } public T ValueIfFalse { get; } public BgChooseExpr(BgBool condition, T valueIfTrue, T valueIfFalse) : base(BgExprFlags.None) { Condition = condition; ValueIfTrue = valueIfTrue; ValueIfFalse = valueIfFalse; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.Choose); writer.WriteExpr(Condition); writer.WriteExprAsFragment(ValueIfTrue); writer.WriteExprAsFragment(ValueIfFalse); } public override BgString ToBgString() => throw new InvalidOperationException(); } class BgThrowExpr : BgExpr { public string SourcePath { get; } public int SourceLine { get; } public BgString Message { get; } public BgThrowExpr(string sourcePath, int sourceLine, BgString message) : base(BgExprFlags.None) { SourcePath = sourcePath; SourceLine = sourceLine; Message = message; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.Throw); writer.WriteString(SourcePath); writer.WriteUnsignedInteger(SourceLine); writer.WriteExpr(Message); } public override BgString ToBgString() => Message; } class BgNullExpr : BgExpr { public BgNullExpr() : base(BgExprFlags.NotInterned) { } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.Null); } public override BgString ToBgString() => "null"; } /// /// Extension methods for expressions /// public static class BgExprExtensions { /// /// Chose an expression if a condition evaluates to true /// /// Type of the expression /// The expression value /// Condition to test /// Value to return if the condition is true /// New expression public static T If(this T expr, BgBool condition, T value) where T : BgExpr { return BgExpr.Choose(condition, value, expr); } /// /// Chose an expression if a condition evaluates to true /// /// Type of the expression /// The expression value /// Condition to test /// Function to apply to the expression if the condition is true /// New expression public static T If(this T expr, BgBool condition, Func func) where T : BgExpr { return BgExpr.Choose(condition, func(expr), expr); } } }