2007-06-20 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                 public 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                         LocalInfo li = null;
42                         foreach (LocalInfo li_temp in Block.Variables.Values)
43                         {
44                                 li = li_temp;
45                                 break;
46                         }
47
48                         Expression e = query.BuildQueryClause (ec, this, From, li);
49                         return e.Resolve (ec);
50                 }
51
52                 public override void Emit (EmitContext ec)
53                 {
54                         throw new NotSupportedException ();
55                 }
56         }
57
58         public abstract class ALinqExpression : Expression
59         {
60                 // Dictionary of method name -> MethodGroupExpr
61                 static Hashtable methods = new Hashtable ();
62                 static Type enumerable_class;
63
64                 protected abstract string MethodName { get; }
65
66                 protected MethodGroupExpr MethodGroup {
67                         get {
68                                 MethodGroupExpr method_group = (MethodGroupExpr)methods [MethodName];
69                                 if (method_group != null)
70                                         return method_group;
71
72                                 if (enumerable_class == null)
73                                         enumerable_class = TypeManager.CoreLookupType ("System.Linq", "Enumerable");
74
75                                 MemberList ml = TypeManager.FindMembers (enumerable_class,
76                                         MemberTypes.Method, BindingFlags.Static | BindingFlags.Public,
77                                         Type.FilterName, MethodName);
78
79                                 method_group = new MethodGroupExpr (ArrayList.Adapter (ml), loc);
80                                 methods.Add (MethodName, method_group);
81                                 return method_group;
82                         }
83                 }
84         }
85
86         public abstract class AQueryClause : ALinqExpression
87         {
88                 public AQueryClause Next;
89                 protected Expression expr;
90
91                 protected AQueryClause (Expression expr, Location loc)
92                 {
93                         this.expr = expr;
94                         this.loc = loc;
95                 }
96
97                 public override Expression DoResolve (EmitContext ec)
98                 {
99                         return expr.DoResolve (ec);
100                 }
101
102                 public override void Emit (EmitContext ec)
103                 {
104                         throw new NotSupportedException ();
105                 }
106
107                 public Expression BuildQueryClause (EmitContext ec, QueryExpression top, Expression from, LocalInfo li)
108                 {
109                         Parameters parameters = new Parameters (new Parameter (li.Type, li.Name, Parameter.Modifier.NONE, null, loc));
110                         AnonymousMethodExpression ame = new AnonymousMethodExpression (
111                                 null, null, top.Host,
112                                 parameters,
113                                 top.Block, loc);
114                         ame.Block = new ToplevelBlock (parameters, loc);
115                         ame.Block.AddStatement (new Return (expr, loc));
116
117                         expr = new Invocation (MethodGroup, CreateArguments (ame, from));
118                         if (Next != null)
119                                 return Next.BuildQueryClause (ec, top, this, li);
120
121                         return expr;
122                 }
123                                                
124                 protected virtual ArrayList CreateArguments (AnonymousMethodExpression ame, Expression from)
125                 {
126                         ArrayList args = new ArrayList (2);
127                         args.Add (new Argument (from));
128                         args.Add (new Argument (ame));
129                         return args;
130                 }
131         }
132
133         public class Cast : ALinqExpression
134         {
135                 readonly Expression expr;
136                 readonly Expression cast_type;
137
138                 public Cast (Expression type, Expression expr, Location loc)
139                 {
140                         this.cast_type = type;
141                         this.expr = expr;
142                         this.loc = loc;
143                 }
144
145                 protected override string MethodName {
146                         get { return "Cast"; }
147                 }
148
149                 public override Expression DoResolve (EmitContext ec)
150                 {
151                         TypeArguments type_arguments = new TypeArguments (loc, cast_type);
152                         Expression cast = MethodGroup.ResolveGeneric (ec, type_arguments);
153
154                         ArrayList args = new ArrayList (1);
155                         args.Add (new Argument (expr));
156                         return new Invocation (cast, args).DoResolve (ec);
157                 }
158
159                 public override void Emit (EmitContext ec)
160                 {
161                         throw new NotSupportedException ();
162                 }
163         }
164
165         public class GroupBy : AQueryClause
166         {
167                 readonly Expression element_selector;
168                 
169                 public GroupBy (Expression elementSelector, Expression keySelector, Location loc)
170                         : base (keySelector, loc)
171                 {
172                         this.element_selector = elementSelector;
173                 }
174
175                 protected override ArrayList CreateArguments (AnonymousMethodExpression ame, Expression from)
176                 {
177                         ArrayList args = base.CreateArguments (ame, from);
178                         
179                         // A query can be optimized when selector is not group by specific
180                         if (!element_selector.Equals (from)) {
181                                 AnonymousMethodExpression am_element = new AnonymousMethodExpression (
182                                         null, null, ame.Host, ame.Parameters, ame.Container, loc);
183                                 am_element.Block = new ToplevelBlock (ame.Parameters, loc);
184                                 am_element.Block.AddStatement (new Return (element_selector, loc));
185                                 
186                                 args.Add (new Argument (am_element));
187                         }
188                         return args;
189                 }
190
191                 protected override string MethodName {
192                         get { return "GroupBy"; }
193                 }
194         }
195
196         public class Select : AQueryClause
197         {
198                 public Select (Expression expr, Location loc)
199                         : base (expr, loc)
200                 {
201                 }
202
203                 protected override string MethodName {
204                         get { return "Select"; }
205                 }
206         }
207
208         public class Where : AQueryClause
209         {
210                 public Where (Expression expr, Location loc)
211                         : base (expr, loc)
212                 {
213                 }
214
215                 protected override string MethodName {
216                         get { return "Where"; }
217                 }
218         }
219 }