/* Use of included script samples are subject to the terms specified at http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx Written by Jomo Fisher */ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Collections.ObjectModel; namespace ConsoleApplication2 { static class FuncOp { private class Binding { public Func Bound { get; private set; } public Func, Func, T, TResult> Unbound { get; private set; } public Binding LastBinding { get; private set; } public Binding(Func bound, Func, Func, T, TResult> unbound, Binding lastBinding) { Bound = bound; Unbound = unbound; LastBinding = lastBinding; } public TResult Invoke(T t) { return Bound(t); } } private class RefOf { public T Ref { get; set; } } public static Func Create(Func, T, TResult> function) { var selfRef = new RefOf>(); var unbound = new Func, Func, T, TResult>( (s, l, t) => function(s, t) ); selfRef.Ref = Chain(selfRef, unbound, null); return selfRef.Ref; } private static Func Chain(RefOf> selfRef, Func, Func, T, TResult> unbound, Binding lastBinding) { Func last = null; Func bound = (t) => unbound(selfRef.Ref, last, t); if (lastBinding != null) last = Chain(selfRef, lastBinding.Unbound, lastBinding.LastBinding); var binding = new Binding(bound, unbound, lastBinding); return new Func(binding.Invoke); } public static Func Chain(this Func lastFunction, Func, Func, T, TResult> selfFunction) { var selfRef = new RefOf>(); var lastBinding = (Binding)lastFunction.Target; selfRef.Ref = Chain(selfRef, selfFunction, lastBinding); return selfRef.Ref; } } public static class ExprOp { static public Func Visit = FuncOp.Create( (self, expr) => { switch (expr.NodeType) { case ExpressionType.TypeIs: var tbe = (TypeBinaryExpression)expr; var tbex = self(tbe.Expression); return (tbe.Expression == tbex) ? expr : Expression.TypeIs(tbex, tbe.TypeOperand); case ExpressionType.Conditional: var ce = (ConditionalExpression)expr; var t = self(ce.Test); var it = self(ce.IfTrue); var @if = self(ce.IfFalse); return (t == ce.Test && it == ce.IfTrue && @if == ce.IfFalse) ? expr : Expression.Condition(t, it, @if); case ExpressionType.MemberAccess: var ma = (MemberExpression)expr; var maex = self(ma.Expression); return (maex == ma.Expression) ? expr : Expression.MakeMemberAccess(maex, ma.Member); case ExpressionType.Call: var mce = (MethodCallExpression)expr; var o = self(mce.Object); var ca = self.VisitExpressionList(mce.Arguments); return (o == mce.Object && ca != mce.Arguments) ? expr : Expression.Call(o, mce.Method, ca); case ExpressionType.Lambda: var le = (LambdaExpression)expr; var b = self(le.Body); return (b == le.Body) ? expr : Expression.Lambda(le.Type, b, le.Parameters); case ExpressionType.New: var ne = (NewExpression)expr; var nar = self.VisitExpressionList(ne.Arguments); if (nar != ne.Arguments) return Expression.New(ne.Constructor, nar); return expr; case ExpressionType.NewArrayInit: case ExpressionType.NewArrayBounds: var na = (NewArrayExpression)expr; var inits = self.VisitExpressionList(na.Expressions); if (inits != na.Expressions) return (na.NodeType == ExpressionType.NewArrayInit) ? Expression.NewArrayInit(na.Type.GetElementType(), inits) : Expression.NewArrayBounds(na.Type.GetElementType(), inits); return expr; case ExpressionType.Invoke: var inv = (InvocationExpression)expr; var args = self.VisitExpressionList(inv.Arguments); var ie = self(inv.Expression); return (args == inv.Arguments && ie == inv.Expression) ? expr : Expression.Invoke(ie, args); case ExpressionType.MemberInit: var mi = (MemberInitExpression)expr; var n = (NewExpression)self(mi.NewExpression); var bindings = self.VisitBindingList(mi.Bindings); return (n == mi.NewExpression && bindings == mi.Bindings) ? expr : Expression.MemberInit(n, bindings); case ExpressionType.ListInit: var li = (ListInitExpression)expr; var lin = (NewExpression)self(li.NewExpression); return (lin == li.NewExpression) ? expr : Expression.ListInit(lin); } if (expr.IsBinary()) { var b = (BinaryExpression)expr; var left = self(b.Left); var right = self(b.Right); return (left == b.Left && right == b.Right) ? expr : Expression.MakeBinary(expr.NodeType, left, right); } else if (expr.IsUnary()) { var u = (UnaryExpression)expr; var op = self(u.Operand); return (u.Operand == op) ? expr : Expression.MakeUnary(u.NodeType, op, expr.Type); } return expr; } ); public static bool IsBinary(this Expression expr) { return expr is BinaryExpression; } public static bool IsUnary(this Expression expr) { return expr is UnaryExpression; } public static MemberBinding VisitBinding(this Func self, MemberBinding b) { switch (b.BindingType) { case MemberBindingType.Assignment: return self.VisitMemberAssignment((MemberAssignment)b); } return self.VisitMemberMemberBinding((MemberMemberBinding)b); } public static MemberAssignment VisitMemberAssignment(this Func self, MemberAssignment assignment) { var e = self(assignment.Expression); return (e == assignment.Expression) ? assignment : Expression.Bind(assignment.Member, e); } public static MemberMemberBinding VisitMemberMemberBinding(this Func self, MemberMemberBinding binding) { var bindings = self.VisitBindingList(binding.Bindings); return (bindings == binding.Bindings) ? binding : Expression.MemberBind(binding.Member, bindings); } public static ReadOnlyCollection VisitExpressionList(this Func self, ReadOnlyCollection original) { return VisitList(original, e => self(e)); } public static ReadOnlyCollection VisitBindingList(this Func self, ReadOnlyCollection original) { return VisitList(original, e => self.VisitBinding(e)); } private static ReadOnlyCollection VisitList(ReadOnlyCollection original, Func op) { List @new = null; for (int i = 0, n = original.Count; i < n; i++) { T init = op(original[i]); if (@new != null) @new.Add(init); else if (!init.Equals(original[i])) { @new = new List(n); for (int j = 0; j < i; j++) @new.Add(original[j]); @new.Add(init); } } return (@new == null) ? original : @new.AsReadOnly(); } } class Program { static void Main(string[] args) { var RewriteSubtractToAdd = ExprOp.Visit.Chain( (self, last, expr) => { switch (expr.NodeType) { case ExpressionType.Subtract: var b = (BinaryExpression)expr; return Expression.Add(b.Left, b.Right); default: return last(expr); } } ); Expression> SubtractOneExpr = (n => n - 1); var AddOneExpr = (LambdaExpression)RewriteSubtractToAdd(SubtractOneExpr); var AddOne = (Func)AddOneExpr.Compile(); Console.WriteLine(AddOne(5)); } } }