Created
April 19, 2016 08:16
-
-
Save oguimbal/5e726c00881b2feb7ec10ba998b9b9c2 to your computer and use it in GitHub Desktop.
Simplify expressions by executing each subnode wich does not depends on an expression parameter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq.Expressions; | |
namespace Utilities | |
{ | |
public static class ExpressionSimplifier | |
{ | |
public static Expression<T> SimplifyLambda<T>(this Expression<T> expression) | |
{ | |
var bodySimplified = Simplify(expression.Body); | |
var ret = Expression.Lambda<T>(bodySimplified, expression.Parameters); | |
return ret; | |
} | |
public static Expression Simplify(Expression expression) | |
{ | |
if (expression.NodeType == ExpressionType.Constant) | |
return expression; | |
var nominator = new Nominator(); | |
nominator.Visit(expression); | |
if (nominator.mayBeEvaluated.Count == 0) // no possible evaluations | |
return expression; | |
var ret = new Evaluator(nominator.mayBeEvaluated).Visit(expression); | |
return ret; | |
} | |
class Nominator : ExpressionVisitor | |
{ | |
public readonly HashSet<Expression> mayBeEvaluated = new HashSet<Expression>(); | |
bool evaluate = true; | |
public override Expression Visit(Expression node) | |
{ | |
if (node == null) | |
return null; | |
// here 'evaluate' means nothing for this stack. | |
var oldEvaluate = evaluate; | |
evaluate = true; | |
var visited = base.Visit(node); | |
// when arriving here, 'evaluate' is true only if sub-evaluations can be evaluated | |
if (evaluate) | |
mayBeEvaluated.Add(node); | |
evaluate = oldEvaluate && evaluate; | |
return visited; | |
} | |
protected override Expression VisitNew(NewExpression node) | |
{ | |
evaluate = false; | |
return base.VisitNew(node); | |
} | |
readonly HashSet<ParameterExpression> evaluableParameters = new HashSet<ParameterExpression>(); | |
protected override Expression VisitParameter(ParameterExpression node) | |
{ | |
evaluate &= evaluableParameters.Contains(node); | |
return base.VisitParameter(node); | |
} | |
protected override Expression VisitMember(MemberExpression node) | |
{ | |
if (node.Expression != null) | |
{ | |
Visit(node.Expression); | |
evaluate = evaluate | |
&& mayBeEvaluated.Contains(node.Expression); | |
} | |
return node; | |
} | |
} | |
class Evaluator : ExpressionVisitor | |
{ | |
readonly HashSet<Expression> mayBeEvaluated; | |
public Evaluator(HashSet<Expression> mayBeEvaluated) | |
{ | |
this.mayBeEvaluated = mayBeEvaluated; | |
} | |
public override Expression Visit(Expression node) | |
{ | |
if (mayBeEvaluated.Contains(node)) | |
{ | |
if (node.NodeType == ExpressionType.Constant) | |
{ | |
// no need to compile something for constants | |
return node; | |
} | |
var value = Expression.Lambda<Func<object>>(Expression.Convert(node, typeof(object))).Compile()(); | |
return Expression.Constant(value, node.Type); | |
} | |
return base.Visit(node); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment