Merge pull request #1851 from esdrubal/mmf
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / DebugViewWriter.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Diagnostics;
19 using System.Dynamic;
20 using System.Dynamic.Utils;
21 using System.Globalization;
22 using System.IO;
23 using System.Reflection;
24 using System.Runtime.CompilerServices;
25 using System.Collections.ObjectModel;
26
27 #if !FEATURE_CORE_DLR
28 namespace Microsoft.Scripting.Ast {
29 #else
30 namespace System.Linq.Expressions {
31 #endif
32     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
33     internal sealed class DebugViewWriter : ExpressionVisitor {
34         [Flags]
35         private enum Flow {
36             None,
37             Space,
38             NewLine,
39
40             Break = 0x8000      // newline if column > MaxColumn
41         };
42
43         private const int Tab = 4;
44         private const int MaxColumn = 120;
45
46         private TextWriter _out;
47         private int _column;
48
49         private Stack<int> _stack = new Stack<int>();
50         private int _delta;
51         private Flow _flow;
52
53         // All the unique lambda expressions in the ET, will be used for displaying all
54         // the lambda definitions.
55         private Queue<LambdaExpression> _lambdas;
56
57         // Associate every unique anonymous LambdaExpression in the tree with an integer.
58         // The id is used to create a name for the anonymous lambda.
59         //
60         private Dictionary<LambdaExpression, int> _lambdaIds;
61
62         // Associate every unique anonymous parameter or variable in the tree with an integer.
63         // The id is used to create a name for the anonymous parameter or variable.
64         //
65         private Dictionary<ParameterExpression, int> _paramIds;
66
67         // Associate every unique anonymous LabelTarget in the tree with an integer.
68         // The id is used to create a name for the anonymous LabelTarget.
69         //
70         private Dictionary<LabelTarget, int> _labelIds;
71
72         private DebugViewWriter(TextWriter file) {
73             _out = file;
74         }
75
76         private int Base {
77             get {
78                 return _stack.Count > 0 ? _stack.Peek() : 0;
79             }
80         }
81
82         private int Delta {
83             get { return _delta; }
84         }
85
86         private int Depth {
87             get { return Base + Delta; }
88         }
89
90         private void Indent() {
91             _delta += Tab;
92         }
93         private void Dedent() {
94             _delta -= Tab;
95         }
96
97         private void NewLine() {
98             _flow = Flow.NewLine;
99         }
100
101         private static int GetId<T>(T e, ref Dictionary<T, int> ids) {
102             if (ids == null) {
103                 ids = new Dictionary<T, int>();
104                 ids.Add(e, 1);
105                 return 1;
106             } else {
107                 int id;
108                 if (!ids.TryGetValue(e, out id)) {
109                     // e is met the first time
110                     id = ids.Count + 1;
111                     ids.Add(e, id);
112                 }
113                 return id;
114             }
115         }
116
117         private int GetLambdaId(LambdaExpression le) {
118             Debug.Assert(String.IsNullOrEmpty(le.Name));
119             return GetId(le, ref _lambdaIds);
120         }
121
122         private int GetParamId(ParameterExpression p) {
123             Debug.Assert(String.IsNullOrEmpty(p.Name));
124             return GetId(p, ref _paramIds);
125         }
126
127         private int GetLabelTargetId(LabelTarget target) {
128             Debug.Assert(String.IsNullOrEmpty(target.Name));
129             return GetId(target, ref _labelIds);
130         }
131
132         /// <summary>
133         /// Write out the given AST
134         /// </summary>
135         internal static void WriteTo(Expression node, TextWriter writer) {
136             Debug.Assert(node != null);
137             Debug.Assert(writer != null);
138
139             new DebugViewWriter(writer).WriteTo(node);
140         }
141
142         private void WriteTo(Expression node) {
143             var lambda = node as LambdaExpression;
144             if (lambda != null) {
145                 WriteLambda(lambda);
146             } else {
147                 Visit(node);
148                 Debug.Assert(_stack.Count == 0);
149             }
150
151             //
152             // Output all lambda expression definitions.
153             // in the order of their appearances in the tree.
154             //
155             while (_lambdas != null && _lambdas.Count > 0) {
156                 WriteLine();
157                 WriteLine();
158                 WriteLambda(_lambdas.Dequeue());
159             }
160         }
161
162         #region The printing code
163
164         private void Out(string s) {
165             Out(Flow.None, s, Flow.None);
166         }
167
168         private void Out(Flow before, string s) {
169             Out(before, s, Flow.None);
170         }
171
172         private void Out(string s, Flow after) {
173             Out(Flow.None, s, after);
174         }
175
176         private void Out(Flow before, string s, Flow after) {
177             switch (GetFlow(before)) {
178                 case Flow.None:
179                     break;
180                 case Flow.Space:
181                     Write(" ");
182                     break;
183                 case Flow.NewLine:
184                     WriteLine();
185                     Write(new String(' ', Depth));
186                     break;
187             }
188             Write(s);
189             _flow = after;
190         }
191
192         private void WriteLine() {
193             _out.WriteLine();
194             _column = 0;
195         }
196         private void Write(string s) {
197             _out.Write(s);
198             _column += s.Length;
199         }
200
201         private Flow GetFlow(Flow flow) {
202             Flow last;
203
204             last = CheckBreak(_flow);
205             flow = CheckBreak(flow);
206
207             // Get the biggest flow that is requested None < Space < NewLine
208             return (Flow)System.Math.Max((int)last, (int)flow);
209         }
210
211         private Flow CheckBreak(Flow flow) {
212             if ((flow & Flow.Break) != 0) {
213                 if (_column > (MaxColumn + Depth)) {
214                     flow = Flow.NewLine;
215                 } else {
216                     flow &= ~Flow.Break;
217                 }
218             }
219             return flow;
220         }
221
222         #endregion
223
224         #region The AST Output
225
226         // More proper would be to make this a virtual method on Action
227         private static string FormatBinder(CallSiteBinder binder) {
228             ConvertBinder convert;
229             GetMemberBinder getMember;
230             SetMemberBinder setMember;
231             DeleteMemberBinder deleteMember;
232             InvokeMemberBinder call;
233             UnaryOperationBinder unary;
234             BinaryOperationBinder binary;
235
236             if ((convert = binder as ConvertBinder) != null) {
237                 return "Convert " + convert.Type.ToString();
238             } else if ((getMember = binder as GetMemberBinder) != null) {
239                 return "GetMember " + getMember.Name;
240             } else if ((setMember = binder as SetMemberBinder) != null) {
241                 return "SetMember " + setMember.Name;
242             } else if ((deleteMember = binder as DeleteMemberBinder) != null) {
243                 return "DeleteMember " + deleteMember.Name;
244             } else if (binder is GetIndexBinder) {
245                 return "GetIndex";
246             } else if (binder is SetIndexBinder) {
247                 return "SetIndex";
248             } else if (binder is DeleteIndexBinder) {
249                 return "DeleteIndex";
250             } else if ((call = binder as InvokeMemberBinder) != null) {
251                 return "Call " + call.Name;
252             } else if (binder is InvokeBinder) {
253                 return "Invoke";
254             } else if (binder is CreateInstanceBinder) {
255                 return "Create";
256             } else if ((unary = binder as UnaryOperationBinder) != null) {
257                 return "UnaryOperation " + unary.Operation;
258             } else if ((binary = binder as BinaryOperationBinder) != null) {
259                 return "BinaryOperation " + binary.Operation;
260             } else {
261                 return binder.ToString();
262             }
263         }
264
265         private void VisitExpressions<T>(char open, IList<T> expressions) where T : Expression {
266             VisitExpressions<T>(open, ',', expressions);
267         }
268
269         private void VisitExpressions<T>(char open, char separator, IList<T> expressions) where T : Expression {
270             VisitExpressions(open, separator, expressions, e => Visit(e));
271         }
272
273         private void VisitDeclarations(IList<ParameterExpression> expressions) {
274             VisitExpressions('(', ',', expressions, variable =>
275             {
276                 Out(variable.Type.ToString());
277                 if (variable.IsByRef) {
278                     Out("&");
279                 }
280                 Out(" ");
281                 VisitParameter(variable);
282             });
283         }
284
285         private void VisitExpressions<T>(char open, char separator, IList<T> expressions, Action<T> visit) {
286             Out(open.ToString());
287
288             if (expressions != null) {
289                 Indent();
290                 bool isFirst = true;
291                 foreach (T e in expressions) {
292                     if (isFirst) {
293                         if (open == '{' || expressions.Count > 1) {
294                             NewLine();
295                         }
296                         isFirst = false;
297                     } else {
298                         Out(separator.ToString(), Flow.NewLine);
299                     }
300                     visit(e);
301                 }
302                 Dedent();
303             }
304
305             char close;
306             switch (open) {
307                 case '(': close = ')'; break;
308                 case '{': close = '}'; break;
309                 case '[': close = ']'; break;
310                 case '<': close = '>'; break;
311                 default: throw ContractUtils.Unreachable;
312             }
313
314             if (open == '{') {
315                 NewLine();
316             }
317             Out(close.ToString(), Flow.Break);
318         }
319
320         protected internal override Expression VisitDynamic(DynamicExpression node) {
321             Out(".Dynamic", Flow.Space);
322             Out(FormatBinder(node.Binder));
323             VisitExpressions('(', node.Arguments);
324             return node;
325         }
326
327         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
328         protected internal override Expression VisitBinary(BinaryExpression node) {
329             if (node.NodeType == ExpressionType.ArrayIndex) {
330                 ParenthesizedVisit(node, node.Left);
331                 Out("[");
332                 Visit(node.Right);
333                 Out("]");
334             } else {
335                 bool parenthesizeLeft = NeedsParentheses(node, node.Left);
336                 bool parenthesizeRight = NeedsParentheses(node, node.Right);
337
338                 string op;
339                 bool isChecked = false;
340                 Flow beforeOp = Flow.Space;
341                 switch (node.NodeType) {
342                     case ExpressionType.Assign: op = "="; break;
343                     case ExpressionType.Equal: op = "=="; break;
344                     case ExpressionType.NotEqual: op = "!="; break;
345                     case ExpressionType.AndAlso: op = "&&"; beforeOp = Flow.Break | Flow.Space; break;
346                     case ExpressionType.OrElse: op = "||"; beforeOp = Flow.Break | Flow.Space; break;
347                     case ExpressionType.GreaterThan: op = ">"; break;
348                     case ExpressionType.LessThan: op = "<"; break;
349                     case ExpressionType.GreaterThanOrEqual: op = ">="; break;
350                     case ExpressionType.LessThanOrEqual: op = "<="; break;
351                     case ExpressionType.Add: op = "+"; break;
352                     case ExpressionType.AddAssign: op = "+="; break;
353                     case ExpressionType.AddAssignChecked: op = "+="; isChecked = true; break;
354                     case ExpressionType.AddChecked: op = "+"; isChecked = true; break;
355                     case ExpressionType.Subtract: op = "-"; break;
356                     case ExpressionType.SubtractAssign: op = "-="; break;
357                     case ExpressionType.SubtractAssignChecked: op = "-="; isChecked = true; break;
358                     case ExpressionType.SubtractChecked: op = "-"; isChecked = true; break;
359                     case ExpressionType.Divide: op = "/"; break;
360                     case ExpressionType.DivideAssign: op = "/="; break;
361                     case ExpressionType.Modulo: op = "%"; break;
362                     case ExpressionType.ModuloAssign: op = "%="; break;
363                     case ExpressionType.Multiply: op = "*"; break;
364                     case ExpressionType.MultiplyAssign: op = "*="; break;
365                     case ExpressionType.MultiplyAssignChecked: op = "*="; isChecked = true; break;
366                     case ExpressionType.MultiplyChecked: op = "*"; isChecked = true; break;
367                     case ExpressionType.LeftShift: op = "<<"; break;
368                     case ExpressionType.LeftShiftAssign: op = "<<="; break;
369                     case ExpressionType.RightShift: op = ">>"; break;
370                     case ExpressionType.RightShiftAssign: op = ">>="; break;
371                     case ExpressionType.And: op = "&"; break;
372                     case ExpressionType.AndAssign: op = "&="; break;
373                     case ExpressionType.Or: op = "|"; break;
374                     case ExpressionType.OrAssign: op = "|="; break;
375                     case ExpressionType.ExclusiveOr: op = "^"; break;
376                     case ExpressionType.ExclusiveOrAssign: op = "^="; break;
377                     case ExpressionType.Power: op = "**"; break;
378                     case ExpressionType.PowerAssign: op = "**="; break;
379                     case ExpressionType.Coalesce: op = "??"; break;
380
381                     default:
382                         throw new InvalidOperationException();
383                 }
384
385                 if (parenthesizeLeft) {
386                     Out("(", Flow.None);
387                 }
388
389                 Visit(node.Left);
390                 if (parenthesizeLeft) {
391                     Out(Flow.None, ")", Flow.Break);
392                 }
393
394                 // prepend # to the operator to represent checked op
395                 if (isChecked) {
396                     op = String.Format(
397                             CultureInfo.CurrentCulture,
398                             "#{0}",
399                             op
400                     );
401                 }
402                 Out(beforeOp, op, Flow.Space | Flow.Break);
403
404                 if (parenthesizeRight) {
405                     Out("(", Flow.None);
406                 }
407                 Visit(node.Right);
408                 if (parenthesizeRight) {
409                     Out(Flow.None, ")", Flow.Break);
410                 }
411             }
412             return node;
413         }
414
415         protected internal override Expression VisitParameter(ParameterExpression node) {
416             // Have '$' for the DebugView of ParameterExpressions
417             Out("$");
418             if (String.IsNullOrEmpty(node.Name)) {
419                 // If no name if provided, generate a name as $var1, $var2.
420                 // No guarantee for not having name conflicts with user provided variable names.
421                 //
422                 int id = GetParamId(node);
423                 Out("var" + id);
424             } else {
425                 Out(GetDisplayName(node.Name));
426             }
427             return node;
428         }
429
430         protected internal override Expression VisitLambda<T>(Expression<T> node) {
431             Out(
432                 String.Format(CultureInfo.CurrentCulture,
433                     "{0} {1}<{2}>",
434                     ".Lambda",
435                     GetLambdaName(node),
436                     node.Type.ToString()
437                 )
438             );
439
440             if (_lambdas == null) {
441                 _lambdas = new Queue<LambdaExpression>();
442             }
443
444             // N^2 performance, for keeping the order of the lambdas.
445             if (!_lambdas.Contains(node)) {
446                 _lambdas.Enqueue(node);
447             }
448
449             return node;
450         }
451
452         private static bool IsSimpleExpression(Expression node) {
453             var binary = node as BinaryExpression;
454             if (binary != null) {
455                 return !(binary.Left is BinaryExpression || binary.Right is BinaryExpression);
456             }
457
458             return false;
459         }
460
461         protected internal override Expression VisitConditional(ConditionalExpression node) {
462             if (IsSimpleExpression(node.Test)) {
463                 Out(".If (");
464                 Visit(node.Test);
465                 Out(") {", Flow.NewLine);
466             } else {
467                 Out(".If (", Flow.NewLine);
468                 Indent();
469                 Visit(node.Test);
470                 Dedent();
471                 Out(Flow.NewLine, ") {", Flow.NewLine);
472             }
473             Indent();
474             Visit(node.IfTrue);
475             Dedent();
476             Out(Flow.NewLine, "} .Else {", Flow.NewLine);
477             Indent();
478             Visit(node.IfFalse);
479             Dedent();
480             Out(Flow.NewLine, "}");
481             return node;
482         }
483
484         protected internal override Expression VisitConstant(ConstantExpression node) {
485             object value = node.Value;
486
487             if (value == null) {
488                 Out("null");
489             } else if ((value is string) && node.Type == typeof(string)) {
490                 Out(String.Format(
491                     CultureInfo.CurrentCulture,
492                     "\"{0}\"",
493                     value));
494             } else if ((value is char) && node.Type == typeof(char)) {
495                     Out(String.Format(
496                         CultureInfo.CurrentCulture,
497                         "'{0}'",
498                         value));
499             } else if ((value is int) && node.Type == typeof(int)
500                 || (value is bool) && node.Type == typeof(bool)) {
501                 Out(value.ToString());
502             } else {
503                 string suffix = GetConstantValueSuffix(node.Type);
504                 if (suffix != null) {
505                     Out(value.ToString());
506                     Out(suffix);
507                 } else {
508                     Out(String.Format(
509                         CultureInfo.CurrentCulture,
510                         ".Constant<{0}>({1})",
511                         node.Type.ToString(),
512                         value));
513                 }
514             }
515             return node;
516         }
517
518         private static string GetConstantValueSuffix(Type type) {
519             if (type == typeof(UInt32)) {
520                 return "U";
521             }
522             if (type == typeof(Int64)) {
523                 return "L";
524             }
525             if (type == typeof(UInt64)) {
526                 return "UL";
527             }
528             if (type == typeof(Double)) {
529                 return "D";
530             }
531             if (type == typeof(Single)) {
532                 return "F";
533             }
534             if (type == typeof(Decimal)) {
535                 return "M";
536             }
537             return null;
538         }
539
540         protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
541             Out(".RuntimeVariables");
542             VisitExpressions('(', node.Variables);
543             return node;
544         }
545
546         // Prints ".instanceField" or "declaringType.staticField"
547         private void OutMember(Expression node, Expression instance, MemberInfo member) {
548             if (instance != null) {
549                 ParenthesizedVisit(node, instance);
550                 Out("." + member.Name);
551             } else {
552                 // For static members, include the type name
553                 Out(member.DeclaringType.ToString() + "." + member.Name);
554             }
555         }
556
557         protected internal override Expression VisitMember(MemberExpression node) {
558             OutMember(node, node.Expression, node.Member);
559             return node;
560         }
561
562         protected internal override Expression VisitInvocation(InvocationExpression node) {
563             Out(".Invoke ");
564             ParenthesizedVisit(node, node.Expression);
565             VisitExpressions('(', node.Arguments);
566             return node;
567         }
568
569         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
570         private static bool NeedsParentheses(Expression parent, Expression child) {
571             Debug.Assert(parent != null);
572             if (child == null) {
573                 return false;
574             }
575
576             // Some nodes always have parentheses because of how they are
577             // displayed, for example: ".Unbox(obj.Foo)"
578             switch (parent.NodeType) {
579                 case ExpressionType.Increment:
580                 case ExpressionType.Decrement:
581                 case ExpressionType.IsTrue:
582                 case ExpressionType.IsFalse:
583                 case ExpressionType.Unbox:
584                     return true;
585             }
586
587             int childOpPrec = GetOperatorPrecedence(child);
588             int parentOpPrec = GetOperatorPrecedence(parent);
589
590             if (childOpPrec == parentOpPrec) {
591                 // When parent op and child op has the same precedence,
592                 // we want to be a little conservative to have more clarity.
593                 // Parentheses are not needed if
594                 // 1) Both ops are &&, ||, &, |, or ^, all of them are the only
595                 // op that has the precedence.
596                 // 2) Parent op is + or *, e.g. x + (y - z) can be simplified to
597                 // x + y - z.
598                 // 3) Parent op is -, / or %, and the child is the left operand.
599                 // In this case, if left and right operand are the same, we don't
600                 // remove parenthesis, e.g. (x + y) - (x + y)
601                 // 
602                 switch (parent.NodeType) {
603                     case ExpressionType.AndAlso:
604                     case ExpressionType.OrElse:
605                     case ExpressionType.And:
606                     case ExpressionType.Or:
607                     case ExpressionType.ExclusiveOr:
608                         // Since these ops are the only ones on their precedence,
609                         // the child op must be the same.
610                         Debug.Assert(child.NodeType == parent.NodeType);
611                         // We remove the parenthesis, e.g. x && y && z
612                         return false;
613                     case ExpressionType.Add:
614                     case ExpressionType.AddChecked:
615                     case ExpressionType.Multiply:
616                     case ExpressionType.MultiplyChecked:
617                         return false;
618                     case ExpressionType.Subtract:
619                     case ExpressionType.SubtractChecked:
620                     case ExpressionType.Divide:
621                     case ExpressionType.Modulo:
622                         BinaryExpression binary = parent as BinaryExpression;
623                         Debug.Assert(binary != null);
624                         // Need to have parenthesis for the right operand.
625                         return child == binary.Right;
626                 }
627                 return true;
628             }
629
630             // Special case: negate of a constant needs parentheses, to
631             // disambiguate it from a negative constant.
632             if (child != null && child.NodeType == ExpressionType.Constant &&
633                 (parent.NodeType == ExpressionType.Negate || parent.NodeType == ExpressionType.NegateChecked)) {
634                 return true;
635             }
636
637             // If the parent op has higher precedence, need parentheses for the child.
638             return childOpPrec < parentOpPrec;
639         }
640
641         // the greater the higher
642         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
643         private static int GetOperatorPrecedence(Expression node) {
644
645             // Roughly matches C# operator precedence, with some additional
646             // operators. Also things which are not binary/unary expressions,
647             // such as conditional and type testing, don't use this mechanism.
648             switch (node.NodeType) {
649                 // Assignment
650                 case ExpressionType.Assign:
651                 case ExpressionType.ExclusiveOrAssign:
652                 case ExpressionType.AddAssign:
653                 case ExpressionType.AddAssignChecked:
654                 case ExpressionType.SubtractAssign:
655                 case ExpressionType.SubtractAssignChecked:
656                 case ExpressionType.DivideAssign:
657                 case ExpressionType.ModuloAssign:
658                 case ExpressionType.MultiplyAssign:
659                 case ExpressionType.MultiplyAssignChecked:
660                 case ExpressionType.LeftShiftAssign:
661                 case ExpressionType.RightShiftAssign:
662                 case ExpressionType.AndAssign:
663                 case ExpressionType.OrAssign:
664                 case ExpressionType.PowerAssign:
665                 case ExpressionType.Coalesce:
666                     return 1;
667
668                 // Conditional (?:) would go here
669
670                 // Conditional OR
671                 case ExpressionType.OrElse:
672                     return 2;
673
674                 // Conditional AND
675                 case ExpressionType.AndAlso:
676                     return 3;
677
678                 // Logical OR
679                 case ExpressionType.Or:
680                     return 4;
681
682                 // Logical XOR
683                 case ExpressionType.ExclusiveOr:
684                     return 5;
685
686                 // Logical AND
687                 case ExpressionType.And:
688                     return 6;
689
690                 // Equality
691                 case ExpressionType.Equal:
692                 case ExpressionType.NotEqual:
693                     return 7;
694
695                 // Relational, type testing
696                 case ExpressionType.GreaterThan:
697                 case ExpressionType.LessThan:
698                 case ExpressionType.GreaterThanOrEqual:
699                 case ExpressionType.LessThanOrEqual:
700                 case ExpressionType.TypeAs:
701                 case ExpressionType.TypeIs:
702                 case ExpressionType.TypeEqual:
703                     return 8;
704
705                 // Shift
706                 case ExpressionType.LeftShift:
707                 case ExpressionType.RightShift:
708                     return 9;
709
710                 // Additive
711                 case ExpressionType.Add:
712                 case ExpressionType.AddChecked:
713                 case ExpressionType.Subtract:
714                 case ExpressionType.SubtractChecked:
715                     return 10;
716
717                 // Multiplicative
718                 case ExpressionType.Divide:
719                 case ExpressionType.Modulo:
720                 case ExpressionType.Multiply:
721                 case ExpressionType.MultiplyChecked:
722                     return 11;
723
724                 // Unary
725                 case ExpressionType.Negate:
726                 case ExpressionType.NegateChecked:
727                 case ExpressionType.UnaryPlus:
728                 case ExpressionType.Not:
729                 case ExpressionType.Convert:
730                 case ExpressionType.ConvertChecked:
731                 case ExpressionType.PreIncrementAssign:
732                 case ExpressionType.PreDecrementAssign:
733                 case ExpressionType.OnesComplement:
734                 case ExpressionType.Increment:
735                 case ExpressionType.Decrement:
736                 case ExpressionType.IsTrue:
737                 case ExpressionType.IsFalse:
738                 case ExpressionType.Unbox:
739                 case ExpressionType.Throw:
740                     return 12;
741
742                 // Power, which is not in C#
743                 // But VB/Python/Ruby put it here, above unary.
744                 case ExpressionType.Power:
745                     return 13;
746
747                 // Primary, which includes all other node types:
748                 //   member access, calls, indexing, new.
749                 case ExpressionType.PostIncrementAssign:
750                 case ExpressionType.PostDecrementAssign:
751                 default:
752                     return 14;
753
754                 // These aren't expressions, so never need parentheses:
755                 //   constants, variables
756                 case ExpressionType.Constant:
757                 case ExpressionType.Parameter:
758                     return 15;
759             }
760         }
761
762         private void ParenthesizedVisit(Expression parent, Expression nodeToVisit) {
763             if (NeedsParentheses(parent, nodeToVisit)) {
764                 Out("(");
765                 Visit(nodeToVisit);
766                 Out(")");
767             } else {
768                 Visit(nodeToVisit);
769             }
770         }
771
772         protected internal override Expression VisitMethodCall(MethodCallExpression node) {
773             Out(".Call ");
774             if (node.Object != null) {
775                 ParenthesizedVisit(node, node.Object);
776             } else if (node.Method.DeclaringType != null) {
777                 Out(node.Method.DeclaringType.ToString());
778             } else {
779                 Out("<UnknownType>");
780             }
781             Out(".");
782             Out(node.Method.Name);
783             VisitExpressions('(', node.Arguments);
784             return node;
785         }
786
787         protected internal override Expression VisitNewArray(NewArrayExpression node) {
788             if (node.NodeType == ExpressionType.NewArrayBounds) {
789                 // .NewArray MyType[expr1, expr2]
790                 Out(".NewArray " + node.Type.GetElementType().ToString());
791                 VisitExpressions('[', node.Expressions);
792             } else {
793                 // .NewArray MyType {expr1, expr2}
794                 Out(".NewArray " + node.Type.ToString(), Flow.Space);
795                 VisitExpressions('{', node.Expressions);
796             }
797             return node;
798         }
799
800         protected internal override Expression VisitNew(NewExpression node) {
801             Out(".New " + node.Type.ToString());
802             VisitExpressions('(', node.Arguments);
803             return node;
804         }
805
806         protected override ElementInit VisitElementInit(ElementInit node) {
807             if (node.Arguments.Count == 1) {
808                 Visit(node.Arguments[0]);
809             } else {
810                 VisitExpressions('{', node.Arguments);
811             }
812             return node;
813         }
814
815         protected internal override Expression VisitListInit(ListInitExpression node) {
816             Visit(node.NewExpression);
817             VisitExpressions('{', ',', node.Initializers, e => VisitElementInit(e));
818             return node;
819         }
820
821         protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
822             Out(assignment.Member.Name);
823             Out(Flow.Space, "=", Flow.Space);
824             Visit(assignment.Expression);
825             return assignment;
826         }
827
828         protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
829             Out(binding.Member.Name);
830             Out(Flow.Space, "=", Flow.Space);
831             VisitExpressions('{', ',', binding.Initializers, e => VisitElementInit(e));
832             return binding;
833         }
834
835         protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) {
836             Out(binding.Member.Name);
837             Out(Flow.Space, "=", Flow.Space);
838             VisitExpressions('{', ',', binding.Bindings, e => VisitMemberBinding(e));
839             return binding;
840         }
841
842         protected internal override Expression VisitMemberInit(MemberInitExpression node) {
843             Visit(node.NewExpression);
844             VisitExpressions('{', ',', node.Bindings, e => VisitMemberBinding(e));
845             return node;
846         }
847
848         protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) {
849             ParenthesizedVisit(node, node.Expression);
850             switch (node.NodeType) {
851                 case ExpressionType.TypeIs:
852                     Out(Flow.Space, ".Is", Flow.Space);
853                     break;
854                 case ExpressionType.TypeEqual:
855                     Out(Flow.Space, ".TypeEqual", Flow.Space);
856                     break;
857             }
858             Out(node.TypeOperand.ToString());
859             return node;
860         }
861
862         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
863         protected internal override Expression VisitUnary(UnaryExpression node) {
864             switch (node.NodeType) {
865                 case ExpressionType.Convert:
866                     Out("(" + node.Type.ToString() + ")");
867                     break;
868                 case ExpressionType.ConvertChecked:
869                     Out("#(" + node.Type.ToString() + ")");
870                     break;
871                 case ExpressionType.TypeAs:
872                     break;
873                 case ExpressionType.Not:
874                     Out(node.Type == typeof(bool) ? "!" : "~");
875                     break;
876                 case ExpressionType.OnesComplement:
877                     Out("~");
878                     break;
879                 case ExpressionType.Negate:
880                     Out("-");
881                     break;
882                 case ExpressionType.NegateChecked:
883                     Out("#-");
884                     break;
885                 case ExpressionType.UnaryPlus:
886                     Out("+");
887                     break;
888                 case ExpressionType.ArrayLength:
889                     break;
890                 case ExpressionType.Quote:
891                     Out("'");
892                     break;
893                 case ExpressionType.Throw:
894                     if (node.Operand == null) {
895                         Out(".Rethrow");
896                     } else {
897                         Out(".Throw", Flow.Space);
898                     }
899                     break;
900                 case ExpressionType.IsFalse:
901                     Out(".IsFalse");
902                     break;
903                 case ExpressionType.IsTrue:
904                     Out(".IsTrue");
905                     break;
906                 case ExpressionType.Decrement:
907                     Out(".Decrement");
908                     break;
909                 case ExpressionType.Increment:
910                     Out(".Increment");
911                     break;
912                 case ExpressionType.PreDecrementAssign:
913                     Out("--");
914                     break;
915                 case ExpressionType.PreIncrementAssign:
916                     Out("++");
917                     break;
918                 case ExpressionType.Unbox:
919                     Out(".Unbox");
920                     break;
921             }
922
923             ParenthesizedVisit(node, node.Operand);
924
925             switch (node.NodeType) {
926                 case ExpressionType.TypeAs:
927                     Out(Flow.Space, ".As", Flow.Space | Flow.Break);
928                     Out(node.Type.ToString());
929                     break;
930
931                 case ExpressionType.ArrayLength:
932                     Out(".Length");
933                     break;
934
935                 case ExpressionType.PostDecrementAssign:
936                     Out("--");
937                     break;
938
939                 case ExpressionType.PostIncrementAssign:
940                     Out("++");
941                     break;
942             }
943             return node;
944         }
945
946         protected internal override Expression VisitBlock(BlockExpression node) {
947             Out(".Block");
948
949             // Display <type> if the type of the BlockExpression is different from the
950             // last expression's type in the block.
951             if (node.Type != node.GetExpression(node.ExpressionCount - 1).Type) {
952                 Out(String.Format(CultureInfo.CurrentCulture, "<{0}>", node.Type.ToString()));
953             }
954
955             VisitDeclarations(node.Variables);
956             Out(" ");
957             // Use ; to separate expressions in the block
958             VisitExpressions('{', ';', node.Expressions);
959
960             return node;
961         }
962
963         protected internal override Expression VisitDefault(DefaultExpression node) {
964             Out(".Default(" + node.Type.ToString() + ")");
965             return node;
966         }
967
968         protected internal override Expression VisitLabel(LabelExpression node) {
969             Out(".Label", Flow.NewLine);
970             Indent();
971             Visit(node.DefaultValue);
972             Dedent();
973             NewLine();
974             DumpLabel(node.Target);
975             return node;
976         }
977
978         protected internal override Expression VisitGoto(GotoExpression node) {
979             Out("." + node.Kind.ToString(), Flow.Space);
980             Out(GetLabelTargetName(node.Target), Flow.Space);
981             Out("{", Flow.Space);
982             Visit(node.Value);
983             Out(Flow.Space, "}");
984             return node;
985         }
986
987         protected internal override Expression VisitLoop(LoopExpression node) {
988             Out(".Loop", Flow.Space);
989             if (node.ContinueLabel != null) {
990                 DumpLabel(node.ContinueLabel);
991             }
992             Out(" {", Flow.NewLine);
993             Indent();
994             Visit(node.Body);
995             Dedent();
996             Out(Flow.NewLine, "}");
997             if (node.BreakLabel != null) {
998                 Out("", Flow.NewLine);
999                 DumpLabel(node.BreakLabel);
1000             }
1001             return node;
1002         }
1003
1004         protected override SwitchCase VisitSwitchCase(SwitchCase node) {
1005             foreach (var test in node.TestValues) {
1006                 Out(".Case (");
1007                 Visit(test);
1008                 Out("):", Flow.NewLine);
1009             }
1010             Indent(); Indent();
1011             Visit(node.Body);
1012             Dedent(); Dedent();
1013             NewLine();
1014             return node;
1015         }
1016
1017         protected internal override Expression VisitSwitch(SwitchExpression node) {
1018             Out(".Switch ");
1019             Out("(");
1020             Visit(node.SwitchValue);
1021             Out(") {", Flow.NewLine);
1022             Visit(node.Cases, VisitSwitchCase);
1023             if (node.DefaultBody != null) {
1024                 Out(".Default:", Flow.NewLine);
1025                 Indent(); Indent();
1026                 Visit(node.DefaultBody);
1027                 Dedent(); Dedent();
1028                 NewLine();
1029             }
1030             Out("}");
1031             return node;
1032         }
1033
1034         protected override CatchBlock VisitCatchBlock(CatchBlock node) {
1035             Out(Flow.NewLine, "} .Catch (" + node.Test.ToString());
1036             if (node.Variable != null) {
1037                 Out(Flow.Space, "");
1038                 VisitParameter(node.Variable);
1039             }
1040             if (node.Filter != null) {
1041                 Out(") .If (", Flow.Break);
1042                 Visit(node.Filter);
1043             }
1044             Out(") {", Flow.NewLine);
1045             Indent();
1046             Visit(node.Body);
1047             Dedent();
1048             return node;
1049         }
1050
1051         protected internal override Expression VisitTry(TryExpression node) {
1052             Out(".Try {", Flow.NewLine);
1053             Indent();
1054             Visit(node.Body);
1055             Dedent();
1056             Visit(node.Handlers, VisitCatchBlock);
1057             if (node.Finally != null) {
1058                 Out(Flow.NewLine, "} .Finally {", Flow.NewLine);
1059                 Indent();
1060                 Visit(node.Finally);
1061                 Dedent();
1062             } else if (node.Fault != null) {
1063                 Out(Flow.NewLine, "} .Fault {", Flow.NewLine);
1064                 Indent();
1065                 Visit(node.Fault);
1066                 Dedent();
1067             }
1068
1069             Out(Flow.NewLine, "}");
1070             return node;
1071         }
1072
1073         protected internal override Expression VisitIndex(IndexExpression node) {
1074             if (node.Indexer != null) {
1075                 OutMember(node, node.Object, node.Indexer);
1076             } else {
1077                 ParenthesizedVisit(node, node.Object);
1078             }
1079
1080             VisitExpressions('[', node.Arguments);
1081             return node;
1082         }
1083
1084         protected internal override Expression VisitExtension(Expression node) {
1085             Out(String.Format(CultureInfo.CurrentCulture, ".Extension<{0}>", node.GetType().ToString()));
1086
1087             if (node.CanReduce) {
1088                 Out(Flow.Space, "{", Flow.NewLine);
1089                 Indent();
1090                 Visit(node.Reduce());
1091                 Dedent();
1092                 Out(Flow.NewLine, "}");
1093             }
1094
1095             return node;
1096         }
1097
1098         protected internal override Expression VisitDebugInfo(DebugInfoExpression node) {
1099             Out(String.Format(
1100                 CultureInfo.CurrentCulture,
1101                 ".DebugInfo({0}: {1}, {2} - {3}, {4})",
1102                 node.Document.FileName,
1103                 node.StartLine,
1104                 node.StartColumn,
1105                 node.EndLine,
1106                 node.EndColumn)
1107             );
1108             return node;
1109         }
1110
1111
1112         private void DumpLabel(LabelTarget target) {
1113             Out(String.Format(CultureInfo.CurrentCulture, ".LabelTarget {0}:", GetLabelTargetName(target)));
1114         }
1115
1116         private string GetLabelTargetName(LabelTarget target) {
1117             if (string.IsNullOrEmpty(target.Name)) {
1118                 // Create the label target name as #Label1, #Label2, etc.
1119                 return String.Format(CultureInfo.CurrentCulture, "#Label{0}", GetLabelTargetId(target));
1120             } else {
1121                 return GetDisplayName(target.Name);
1122             }
1123         }
1124
1125         private void WriteLambda(LambdaExpression lambda) {
1126             Out(
1127                 String.Format(
1128                     CultureInfo.CurrentCulture,
1129                     ".Lambda {0}<{1}>",
1130                     GetLambdaName(lambda),
1131                     lambda.Type.ToString())
1132             );
1133
1134             VisitDeclarations(lambda.Parameters);
1135
1136             Out(Flow.Space, "{", Flow.NewLine);
1137             Indent();
1138             Visit(lambda.Body);
1139             Dedent();
1140             Out(Flow.NewLine, "}");
1141             Debug.Assert(_stack.Count == 0);
1142         }
1143
1144         private string GetLambdaName(LambdaExpression lambda) {
1145             if (String.IsNullOrEmpty(lambda.Name)) {
1146                 return "#Lambda" + GetLambdaId(lambda);
1147             }
1148             return GetDisplayName(lambda.Name);
1149         }
1150
1151         /// <summary>
1152         /// Return true if the input string contains any whitespace character.
1153         /// Otherwise false.
1154         /// </summary>
1155         private static bool ContainsWhiteSpace(string name) {
1156             foreach (char c in name) {
1157                 if (Char.IsWhiteSpace(c)) {
1158                     return true;
1159                 }
1160             }
1161             return false;
1162         }
1163
1164         private static string QuoteName(string name) {
1165             return String.Format(CultureInfo.CurrentCulture, "'{0}'", name);
1166         }
1167
1168         private static string GetDisplayName(string name) {
1169             if (ContainsWhiteSpace(name)) {
1170                 // if name has whitespaces in it, quote it
1171                 return QuoteName(name);
1172             } else {
1173                 return name;
1174             }
1175         }
1176
1177         #endregion
1178     }
1179 }