New tests, update.
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / ExpressionPrinter.cs
1 //
2 // ExpressionPrinter.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2008 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.Collections.ObjectModel;
32 using System.Text;
33
34 namespace System.Linq.Expressions {
35
36         class ExpressionPrinter : ExpressionVisitor {
37
38                 StringBuilder builder;
39
40                 const string ListSeparator = ", ";
41
42                 ExpressionPrinter (StringBuilder builder)
43                 {
44                         this.builder = builder;
45                 }
46
47                 ExpressionPrinter () : this (new StringBuilder ())
48                 {
49                 }
50
51                 public static string ToString (Expression expression)
52                 {
53                         var printer = new ExpressionPrinter ();
54                         printer.Visit (expression);
55                         return printer.builder.ToString ();
56                 }
57
58                 public static string ToString (ElementInit init)
59                 {
60                         var printer = new ExpressionPrinter ();
61                         printer.VisitElementInitializer (init);
62                         return printer.builder.ToString ();
63                 }
64
65                 public static string ToString (MemberBinding binding)
66                 {
67                         var printer = new ExpressionPrinter ();
68                         printer.VisitBinding (binding);
69                         return printer.builder.ToString ();
70                 }
71
72                 void Print (string str)
73                 {
74                         builder.Append (str);
75                 }
76
77                 void Print (object obj)
78                 {
79                         builder.Append (obj);
80                 }
81
82                 void Print (string str, params object [] objs)
83                 {
84                         builder.AppendFormat (str, objs);
85                 }
86
87                 protected override void VisitElementInitializer (ElementInit initializer)
88                 {
89                         Print (initializer.AddMethod);
90                         Print ("(");
91                         VisitExpressionList (initializer.Arguments);
92                         Print (")");
93                 }
94
95                 protected override void VisitUnary (UnaryExpression unary)
96                 {
97                         switch (unary.NodeType) {
98                         case ExpressionType.ArrayLength:
99                         case ExpressionType.Convert:
100                         case ExpressionType.ConvertChecked:
101                         case ExpressionType.Not:
102                                 Print ("{0}(", unary.NodeType);
103                                 Visit (unary.Operand);
104                                 Print (")");
105                                 return;
106                         case ExpressionType.Negate:
107                                 Print ("-");
108                                 Visit (unary.Operand);
109                                 return;
110                         case ExpressionType.Quote:
111                                 Visit (unary.Operand);
112                                 return;
113                         case ExpressionType.TypeAs:
114                                 Print ("(");
115                                 Visit (unary.Operand);
116                                 Print (" As {0})", unary.Type.Name);
117                                 return;
118                         case ExpressionType.UnaryPlus:
119                                 Print ("+");
120                                 Visit (unary.Operand);
121                                 return;
122                         }
123
124                         throw new NotImplementedException ();
125                 }
126
127                 static string OperatorToString (BinaryExpression binary)
128                 {
129                         switch (binary.NodeType) {
130                         case ExpressionType.Add:
131                         case ExpressionType.AddChecked:
132                                 return "+";
133                         case ExpressionType.AndAlso:
134                                 return "&&";
135                         case ExpressionType.Coalesce:
136                                 return "??";
137                         case ExpressionType.Divide:
138                                 return "/";
139                         case ExpressionType.Equal:
140                                 return "=";
141                         case ExpressionType.ExclusiveOr:
142                                 return "^";
143                         case ExpressionType.GreaterThan:
144                                 return ">";
145                         case ExpressionType.GreaterThanOrEqual:
146                                 return ">=";
147                         case ExpressionType.LeftShift:
148                                 return "<<";
149                         case ExpressionType.LessThan:
150                                 return "<";
151                         case ExpressionType.LessThanOrEqual:
152                                 return "<=";
153                         case ExpressionType.Modulo:
154                                 return "%";
155                         case ExpressionType.Multiply:
156                         case ExpressionType.MultiplyChecked:
157                                 return "*";
158                         case ExpressionType.NotEqual:
159                                 return "!=";
160                         case ExpressionType.OrElse:
161                                 return "||";
162                         case ExpressionType.Power:
163                                 return "^";
164                         case ExpressionType.RightShift:
165                                 return ">>";
166                         case ExpressionType.Subtract:
167                         case ExpressionType.SubtractChecked:
168                                 return "-";
169                         case ExpressionType.And:
170                                 return IsBoolean (binary) ? "And" : "&";
171                         case ExpressionType.Or:
172                                 return IsBoolean (binary) ? "Or" : "|";
173                         default:
174                                 return null;
175                         }
176                 }
177
178                 static bool IsBoolean (Expression expression)
179                 {
180                         return expression.Type == typeof (bool) || expression.Type == typeof (bool?);
181                 }
182
183                 void PrintArrayIndex (BinaryExpression index)
184                 {
185                         Visit (index.Left);
186                         Print ("[");
187                         Visit (index.Right);
188                         Print ("]");
189                 }
190
191                 protected override void VisitBinary (BinaryExpression binary)
192                 {
193                         switch (binary.NodeType) {
194                         case ExpressionType.ArrayIndex:
195                                 PrintArrayIndex (binary);
196                                 return;
197                         default:
198                                 Print ("(");
199                                 Visit (binary.Left);
200                                 Print (" {0} ", OperatorToString (binary));
201                                 Visit (binary.Right);
202                                 Print (")");
203                                 return;
204                         }
205                 }
206
207                 protected override void VisitTypeIs (TypeBinaryExpression type)
208                 {
209                         switch (type.NodeType) {
210                         case ExpressionType.TypeIs:
211                                 Print ("(");
212                                 Visit (type.Expression);
213                                 Print (" Is {0})", type.TypeOperand.Name);
214                                 return;
215                         }
216
217                         throw new NotImplementedException ();
218                 }
219
220                 protected override void VisitConstant (ConstantExpression constant)
221                 {
222                         var value = constant.Value;
223
224                         if (value == null) {
225                                 Print ("null");
226                         } else if (value is string) {
227                                 Print ("\"");
228                                 Print (value);
229                                 Print ("\"");
230                         } else if (!HasStringRepresentation (value)) {
231                                 Print ("value(");
232                                 Print (value);
233                                 Print (")");
234                         } else
235                                 Print (value);
236                 }
237
238                 static bool HasStringRepresentation (object obj)
239                 {
240                         return obj.ToString () != obj.GetType ().ToString ();
241                 }
242
243                 protected override void VisitConditional (ConditionalExpression conditional)
244                 {
245                         Print ("IIF(");
246                         Visit (conditional.Test);
247                         Print (ListSeparator);
248                         Visit (conditional.IfTrue);
249                         Print (ListSeparator);
250                         Visit (conditional.IfFalse);
251                         Print (")");
252                 }
253
254                 protected override void VisitParameter (ParameterExpression parameter)
255                 {
256                         Print (parameter.Name ?? "<param>");
257                 }
258
259                 protected override void VisitMemberAccess (MemberExpression access)
260                 {
261                         if (access.Expression == null)
262                                 Print (access.Member.DeclaringType.Name);
263                         else
264                                 Visit (access.Expression);
265
266                         Print (".{0}", access.Member.Name);
267                 }
268
269                 protected override void VisitMethodCall (MethodCallExpression call)
270                 {
271                         if (call.Object != null) {
272                                 Visit (call.Object);
273                                 Print (".");
274                         }
275                         Print (call.Method.Name);
276                         Print ("(");
277                         VisitExpressionList (call.Arguments);
278                         Print (")");
279                 }
280
281                 protected override void VisitMemberAssignment (MemberAssignment assignment)
282                 {
283                         Print ("{0} = ", assignment.Member.Name);
284                         Visit (assignment.Expression);
285                 }
286
287                 protected override void VisitMemberMemberBinding (MemberMemberBinding binding)
288                 {
289                         Print (binding.Member.Name);
290                         Print (" = {");
291                         // VisitBindingList (binding.Bindings);
292                         VisitList (binding.Bindings, VisitBinding);
293                         Print ("}");
294                 }
295
296                 protected override void VisitMemberListBinding (MemberListBinding binding)
297                 {
298                         Print (binding.Member.Name);
299                         Print (" = {");
300                         // replace when the patch to the visitor is in
301                         // VisitElementInitializerList (binding.Initializers);
302                         VisitList (binding.Initializers, VisitElementInitializer);
303                         Print ("}");
304                 }
305
306                 protected override void VisitList<T> (ReadOnlyCollection<T> list, Action<T> visitor)
307                 {
308                         for (int i = 0; i < list.Count; i++) {
309                                 if (i > 0)
310                                         Print (ListSeparator);
311
312                                 visitor (list [i]);
313                         }
314                 }
315
316                 protected override void VisitLambda (LambdaExpression lambda)
317                 {
318                         if (lambda.Parameters.Count != 1) {
319                                 Print ("(");
320                                 // replace when the patch to the visitor is in
321                                 // VisitExpressionList (lambda.Parameters);
322                                 VisitList (lambda.Parameters, Visit);
323                                 Print (")");
324                         } else
325                                 Visit (lambda.Parameters [0]);
326
327                         Print (" => ");
328                         Visit (lambda.Body);
329                 }
330
331                 protected override void VisitNew (NewExpression nex)
332                 {
333                         Print ("new {0}(", nex.Type.Name);
334                         if (nex.Members != null && nex.Members.Count > 0) {
335                                 for (int i = 0; i < nex.Members.Count; i++) {
336                                         if (i > 0)
337                                                 Print (ListSeparator);
338
339                                         Print ("{0} = ", nex.Members [i].Name);
340                                         Visit (nex.Arguments [i]);
341                                 }
342                         } else
343                                 VisitExpressionList (nex.Arguments);
344                         Print (")");
345                 }
346
347                 protected override void VisitMemberInit (MemberInitExpression init)
348                 {
349                         Visit (init.NewExpression);
350                         Print (" {");
351                         // VisitBindingList (init.Bindings)
352                         VisitList (init.Bindings, VisitBinding);
353                         Print ("}");
354                 }
355
356                 protected override void VisitListInit (ListInitExpression init)
357                 {
358                         Visit (init.NewExpression);
359                         Print (" {");
360                         // VisitElementInitializerList
361                         VisitList (init.Initializers, VisitElementInitializer);
362                         Print ("}");
363                 }
364
365                 protected override void VisitNewArray (NewArrayExpression newArray)
366                 {
367                         Print ("new ");
368                         switch (newArray.NodeType) {
369                         case ExpressionType.NewArrayBounds:
370                                 Print (newArray.Type);
371                                 Print ("(");
372                                 VisitExpressionList (newArray.Expressions);
373                                 Print (")");
374                                 return;
375                         case ExpressionType.NewArrayInit:
376                                 Print ("[] {");
377                                 VisitExpressionList (newArray.Expressions);
378                                 Print ("}");
379                                 return;
380                         }
381
382                         throw new NotSupportedException ();
383                 }
384
385                 protected override void VisitInvocation (InvocationExpression invocation)
386                 {
387                         Print ("Invoke(");
388                         Visit (invocation.Expression);
389
390                         if (invocation.Arguments.Count != 0) {
391                                 Print (ListSeparator);
392                                 VisitExpressionList (invocation.Arguments);
393                         }
394
395                         Print (")");
396                 }
397         }
398 }