// Copyright Epic Games, Inc. All Rights Reserved. using System; namespace EpicGames.BuildGraph.Expressions { /// /// Abstract base class for function expressions /// public abstract class BgFunc { /// /// The function expression. /// public BgExpr Body { get; } /// /// Constructor /// /// Expression to be evaluated protected BgFunc(BgExpr body) { // Evaluate the body twice, and determine which expressions are captured outside this context // Lexical scope can include those expressions too - if they are declared locally, they will not be duplicated Body = body; } } /// /// A function taking no arguments /// /// Result type public class BgFunc : BgFunc where TResult : BgExpr { /// /// Constructor /// /// Function to construct from public BgFunc(Func func) : base(func()) { } /// /// Implicit conversion from a regular C# function /// public static implicit operator BgFunc(Func func) => new BgFunc(func); /// /// Call the function with the given arguments /// /// Result from the function public TResult Call() => BgType.Wrap(new BgFuncCallExpr(this)); } /// /// A function taking a single argument /// /// Type of the function argument /// Result type public class BgFunc : BgFunc where TArg : BgExpr where TResult : BgExpr { /// /// Constructor /// /// Function to construct from public BgFunc(Func func) : base(func(BgType.Wrap(new BgFuncArgumentExpr(0)))) { } /// /// Implicit conversion from a regular C# function /// public static implicit operator BgFunc(Func func) => new BgFunc(func); /// /// Call the function with the given arguments /// /// Argument to pass to the function /// Result from the function public TResult Call(TArg arg) => BgType.Wrap(new BgFuncCallExpr(this, arg)); } /// /// A function taking two arguments /// /// Type of the first function argument /// Type of the second function argument /// Result type public class BgFunc : BgFunc where TArg1 : BgExpr where TArg2 : BgExpr where TResult : BgExpr { /// /// Constructor /// /// Function to construct from public BgFunc(Func func) : base(func(BgType.Wrap(new BgFuncArgumentExpr(0)), BgType.Wrap(new BgFuncArgumentExpr(1)))) { } /// /// Implicit conversion from a regular C# function /// public static implicit operator BgFunc(Func func) => new BgFunc(func); /// /// Call the function with the given arguments /// /// First argument to the function /// Second argument to the function /// Result from the function public TResult Call(TArg1 arg1, TArg2 arg2) => BgType.Wrap(new BgFuncCallExpr(this, arg1, arg2)); } #region Expression classes class BgFuncArgumentExpr : BgExpr { uint Index { get; } public BgFuncArgumentExpr(uint index) : base(BgExprFlags.NotInterned) { Index = index; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.Argument); writer.WriteUnsignedInteger(Index); } public override BgString ToBgString() => "{Function}"; } class BgFuncCallExpr : BgExpr where T : BgExpr { readonly BgFunc _function; readonly BgExpr[] _arguments; public BgFuncCallExpr(BgFunc function, params BgExpr[] arguments) : base(BgExprFlags.None) { _function = function; _arguments = arguments; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.Call); writer.WriteExprAsFragment(_function.Body); writer.WriteUnsignedInteger((uint)_arguments.Length); foreach (BgExpr argument in _arguments) { argument.Write(writer); } } public override BgString ToBgString() => throw new InvalidOperationException(); } #endregion }