399 lines
9.6 KiB
C#
399 lines
9.6 KiB
C#
|
//
|
||
|
// 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 ?? "<param>");
|
||
|
}
|
||
|
|
||
|
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<T> (ReadOnlyCollection<T> list, Action<T> 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 (")");
|
||
|
}
|
||
|
}
|
||
|
}
|