From 7a2bcf3eee39b2782eaeef98ddb561111e9d0699 Mon Sep 17 00:00:00 2001 From: James Cracknell Date: Sat, 16 Nov 2024 09:00:16 -0700 Subject: [PATCH] Attempt to derive aggregate seed/fallback parameter names from input expressions This reverts commit eddd8c1ec4ce1a4bdd79c55269e2beb71f4c8649. --- .../src/ExpressionHelper.Aggregate.cs | 5 ++-- .../src/ExpressionHelper.AggregateTree.cs | 5 ++-- src/Arborist/src/ExpressionHelper.And.cs | 14 ++++++++--- src/Arborist/src/ExpressionHelper.AndTree.cs | 14 ++++++++--- src/Arborist/src/ExpressionHelper.Or.cs | 14 ++++++++--- src/Arborist/src/ExpressionHelper.OrTree.cs | 14 ++++++++--- .../src/ExpressionHelper.Predicates.cs | 25 +++++++++++-------- src/Arborist/src/ExpressionHelper.cs | 4 +-- .../test/ExpressionHelperTests.Aggregate.cs | 6 ++--- .../ExpressionHelperTests.AggregateTree.cs | 6 ++--- 10 files changed, 67 insertions(+), 40 deletions(-) diff --git a/src/Arborist/src/ExpressionHelper.Aggregate.cs b/src/Arborist/src/ExpressionHelper.Aggregate.cs index 28cb126..d163849 100644 --- a/src/Arborist/src/ExpressionHelper.Aggregate.cs +++ b/src/Arborist/src/ExpressionHelper.Aggregate.cs @@ -137,7 +137,6 @@ AggregateOptions options if(!enumerator.MoveNext()) return seed; - var parameters = enumerator.Current.Parameters; var replacements = new Dictionary(); var replacingVisitor = new ReplacingExpressionVisitor(replacements); var body = seed.Body; @@ -148,7 +147,7 @@ AggregateOptions options var current = enumerator.Current; replacements.Clear(); - foreach(var (ps, pr) in current.Parameters.Zip(parameters)) + foreach(var (ps, pr) in current.Parameters.Zip(seed.Parameters)) replacements[ps] = pr; var right = replacingVisitor.Visit(current.Body); @@ -167,7 +166,7 @@ AggregateOptions options body = replacingVisitor.Visit(binaryOperator.Body); } while(enumerator.MoveNext()); - return Expression.Lambda(body, parameters); + return Expression.Lambda(body, seed.Parameters); } } diff --git a/src/Arborist/src/ExpressionHelper.AggregateTree.cs b/src/Arborist/src/ExpressionHelper.AggregateTree.cs index 3a98a2f..05cc87e 100644 --- a/src/Arborist/src/ExpressionHelper.AggregateTree.cs +++ b/src/Arborist/src/ExpressionHelper.AggregateTree.cs @@ -122,13 +122,12 @@ LambdaExpression binaryOperator case 1: return expressionList[0]; } - var parameters = expressionList[0].Parameters; var replacements = new Dictionary(); var replacementVisitor = new ReplacingExpressionVisitor(replacements); return Expression.Lambda( - AggregateTreeBody(expressionList, binaryOperator, 0, expressionList.Count, parameters, replacements, replacementVisitor), - parameters + AggregateTreeBody(expressionList, binaryOperator, 0, expressionList.Count, fallback.Parameters, replacements, replacementVisitor), + fallback.Parameters ); } diff --git a/src/Arborist/src/ExpressionHelper.And.cs b/src/Arborist/src/ExpressionHelper.And.cs index ab15265..3c69bb6 100644 --- a/src/Arborist/src/ExpressionHelper.And.cs +++ b/src/Arborist/src/ExpressionHelper.And.cs @@ -1,3 +1,5 @@ +using Arborist.Internal; + namespace Arborist; public static partial class ExpressionHelper { @@ -52,11 +54,15 @@ IEnumerable>> predicates AndUnsafe(predicates); public static Expression AndUnsafe(IEnumerable> predicates) - where TPredicate : Delegate => - (Expression)AggregateImpl( - expressions: predicates, - seed: Const(true), + where TPredicate : Delegate + { + var predicateList = CollectionHelpers.AsReadOnlyList(predicates); + + return (Expression)AggregateImpl( + expressions: predicateList, + seed: Const(predicateList.FirstOrDefault()?.Parameters, true), binaryOperator: ExpressionOn.Of(static (a, b) => a && b), options: new() { DiscardSeedExpression = true } ); + } } diff --git a/src/Arborist/src/ExpressionHelper.AndTree.cs b/src/Arborist/src/ExpressionHelper.AndTree.cs index cc3e1dc..6955eeb 100644 --- a/src/Arborist/src/ExpressionHelper.AndTree.cs +++ b/src/Arborist/src/ExpressionHelper.AndTree.cs @@ -1,3 +1,5 @@ +using Arborist.Internal; + namespace Arborist; public static partial class ExpressionHelper { @@ -77,10 +79,14 @@ IEnumerable>> predicates AndTreeUnsafe(predicates); public static Expression AndTreeUnsafe(IEnumerable> predicates) - where TPredicate : Delegate => - (Expression)AggregateTreeImpl( - expressions: predicates, - fallback: Const(true), + where TPredicate : Delegate + { + var predicateList = CollectionHelpers.AsReadOnlyList(predicates); + + return (Expression)AggregateTreeImpl( + expressions: predicateList, + fallback: Const(predicateList.FirstOrDefault()?.Parameters, true), binaryOperator: ExpressionOn.Of(static (a, b) => a && b) ); + } } diff --git a/src/Arborist/src/ExpressionHelper.Or.cs b/src/Arborist/src/ExpressionHelper.Or.cs index 96831fd..188c13d 100644 --- a/src/Arborist/src/ExpressionHelper.Or.cs +++ b/src/Arborist/src/ExpressionHelper.Or.cs @@ -1,3 +1,5 @@ +using Arborist.Internal; + namespace Arborist; public static partial class ExpressionHelper { @@ -52,11 +54,15 @@ IEnumerable>> predicates OrUnsafe(predicates); public static Expression OrUnsafe(IEnumerable> predicates) - where TPredicate : Delegate => - (Expression)AggregateImpl( - expressions: predicates, - seed: Const(false), + where TPredicate : Delegate + { + var predicateList = CollectionHelpers.AsReadOnlyList(predicates); + + return (Expression)AggregateImpl( + expressions: predicateList, + seed: Const(predicateList.FirstOrDefault()?.Parameters, false), binaryOperator: ExpressionOn.Of(static (a, b) => a || b), options: new() { DiscardSeedExpression = false } ); + } } diff --git a/src/Arborist/src/ExpressionHelper.OrTree.cs b/src/Arborist/src/ExpressionHelper.OrTree.cs index 133157f..4650af8 100644 --- a/src/Arborist/src/ExpressionHelper.OrTree.cs +++ b/src/Arborist/src/ExpressionHelper.OrTree.cs @@ -1,3 +1,5 @@ +using Arborist.Internal; + namespace Arborist; public static partial class ExpressionHelper { @@ -77,10 +79,14 @@ IEnumerable>> predicates OrTreeUnsafe(predicates); public static Expression OrTreeUnsafe(IEnumerable> predicates) - where TPredicate : Delegate => - (Expression)AggregateTreeImpl( - expressions: predicates, - fallback: Const(false), + where TPredicate : Delegate + { + var predicateList = CollectionHelpers.AsReadOnlyList(predicates); + + return (Expression)AggregateTreeImpl( + expressions: predicateList, + fallback: Const(predicateList.FirstOrDefault()?.Parameters, false), binaryOperator: ExpressionOn.Of(static (a, b) => a || b) ); + } } diff --git a/src/Arborist/src/ExpressionHelper.Predicates.cs b/src/Arborist/src/ExpressionHelper.Predicates.cs index 81236b3..7fedab5 100644 --- a/src/Arborist/src/ExpressionHelper.Predicates.cs +++ b/src/Arborist/src/ExpressionHelper.Predicates.cs @@ -1,3 +1,4 @@ +using Arborist.Internal; using Arborist.Utils; namespace Arborist; @@ -8,23 +9,25 @@ public static partial class ExpressionHelper { /// together. Returns a true-valued predicate expression if the provided collection of predicates /// is empty. /// - public static Expression And(params Expression[] expressions) + public static Expression And(params Expression[] predicates) where TDelegate : Delegate => - And(expressions.AsEnumerable()); + And(predicates.AsEnumerable()); /// /// Combines the provided predicate expressions into a single expression by ANDing their bodies /// together. Returns a true-valued predicate expression if the provided collection of predicates /// is empty. /// - public static Expression And(IEnumerable> expressions) + public static Expression And(IEnumerable> predicates) where TDelegate : Delegate { AssertPredicateExpressionType(typeof(TDelegate)); + var predicateList = CollectionHelpers.AsReadOnlyList(predicates); + return (Expression)AggregateImpl( - expressions: expressions, - seed: Const(true), + expressions: predicateList, + seed: Const(predicateList.FirstOrDefault()?.Parameters, true), binaryOperator: ExpressionOn.Of(static (a, b) => a && b), options: new() { DiscardSeedExpression = true } ); @@ -35,23 +38,25 @@ public static Expression And(IEnumerable - public static Expression Or(params Expression[] expressions) + public static Expression Or(params Expression[] predicates) where TDelegate : Delegate => - Or(expressions.AsEnumerable()); + Or(predicates.AsEnumerable()); /// /// Combines the provided predicate expressions into a single expression by ORing their bodies /// together. Returns a false-valued predicate expression if the provided collection of predicates /// is empty. /// - public static Expression Or(IEnumerable> expressions) + public static Expression Or(IEnumerable> predicates) where TDelegate : Delegate { AssertPredicateExpressionType(typeof(TDelegate)); + var predicateList = CollectionHelpers.AsReadOnlyList(predicates); + return (Expression)AggregateImpl( - expressions: expressions, - seed: Const(false), + expressions: predicateList, + seed: Const(predicateList.FirstOrDefault()?.Parameters, false), binaryOperator: ExpressionOn.Of(static (a, b) => a || b), options: new() { DiscardSeedExpression = true } ); diff --git a/src/Arborist/src/ExpressionHelper.cs b/src/Arborist/src/ExpressionHelper.cs index fbf0c54..f139082 100644 --- a/src/Arborist/src/ExpressionHelper.cs +++ b/src/Arborist/src/ExpressionHelper.cs @@ -29,7 +29,7 @@ internal static bool IsFuncExpressionType(Type type) => internal static bool IsPredicateExpressionType(Type type) => IsFuncExpressionType(type) && typeof(bool) == type.GetGenericArguments()[^1]; - internal static Expression Const(object? value) + internal static Expression Const(IReadOnlyCollection? parameters, object? value) where TFunc : Delegate { AssertFuncExpressionType(typeof(TFunc)); @@ -39,7 +39,7 @@ internal static Expression Const(object? value) return Expression.Lambda( Expression.Constant(value, resultType), - genericArguments[..^1].Select(Expression.Parameter) + parameters ?? genericArguments[..^1].Select(Expression.Parameter) ); } } diff --git a/src/Arborist/test/ExpressionHelperTests.Aggregate.cs b/src/Arborist/test/ExpressionHelperTests.Aggregate.cs index ddea33d..a421833 100644 --- a/src/Arborist/test/ExpressionHelperTests.Aggregate.cs +++ b/src/Arborist/test/ExpressionHelperTests.Aggregate.cs @@ -41,7 +41,7 @@ public void Aggregate2_should_work_as_expected() { ExpressionOn.Of((c, o) => c.Name), ExpressionOn.Of((c, o) => o.Name) }, - seed: ExpressionOn.Of((p0, p1) => ""), + seed: ExpressionOn.Of((c, o) => ""), binaryOperator: (acc, v) => acc + v ); @@ -57,7 +57,7 @@ public void Aggregate3_should_work_as_expected() { ExpressionOn.Of((c, o, i) => o.Name), ExpressionOn.Of((c, o, i) => i.ToString()) }, - seed: ExpressionOn.Of((p0, p1, p2) => ""), + seed: ExpressionOn.Of((c, o, i) => ""), binaryOperator: (acc, v) => acc + v ); @@ -74,7 +74,7 @@ public void Aggregate4_should_work_as_expected() { ExpressionOn.Of((c, o, i, s) => i.ToString()), ExpressionOn.Of((c, o, i, s) => s.ToLowerInvariant()) }, - seed: ExpressionOn.Of((p0, p1, p2, p3) => ""), + seed: ExpressionOn.Of((c, o, i, s) => ""), binaryOperator: (acc, v) => acc + v ); diff --git a/src/Arborist/test/ExpressionHelperTests.AggregateTree.cs b/src/Arborist/test/ExpressionHelperTests.AggregateTree.cs index c022316..b6eee93 100644 --- a/src/Arborist/test/ExpressionHelperTests.AggregateTree.cs +++ b/src/Arborist/test/ExpressionHelperTests.AggregateTree.cs @@ -41,7 +41,7 @@ public void AggregateTree2_should_work_as_expected() { ExpressionOn.Of((c, o) => c.Name), ExpressionOn.Of((c, o) => o.Name) }, - fallback: ExpressionOn.Of((p0, p1) => ""), + fallback: ExpressionOn.Of((c, o) => ""), binaryOperator: (acc, v) => acc + v ); @@ -57,7 +57,7 @@ public void AggregateTree3_should_work_as_expected() { ExpressionOn.Of((c, o, i) => o.Name), ExpressionOn.Of((c, o, i) => i.ToString()) }, - fallback: ExpressionOn.Of((p0, p1, p2) => ""), + fallback: ExpressionOn.Of((c, o, i) => ""), binaryOperator: (acc, v) => acc + v ); @@ -74,7 +74,7 @@ public void AggregateTree4_should_work_as_expected() { ExpressionOn.Of((c, o, i, s) => i.ToString()), ExpressionOn.Of((c, o, i, s) => s.ToLowerInvariant()) }, - fallback: ExpressionOn.Of((p0, p1, p2, p3) => ""), + fallback: ExpressionOn.Of((c, o, i, s) => ""), binaryOperator: (acc, v) => acc + v );