From 6a9cd5f4ffd3c022d7209935a5516a3d6e220b28 Mon Sep 17 00:00:00 2001 From: "rupert.avery" Date: Tue, 13 May 2014 16:16:07 +0800 Subject: [PATCH] Support for dynamics --- Mono.Linq.Expressions.csproj | 3 +- Mono.Linq.Expressions/CSharpWriter.cs | 2748 +++++++++++---------- Mono.Linq.Expressions/ExpressionWriter.cs | 139 ++ dbg/Program.cs | 38 +- 4 files changed, 1597 insertions(+), 1331 deletions(-) diff --git a/Mono.Linq.Expressions.csproj b/Mono.Linq.Expressions.csproj index c0fb9a4..8166aeb 100644 --- a/Mono.Linq.Expressions.csproj +++ b/Mono.Linq.Expressions.csproj @@ -1,4 +1,4 @@ - + Debug @@ -55,6 +55,7 @@ AllRules.ruleset + 3.5 diff --git a/Mono.Linq.Expressions/CSharpWriter.cs b/Mono.Linq.Expressions/CSharpWriter.cs index 4103392..80e5a9f 100644 --- a/Mono.Linq.Expressions/CSharpWriter.cs +++ b/Mono.Linq.Expressions/CSharpWriter.cs @@ -28,1337 +28,1431 @@ using System; using System.Collections.Generic; +using System.Dynamic; +using System.Linq; using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Microsoft.CSharp.RuntimeBinder; + +namespace Mono.Linq.Expressions +{ + + public class CSharpWriter : ExpressionWriter + { + + readonly Dictionary unique_names = new Dictionary(); + + int unique_seed; + + public CSharpWriter(IFormatter formatter) + : base(formatter) + { + } + + public override void Write(LambdaExpression expression) + { + VisitLambdaSignature(expression); + VisitLambdaBody(expression); + } + + protected override Expression VisitLambda(Expression node) + { + VisitParameters(node); + WriteSpace(); + WriteToken("=>"); + WriteLine(); + + VisitLambdaBody(node); + + return node; + } + + void VisitLambdaSignature(LambdaExpression node) + { + VisitType(node.ReturnType); + WriteSpace(); + WriteIdentifier(node.Name, node); + VisitParameters(node); + + WriteLine(); + } + + void VisitParameters(LambdaExpression node) + { + VisitParenthesizedList(node.Parameters, parameter => + { + VisitType(parameter.Type); + WriteSpace(); + WriteIdentifier(NameFor(parameter), parameter); + }); + } + + string NameFor(ParameterExpression parameter) + { + if (!string.IsNullOrEmpty(parameter.Name)) + return parameter.Name; + + var name = GeneratedNameFor(parameter); + if (name != null) + return name; + + name = "var_$" + unique_seed++; + unique_names.Add(parameter, name); + return name; + } + + string GeneratedNameFor(ParameterExpression parameter) + { + string name; + if (!unique_names.TryGetValue(parameter, out name)) + return null; + + return name; + } + + void VisitLambdaBody(LambdaExpression node) + { + if (node.Body.NodeType != ExpressionType.Block) + VisitSingleExpressionBody(node); + else + VisitBlockExpressionBody(node); + } + + void VisitBlockExpressionBody(LambdaExpression node) + { + VisitBlockExpression((BlockExpression)node.Body); + } + + static bool IsStatement(Expression expression) + { + switch (expression.NodeType) + { + case ExpressionType.Conditional: + return !IsTernaryConditional((ConditionalExpression)expression); + case ExpressionType.Try: + case ExpressionType.Loop: + case ExpressionType.Switch: + return true; + default: + var custom = expression as CustomExpression; + if (custom != null) + return IsStatement(custom); + + return false; + } + } + + static bool IsStatement(CustomExpression expression) + { + switch (expression.CustomNodeType) + { + case CustomExpressionType.DoWhileExpression: + case CustomExpressionType.ForExpression: + case CustomExpressionType.ForEachExpression: + case CustomExpressionType.UsingExpression: + case CustomExpressionType.WhileExpression: + return true; + default: + return false; + } + } + + static bool IsActualStatement(Expression expression) + { + switch (expression.NodeType) + { + case ExpressionType.Label: + return false; + case ExpressionType.Conditional: + return IsTernaryConditional((ConditionalExpression)expression); + case ExpressionType.Try: + case ExpressionType.Loop: + case ExpressionType.Switch: + return false; + default: + return true; + } + } + + + void VisitSingleExpressionBody(LambdaExpression node) + { + VisitBlock(() => + { + if (node.ReturnType != typeof(void) && !IsStatement(node.Body)) + { + WriteKeyword("return"); + WriteSpace(); + } + + Visit(node.Body); + + if (!IsStatement(node.Body)) + { + WriteToken(";"); + WriteLine(); + } + }); + } + + void VisitType(Type type) + { + if (type.IsArray) + { + VisitArrayType(type); + return; + } + + if (type.IsGenericParameter) + { + WriteReference(type.Name, type); + return; + } + + if (type.IsGenericType && type.IsGenericTypeDefinition) + { + VisitGenericTypeDefinition(type); + return; + } + + if (type.IsGenericType && !type.IsGenericTypeDefinition) + { + VisitGenericTypeInstance(type); + return; + } + + VisitSimpleType(type); + } + + void VisitArrayType(Type type) + { + VisitType(type.GetElementType()); + WriteToken("["); + for (int i = 1; i < type.GetArrayRank(); i++) + WriteToken(","); + WriteToken("]"); + } + + void VisitGenericTypeDefinition(Type type) + { + WriteReference(CleanGenericName(type), type); + WriteToken("<"); + var arity = type.GetGenericArguments().Length; + for (int i = 1; i < arity; i++) + WriteToken(","); + WriteToken(">"); + } + + void VisitGenericTypeInstance(Type type) + { + WriteReference(CleanGenericName(type), type); + + VisitGenericArguments(type.GetGenericArguments()); + } + + void VisitGenericArguments(Type[] generic_arguments) + { + VisitList(generic_arguments, "<", VisitType, ">"); + } + + static string CleanGenericName(Type type) + { + var name = type.Name; + var position = name.LastIndexOf("`"); + if (position == -1) + return name; + + return name.Substring(0, position); + } + + void VisitSimpleType(Type type) + { + WriteReference(GetSimpleTypeName(type), type); + } + + static string GetSimpleTypeName(Type type) + { + if (type == typeof(void)) + return "void"; + if (type == typeof(object)) + return "object"; + + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + return "bool"; + case TypeCode.Byte: + return "byte"; + case TypeCode.Char: + return "char"; + case TypeCode.Decimal: + return "decimal"; + case TypeCode.Double: + return "double"; + case TypeCode.Int16: + return "short"; + case TypeCode.Int32: + return "int"; + case TypeCode.Int64: + return "long"; + case TypeCode.SByte: + return "sbyte"; + case TypeCode.Single: + return "float"; + case TypeCode.String: + return "string"; + case TypeCode.UInt16: + return "ushort"; + case TypeCode.UInt32: + return "uint"; + case TypeCode.UInt64: + return "ulong"; + default: + return type.Name; + } + } + + protected override Expression VisitBlock(BlockExpression node) + { + VisitBlockExpression(node); + + return node; + } + + void VisitBlock(Action action) + { + WriteToken("{"); + WriteLine(); + Indent(); + + action(); + + Dedent(); + WriteToken("}"); + } + + void VisitBlockExpression(BlockExpression node) + { + VisitBlock(() => + { + VisitBlockVariables(node); + VisitBlockExpressions(node); + }); + } + + void VisitBlockExpressions(BlockExpression node) + { + for (int i = 0; i < node.Expressions.Count; i++) + { + var expression = node.Expressions[i]; + + if (IsActualStatement(expression) && RequiresExplicitReturn(node, i, node.Type != typeof(void))) + { + WriteKeyword("return"); + WriteSpace(); + } + + Write(expression); + + if (!IsActualStatement(expression)) + continue; + + WriteToken(";"); + WriteLine(); + } + } + + void VisitBlockVariables(BlockExpression node) + { + foreach (var variable in node.Variables) + { + VisitType(variable.Type); + WriteSpace(); + WriteIdentifier(NameFor(variable), variable); + WriteToken(";"); + WriteLine(); + } + + if (node.Variables.Count > 0) + WriteLine(); + } + + static bool RequiresExplicitReturn(BlockExpression node, int index, bool return_last) + { + if (!return_last) + return false; + + var last_index = node.Expressions.Count - 1; + if (index != last_index) + return false; + + var last = node.Expressions[last_index]; + if (last.Is(ExpressionType.Goto) && ((GotoExpression)last).Kind == GotoExpressionKind.Return) + return false; + + return true; + } + + protected override Expression VisitBinary(BinaryExpression node) + { + if (IsChecked(node.NodeType)) + VisitCheckedBinary(node); + else if (node.Is(ExpressionType.Assign)) + VisitAssign(node); + else if (IsPower(node.NodeType)) + VisitPower(node); + else if (node.Is(ExpressionType.ArrayIndex)) + VisitArrayIndex(node); + else + VisitSimpleBinary(node); + + return node; + } + + void VisitArrayIndex(BinaryExpression node) + { + Visit(node.Left); + WriteToken("["); + Visit(node.Right); + WriteToken("]"); + } + + void VisitAssign(BinaryExpression node) + { + Visit(node.Left); + WriteSpace(); + WriteToken(GetBinaryOperator(node.NodeType)); + WriteSpace(); + Visit(node.Right); + } + + void VisitPower(BinaryExpression node) + { + var pow = Expression.Call(typeof(System.Math).GetMethod("Pow"), node.Left, node.Right); + + if (node.Is(ExpressionType.Power)) + Visit(pow); + else if (node.Is(ExpressionType.PowerAssign)) + Visit(Expression.Assign(node.Left, pow)); + } + + static bool IsPower(ExpressionType type) + { + return type == ExpressionType.Power || type == ExpressionType.PowerAssign; + } + + void VisitSimpleBinary(BinaryExpression node) + { + VisitParenthesizedExpression(node.Left); + WriteSpace(); + WriteToken(GetBinaryOperator(node.NodeType)); + WriteSpace(); + VisitParenthesizedExpression(node.Right); + } + + void VisitParenthesizedExpression(Expression expression) + { + if (RequiresParentheses(expression)) + { + WriteToken("("); + Visit(expression); + WriteToken(")"); + return; + } + + Visit(expression); + } + + static bool RequiresParentheses(Expression expression) + { + switch (expression.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.Coalesce: + case ExpressionType.Decrement: + case ExpressionType.Divide: + case ExpressionType.Dynamic: + case ExpressionType.Equal: + case ExpressionType.ExclusiveOr: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.Increment: + case ExpressionType.LeftShift: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.Modulo: + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + case ExpressionType.Negate: + case ExpressionType.Not: + case ExpressionType.NotEqual: + case ExpressionType.OnesComplement: + case ExpressionType.Or: + case ExpressionType.OrElse: + case ExpressionType.Power: + case ExpressionType.RightShift: + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + case ExpressionType.UnaryPlus: + return true; + default: + return false; + } + } + + void VisitCheckedBinary(BinaryExpression node) + { + VisitChecked(() => VisitSimpleBinary(node)); + } + + void VisitChecked(Action action) + { + WriteKeyword("checked"); + WriteSpace(); + WriteToken("{"); + + WriteSpace(); + + action(); + + WriteSpace(); + + WriteToken("}"); + } + + static string GetBinaryOperator(ExpressionType type) + { + switch (type) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + return "+"; + case ExpressionType.AddAssign: + case ExpressionType.AddAssignChecked: + return "+="; + case ExpressionType.And: + return "&"; + case ExpressionType.AndAlso: + return "&&"; + case ExpressionType.AndAssign: + return "&="; + case ExpressionType.Assign: + return "="; + case ExpressionType.Coalesce: + return "??"; + case ExpressionType.Divide: + return "/"; + case ExpressionType.DivideAssign: + return "/="; + case ExpressionType.Equal: + return "=="; + case ExpressionType.ExclusiveOr: + return "^"; + case ExpressionType.ExclusiveOrAssign: + return "^="; + case ExpressionType.GreaterThan: + return ">"; + case ExpressionType.GreaterThanOrEqual: + return ">="; + case ExpressionType.LeftShift: + return "<<"; + case ExpressionType.LeftShiftAssign: + return "<<="; + case ExpressionType.LessThan: + return "<"; + case ExpressionType.LessThanOrEqual: + return "<="; + case ExpressionType.Modulo: + return "%"; + case ExpressionType.ModuloAssign: + return "%="; + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + return "*"; + case ExpressionType.MultiplyAssign: + case ExpressionType.MultiplyAssignChecked: + return "*="; + case ExpressionType.NotEqual: + return "!="; + case ExpressionType.Or: + return "|"; + case ExpressionType.OrAssign: + return "|="; + case ExpressionType.OrElse: + return "||"; + case ExpressionType.RightShift: + return ">>"; + case ExpressionType.RightShiftAssign: + return ">>="; + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + return "-"; + case ExpressionType.SubtractAssign: + case ExpressionType.SubtractAssignChecked: + return "-="; + default: + throw new NotImplementedException(type.ToString()); + } + } + + static bool IsChecked(ExpressionType type) + { + switch (type) + { + case ExpressionType.AddAssignChecked: + case ExpressionType.AddChecked: + case ExpressionType.MultiplyAssignChecked: + case ExpressionType.MultiplyChecked: + case ExpressionType.NegateChecked: + case ExpressionType.SubtractAssignChecked: + case ExpressionType.SubtractChecked: + return true; + default: + return false; + } + } + + protected override Expression VisitUnary(UnaryExpression node) + { + if (IsChecked(node.NodeType)) + { + VisitCheckedUnary(node); + return node; + } + + switch (node.NodeType) + { + case ExpressionType.Throw: + VisitThrow(node); + break; + case ExpressionType.IsTrue: + VisitIsTrue(node); + break; + case ExpressionType.IsFalse: + VisitIsFalse(node); + break; + case ExpressionType.ArrayLength: + VisitArrayLength(node); + break; + case ExpressionType.TypeAs: + VisitTypeAs(node); + break; + case ExpressionType.Increment: + VisitIncrement(node); + break; + case ExpressionType.Decrement: + VisitDecrement(node); + break; + case ExpressionType.PreDecrementAssign: + VisitPreDecrementAssign(node); + break; + case ExpressionType.PostDecrementAssign: + VisitPostDecrementAssign(node); + break; + case ExpressionType.PreIncrementAssign: + VisitPreIncrementAssign(node); + break; + case ExpressionType.PostIncrementAssign: + VisitPostIncrementAssign(node); + break; + case ExpressionType.ConvertChecked: + VisitConvertChecked(node); + break; + case ExpressionType.Convert: + case ExpressionType.Unbox: + VisitConvert(node); + break; + case ExpressionType.Quote: + Visit(node.Operand); + break; + default: + VisitSimpleUnary(node); + break; + } + + return node; + } + + void VisitConvert(UnaryExpression node) + { + WriteToken("("); + VisitType(node.Type); + WriteToken(")"); + + VisitParenthesizedExpression(node.Operand); + } + + void VisitConvertChecked(UnaryExpression node) + { + VisitChecked(() => VisitConvert(node)); + } + + void VisitPostIncrementAssign(UnaryExpression node) + { + Visit(node.Operand); + WriteToken("++"); + } + + void VisitPreIncrementAssign(UnaryExpression node) + { + WriteToken("++"); + Visit(node.Operand); + } + + void VisitPostDecrementAssign(UnaryExpression node) + { + Visit(node.Operand); + WriteToken("--"); + } + + void VisitPreDecrementAssign(UnaryExpression node) + { + WriteToken("--"); + Visit(node.Operand); + } + + void VisitDecrement(UnaryExpression node) + { + Visit(Expression.Subtract(node.Operand, Expression.Constant(1))); + } + + void VisitIncrement(UnaryExpression node) + { + Visit(Expression.Add(node.Operand, Expression.Constant(1))); + } + + void VisitIsTrue(UnaryExpression node) + { + Visit(Expression.Equal(node.Operand, Expression.Constant(true))); + } + + void VisitIsFalse(UnaryExpression node) + { + Visit(Expression.Equal(node.Operand, Expression.Constant(false))); + } + + void VisitArrayLength(UnaryExpression node) + { + Visit(Expression.Property(node.Operand, "Length")); + } + + void VisitTypeAs(UnaryExpression node) + { + Visit(node.Operand); + WriteSpace(); + WriteKeyword("as"); + WriteSpace(); + VisitType(node.Type); + } + + void VisitThrow(UnaryExpression node) + { + WriteKeyword("throw"); + WriteSpace(); + Visit(node.Operand); + } + + void VisitCheckedUnary(UnaryExpression node) + { + VisitChecked(() => VisitSimpleUnary(node)); + } + + void VisitSimpleUnary(UnaryExpression node) + { + WriteToken(GetUnaryOperator(node.NodeType)); + VisitParenthesizedExpression(node.Operand); + } + + static string GetUnaryOperator(ExpressionType type) + { + switch (type) + { + case ExpressionType.UnaryPlus: + return "+"; + case ExpressionType.Not: + return "!"; + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + return "-"; + case ExpressionType.OnesComplement: + return "~"; + default: + throw new NotImplementedException(type.ToString()); + } + } + + protected override Expression VisitParameter(ParameterExpression node) + { + WriteIdentifier(NameFor(node), node); + + return node; + } + + protected override Expression VisitConditional(ConditionalExpression node) + { + if (IsTernaryConditional(node)) + VisitConditionalExpression(node); + else + VisitConditionalStatement(node); + + return node; + } + + void VisitConditionalExpression(ConditionalExpression node) + { + WriteToken("("); + Visit(node.Test); + WriteSpace(); + WriteToken("?"); + WriteSpace(); + Visit(node.IfTrue); + WriteSpace(); + WriteToken(":"); + WriteSpace(); + Visit(node.IfFalse); + WriteToken(")"); + } + + void VisitConditionalStatement(ConditionalExpression node) + { + WriteKeyword("if"); + WriteSpace(); + WriteToken("("); + + Visit(node.Test); + + WriteToken(")"); + WriteLine(); + + VisitAsBlock(node.IfTrue); + + if (node.IfFalse != null) + { + WriteKeyword("else"); + WriteLine(); + + VisitAsBlock(node.IfFalse); + } + } + + static bool IsTernaryConditional(ConditionalExpression node) + { + return node.Type != typeof(void) && (node.IfTrue.NodeType != ExpressionType.Block + || (node.IfFalse != null && node.IfFalse.NodeType != ExpressionType.Block)); + } + + protected override Expression VisitGoto(GotoExpression node) + { + switch (node.Kind) + { + case GotoExpressionKind.Return: + WriteKeyword("return"); + WriteSpace(); + Visit(node.Value); + break; + case GotoExpressionKind.Break: + WriteKeyword("break"); + break; + case GotoExpressionKind.Continue: + WriteKeyword("continue"); + break; + case GotoExpressionKind.Goto: + WriteKeyword("goto"); + WriteSpace(); + Visit(node.Value); + break; + default: + throw new NotSupportedException(); + } + + return node; + } + + protected override Expression VisitConstant(ConstantExpression node) + { + WriteLiteral(GetLiteral(node.Value)); + + return node; + } + + static string GetLiteral(object value) + { + if (value == null) + return "null"; + + if (value.GetType().IsEnum) + return GetEnumLiteral(value); + + switch (Type.GetTypeCode(value.GetType())) + { + case TypeCode.Boolean: + return ((bool)value) ? "true" : "false"; + case TypeCode.Char: + return "'" + ((char)value) + "'"; + case TypeCode.String: + return "\"" + ((string)value) + "\""; + case TypeCode.Int32: + return ((IFormattable)value).ToString(null, System.Globalization.CultureInfo.InvariantCulture); + default: + return value.ToString(); + } + } + + static string GetEnumLiteral(object value) + { + var type = value.GetType(); + if (Enum.IsDefined(type, value)) + return type.Name + "." + Enum.GetName(type, value); + + throw new NotSupportedException(); + } + + protected override Expression VisitLabel(LabelExpression node) + { + return node; + } + + protected override LabelTarget VisitLabelTarget(LabelTarget target) + { + Dedent(); + WriteIdentifier(target.Name, target); + WriteToken(":"); + WriteLine(); + Indent(); + + return target; + } + + protected override Expression VisitInvocation(InvocationExpression node) + { + Visit(node.Expression); + VisitArguments(node.Arguments); + + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var method = node.Method; + + if (node.Object != null) + Visit(node.Object); + else + VisitType(method.DeclaringType); + + WriteToken("."); + + WriteReference(method.Name, method); + + if (method.IsGenericMethod && !method.IsGenericMethodDefinition) + VisitGenericArguments(method.GetGenericArguments()); + + VisitArguments(node.Arguments); + + return node; + } + + void VisitParenthesizedList(IList list, Action writer) + { + VisitList(list, "(", writer, ")"); + } + + void VisitBracedList(IList list, Action writer) + { + VisitList(list, "{", writer, "}"); + } + + void VisitBracketedList(IList list, Action writer) + { + VisitList(list, "[", writer, "]"); + } + + void VisitList(IList list, string opening, Action writer, string closing) + { + WriteToken(opening); + + for (int i = 0; i < list.Count; i++) + { + if (i > 0) + { + WriteToken(","); + WriteSpace(); + } + writer(list[i]); + } + + WriteToken(closing); + } + + void VisitArguments(IList expressions) + { + VisitParenthesizedList(expressions, e => Visit(e)); + } + + protected override Expression VisitNew(NewExpression node) + { + WriteKeyword("new"); + WriteSpace(); + VisitType(node.Constructor.DeclaringType); + VisitArguments(node.Arguments); + + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + if (node.Expression != null) + Visit(node.Expression); + else + VisitType(node.Member.DeclaringType); + + WriteToken("."); + WriteReference(node.Member.Name, node.Member); + + return node; + } + + protected override Expression VisitIndex(IndexExpression node) + { + Visit(node.Object); + VisitBracketedList(node.Arguments, expression => Visit(expression)); + + return node; + } -namespace Mono.Linq.Expressions { - - public class CSharpWriter : ExpressionWriter { - - readonly Dictionary unique_names = new Dictionary (); - - int unique_seed; - - public CSharpWriter (IFormatter formatter) - : base (formatter) - { - } - - public override void Write (LambdaExpression expression) - { - VisitLambdaSignature (expression); - VisitLambdaBody (expression); - } - - protected override Expression VisitLambda (Expression node) - { - VisitParameters (node); - WriteSpace (); - WriteToken ("=>"); - WriteLine (); - - VisitLambdaBody (node); - - return node; - } - - void VisitLambdaSignature (LambdaExpression node) - { - VisitType (node.ReturnType); - WriteSpace (); - WriteIdentifier (node.Name, node); - VisitParameters (node); - - WriteLine (); - } - - void VisitParameters (LambdaExpression node) - { - VisitParenthesizedList (node.Parameters, parameter => { - VisitType (parameter.Type); - WriteSpace (); - WriteIdentifier (NameFor (parameter), parameter); - }); - } - - string NameFor (ParameterExpression parameter) - { - if (!string.IsNullOrEmpty (parameter.Name)) - return parameter.Name; - - var name = GeneratedNameFor (parameter); - if (name != null) - return name; - - name = "var_$" + unique_seed++; - unique_names.Add (parameter, name); - return name; - } - - string GeneratedNameFor (ParameterExpression parameter) - { - string name; - if (!unique_names.TryGetValue (parameter, out name)) - return null; - - return name; - } - - void VisitLambdaBody (LambdaExpression node) - { - if (node.Body.NodeType != ExpressionType.Block) - VisitSingleExpressionBody (node); - else - VisitBlockExpressionBody (node); - } - - void VisitBlockExpressionBody (LambdaExpression node) - { - VisitBlockExpression ((BlockExpression) node.Body); - } - - static bool IsStatement (Expression expression) - { - switch (expression.NodeType) { - case ExpressionType.Conditional: - return !IsTernaryConditional ((ConditionalExpression) expression); - case ExpressionType.Try: - case ExpressionType.Loop: - case ExpressionType.Switch: - return true; - default: - var custom = expression as CustomExpression; - if (custom != null) - return IsStatement (custom); - - return false; - } - } - - static bool IsStatement (CustomExpression expression) - { - switch (expression.CustomNodeType) { - case CustomExpressionType.DoWhileExpression: - case CustomExpressionType.ForExpression: - case CustomExpressionType.ForEachExpression: - case CustomExpressionType.UsingExpression: - case CustomExpressionType.WhileExpression: - return true; - default: - return false; - } - } - - static bool IsActualStatement (Expression expression) - { - switch (expression.NodeType) { - case ExpressionType.Label: - return false; - case ExpressionType.Conditional: - return IsTernaryConditional ((ConditionalExpression) expression); - case ExpressionType.Try: - case ExpressionType.Loop: - case ExpressionType.Switch: - return false; - default: - return true; - } - } - - - void VisitSingleExpressionBody (LambdaExpression node) - { - VisitBlock (() => { - if (node.ReturnType != typeof (void) && !IsStatement (node.Body)) { - WriteKeyword ("return"); - WriteSpace (); - } - - Visit (node.Body); - - if (!IsStatement (node.Body)) { - WriteToken (";"); - WriteLine (); - } - }); - } - - void VisitType (Type type) - { - if (type.IsArray) { - VisitArrayType (type); - return; - } - - if (type.IsGenericParameter) { - WriteReference (type.Name, type); - return; - } - - if (type.IsGenericType && type.IsGenericTypeDefinition) { - VisitGenericTypeDefinition (type); - return; - } - - if (type.IsGenericType && !type.IsGenericTypeDefinition) { - VisitGenericTypeInstance (type); - return; - } - - VisitSimpleType (type); - } - - void VisitArrayType (Type type) - { - VisitType (type.GetElementType ()); - WriteToken ("["); - for (int i = 1; i < type.GetArrayRank (); i++) - WriteToken (","); - WriteToken ("]"); - } - - void VisitGenericTypeDefinition (Type type) - { - WriteReference (CleanGenericName (type), type); - WriteToken ("<"); - var arity = type.GetGenericArguments ().Length; - for (int i = 1; i < arity; i++) - WriteToken (","); - WriteToken (">"); - } - - void VisitGenericTypeInstance (Type type) - { - WriteReference (CleanGenericName (type), type); - - VisitGenericArguments (type.GetGenericArguments ()); - } - - void VisitGenericArguments (Type [] generic_arguments) - { - VisitList (generic_arguments, "<", VisitType, ">"); - } - - static string CleanGenericName (Type type) - { - var name = type.Name; - var position = name.LastIndexOf ("`"); - if (position == -1) - return name; - - return name.Substring (0, position); - } - - void VisitSimpleType (Type type) - { - WriteReference (GetSimpleTypeName (type), type); - } - - static string GetSimpleTypeName (Type type) - { - if (type == typeof (void)) - return "void"; - if (type == typeof (object)) - return "object"; - - switch (Type.GetTypeCode (type)) { - case TypeCode.Boolean: - return "bool"; - case TypeCode.Byte: - return "byte"; - case TypeCode.Char: - return "char"; - case TypeCode.Decimal: - return "decimal"; - case TypeCode.Double: - return "double"; - case TypeCode.Int16: - return "short"; - case TypeCode.Int32: - return "int"; - case TypeCode.Int64: - return "long"; - case TypeCode.SByte: - return "sbyte"; - case TypeCode.Single: - return "float"; - case TypeCode.String: - return "string"; - case TypeCode.UInt16: - return "ushort"; - case TypeCode.UInt32: - return "uint"; - case TypeCode.UInt64: - return "ulong"; - default: - return type.Name; - } - } - - protected override Expression VisitBlock (BlockExpression node) - { - VisitBlockExpression (node); - - return node; - } - - void VisitBlock (Action action) - { - WriteToken ("{"); - WriteLine (); - Indent (); - - action (); - - Dedent (); - WriteToken ("}"); - } - - void VisitBlockExpression (BlockExpression node) - { - VisitBlock (() => { - VisitBlockVariables (node); - VisitBlockExpressions (node); - }); - } - - void VisitBlockExpressions (BlockExpression node) - { - for (int i = 0; i < node.Expressions.Count; i++) { - var expression = node.Expressions [i]; - - if (IsActualStatement (expression) && RequiresExplicitReturn (node, i, node.Type != typeof (void))) { - WriteKeyword ("return"); - WriteSpace (); - } - - Write (expression); - - if (!IsActualStatement (expression)) - continue; - - WriteToken (";"); - WriteLine (); - } - } - - void VisitBlockVariables (BlockExpression node) - { - foreach (var variable in node.Variables) { - VisitType (variable.Type); - WriteSpace (); - WriteIdentifier (NameFor (variable), variable); - WriteToken (";"); - WriteLine (); - } - - if (node.Variables.Count > 0) - WriteLine (); - } - - static bool RequiresExplicitReturn (BlockExpression node, int index, bool return_last) - { - if (!return_last) - return false; - - var last_index = node.Expressions.Count - 1; - if (index != last_index) - return false; - - var last = node.Expressions [last_index]; - if (last.Is (ExpressionType.Goto) && ((GotoExpression) last).Kind == GotoExpressionKind.Return) - return false; - - return true; - } - - protected override Expression VisitBinary (BinaryExpression node) - { - if (IsChecked (node.NodeType)) - VisitCheckedBinary (node); - else if (node.Is (ExpressionType.Assign)) - VisitAssign (node); - else if (IsPower (node.NodeType)) - VisitPower (node); - else if (node.Is (ExpressionType.ArrayIndex)) - VisitArrayIndex (node); - else - VisitSimpleBinary (node); - - return node; - } - - void VisitArrayIndex (BinaryExpression node) - { - Visit (node.Left); - WriteToken ("["); - Visit (node.Right); - WriteToken ("]"); - } - - void VisitAssign (BinaryExpression node) - { - Visit (node.Left); - WriteSpace (); - WriteToken (GetBinaryOperator (node.NodeType)); - WriteSpace (); - Visit (node.Right); - } - - void VisitPower (BinaryExpression node) - { - var pow = Expression.Call (typeof (System.Math).GetMethod ("Pow"), node.Left, node.Right); - - if (node.Is (ExpressionType.Power)) - Visit (pow); - else if (node.Is (ExpressionType.PowerAssign)) - Visit (Expression.Assign (node.Left, pow)); - } - - static bool IsPower (ExpressionType type) - { - return type == ExpressionType.Power || type == ExpressionType.PowerAssign; - } - - void VisitSimpleBinary (BinaryExpression node) - { - VisitParenthesizedExpression (node.Left); - WriteSpace (); - WriteToken (GetBinaryOperator (node.NodeType)); - WriteSpace (); - VisitParenthesizedExpression (node.Right); - } - - void VisitParenthesizedExpression (Expression expression) - { - if (RequiresParentheses (expression)) { - WriteToken ("("); - Visit (expression); - WriteToken (")"); - return; - } - - Visit (expression); - } - - static bool RequiresParentheses (Expression expression) - { - switch (expression.NodeType) { - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.And: - case ExpressionType.AndAlso: - case ExpressionType.Coalesce: - case ExpressionType.Decrement: - case ExpressionType.Divide: - case ExpressionType.Equal: - case ExpressionType.ExclusiveOr: - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.Increment: - case ExpressionType.LeftShift: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.Modulo: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.Negate: - case ExpressionType.Not: - case ExpressionType.NotEqual: - case ExpressionType.OnesComplement: - case ExpressionType.Or: - case ExpressionType.OrElse: - case ExpressionType.Power: - case ExpressionType.RightShift: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - case ExpressionType.UnaryPlus: - return true; - default: - return false; - } - } - - void VisitCheckedBinary (BinaryExpression node) - { - VisitChecked (() => VisitSimpleBinary (node)); - } - - void VisitChecked (Action action) - { - WriteKeyword ("checked"); - WriteSpace (); - WriteToken ("{"); - - WriteSpace (); - - action (); - - WriteSpace (); - - WriteToken ("}"); - } - - static string GetBinaryOperator (ExpressionType type) - { - switch (type) { - case ExpressionType.Add: - case ExpressionType.AddChecked: - return "+"; - case ExpressionType.AddAssign: - case ExpressionType.AddAssignChecked: - return "+="; - case ExpressionType.And: - return "&"; - case ExpressionType.AndAlso: - return "&&"; - case ExpressionType.AndAssign: - return "&="; - case ExpressionType.Assign: - return "="; - case ExpressionType.Coalesce: - return "??"; - case ExpressionType.Divide: - return "/"; - case ExpressionType.DivideAssign: - return "/="; - case ExpressionType.Equal: - return "=="; - case ExpressionType.ExclusiveOr: - return "^"; - case ExpressionType.ExclusiveOrAssign: - return "^="; - case ExpressionType.GreaterThan: - return ">"; - case ExpressionType.GreaterThanOrEqual: - return ">="; - case ExpressionType.LeftShift: - return "<<"; - case ExpressionType.LeftShiftAssign: - return "<<="; - case ExpressionType.LessThan: - return "<"; - case ExpressionType.LessThanOrEqual: - return "<="; - case ExpressionType.Modulo: - return "%"; - case ExpressionType.ModuloAssign: - return "%="; - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - return "*"; - case ExpressionType.MultiplyAssign: - case ExpressionType.MultiplyAssignChecked: - return "*="; - case ExpressionType.NotEqual: - return "!="; - case ExpressionType.Or: - return "|"; - case ExpressionType.OrAssign: - return "|="; - case ExpressionType.OrElse: - return "||"; - case ExpressionType.RightShift: - return ">>"; - case ExpressionType.RightShiftAssign: - return ">>="; - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - return "-"; - case ExpressionType.SubtractAssign: - case ExpressionType.SubtractAssignChecked: - return "-="; - default: - throw new NotImplementedException (type.ToString ()); - } - } - - static bool IsChecked (ExpressionType type) - { - switch (type) { - case ExpressionType.AddAssignChecked: - case ExpressionType.AddChecked: - case ExpressionType.MultiplyAssignChecked: - case ExpressionType.MultiplyChecked: - case ExpressionType.NegateChecked: - case ExpressionType.SubtractAssignChecked: - case ExpressionType.SubtractChecked: - return true; - default: - return false; - } - } - - protected override Expression VisitUnary (UnaryExpression node) - { - if (IsChecked (node.NodeType)) { - VisitCheckedUnary (node); - return node; - } - - switch (node.NodeType) { - case ExpressionType.Throw: - VisitThrow (node); - break; - case ExpressionType.IsTrue: - VisitIsTrue (node); - break; - case ExpressionType.IsFalse: - VisitIsFalse (node); - break; - case ExpressionType.ArrayLength: - VisitArrayLength (node); - break; - case ExpressionType.TypeAs: - VisitTypeAs (node); - break; - case ExpressionType.Increment: - VisitIncrement (node); - break; - case ExpressionType.Decrement: - VisitDecrement (node); - break; - case ExpressionType.PreDecrementAssign: - VisitPreDecrementAssign (node); - break; - case ExpressionType.PostDecrementAssign: - VisitPostDecrementAssign (node); - break; - case ExpressionType.PreIncrementAssign: - VisitPreIncrementAssign (node); - break; - case ExpressionType.PostIncrementAssign: - VisitPostIncrementAssign (node); - break; - case ExpressionType.ConvertChecked: - VisitConvertChecked (node); - break; - case ExpressionType.Convert: - case ExpressionType.Unbox: - VisitConvert (node); - break; - case ExpressionType.Quote: - Visit (node.Operand); - break; - default: - VisitSimpleUnary (node); - break; - } - - return node; - } - - void VisitConvert (UnaryExpression node) - { - WriteToken ("("); - VisitType (node.Type); - WriteToken (")"); - - VisitParenthesizedExpression (node.Operand); - } - - void VisitConvertChecked (UnaryExpression node) - { - VisitChecked (() => VisitConvert (node)); - } - - void VisitPostIncrementAssign (UnaryExpression node) - { - Visit (node.Operand); - WriteToken ("++"); - } - - void VisitPreIncrementAssign (UnaryExpression node) - { - WriteToken ("++"); - Visit (node.Operand); - } - - void VisitPostDecrementAssign (UnaryExpression node) - { - Visit (node.Operand); - WriteToken ("--"); - } - - void VisitPreDecrementAssign (UnaryExpression node) - { - WriteToken ("--"); - Visit (node.Operand); - } - - void VisitDecrement (UnaryExpression node) - { - Visit (Expression.Subtract (node.Operand, Expression.Constant (1))); - } - - void VisitIncrement (UnaryExpression node) - { - Visit (Expression.Add (node.Operand, Expression.Constant (1))); - } - - void VisitIsTrue (UnaryExpression node) - { - Visit (Expression.Equal (node.Operand, Expression.Constant (true))); - } - - void VisitIsFalse (UnaryExpression node) - { - Visit (Expression.Equal (node.Operand, Expression.Constant (false))); - } - - void VisitArrayLength (UnaryExpression node) - { - Visit (Expression.Property (node.Operand, "Length")); - } - - void VisitTypeAs (UnaryExpression node) - { - Visit (node.Operand); - WriteSpace (); - WriteKeyword ("as"); - WriteSpace (); - VisitType (node.Type); - } - - void VisitThrow (UnaryExpression node) - { - WriteKeyword ("throw"); - WriteSpace (); - Visit (node.Operand); - } - - void VisitCheckedUnary (UnaryExpression node) - { - VisitChecked (() => VisitSimpleUnary (node)); - } - - void VisitSimpleUnary (UnaryExpression node) - { - WriteToken (GetUnaryOperator (node.NodeType)); - VisitParenthesizedExpression (node.Operand); - } - - static string GetUnaryOperator (ExpressionType type) - { - switch (type) { - case ExpressionType.UnaryPlus: - return "+"; - case ExpressionType.Not: - return "!"; - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - return "-"; - case ExpressionType.OnesComplement: - return "~"; - default: - throw new NotImplementedException (type.ToString ()); - } - } - - protected override Expression VisitParameter (ParameterExpression node) - { - WriteIdentifier (NameFor (node), node); - - return node; - } - - protected override Expression VisitConditional (ConditionalExpression node) - { - if (IsTernaryConditional (node)) - VisitConditionalExpression (node); - else - VisitConditionalStatement (node); - - return node; - } - - void VisitConditionalExpression (ConditionalExpression node) - { - Visit (node.Test); - WriteSpace (); - WriteToken ("?"); - WriteSpace (); - Visit (node.IfTrue); - WriteSpace (); - WriteToken (":"); - WriteSpace (); - Visit (node.IfFalse); - } - - void VisitConditionalStatement (ConditionalExpression node) - { - WriteKeyword ("if"); - WriteSpace (); - WriteToken ("("); - - Visit (node.Test); - - WriteToken (")"); - WriteLine (); - - VisitAsBlock (node.IfTrue); - - if (node.IfFalse != null) { - WriteKeyword ("else"); - WriteLine (); - - VisitAsBlock (node.IfFalse); - } - } - - static bool IsTernaryConditional (ConditionalExpression node) - { - return node.Type != typeof (void) && (node.IfTrue.NodeType != ExpressionType.Block - || (node.IfFalse != null && node.IfFalse.NodeType != ExpressionType.Block)); - } - - protected override Expression VisitGoto (GotoExpression node) - { - switch (node.Kind) { - case GotoExpressionKind.Return: - WriteKeyword ("return"); - WriteSpace (); - Visit (node.Value); - break; - case GotoExpressionKind.Break: - WriteKeyword ("break"); - break; - case GotoExpressionKind.Continue: - WriteKeyword ("continue"); - break; - case GotoExpressionKind.Goto: - WriteKeyword ("goto"); - WriteSpace (); - Visit (node.Value); - break; - default: - throw new NotSupportedException (); - } - - return node; - } - - protected override Expression VisitConstant (ConstantExpression node) - { - WriteLiteral (GetLiteral (node.Value)); - - return node; - } - - static string GetLiteral (object value) - { - if (value == null) - return "null"; - - if (value.GetType ().IsEnum) - return GetEnumLiteral (value); - - switch (Type.GetTypeCode (value.GetType ())) { - case TypeCode.Boolean: - return ((bool) value) ? "true" : "false"; - case TypeCode.Char: - return "'" + ((char) value) + "'"; - case TypeCode.String: - return "\"" + ((string) value) + "\""; - case TypeCode.Int32: - return ((IFormattable) value).ToString (null, System.Globalization.CultureInfo.InvariantCulture); - default: - return value.ToString (); - } - } - - static string GetEnumLiteral (object value) - { - var type = value.GetType (); - if (Enum.IsDefined (type, value)) - return type.Name + "." + Enum.GetName (type, value); - - throw new NotSupportedException (); - } - - protected override Expression VisitLabel (LabelExpression node) - { - return node; - } - - protected override LabelTarget VisitLabelTarget (LabelTarget target) - { - Dedent (); - WriteIdentifier (target.Name, target); - WriteToken (":"); - WriteLine (); - Indent (); - - return target; - } - - protected override Expression VisitInvocation (InvocationExpression node) - { - Visit (node.Expression); - VisitArguments (node.Arguments); - - return node; - } - - protected override Expression VisitMethodCall (MethodCallExpression node) - { - var method = node.Method; - - if (node.Object != null) - Visit (node.Object); - else - VisitType (method.DeclaringType); - - WriteToken ("."); - - WriteReference (method.Name, method); - - if (method.IsGenericMethod && !method.IsGenericMethodDefinition) - VisitGenericArguments (method.GetGenericArguments ()); - - VisitArguments (node.Arguments); - - return node; - } - - void VisitParenthesizedList (IList list, Action writer) - { - VisitList (list, "(", writer, ")"); - } - - void VisitBracedList (IList list, Action writer) - { - VisitList (list, "{", writer, "}"); - } - - void VisitBracketedList (IList list, Action writer) - { - VisitList (list, "[", writer, "]"); - } - - void VisitList (IList list, string opening, Action writer, string closing) - { - WriteToken (opening); - - for (int i = 0; i < list.Count; i++) { - if (i > 0) { - WriteToken (","); - WriteSpace (); - } - - writer (list [i]); - } - - WriteToken (closing); - } - - void VisitArguments (IList expressions) - { - VisitParenthesizedList (expressions, e => Visit (e)); - } - - protected override Expression VisitNew (NewExpression node) - { - WriteKeyword ("new"); - WriteSpace (); - VisitType (node.Constructor.DeclaringType); - VisitArguments (node.Arguments); + protected override Expression VisitNewArray(NewArrayExpression node) + { + if (node.Is(ExpressionType.NewArrayInit)) + VisitNewArrayInit(node); + else if (node.Is(ExpressionType.NewArrayBounds)) + VisitNewArrayBounds(node); - return node; - } + return node; + } - protected override Expression VisitMember (MemberExpression node) - { - if (node.Expression != null) - Visit (node.Expression); - else - VisitType (node.Member.DeclaringType); + void VisitNewArrayBounds(NewArrayExpression node) + { + WriteKeyword("new"); + WriteSpace(); + VisitType(node.Type.GetElementType()); - WriteToken ("."); - WriteReference (node.Member.Name, node.Member); + VisitBracketedList(node.Expressions, expression => Visit(expression)); + } - return node; - } - - protected override Expression VisitIndex (IndexExpression node) - { - Visit (node.Object); - VisitBracketedList (node.Arguments, expression => Visit (expression)); - - return node; - } - - protected override Expression VisitNewArray (NewArrayExpression node) - { - if (node.Is (ExpressionType.NewArrayInit)) - VisitNewArrayInit (node); - else if (node.Is (ExpressionType.NewArrayBounds)) - VisitNewArrayBounds (node); - - return node; - } - - void VisitNewArrayBounds (NewArrayExpression node) - { - WriteKeyword ("new"); - WriteSpace (); - VisitType (node.Type.GetElementType ()); - - VisitBracketedList (node.Expressions, expression => Visit (expression)); - } - - void VisitNewArrayInit (NewArrayExpression node) - { - WriteKeyword ("new"); - WriteSpace (); - VisitType (node.Type); - WriteSpace (); - - VisitBracedList (node.Expressions, expression => Visit (expression)); - } - - protected override Expression VisitListInit (ListInitExpression node) - { - Visit (node.NewExpression); - WriteSpace (); - - VisitInitializers (node.Initializers); - - return node; - } - - void VisitInitializers (IList initializers) - { - VisitBracedList (initializers, initializer => VisitElementInit (initializer)); - } - - protected override Expression VisitMemberInit (MemberInitExpression node) - { - Visit (node.NewExpression); - - VisitBindings (node.Bindings); - - return node; - } - - void VisitBindings (IList bindings) - { - WriteLine (); - - WriteToken ("{"); - WriteLine (); - Indent (); - - for (int i = 0; i < bindings.Count; i++) { - var binding = bindings [i]; - - VisitMemberBinding (binding); - - if (i < bindings.Count - 1) - WriteToken (","); - - WriteLine (); - } - - Dedent (); - WriteToken ("}"); - } - - protected override MemberAssignment VisitMemberAssignment (MemberAssignment node) - { - VisitMemberBindingMember (node); - WriteSpace (); - - Visit (node.Expression); - - return node; - } - - void VisitMemberBindingMember (MemberBinding node) - { - WriteReference (node.Member.Name, node.Member); - WriteSpace (); - WriteToken ("="); - } - - protected override MemberListBinding VisitMemberListBinding (MemberListBinding node) - { - VisitMemberBindingMember (node); - WriteSpace (); - - VisitInitializers (node.Initializers); - - return node; - } - - protected override MemberMemberBinding VisitMemberMemberBinding (MemberMemberBinding node) - { - VisitMemberBindingMember (node); - - VisitBindings (node.Bindings); - - return node; - } - - protected override ElementInit VisitElementInit (ElementInit node) - { - if (node.Arguments.Count == 1) - Visit (node.Arguments [0]); - else - VisitBracedList (node.Arguments, expression => Visit (expression)); - - return node; - } - - protected override Expression VisitTypeBinary (TypeBinaryExpression node) - { - if (node.Is (ExpressionType.TypeEqual)) - VisitTypeEqual (node); - else if (node.Is (ExpressionType.TypeIs)) - VisitTypeIs (node); - - return node; - } - - void VisitTypeIs (TypeBinaryExpression node) - { - Visit (node.Expression); - WriteSpace (); - WriteKeyword ("is"); - WriteSpace (); - VisitType (node.TypeOperand); - } - - void VisitTypeEqual (TypeBinaryExpression node) - { - Visit (Expression.Call ( - node.Expression, - typeof (object).GetMethod ("GetType", Type.EmptyTypes))); - - WriteSpace (); - WriteToken ("=="); - WriteSpace (); - - WriteKeyword ("typeof"); - WriteToken ("("); - VisitType (node.TypeOperand); - WriteToken (")"); - } - - protected override Expression VisitTry (TryExpression node) - { - WriteKeyword ("try"); - WriteLine (); - VisitAsBlock (node.Body); - - foreach (var handler in node.Handlers) - VisitCatchBlock (handler); - - if (node.Fault != null) { - WriteKeyword ("fault"); - WriteLine (); - VisitAsBlock (node.Fault); - } - - if (node.Finally != null) { - WriteKeyword ("finally"); - WriteLine (); - VisitAsBlock (node.Finally); - } - - return node; - } - - void VisitAsBlock (Expression node) - { - Visit (node.Is (ExpressionType.Block) ? node : Expression.Block (node)); - WriteLine (); - } - - protected override CatchBlock VisitCatchBlock (CatchBlock node) - { - WriteKeyword ("catch"); - - WriteSpace (); - WriteToken ("("); - VisitType (node.Test); - if (node.Variable != null) { - WriteSpace (); - WriteIdentifier (NameFor (node.Variable), node.Variable); - } - WriteToken (")"); - - if (node.Filter != null) { - WriteSpace (); - WriteKeyword ("if"); - WriteSpace (); - WriteToken ("("); - Visit (node.Filter); - WriteToken (")"); - } - WriteLine (); - - VisitAsBlock (node.Body); - - return node; - } - - protected override Expression VisitLoop (LoopExpression node) - { - WriteKeyword ("for"); - WriteSpace (); - WriteToken ("("); - WriteToken (";"); - WriteToken (";"); - WriteToken (")"); - WriteLine (); - - VisitAsBlock (node.Body); - - return node; - } - - protected override Expression VisitSwitch (SwitchExpression node) - { - WriteKeyword ("switch"); - WriteSpace (); - WriteToken ("("); - Visit (node.SwitchValue); - WriteToken (")"); - WriteLine (); - - VisitBlock (() => { - foreach (var @case in node.Cases) - VisitSwitchCase (@case); - - if (node.DefaultBody != null) { - WriteKeyword ("default"); - WriteToken (":"); - WriteLine (); - - VisitAsBlock (node.DefaultBody); - } - }); - - WriteLine (); - - return node; - } - - protected override SwitchCase VisitSwitchCase (SwitchCase node) - { - foreach (var value in node.TestValues) { - WriteKeyword ("case"); - WriteSpace (); - Visit (value); - WriteToken (":"); - WriteLine (); - } - - VisitAsBlock (node.Body); - - return node; - } - - protected override Expression VisitDefault (DefaultExpression node) - { - WriteKeyword ("default"); - WriteToken ("("); - VisitType (node.Type); - WriteToken (")"); - - return node; - } - - protected internal override Expression VisitForExpression (ForExpression node) - { - WriteKeyword ("for"); - WriteSpace (); - WriteToken ("("); - VisitType (node.Variable.Type); - WriteSpace (); - Visit (node.Variable); - WriteSpace (); - WriteToken ("="); - WriteSpace (); - Visit (node.Initializer); - WriteToken (";"); - WriteSpace (); - Visit (node.Test); - WriteToken (";"); - WriteSpace (); - Visit (node.Step); - WriteToken (")"); - WriteLine (); - - VisitAsBlock (node.Body); - - return node; - } - - protected internal override Expression VisitForEachExpression (ForEachExpression node) - { - WriteKeyword ("foreach"); - WriteSpace (); - WriteToken ("("); - VisitType (node.Variable.Type); - WriteSpace (); - Visit (node.Variable); - WriteSpace (); - WriteKeyword ("in"); - WriteSpace (); - Visit (node.Enumerable); - WriteToken (")"); - WriteLine (); - - VisitAsBlock (node.Body); - - return node; - } - - protected internal override Expression VisitUsingExpression (UsingExpression node) - { - WriteKeyword ("using"); - WriteSpace (); - WriteToken ("("); - Visit (node.Disposable); - WriteToken (")"); - WriteLine (); - - VisitAsBlock (node.Body); - - return node; - } - - protected internal override Expression VisitDoWhileExpression (DoWhileExpression node) - { - WriteKeyword ("do"); - WriteLine (); - - VisitAsBlock (node.Body); - - WriteKeyword ("while"); - WriteSpace (); - WriteToken ("("); - Visit (node.Test); - WriteToken (")"); - WriteToken (";"); - WriteLine (); - - return node; - } - - protected internal override Expression VisitWhileExpression (WhileExpression node) - { - WriteKeyword ("while"); - WriteSpace (); - WriteToken ("("); - Visit (node.Test); - WriteToken (")"); - WriteLine (); - - VisitAsBlock (node.Body); - - return node; - } - } + void VisitNewArrayInit(NewArrayExpression node) + { + WriteKeyword("new"); + WriteSpace(); + VisitType(node.Type); + WriteSpace(); + + VisitBracedList(node.Expressions, expression => Visit(expression)); + } + + protected override Expression VisitListInit(ListInitExpression node) + { + Visit(node.NewExpression); + WriteSpace(); + + VisitInitializers(node.Initializers); + + return node; + } + + void VisitInitializers(IList initializers) + { + VisitBracedList(initializers, initializer => VisitElementInit(initializer)); + } + + protected override Expression VisitMemberInit(MemberInitExpression node) + { + Visit(node.NewExpression); + + VisitBindings(node.Bindings); + + return node; + } + + void VisitBindings(IList bindings) + { + WriteLine(); + + WriteToken("{"); + WriteLine(); + Indent(); + + for (int i = 0; i < bindings.Count; i++) + { + var binding = bindings[i]; + + VisitMemberBinding(binding); + + if (i < bindings.Count - 1) + WriteToken(","); + + WriteLine(); + } + + Dedent(); + WriteToken("}"); + } + + protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) + { + VisitMemberBindingMember(node); + WriteSpace(); + + Visit(node.Expression); + + return node; + } + + void VisitMemberBindingMember(MemberBinding node) + { + WriteReference(node.Member.Name, node.Member); + WriteSpace(); + WriteToken("="); + } + + protected override MemberListBinding VisitMemberListBinding(MemberListBinding node) + { + VisitMemberBindingMember(node); + WriteSpace(); + + VisitInitializers(node.Initializers); + + return node; + } + + protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node) + { + VisitMemberBindingMember(node); + + VisitBindings(node.Bindings); + + return node; + } + + protected override ElementInit VisitElementInit(ElementInit node) + { + if (node.Arguments.Count == 1) + Visit(node.Arguments[0]); + else + VisitBracedList(node.Arguments, expression => Visit(expression)); + + return node; + } + + protected override Expression VisitTypeBinary(TypeBinaryExpression node) + { + if (node.Is(ExpressionType.TypeEqual)) + VisitTypeEqual(node); + else if (node.Is(ExpressionType.TypeIs)) + VisitTypeIs(node); + + return node; + } + + void VisitTypeIs(TypeBinaryExpression node) + { + Visit(node.Expression); + WriteSpace(); + WriteKeyword("is"); + WriteSpace(); + VisitType(node.TypeOperand); + } + + void VisitTypeEqual(TypeBinaryExpression node) + { + Visit(Expression.Call( + node.Expression, + typeof(object).GetMethod("GetType", Type.EmptyTypes))); + + WriteSpace(); + WriteToken("=="); + WriteSpace(); + + WriteKeyword("typeof"); + WriteToken("("); + VisitType(node.TypeOperand); + WriteToken(")"); + } + + protected override Expression VisitTry(TryExpression node) + { + WriteKeyword("try"); + WriteLine(); + VisitAsBlock(node.Body); + + foreach (var handler in node.Handlers) + VisitCatchBlock(handler); + + if (node.Fault != null) + { + WriteKeyword("fault"); + WriteLine(); + VisitAsBlock(node.Fault); + } + + if (node.Finally != null) + { + WriteKeyword("finally"); + WriteLine(); + VisitAsBlock(node.Finally); + } + + return node; + } + + void VisitAsBlock(Expression node) + { + Visit(node.Is(ExpressionType.Block) ? node : Expression.Block(node)); + WriteLine(); + } + + protected override CatchBlock VisitCatchBlock(CatchBlock node) + { + WriteKeyword("catch"); + + WriteSpace(); + WriteToken("("); + VisitType(node.Test); + if (node.Variable != null) + { + WriteSpace(); + WriteIdentifier(NameFor(node.Variable), node.Variable); + } + WriteToken(")"); + + if (node.Filter != null) + { + WriteSpace(); + WriteKeyword("if"); + WriteSpace(); + WriteToken("("); + Visit(node.Filter); + WriteToken(")"); + } + WriteLine(); + + VisitAsBlock(node.Body); + + return node; + } + + protected override Expression VisitLoop(LoopExpression node) + { + WriteKeyword("for"); + WriteSpace(); + WriteToken("("); + WriteToken(";"); + WriteToken(";"); + WriteToken(")"); + WriteLine(); + + VisitAsBlock(node.Body); + + return node; + } + + protected override Expression VisitSwitch(SwitchExpression node) + { + WriteKeyword("switch"); + WriteSpace(); + WriteToken("("); + Visit(node.SwitchValue); + WriteToken(")"); + WriteLine(); + + VisitBlock(() => + { + foreach (var @case in node.Cases) + VisitSwitchCase(@case); + + if (node.DefaultBody != null) + { + WriteKeyword("default"); + WriteToken(":"); + WriteLine(); + + VisitAsBlock(node.DefaultBody); + } + }); + + WriteLine(); + + return node; + } + + protected override SwitchCase VisitSwitchCase(SwitchCase node) + { + foreach (var value in node.TestValues) + { + WriteKeyword("case"); + WriteSpace(); + Visit(value); + WriteToken(":"); + WriteLine(); + } + + VisitAsBlock(node.Body); + + return node; + } + + protected override Expression VisitDefault(DefaultExpression node) + { + WriteKeyword("default"); + WriteToken("("); + VisitType(node.Type); + WriteToken(")"); + + return node; + } + + protected internal override Expression VisitForExpression(ForExpression node) + { + WriteKeyword("for"); + WriteSpace(); + WriteToken("("); + VisitType(node.Variable.Type); + WriteSpace(); + Visit(node.Variable); + WriteSpace(); + WriteToken("="); + WriteSpace(); + Visit(node.Initializer); + WriteToken(";"); + WriteSpace(); + Visit(node.Test); + WriteToken(";"); + WriteSpace(); + Visit(node.Step); + WriteToken(")"); + WriteLine(); + + VisitAsBlock(node.Body); + + return node; + } + + protected internal override Expression VisitForEachExpression(ForEachExpression node) + { + WriteKeyword("foreach"); + WriteSpace(); + WriteToken("("); + VisitType(node.Variable.Type); + WriteSpace(); + Visit(node.Variable); + WriteSpace(); + WriteKeyword("in"); + WriteSpace(); + Visit(node.Enumerable); + WriteToken(")"); + WriteLine(); + + VisitAsBlock(node.Body); + + return node; + } + + protected internal override Expression VisitUsingExpression(UsingExpression node) + { + WriteKeyword("using"); + WriteSpace(); + WriteToken("("); + Visit(node.Disposable); + WriteToken(")"); + WriteLine(); + + VisitAsBlock(node.Body); + + return node; + } + + protected internal override Expression VisitDoWhileExpression(DoWhileExpression node) + { + WriteKeyword("do"); + WriteLine(); + + VisitAsBlock(node.Body); + + WriteKeyword("while"); + WriteSpace(); + WriteToken("("); + Visit(node.Test); + WriteToken(")"); + WriteToken(";"); + WriteLine(); + + return node; + } + + protected internal override Expression VisitWhileExpression(WhileExpression node) + { + WriteKeyword("while"); + WriteSpace(); + WriteToken("("); + Visit(node.Test); + WriteToken(")"); + WriteLine(); + + VisitAsBlock(node.Body); + + return node; + } + + protected override Expression VisitDynamic(DynamicExpression node) + { + if (typeof(GetMemberBinder).IsAssignableFrom(node.Binder.GetType())) + { + var binder = (GetMemberBinder)node.Binder; + Visit(node.Arguments[0]); + Write("."); + Write(binder.Name); + } + else if (typeof(SetMemberBinder).IsAssignableFrom(node.Binder.GetType())) + { + var binder = (SetMemberBinder)node.Binder; + Visit(node.Arguments[0]); + Write("."); + Write(binder.Name); + } + else if (typeof(BinaryOperationBinder).IsAssignableFrom(node.Binder.GetType())) + { + var binder = (BinaryOperationBinder)node.Binder; + Visit(node.Arguments[0]); + Write(binder.Operation); + Visit(node.Arguments[1]); + } + else if (typeof(UnaryOperationBinder).IsAssignableFrom(node.Binder.GetType())) + { + var binder = (UnaryOperationBinder)node.Binder; + Write(binder.Operation); + Visit(node.Arguments[0]); + } + else if (typeof(InvokeMemberBinder).IsAssignableFrom(node.Binder.GetType())) + { + var binder = (InvokeMemberBinder)node.Binder; + Visit(node.Arguments[0]); + WriteToken("."); + Write(binder.Name); + WriteToken("("); + foreach (var expression in node.Arguments.Skip(1)) + { + Visit(expression); + if (expression != node.Arguments.Last()) + { + WriteToken(","); + WriteSpace(); + } + } + WriteToken(")"); + } + return node; + } + } } diff --git a/Mono.Linq.Expressions/ExpressionWriter.cs b/Mono.Linq.Expressions/ExpressionWriter.cs index af364fa..be83ff0 100644 --- a/Mono.Linq.Expressions/ExpressionWriter.cs +++ b/Mono.Linq.Expressions/ExpressionWriter.cs @@ -26,6 +26,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Linq.Expressions; namespace Mono.Linq.Expressions { @@ -49,6 +50,144 @@ public virtual void Write (Expression expression) Visit (expression); } + public virtual void Write(ExpressionType expressionType) + { + WriteSpace(); + switch (expressionType) + { + case ExpressionType.Add: + Write("+"); + break; + case ExpressionType.AddChecked: + Write("+"); + break; + case ExpressionType.And: + Write("&"); + break; + case ExpressionType.AndAlso: + Write("&&"); + break; + case ExpressionType.Coalesce: + Write("??"); + break; + case ExpressionType.Divide: + Write("/"); + break; + case ExpressionType.Equal: + Write("=="); + break; + case ExpressionType.ExclusiveOr: + Write("^"); + break; + case ExpressionType.GreaterThan: + Write(">"); + break; + case ExpressionType.GreaterThanOrEqual: + Write(">="); + break; + case ExpressionType.LeftShift: + Write("<<"); + break; + case ExpressionType.LessThan: + Write("<"); + break; + case ExpressionType.LessThanOrEqual: + Write("<="); + break; + case ExpressionType.Modulo: + Write("%"); + break; + case ExpressionType.Multiply: + Write("*"); + break; + case ExpressionType.MultiplyChecked: + Write("*"); + break; + case ExpressionType.Negate: + Write("-"); + break; + case ExpressionType.UnaryPlus: + Write("+"); + break; + case ExpressionType.NegateChecked: + Write("-"); + break; + case ExpressionType.Not: + Write("!"); + break; + case ExpressionType.NotEqual: + Write("!="); + break; + case ExpressionType.Or: + Write("|"); + break; + case ExpressionType.OrElse: + Write("||"); + break; + case ExpressionType.RightShift: + Write(">>"); + break; + case ExpressionType.Subtract: + Write("-"); + break; + case ExpressionType.SubtractChecked: + Write("-"); + break; + case ExpressionType.Assign: + Write("="); + break; + case ExpressionType.Decrement: + Write("--"); + break; + case ExpressionType.Increment: + Write("++"); + break; + case ExpressionType.AddAssign: + Write("+="); + break; + case ExpressionType.AndAssign: + Write("&="); + break; + case ExpressionType.DivideAssign: + Write("/="); + break; + case ExpressionType.ExclusiveOrAssign: + Write("^="); + break; + case ExpressionType.LeftShiftAssign: + Write("<<="); + break; + case ExpressionType.ModuloAssign: + Write("%="); + break; + case ExpressionType.MultiplyAssign: + Write("*="); + break; + case ExpressionType.OrAssign: + Write("|="); + break; + case ExpressionType.RightShiftAssign: + Write(">>="); + break; + case ExpressionType.SubtractAssign: + Write("-="); + break; + case ExpressionType.AddAssignChecked: + Write("+="); + break; + case ExpressionType.MultiplyAssignChecked: + Write("*="); + break; + case ExpressionType.SubtractAssignChecked: + Write("-="); + break; + case ExpressionType.OnesComplement: + Write("~"); + break; + } + WriteSpace(); + } + public virtual void Write (ElementInit initializer) { VisitElementInit (initializer); diff --git a/dbg/Program.cs b/dbg/Program.cs index 613f5b3..3d58b45 100644 --- a/dbg/Program.cs +++ b/dbg/Program.cs @@ -1,9 +1,14 @@ using System; +using System.Collections.Generic; +using System.Dynamic; using System.IO; +using System.Linq; using System.Linq.Expressions; using System.Reflection; - +using Microsoft.CSharp.RuntimeBinder; using Mono.Linq.Expressions; +//using Binder = System.Reflection.Binder; +using Binder = Microsoft.CSharp.RuntimeBinder.Binder; class Program { @@ -12,6 +17,31 @@ static void Main (string [] args) var a = Expression.Parameter (typeof (int), "a"); var b = Expression.Parameter (typeof (int), "b"); + var scope = Expression.Parameter(typeof(object), "scope"); + + var instance = Expression.Constant(scope); + + var binder = Binder.GetMember( + CSharpBinderFlags.None, + "Text", + typeof(object), + new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) } + ); + + var expArgs = new List() { instance }; + + var binderM = Binder.InvokeMember( + CSharpBinderFlags.None, + "ToString", + null, + typeof(object), + expArgs.Select(x => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)) + ); + + Expression resultm = Expression.Dynamic(binderM, typeof(object), expArgs); + + Expression result = Expression.Dynamic(binder, typeof(object), instance); + var return_label = Expression.Label (typeof (int), "ret"); var c = Expression.Parameter (typeof (int), "c"); @@ -27,17 +57,19 @@ static void Main (string [] args) new [] { d }, Expression.Assign (d, Expression.SubtractChecked (a, b)), Expression.SubtractAssignChecked (d, Expression.Constant (42)), + result, + resultm, d); var conditional = Expression.Condition ( Expression.GreaterThan (a, b), left, right); - var lambda = Expression.Lambda> (conditional, a, b); + var lambda = Expression.Lambda> (conditional, a, b); var add = lambda.Compile (); - Console.WriteLine (add (2, 2)); + //Console.WriteLine (add (2, 2)); Console.WriteLine ("--------------------");