// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace EpicGames.BuildGraph.Expressions { /// /// Utility methods for lists /// public static class BgList { /// /// Gets an empty list of the given type /// /// /// public static BgList Empty() where T : BgExpr => BgList.Empty; /// /// Crates a list from an array of values /// /// Sequence to construct from /// public static BgList Create(IEnumerable items) => BgList.Create(items.Select(x => (BgString)x)); /// /// Crates a list from an array of values /// /// Sequence to construct from /// public static BgList Create(params string[] items) => BgList.Create(items.Select(x => (BgString)x)); /// /// Crates a list from an array of values /// /// Sequence to construct from /// public static BgList Create(IEnumerable items) where T : BgExpr => BgList.Create(items); /// /// Crates a list from an array of values /// /// Sequence to construct from /// public static BgList Create(params T[] items) where T : BgExpr => BgList.Create(items); /// /// Concatenates two lists together /// /// /// /// public static BgList Concat(BgList lhs, BgList rhs) where T : BgExpr => BgList.Concat(lhs, rhs); /// /// Concatenates two lists together /// /// /// public static BgList Concat(params BgList[] others) where T : BgExpr => BgList.Concat(others); } /// /// Abstract base class for expressions returning an immutable list of values /// [BgType(typeof(BgListType<>))] public abstract class BgList : BgExpr where T : BgExpr { /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] public Type ElementType => typeof(T); /// /// Constant representation of an empty list /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] public static BgList Empty { get; } = new BgListEmptyExpr(); /// /// Constructor /// /// Flags for this expression protected BgList(BgExprFlags flags) : base(flags) { } /// public T this[BgInt index] => BgType.Wrap(new BgListElementExpr(this, index)); /// /// Implicit conversion operator from a single value /// public static implicit operator BgList(T item) => Create(item); /// /// Implicit conversion operator from an array of values /// public static implicit operator BgList(T[] items) => Create(items); /// /// Implicit conversion operator from a list of values /// public static implicit operator BgList(List items) => Create(items); /// /// Crates a list from an array of values /// /// Sequence to construct from /// public static BgList Create(IEnumerable items) => Empty.Add(items); /// /// Crates a list from an array of values /// /// Sequence to construct from /// public static BgList Create(params T[] items) => Empty.Add(items); /// /// Concatenates two lists together /// /// /// /// public static BgList Concat(BgList lhs, BgList rhs) => new BgListConcatExpr(lhs, rhs); /// /// Concatenates two lists together /// /// /// public static BgList Concat(params BgList[] others) { BgList result = BgList.Empty; for (int idx = 0; idx < others.Length; idx++) { result = Concat(result, others[idx]); } return result; } /// /// Gets the length of this list /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] public BgInt Count => new BgListCountExpr(this); /// /// Adds an item to the end of the list, returning the new list /// /// Items to add /// New list containing the given items public BgList Add(T item) => new BgListPushExpr(this, item); /// /// Adds items to the end of the list, returning the new list /// /// Items to add /// New list containing the given items public BgList Add(params T[] items) => Add((IEnumerable)items); /// public BgList Add(BgList list) => Concat(this, list); /// public BgList Add(IEnumerable items) { BgList list = this; foreach (T item in items) { list = list.Add(item); } return list; } /// public BgList Union(params T[] items) => Union(Create(items)); /// public BgList Union(IEnumerable items) => Union(Create(items)); /// /// Creates the union of this list with another /// /// Items to add /// Union with the given items public BgList Union(BgList items) => new BgListUnionExpr(this, items); /// /// Removes the given items from this list /// /// Items to remove /// New list without the given items public BgList Except(BgList items) => new BgListExceptExpr(this, items); /// /// Removes any duplicate items from the list. The first item in the list is retained in its original order. /// /// New list containing the distinct items. public BgList Distinct() => new BgListDistinctExpr(this); /// public BgList Select(Func function) where TResult : BgExpr => new BgListSelectExpr(this, function); /// public BgList Select(BgFunc function) where TResult : BgExpr => new BgListSelectExpr(this, function); /// public BgList Where(Func predicate) => new BgListWhereExpr(this, predicate); /// public BgBool Contains(T item) => new BgListContainsExpr(this, item); /// /// Creates a lazily evaluated copy of this list, if it's not constant /// /// public BgList Lazy() => new BgListLazyEval(this); /// public override BgString ToBgString() => "{List}"; } /// /// Traits for a /// class BgListType : BgType> where T : BgExpr { /// public override BgList Constant(object value) { IEnumerable elements = (IEnumerable)value; List items = new List(); foreach (object element in elements) { items.Add(BgType.Constant(element)); } return new BgListConstantExpr(items); } // => new BgListConstantExpr(((IEnumerable)value).Select(x => BgType.Constant(x)).ToList()); /// public override BgList Wrap(BgExpr expr) => new BgListWrappedExpr(expr); } #region Expression classes class BgListEmptyExpr : BgList where T : BgExpr { public BgListEmptyExpr() : base(BgExprFlags.NotInterned) { } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListEmpty); } } class BgListConstantExpr : BgList where T : BgExpr { public IReadOnlyList Value { get; } public BgListConstantExpr(IEnumerable value) : base(BgExprFlags.NotInterned) { Value = value.ToArray(); } public override void Write(BgBytecodeWriter writer) { throw new NotImplementedException(); } } class BgListPushExpr : BgList where T : BgExpr { public BgList List { get; } public T Item { get; } public BgListPushExpr(BgList list, T item) : base(BgExprFlags.None) { List = list; Item = item; } public override void Write(BgBytecodeWriter writer) { if ((Item.Flags & BgExprFlags.Eager) != 0) { writer.WriteOpcode(BgOpcode.ListPush); writer.WriteExpr(List); writer.WriteExpr(Item); } else { writer.WriteOpcode(BgOpcode.ListPushLazy); writer.WriteExpr(List); writer.WriteExprAsFragment(Item); } } } class BgListCountExpr : BgInt where T : BgExpr { public BgList List { get; } public BgListCountExpr(BgList list) : base(BgExprFlags.None) { List = list; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListCount); writer.WriteExpr(List); } } class BgListConcatExpr : BgList where T : BgExpr { public BgList Lhs { get; } public BgList Rhs { get; } public BgListConcatExpr(BgList lhs, BgList rhs) : base(BgExprFlags.None) { Lhs = lhs; Rhs = rhs.Lazy(); } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListConcat); writer.WriteExpr(Lhs); writer.WriteExpr(Rhs); } } class BgListElementExpr : BgExpr where T : BgExpr { public BgList List { get; } public BgInt Index { get; } public BgListElementExpr(BgList list, BgInt index) : base(BgExprFlags.None) { List = list; Index = index; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListElement); writer.WriteExpr(List); writer.WriteExpr(Index); } public override BgString ToBgString() => throw new InvalidOperationException(); } class BgListDistinctExpr : BgList where T : BgExpr { public BgList Source { get; } public BgListDistinctExpr(BgList source) : base(BgExprFlags.None) { Source = source; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListDistinct); writer.WriteExpr(Source); } } class BgListSelectExpr : BgList where TIn : BgExpr where TOut : BgExpr { public BgList Source { get; } public BgFunc Function { get; } public BgListSelectExpr(BgList source, BgFunc function) : base(BgExprFlags.None) { Source = source; Function = function; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListSelect); writer.WriteExpr(Source); writer.WriteExprAsFragment(Function.Body); } } class BgListWhereExpr : BgList where T : BgExpr { public BgList Source { get; } public BgFunc Predicate { get; } public BgListWhereExpr(BgList source, BgFunc predicate) : base(BgExprFlags.None) { Source = source; Predicate = predicate; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListWhere); writer.WriteExpr(Source); writer.WriteExprAsFragment(Predicate.Body); } } class BgListUnionExpr : BgList where T : BgExpr { public BgList Input { get; } public BgList Other { get; } public BgListUnionExpr(BgList input, BgList other) : base(BgExprFlags.None) { Input = input; Other = other.Lazy(); } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListUnion); writer.WriteExpr(Input); writer.WriteExpr(Other); } } class BgListExceptExpr : BgList where T : BgExpr { public BgList Input { get; } public BgList Other { get; } public BgListExceptExpr(BgList input, BgList other) : base(BgExprFlags.None) { Input = input; Other = other; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListExcept); writer.WriteExpr(Input); writer.WriteExpr(Other); } } class BgListContainsExpr : BgBool where T : BgExpr { public BgList List { get; } public T Item { get; } public BgListContainsExpr(BgList list, T item) : base(BgExprFlags.None) { List = list; Item = item; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListContains); writer.WriteExpr(List); writer.WriteExpr(Item); } } class BgListLazyEval : BgList where T : BgExpr { BgList List { get; } public BgListLazyEval(BgList list) : base(BgExprFlags.None) { List = list; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListLazy); writer.WriteExprAsFragment(List); } } class BgListWrappedExpr : BgList where T : BgExpr { BgExpr Expr { get; } public BgListWrappedExpr(BgExpr expr) : base(expr.Flags) { Expr = expr; } public override void Write(BgBytecodeWriter writer) => Expr.Write(writer); } #endregion /// /// A list option expression /// public class BgListOption : BgList { /// /// Name of the option /// public BgString Name { get; } /// /// Label to display next to the option /// public BgString? Label { get; } /// /// Help text to display for the user /// public BgString? Description { get; } /// /// Style for this list box /// public BgEnum Style { get; } /// /// Default value for the option /// public BgString? DefaultValue { get; set; } /// /// List of values to choose from /// public BgList? Values { get; set; } /// /// Matching list of descriptions for each value /// public BgList? ValueDescriptions { get; set; } /// /// Constructor /// public BgListOption(string name, BgString? description = null, BgString? defaultValue = null, BgListOptionStyle style = BgListOptionStyle.CheckList, BgList? values = null, BgList? valueDescriptions = null, BgString? label = null) : base(BgExprFlags.None) { Name = name; Label = label; Description = description; DefaultValue = defaultValue; Style = style; Values = values; ValueDescriptions = valueDescriptions; } /// public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.ListOption); writer.WriteExpr(CreateOptionsObject()); } BgObject CreateOptionsObject() { BgObject option = BgObject.Empty; option = option.Set(x => x.Name, Name); if (Label is not null) { option = option.Set(x => x.Label, Label); } if (Description is not null) { option = option.Set(x => x.Description, Description); } if (DefaultValue is not null) { option = option.Set(x => x.DefaultValue, DefaultValue); } if (Style is not null) { option = option.Set(x => x.Style, Style); } if (Values is not null) { option = option.Set(x => x.Values, Values); } if (ValueDescriptions is not null) { option = option.Set(x => x.ValueDescriptions, ValueDescriptions); } return option; } } }