// // ExpressionPrinter.cs // // Author: // Jb Evain (jbevain@novell.com) // // (C) 2008 Novell, Inc. (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; namespace System.Linq.Expressions { class ExpressionPrinter : ExpressionVisitor { StringBuilder builder; const string ListSeparator = ", "; ExpressionPrinter (StringBuilder builder) { this.builder = builder; } ExpressionPrinter () : this (new StringBuilder ()) { } public static string ToString (Expression expression) { var printer = new ExpressionPrinter (); printer.Visit (expression); return printer.builder.ToString (); } public static string ToString (ElementInit init) { var printer = new ExpressionPrinter (); printer.VisitElementInitializer (init); return printer.builder.ToString (); } public static string ToString (MemberBinding binding) { var printer = new ExpressionPrinter (); printer.VisitBinding (binding); return printer.builder.ToString (); } void Print (string str) { builder.Append (str); } void Print (object obj) { builder.Append (obj); } void Print (string str, params object [] objs) { builder.AppendFormat (str, objs); } protected override void VisitElementInitializer (ElementInit initializer) { Print (initializer.AddMethod); Print ("("); VisitExpressionList (initializer.Arguments); Print (")"); } protected override void VisitUnary (UnaryExpression unary) { switch (unary.NodeType) { case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Not: Print ("{0}(", unary.NodeType); Visit (unary.Operand); Print (")"); return; case ExpressionType.Negate: Print ("-"); Visit (unary.Operand); return; case ExpressionType.Quote: Visit (unary.Operand); return; case ExpressionType.TypeAs: Print ("("); Visit (unary.Operand); Print (" As {0})", unary.Type.Name); return; case ExpressionType.UnaryPlus: Print ("+"); Visit (unary.Operand); return; } throw new NotImplementedException (); } static string OperatorToString (BinaryExpression binary) { switch (binary.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: return "+"; case ExpressionType.AndAlso: return "&&"; case ExpressionType.Coalesce: return "??"; case ExpressionType.Divide: return "/"; case ExpressionType.Equal: return "="; case ExpressionType.ExclusiveOr: return "^"; case ExpressionType.GreaterThan: return ">"; case ExpressionType.GreaterThanOrEqual: return ">="; case ExpressionType.LeftShift: return "<<"; case ExpressionType.LessThan: return "<"; case ExpressionType.LessThanOrEqual: return "<="; case ExpressionType.Modulo: return "%"; case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: return "*"; case ExpressionType.NotEqual: return "!="; case ExpressionType.OrElse: return "||"; case ExpressionType.Power: return "^"; case ExpressionType.RightShift: return ">>"; case ExpressionType.Subtract: case ExpressionType.SubtractChecked: return "-"; case ExpressionType.And: return IsBoolean (binary) ? "And" : "&"; case ExpressionType.Or: return IsBoolean (binary) ? "Or" : "|"; default: return null; } } static bool IsBoolean (Expression expression) { return expression.Type == typeof (bool) || expression.Type == typeof (bool?); } void PrintArrayIndex (BinaryExpression index) { Visit (index.Left); Print ("["); Visit (index.Right); Print ("]"); } protected override void VisitBinary (BinaryExpression binary) { switch (binary.NodeType) { case ExpressionType.ArrayIndex: PrintArrayIndex (binary); return; default: Print ("("); Visit (binary.Left); Print (" {0} ", OperatorToString (binary)); Visit (binary.Right); Print (")"); return; } } protected override void VisitTypeIs (TypeBinaryExpression type) { switch (type.NodeType) { case ExpressionType.TypeIs: Print ("("); Visit (type.Expression); Print (" Is {0})", type.TypeOperand.Name); return; } throw new NotImplementedException (); } protected override void VisitConstant (ConstantExpression constant) { var value = constant.Value; if (value == null) { Print ("null"); } else if (value is string) { Print ("\""); Print (value); Print ("\""); } else if (!HasStringRepresentation (value)) { Print ("value("); Print (value); Print (")"); } else Print (value); } static bool HasStringRepresentation (object obj) { return obj.ToString () != obj.GetType ().ToString (); } protected override void VisitConditional (ConditionalExpression conditional) { Print ("IIF("); Visit (conditional.Test); Print (ListSeparator); Visit (conditional.IfTrue); Print (ListSeparator); Visit (conditional.IfFalse); Print (")"); } protected override void VisitParameter (ParameterExpression parameter) { Print (parameter.Name ?? ""); } protected override void VisitMemberAccess (MemberExpression access) { if (access.Expression == null) Print (access.Member.DeclaringType.Name); else Visit (access.Expression); Print (".{0}", access.Member.Name); } protected override void VisitMethodCall (MethodCallExpression call) { if (call.Object != null) { Visit (call.Object); Print ("."); } Print (call.Method.Name); Print ("("); VisitExpressionList (call.Arguments); Print (")"); } protected override void VisitMemberAssignment (MemberAssignment assignment) { Print ("{0} = ", assignment.Member.Name); Visit (assignment.Expression); } protected override void VisitMemberMemberBinding (MemberMemberBinding binding) { Print (binding.Member.Name); Print (" = {"); // VisitBindingList (binding.Bindings); VisitList (binding.Bindings, VisitBinding); Print ("}"); } protected override void VisitMemberListBinding (MemberListBinding binding) { Print (binding.Member.Name); Print (" = {"); // replace when the patch to the visitor is in // VisitElementInitializerList (binding.Initializers); VisitList (binding.Initializers, VisitElementInitializer); Print ("}"); } protected override void VisitList (ReadOnlyCollection list, Action visitor) { for (int i = 0; i < list.Count; i++) { if (i > 0) Print (ListSeparator); visitor (list [i]); } } protected override void VisitLambda (LambdaExpression lambda) { if (lambda.Parameters.Count != 1) { Print ("("); // replace when the patch to the visitor is in // VisitExpressionList (lambda.Parameters); VisitList (lambda.Parameters, Visit); Print (")"); } else Visit (lambda.Parameters [0]); Print (" => "); Visit (lambda.Body); } protected override void VisitNew (NewExpression nex) { Print ("new {0}(", nex.Type.Name); if (nex.Members != null && nex.Members.Count > 0) { for (int i = 0; i < nex.Members.Count; i++) { if (i > 0) Print (ListSeparator); Print ("{0} = ", nex.Members [i].Name); Visit (nex.Arguments [i]); } } else VisitExpressionList (nex.Arguments); Print (")"); } protected override void VisitMemberInit (MemberInitExpression init) { Visit (init.NewExpression); Print (" {"); // VisitBindingList (init.Bindings) VisitList (init.Bindings, VisitBinding); Print ("}"); } protected override void VisitListInit (ListInitExpression init) { Visit (init.NewExpression); Print (" {"); // VisitElementInitializerList VisitList (init.Initializers, VisitElementInitializer); Print ("}"); } protected override void VisitNewArray (NewArrayExpression newArray) { Print ("new "); switch (newArray.NodeType) { case ExpressionType.NewArrayBounds: Print (newArray.Type); Print ("("); VisitExpressionList (newArray.Expressions); Print (")"); return; case ExpressionType.NewArrayInit: Print ("[] {"); VisitExpressionList (newArray.Expressions); Print ("}"); return; } throw new NotSupportedException (); } protected override void VisitInvocation (InvocationExpression invocation) { Print ("Invoke("); Visit (invocation.Expression); if (invocation.Arguments.Count != 0) { Print (ListSeparator); VisitExpressionList (invocation.Arguments); } Print (")"); } } }