2007-08-21 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / gmcs / linq.cs
1 //
2 // linq.cs: support for query expressions
3 //
4 // Authors: Marek Safar (marek.safar@gmail.com)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2007 Novell, Inc
9 //
10
11 using System;
12 using System.Reflection;
13 using System.Collections;
14
15 namespace Mono.CSharp.Linq
16 {
17         // NOTES:
18         // Expression should be IExpression to save some memory and make a few things
19         // easier to read
20         //
21         //
22
23         public class QueryExpression : Expression
24         {
25                 public readonly Block Block;
26                 public readonly TypeContainer Host;
27
28                 AQueryClause query;
29                 Expression from;
30
31                 public QueryExpression (TypeContainer host, Block block, Expression from, AQueryClause query)
32                 {
33                         this.Host = host;
34                         this.Block = block;
35                         this.from = from;
36                         this.query = query;
37                 }
38
39                 public override Expression DoResolve (EmitContext ec)
40                 {
41                         from = from.DoResolve (ec);
42                         if (from == null)
43                                 return null;
44
45                         ICollection values = Block.Variables.Values;
46                         if (values.Count != 1)
47                                 throw new NotImplementedException ("Count != 1");
48
49                         IEnumerator enumerator = values.GetEnumerator ();
50                         enumerator.MoveNext ();
51                         LocalInfo li = (LocalInfo)enumerator.Current;
52
53                         Parameter clause_parameter;
54                         if (li.Type == ImplicitArgument.Instance) {
55                                 clause_parameter = new ImplicitLambdaParameter (li.Name, li.Location);
56                         } else {
57                                 clause_parameter = new Parameter (li.Type, li.Name, Parameter.Modifier.NONE, null, li.Location);
58                         }
59
60                         Expression e = query.BuildQueryClause (ec, this, from, clause_parameter);
61                         e = e.Resolve (ec);
62
63                         if (e == null)
64                                 return null;
65
66                         // TODO: really ?
67                         if (li.VariableType == null)
68                                 li.VariableType = ec.ReturnType;
69
70                         return e;
71                 }
72
73                 public override void Emit (EmitContext ec)
74                 {
75                         throw new NotSupportedException ();
76                 }
77         }
78
79         public abstract class ALinqExpression : Expression
80         {
81                 // Dictionary of method name -> MethodGroupExpr
82                 static Hashtable methods = new Hashtable ();
83                 static Type enumerable_class;
84
85                 protected abstract string MethodName { get; }
86
87                 protected MethodGroupExpr MethodGroup {
88                         get {
89                                 //
90                                 // Even if C# spec indicates that LINQ methods are context specific
91                                 // in reality they are hardcoded
92                                 //                              
93                                 MemberList ml = (MemberList)methods [MethodName];
94                                 if (ml == null) {
95                                         if (enumerable_class == null)
96                                                 enumerable_class = TypeManager.CoreLookupType ("System.Linq", "Enumerable");
97
98                                         ml = TypeManager.FindMembers (enumerable_class,
99                                                 MemberTypes.Method, BindingFlags.Static | BindingFlags.Public,
100                                                 Type.FilterName, MethodName);
101                                 }
102                                 
103                                 return new MethodGroupExpr (ArrayList.Adapter (ml), enumerable_class, loc);
104                         }
105                 }
106         }
107
108         public abstract class AQueryClause : ALinqExpression
109         {
110                 public AQueryClause Next;
111                 protected Expression expr;
112
113                 protected AQueryClause (Expression expr, Location loc)
114                 {
115                         this.expr = expr;
116                         this.loc = loc;
117                 }
118
119                 public override Expression DoResolve (EmitContext ec)
120                 {
121                         return expr.DoResolve (ec);
122                 }
123
124                 public override void Emit (EmitContext ec)
125                 {
126                         throw new NotSupportedException ();
127                 }
128
129                 public Expression BuildQueryClause (EmitContext ec, QueryExpression top, Expression from, Parameter parameter)
130                 {
131                         Parameters parameters = new Parameters (parameter);
132                         LambdaExpression ame = new LambdaExpression (
133                                 null, null, top.Host,
134                                 parameters,
135                                 top.Block, loc);
136                         ame.Block = new ToplevelBlock (parameters, loc);
137                         ame.Block.AddStatement (new Return (expr, loc));
138
139                         expr = new Invocation (MethodGroup, CreateArguments (ame, from));
140                         if (Next != null)
141                                 return Next.BuildQueryClause (ec, top, this, parameter);
142
143                         return expr;
144                 }
145                                                
146                 protected virtual ArrayList CreateArguments (AnonymousMethodExpression ame, Expression from)
147                 {
148                         ArrayList args = new ArrayList (2);
149                         args.Add (new Argument (from));
150                         args.Add (new Argument (ame));
151                         return args;
152                 }
153
154                 public AQueryClause Tail {
155                         get {
156                                 return Next == null ? this : Next.Tail;
157                         }
158                 }
159         }
160
161         public class Cast : ALinqExpression
162         {
163                 readonly Expression expr;
164                 readonly Expression cast_type;
165
166                 public Cast (Expression type, Expression expr, Location loc)
167                 {
168                         this.cast_type = type;
169                         this.expr = expr;
170                         this.loc = loc;
171                 }
172
173                 protected override string MethodName {
174                         get { return "Cast"; }
175                 }
176
177                 public override Expression DoResolve (EmitContext ec)
178                 {
179                         TypeArguments type_arguments = new TypeArguments (loc, cast_type);
180                         Expression cast = MethodGroup.ResolveGeneric (ec, type_arguments);
181
182                         ArrayList args = new ArrayList (1);
183                         args.Add (new Argument (expr));
184                         return new Invocation (cast, args).DoResolve (ec);
185                 }
186
187                 public override void Emit (EmitContext ec)
188                 {
189                         throw new NotSupportedException ();
190                 }
191         }
192
193         public class GroupBy : AQueryClause
194         {
195                 readonly Expression element_selector;
196                 
197                 public GroupBy (Expression elementSelector, Expression keySelector, Location loc)
198                         : base (keySelector, loc)
199                 {
200                         this.element_selector = elementSelector;
201                 }
202
203                 protected override ArrayList CreateArguments (AnonymousMethodExpression ame, Expression from)
204                 {
205                         ArrayList args = base.CreateArguments (ame, from);
206                         
207                         // A query can be optimized when selector is not group by specific
208                         if (!element_selector.Equals (from)) {
209                                 LambdaExpression am_element = new LambdaExpression (
210                                         null, null, ame.Host, ame.Parameters, ame.Container, loc);
211                                 am_element.Block = new ToplevelBlock (ame.Parameters, loc);
212                                 am_element.Block.AddStatement (new Return (element_selector, loc));
213                                 
214                                 args.Add (new Argument (am_element));
215                         }
216                         return args;
217                 }
218
219                 protected override string MethodName {
220                         get { return "GroupBy"; }
221                 }
222         }
223
224         public class Select : AQueryClause
225         {
226                 public Select (Expression expr, Location loc)
227                         : base (expr, loc)
228                 {
229                 }
230
231                 protected override string MethodName {
232                         get { return "Select"; }
233                 }
234         }
235
236         public class Where : AQueryClause
237         {
238                 public Where (Expression expr, Location loc)
239                         : base (expr, loc)
240                 {
241                 }
242
243                 protected override string MethodName {
244                         get { return "Where"; }
245                 }
246         }
247
248         public class OrderByAscending : AQueryClause
249         {
250                 public OrderByAscending (Expression expr)
251                         : base (expr, expr.Location)
252                 {
253                 }
254
255                 protected override string MethodName {
256                         get { return "OrderBy"; }
257                 }
258         }
259
260         public class OrderByDescending : AQueryClause
261         {
262                 public OrderByDescending (Expression expr)
263                         : base (expr, expr.Location)
264                 {
265                 }
266
267                 protected override string MethodName {
268                         get { return "OrderByDescending"; }
269                 }
270         }
271
272         public class ThenByAscending : OrderByAscending
273         {
274                 public ThenByAscending (Expression expr)
275                         : base (expr)
276                 {
277                 }
278
279                 protected override string MethodName {
280                         get { return "ThenBy"; }
281                 }
282         }
283
284         public class ThenByDescending : OrderByDescending
285         {
286                 public ThenByDescending (Expression expr)
287                         : base (expr)
288                 {
289                 }
290
291                 protected override string MethodName {
292                         get { return "ThenByDescending"; }
293                 }
294         }
295
296         //
297         // Only helper for implicit query type expression
298         //
299         public class ImplicitArgument : Expression
300         {
301                 public static ImplicitArgument Instance = new ImplicitArgument ();
302
303                 private ImplicitArgument ()
304                 {
305                 }
306
307                 public override Expression DoResolve (EmitContext ec)
308                 {
309                         throw new NotImplementedException ();
310                 }
311
312                 public override void Emit (EmitContext ec)
313                 {
314                         throw new NotImplementedException ();
315                 }
316         }
317 }