Skip to content

Instantly share code, notes, and snippets.

@oguimbal
Created April 19, 2016 08:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oguimbal/5e726c00881b2feb7ec10ba998b9b9c2 to your computer and use it in GitHub Desktop.
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
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