// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; namespace EpicGames.BuildGraph.Expressions { /// /// Abstract base class for expressions returning a string value /// [BgType(typeof(BgStringType))] public abstract class BgString : BgExpr { /// /// Constant value for an empty string /// public static BgString Empty { get; } = new BgStringEmptyExpr(); /// /// Constructor /// /// Flags for this expression protected BgString(BgExprFlags flags) : base(flags) { } /// /// Implicit conversion from a regular string type /// public static implicit operator BgString(string value) { return new BgStringConstantExpr(value); } /// public static BgBool Equals(BgString lhs, BgString rhs, StringComparison comparison = StringComparison.CurrentCulture) => Compare(lhs, rhs, comparison) == 0; /// public static BgInt Compare(BgString lhs, BgString rhs, StringComparison comparison = StringComparison.CurrentCulture) => new BgStringCompareExpr(lhs, rhs, comparison); /// public static BgString Join(BgString separator, BgList values) => new BgStringJoinExpr(separator, values); /// public BgList Split(BgString separator) => new BgStringSplitExpr(this, separator); /// public static BgString Format(string format, params BgExpr[] args) => new BgStringFormatExpr(format, args); /// public static BgString operator +(BgString lhs, BgString rhs) => new BgStringConcatExpr(lhs, rhs); /// public static BgBool operator ==(BgString lhs, BgString rhs) => Equals(lhs, rhs); /// public static BgBool operator !=(BgString lhs, BgString rhs) => !Equals(lhs, rhs); /// /// Appens another string to this one /// public BgString Append(BgString other) => this + other; /// public BgBool Match(BgString pattern) => new BgStringMatchExpr(this, pattern); /// public BgString Replace(BgString pattern, BgString replace) => new BgStringReplaceExpr(this, pattern, replace); /// public override bool Equals(object? obj) => throw new InvalidOperationException(); /// public override int GetHashCode() => throw new InvalidOperationException(); /// public override BgString ToBgString() => this; } /// /// Traits implementation for /// class BgStringType : BgType { /// public override BgString Constant(object value) => new BgStringConstantExpr((string)value); /// public override BgString Wrap(BgExpr expr) => new BgStringWrappedExpr(expr); } #region Expression classes class BgStringCompareExpr : BgInt { public BgString Lhs { get; } public BgString Rhs { get; } public StringComparison Comparison { get; } public BgStringCompareExpr(BgString lhs, BgString rhs, StringComparison comparison) : base(lhs.Flags & rhs.Flags & BgExprFlags.Eager) { Lhs = lhs; Rhs = rhs; Comparison = comparison; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrCompare); writer.WriteExpr(Lhs); writer.WriteExpr(Rhs); writer.WriteUnsignedInteger((uint)Comparison); } public override string ToString() => $"Compare({Lhs}, {Rhs})"; } class BgStringConcatExpr : BgString { public BgString Lhs { get; } public BgString Rhs { get; } public BgStringConcatExpr(BgString lhs, BgString rhs) : base(lhs.Flags & rhs.Flags & BgExprFlags.Eager) { Lhs = lhs; Rhs = rhs; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrConcat); writer.WriteExpr(Lhs); writer.WriteExpr(Rhs); } public override string ToString() => $"Concat({Lhs}, {Rhs})"; } class BgStringJoinExpr : BgString { public BgString Separator { get; } public BgList Values { get; } public BgStringJoinExpr(BgString separator, BgList values) : base(BgExprFlags.None) { Separator = separator; Values = values; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrJoin); writer.WriteExpr(Separator); writer.WriteExpr(Values); } public override string ToString() => $"Join({Separator}, {Values})"; } class BgStringSplitExpr : BgList { public BgString Source { get; } public BgString Separator { get; } public BgStringSplitExpr(BgString source, BgString separator) : base(source.Flags & separator.Flags & BgExprFlags.Eager) { Source = source; Separator = separator; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrSplit); writer.WriteExpr(Source); writer.WriteExpr(Separator); } public override string ToString() => $"Split({Source}, {Separator})"; } class BgStringMatchExpr : BgBool { public BgString Input { get; } public BgString Pattern { get; } public BgStringMatchExpr(BgString input, BgString pattern) : base(input.Flags & BgExprFlags.Eager) { Input = input; Pattern = pattern; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrMatch); writer.WriteExpr(Input); writer.WriteExpr(Pattern); } } class BgStringReplaceExpr : BgString { public BgString Input { get; } public BgString Pattern { get; } public BgString Replacement { get; } public BgStringReplaceExpr(BgString input, BgString pattern, BgString replacement) : base(input.Flags & replacement.Flags & BgExprFlags.Eager) { Input = input; Pattern = pattern; Replacement = replacement; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrReplace); writer.WriteExpr(Input); writer.WriteExpr(Pattern); writer.WriteExpr(Replacement); } } class BgStringFormatExpr : BgString { readonly BgString _format; readonly BgExpr[] _arguments; public BgStringFormatExpr(BgString format, BgExpr[] arguments) : base(BgExprFlags.None) { _format = format; _arguments = arguments; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrFormat); writer.WriteExpr(_format); writer.WriteUnsignedInteger((ulong)_arguments.Length); foreach (BgExpr argument in _arguments) { writer.WriteExpr(argument); } } } class BgStringEmptyExpr : BgString { public BgStringEmptyExpr() : base(BgExprFlags.NotInterned | BgExprFlags.Eager) { } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrEmpty); } } class BgStringConstantExpr : BgString { public string Value { get; } public BgStringConstantExpr(string value) : base(BgExprFlags.Eager) { Value = value; } public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrLiteral); writer.WriteString(Value); } } class BgStringWrappedExpr : BgString { BgExpr Expr { get; } public BgStringWrappedExpr(BgExpr expr) : base(expr.Flags) { Expr = expr; } public override void Write(BgBytecodeWriter writer) => Expr.Write(writer); } #endregion /// /// Style for a string option /// public enum BgStringOptionStyle { /// /// Free-form text entry /// Text, /// /// List of options /// DropList, } /// /// A string option expression /// public class BgStringOption : BgString { /// /// 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; } /// /// Default value for the option /// public BgString? DefaultValue { get; set; } /// /// Style for this option /// public BgEnum? Style { get; } /// /// Regex for validating values for the option /// public BgString? Pattern { get; set; } /// /// Message to display if validation fails /// public BgString? PatternFailed { 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 BgStringOption(string name, BgString? description = null, BgString? defaultValue = null, BgEnum? style = null, BgString? pattern = null, BgString? patternFailed = null, BgList? values = null, BgList? valueDescriptions = null, BgString? label = null) : base(BgExprFlags.None) { Name = name; Label = label; Description = description; Style = style; DefaultValue = defaultValue; Pattern = pattern; PatternFailed = patternFailed; Values = values; ValueDescriptions = valueDescriptions; } /// public override void Write(BgBytecodeWriter writer) { writer.WriteOpcode(BgOpcode.StrOption); 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 (Pattern is not null) { option = option.Set(x => x.Pattern, Pattern); } if (PatternFailed is not null) { option = option.Set(x => x.PatternFailed, PatternFailed); } 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; } } }