diff --git a/src/System.Management.Automation/engine/lang/parserutils.cs b/src/System.Management.Automation/engine/lang/parserutils.cs index 23438b568a9..3485bda66dc 100644 --- a/src/System.Management.Automation/engine/lang/parserutils.cs +++ b/src/System.Management.Automation/engine/lang/parserutils.cs @@ -350,6 +350,8 @@ static ParserOps() internal static readonly object _TrueObject = (object)true; internal static readonly object _FalseObject = (object)false; + private static readonly CallSite> _toBoolSite = CallSite>.Create(PSConvertBinder.Get(typeof(bool))); + internal static string CharToString(char ch) { if (ch < 255) return s_chars[ch]; @@ -591,6 +593,87 @@ private static void ExtendList(IList list, IList items) } } + internal static bool AnyOperator(ExecutionContext context, + CallSite> getEnumeratorSite, + CallSite> comparerSite, + object left, + object right) + { + ScriptBlock predicate = right as ScriptBlock; + if (predicate == null) + { + return ContainsOperatorCompiled(context, getEnumeratorSite, comparerSite, left, right); + throw InterpreterError.NewInterpreterException(right, typeof(RuntimeException), new ScriptExtent(new ScriptPosition("", 0, 0, ""), new ScriptPosition("", 0, 0, "")), + "BadOperatorArgument", ParserStrings.BadOperatorArgument, "-any", right); + } + + IEnumerator list = getEnumeratorSite.Target.Invoke(getEnumeratorSite, left); + if (list == null || list is EnumerableOps.NonEnumerableObjectEnumerator) + { + object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, left, null, null, null); + return _toBoolSite.Target.Invoke(_toBoolSite, result); + } + + while (EnumerableOps.MoveNext(context, list)) + { + object val = EnumerableOps.Current(list); + object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, val, null, null, null); + if (_toBoolSite.Target.Invoke(_toBoolSite, result)) + { + return true; + } + } + + return false; + } + + internal static bool AllOperator(ExecutionContext context, + CallSite> getEnumeratorSite, + CallSite> comparerSite, + object left, + object right) + { + IEnumerator list = getEnumeratorSite.Target.Invoke(getEnumeratorSite, left); + + ScriptBlock predicate = right as ScriptBlock; + if (predicate == null) + { + if (list == null || list is EnumerableOps.NonEnumerableObjectEnumerator) + { + return (bool)comparerSite.Target.Invoke(comparerSite, left, right); + } + + while (EnumerableOps.MoveNext(context, list)) + { + object val = EnumerableOps.Current(list); + if (!_toBoolSite.Target.Invoke(_toBoolSite, comparerSite.Target.Invoke(comparerSite, val, right))) + { + return false; + } + } + + return true; + } + + if (list == null || list is EnumerableOps.NonEnumerableObjectEnumerator) + { + object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, left, null, null, null); + return _toBoolSite.Target.Invoke(_toBoolSite, result); + } + + while (EnumerableOps.MoveNext(context, list)) + { + object val = EnumerableOps.Current(list); + object result = predicate.DoInvokeReturnAsIs(true, ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, val, null, null, null); + if (!_toBoolSite.Target.Invoke(_toBoolSite, result)) + { + return false; + } + } + + return true; + } + private static object SplitOperatorImpl(ExecutionContext context, IScriptExtent errorPosition, object lval, object rval, SplitImplOptions implOptions, bool ignoreCase) { IEnumerable content = enumerateContent(context, errorPosition, implOptions, lval); diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 2b641bf9b59..27d49a9ac83 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -297,6 +297,10 @@ internal static class CachedReflectionInfo typeof(ParserOps).GetMethod(nameof(ParserOps.UnaryJoinOperator), staticFlags); internal static readonly MethodInfo ParserOps_UnarySplitOperator = typeof(ParserOps).GetMethod(nameof(ParserOps.UnarySplitOperator), staticFlags); + internal static readonly MethodInfo ParserOps_AnyOperator = + typeof(ParserOps).GetMethod(nameof(ParserOps.AnyOperator), staticFlags); + internal static readonly MethodInfo ParserOps_AllOperator = + typeof(ParserOps).GetMethod(nameof(ParserOps.AllOperator), staticFlags); internal static readonly ConstructorInfo Pipe_ctor = typeof(Pipe).GetConstructor(instanceFlags, null, CallingConventions.Standard, new Type[] { typeof(List) }, null); @@ -5008,6 +5012,28 @@ public Expression GenerateCallContains(Expression lhs, Expression rhs, bool igno rhs.Cast(typeof(object))); } + public Expression GenerateCallAny(Expression lhs, Expression rhs, bool ignoreCase) + { + return Expression.Call( + CachedReflectionInfo.ParserOps_AnyOperator, + _executionContextParameter, + Expression.Constant(CallSite>.Create(PSEnumerableBinder.Get())), + Expression.Constant(CallSite>.Create(PSBinaryOperationBinder.Get(ExpressionType.Equal, ignoreCase, true))), + lhs.Cast(typeof(object)), + rhs.Cast(typeof(object))); + } + + public Expression GenerateCallAll(Expression lhs, Expression rhs, bool ignoreCase) + { + return Expression.Call( + CachedReflectionInfo.ParserOps_AllOperator, + _executionContextParameter, + Expression.Constant(CallSite>.Create(PSEnumerableBinder.Get())), + Expression.Constant(CallSite>.Create(PSBinaryOperationBinder.Get(ExpressionType.Equal, ignoreCase, true))), + lhs.Cast(typeof(object)), + rhs.Cast(typeof(object))); + } + public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst) { object constantValue; @@ -5179,6 +5205,10 @@ public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst) return GenerateCallContains(lhs, rhs, true); case TokenKind.Inotcontains: return Expression.Not(GenerateCallContains(lhs, rhs, true)); + case TokenKind.Any: + return GenerateCallAny(lhs, rhs, true); + case TokenKind.All: + return GenerateCallAll(lhs, rhs, true); case TokenKind.Iin: return GenerateCallContains(rhs, lhs, true); case TokenKind.Inotin: diff --git a/src/System.Management.Automation/engine/parser/token.cs b/src/System.Management.Automation/engine/parser/token.cs index def6b8d0dc1..87f121e22aa 100644 --- a/src/System.Management.Automation/engine/parser/token.cs +++ b/src/System.Management.Automation/engine/parser/token.cs @@ -413,6 +413,11 @@ public enum TokenKind /// The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls. Colon = 99, + /// Any operator '-any' + Any = 100, + + /// All operator '-all' + All = 101, #endregion Operators #region Keywords @@ -598,7 +603,7 @@ public enum TokenFlags /// /// The precedence of comparison operators including: '-eq', '-ne', '-ge', '-gt', '-lt', '-le', '-like', '-notlike', - /// '-match', '-notmatch', '-replace', '-contains', '-notcontains', '-in', '-notin', '-split', '-join', '-is', '-isnot', '-as', + /// '-match', '-notmatch', '-replace', '-contains', '-notcontains', '-in', '-notin', '-split', '-join', '-is', '-isnot', '-as', '-any', '-all', /// and all of the case sensitive variants of these operators, if they exists. /// BinaryPrecedenceComparison = 3, @@ -847,8 +852,8 @@ public static class TokenTraits /* Shl */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold, /* Shr */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold, /* Colon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode, - /* Reserved slot 2 */ TokenFlags.None, - /* Reserved slot 3 */ TokenFlags.None, + /* Any */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison, + /* All */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison, /* Reserved slot 4 */ TokenFlags.None, /* Reserved slot 5 */ TokenFlags.None, /* Reserved slot 6 */ TokenFlags.None, @@ -1045,8 +1050,8 @@ public static class TokenTraits /* Shl */ "-shl", /* Shr */ "-shr", /* Colon */ ":", - /* Reserved slot 2 */ string.Empty, - /* Reserved slot 3 */ string.Empty, + /* Any */ "-any", + /* All */ "-all", /* Reserved slot 4 */ string.Empty, /* Reserved slot 5 */ string.Empty, /* Reserved slot 6 */ string.Empty, diff --git a/src/System.Management.Automation/engine/parser/tokenizer.cs b/src/System.Management.Automation/engine/parser/tokenizer.cs index aab4571a9c7..b110aba5efc 100644 --- a/src/System.Management.Automation/engine/parser/tokenizer.cs +++ b/src/System.Management.Automation/engine/parser/tokenizer.cs @@ -637,7 +637,8 @@ private static readonly Dictionary s_operatorTable /*13*/ "isplit", "csplit", "isnot", "is", /*13*/ /*14*/ "as", "f", "and", "band", /*14*/ /*15*/ "or", "bor", "xor", "bxor", /*15*/ - /*16*/ "join", "shl", "shr", /*16*/ + /*16*/ "join", "shl", "shr", "any", /*16*/ + /*17*/ "all", /*17*/ }; private static readonly TokenKind[] s_operatorTokenKind = new TokenKind[] { @@ -656,7 +657,8 @@ private static readonly Dictionary s_operatorTable /*13*/ TokenKind.Isplit, TokenKind.Csplit, TokenKind.IsNot, TokenKind.Is, /*13*/ /*14*/ TokenKind.As, TokenKind.Format, TokenKind.And, TokenKind.Band, /*14*/ /*15*/ TokenKind.Or, TokenKind.Bor, TokenKind.Xor, TokenKind.Bxor, /*15*/ - /*16*/ TokenKind.Join, TokenKind.Shl, TokenKind.Shr, /*16*/ + /*16*/ TokenKind.Join, TokenKind.Shl, TokenKind.Shr, TokenKind.Any, /*16*/ + /*17*/ TokenKind.All, /*17*/ }; #endregion Tables for initialization diff --git a/src/System.Management.Automation/resources/TabCompletionStrings.resx b/src/System.Management.Automation/resources/TabCompletionStrings.resx index 33710a77176..1dae453f4a1 100644 --- a/src/System.Management.Automation/resources/TabCompletionStrings.resx +++ b/src/System.Management.Automation/resources/TabCompletionStrings.resx @@ -329,4 +329,10 @@ Shift Right bit operator. Inserts zero in the left-most bit position. For signed values, sign bit is preserved. + + All operator. Returns TRUE when all of the left hand values are TRUE for the right hand predicate + + + Any operator. Returns TRUE when any of the left hand values are TRUE for the right hand predicate +