2 // linq.cs: support for query expressions
4 // Authors: Marek Safar (marek.safar@gmail.com)
6 // Licensed under the terms of the GNU GPL
8 // (C) 2007 Novell, Inc
12 using System.Reflection;
13 using System.Collections;
15 namespace Mono.CSharp.Linq
18 // Expression should be IExpression to save some memory and make a few things
23 public class QueryExpression : ARangeVariableQueryClause
25 public QueryExpression (Block block, Expression from, AQueryClause query)
26 : base (block, from, from.Location)
31 public override Expression DoResolve (EmitContext ec)
33 Expression e = BuildQueryClause (ec, expr, null, null);
38 protected override string MethodName {
39 get { throw new NotSupportedException (); }
43 public abstract class ALinqExpression : Expression
45 // Dictionary of method name -> MethodGroupExpr
46 static Hashtable methods = new Hashtable ();
47 static Type enumerable_class;
49 protected abstract string MethodName { get; }
51 protected MethodGroupExpr MethodGroup {
54 // Even if C# spec indicates that LINQ methods are context specific
55 // in reality they are hardcoded
57 MemberList ml = (MemberList)methods [MethodName];
59 if (enumerable_class == null)
60 enumerable_class = TypeManager.CoreLookupType ("System.Linq", "Enumerable");
62 ml = TypeManager.FindMembers (enumerable_class,
63 MemberTypes.Method, BindingFlags.Static | BindingFlags.Public,
64 Type.FilterName, MethodName);
67 return new MethodGroupExpr (ArrayList.Adapter (ml), enumerable_class, loc);
72 public abstract class AQueryClause : ALinqExpression
74 public AQueryClause next;
75 /*protected*/ public Expression expr;
77 protected AQueryClause (Expression expr, Location loc)
83 public override Expression DoResolve (EmitContext ec)
85 return expr.DoResolve (ec);
88 public override void Emit (EmitContext ec)
90 throw new NotSupportedException ();
93 public virtual Expression BuildQueryClause (EmitContext ec, Expression from, Parameter parameter, TransparentIdentifiersScope ti)
95 ArrayList args = new ArrayList (2);
96 args.Add (new Argument (from));
97 args.Add (new Argument (CreateSelector (ec, expr, parameter, ti)));
98 expr = new Invocation (MethodGroup, args);
100 return next.BuildQueryClause (ec, this, parameter, ti);
105 protected LambdaExpression CreateSelector (EmitContext ec, Expression expr, Parameter parameter, TransparentIdentifiersScope ti)
107 return CreateSelector (ec, expr, new Parameter [] { parameter }, ti);
110 protected LambdaExpression CreateSelector (EmitContext ec, Expression expr, Parameter[] parameters, TransparentIdentifiersScope ti)
112 Parameters p = new Parameters (parameters);
114 LambdaExpression selector = new LambdaExpression (
115 null, null, (TypeContainer) ec.DeclContainer, p, ec.CurrentBlock, loc);
116 selector.Block = new SelectorBlock (ec.CurrentBlock, p, ti, loc);
117 selector.Block.AddStatement (new Return (expr, loc));
119 selector.CreateAnonymousHelpers ();
120 selector.RootScope.DefineType ();
125 public virtual AQueryClause Next {
131 public AQueryClause Tail {
133 return next == null ? this : next.Tail;
139 // A query clause with an identifier (range variable)
141 public abstract class ARangeVariableQueryClause : AQueryClause
143 public readonly Block block;
144 protected Expression element_selector;
146 protected ARangeVariableQueryClause (Block block, Expression expr, Location loc)
152 protected virtual void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
153 Parameter parameter, TransparentIdentifiersScope ti)
155 args.Add (new Argument (CreateSelector (ec, expr, parentParameter, null)));
156 args.Add (new Argument (CreateSelector (ec, element_selector,
157 new Parameter [] { parentParameter, parameter }, ti)));
161 // Customization for range variables which not only creates a lambda expression but
162 // also builds a chain of range varible pairs
164 public override Expression BuildQueryClause (EmitContext ec, Expression from, Parameter parentParameter, TransparentIdentifiersScope ti)
166 ICollection values = block.Variables.Values;
167 if (values.Count != 1)
168 throw new NotImplementedException ("Count != 1");
170 IEnumerator enumerator = values.GetEnumerator ();
171 enumerator.MoveNext ();
172 LocalInfo li = (LocalInfo) enumerator.Current;
175 if (li.Type == ImplicitQueryParameter.ImplicitType.Instance) {
176 parameter = new ImplicitQueryParameter (li);
178 parameter = new Parameter (li.Type, li.Name, Parameter.Modifier.NONE, null, li.Location);
181 if (parentParameter == null)
182 return next.BuildQueryClause (ec, expr, parameter, ti);
186 // Builds transparent identifiers, each identifier includes its parent
187 // type at index 0, and new value at index 1. This is not valid for the
188 // first one which includes two values directly.
190 ArrayList transp_args = new ArrayList (2);
191 transp_args.Add (new AnonymousTypeParameter (parentParameter));
192 transp_args.Add (CreateAnonymousTypeVariable (parameter));
193 element_selector = new AnonymousTypeDeclaration (transp_args, (TypeContainer) ec.DeclContainer, loc);
196 ArrayList args = new ArrayList (3);
197 args.Add (new Argument (from));
198 AddSelectorArguments (ec, args, parentParameter, parameter, ti);
200 expr = new Invocation (MethodGroup, args);
203 // Parameter indentifiers goes to the scope
205 string[] identifiers;
207 identifiers = new string [] { parentParameter.Name, parameter.Name };
209 identifiers = new string [] { parameter.Name };
212 TransparentParameter tp = new TransparentParameter (loc);
213 return next.BuildQueryClause (ec, this, tp,
214 new TransparentIdentifiersScope (ti, tp, identifiers));
221 // For transparent identifiers, creates an instance of variable expression
223 protected virtual AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
225 return new AnonymousTypeParameter (parameter);
229 public class Cast : ALinqExpression
232 readonly Expression cast_type;
234 public Cast (Expression type, Expression expr, Location loc)
236 this.cast_type = type;
241 protected override void CloneTo (CloneContext clonectx, Expression target)
243 // We don't have to clone cast type
244 Cast t = (Cast) target;
245 t.expr = expr.Clone (clonectx);
248 protected override string MethodName {
249 get { return "Cast"; }
252 public override Expression DoResolve (EmitContext ec)
254 TypeArguments type_arguments = new TypeArguments (loc, cast_type);
255 Expression cast = MethodGroup.ResolveGeneric (ec, type_arguments);
257 ArrayList args = new ArrayList (1);
258 args.Add (new Argument (expr));
259 return new Invocation (cast, args).DoResolve (ec);
262 public override void Emit (EmitContext ec)
264 throw new NotSupportedException ();
268 public class GroupBy : AQueryClause
270 readonly Expression element_selector;
272 public GroupBy (Expression elementSelector, Expression keySelector, Location loc)
273 : base (keySelector, loc)
275 this.element_selector = elementSelector;
278 public override Expression BuildQueryClause (EmitContext ec, Expression from, Parameter parameter, TransparentIdentifiersScope ti)
280 ArrayList args = new ArrayList (3);
281 args.Add (new Argument (from));
282 args.Add (new Argument (CreateSelector (ec, expr, parameter, ti)));
284 // A query can be optimized when selector is not group by specific
285 if (!element_selector.Equals (from))
286 args.Add (new Argument (CreateSelector (ec, element_selector, parameter, ti)));
288 expr = new Invocation (MethodGroup, args);
290 return next.BuildQueryClause (ec, this, parameter, ti);
295 protected override string MethodName {
296 get { return "GroupBy"; }
300 public class Let : ARangeVariableQueryClause
302 class RangeAnonymousTypeParameter : AnonymousTypeParameter
304 readonly Parameter parameter;
306 public RangeAnonymousTypeParameter (Expression initializer, Parameter parameter)
307 : base (initializer, parameter.Name, parameter.Location)
309 this.parameter = parameter;
312 public override Expression DoResolve (EmitContext ec)
314 Expression e = base.DoResolve (ec);
317 // Spread resolved initializer type
319 parameter.ParameterType = type;
320 parameter.Resolve (ec);
327 public Let (Block block, Expression expr)
328 : base (block, expr, expr.Location)
332 protected override void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter, Parameter parameter,
333 TransparentIdentifiersScope ti)
335 args.Add (new Argument (CreateSelector (ec, element_selector, parentParameter, ti)));
338 protected override AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
340 return new RangeAnonymousTypeParameter (expr, parameter);
343 protected override string MethodName {
344 get { return "Select"; }
348 public class Select : AQueryClause
350 public Select (Expression expr, Location loc)
355 protected override string MethodName {
356 get { return "Select"; }
360 public class SelectMany : ARangeVariableQueryClause
362 public SelectMany (Block block, Expression expr)
363 : base (block, expr, expr.Location)
367 protected override string MethodName {
368 get { return "SelectMany"; }
371 public override AQueryClause Next {
373 element_selector = value.expr;
375 // Can be optimized as SelectMany element selector
384 public class Where : AQueryClause
386 public Where (Expression expr, Location loc)
391 protected override string MethodName {
392 get { return "Where"; }
396 public class OrderByAscending : AQueryClause
398 public OrderByAscending (Expression expr)
399 : base (expr, expr.Location)
403 protected override string MethodName {
404 get { return "OrderBy"; }
408 public class OrderByDescending : AQueryClause
410 public OrderByDescending (Expression expr)
411 : base (expr, expr.Location)
415 protected override string MethodName {
416 get { return "OrderByDescending"; }
420 public class ThenByAscending : OrderByAscending
422 public ThenByAscending (Expression expr)
427 protected override string MethodName {
428 get { return "ThenBy"; }
432 public class ThenByDescending : OrderByDescending
434 public ThenByDescending (Expression expr)
439 protected override string MethodName {
440 get { return "ThenByDescending"; }
444 class ImplicitQueryParameter : ImplicitLambdaParameter
446 public sealed class ImplicitType : Expression
448 public static ImplicitType Instance = new ImplicitType ();
450 private ImplicitType ()
454 protected override void CloneTo (CloneContext clonectx, Expression target)
459 public override Expression DoResolve (EmitContext ec)
461 throw new NotSupportedException ();
464 public override void Emit (EmitContext ec)
466 throw new NotSupportedException ();
470 readonly LocalInfo variable;
472 public ImplicitQueryParameter (LocalInfo variable)
473 : base (variable.Name, variable.Location)
475 this.variable = variable;
478 public override bool Resolve (IResolveContext ec)
480 if (!base.Resolve (ec))
483 variable.VariableType = parameter_type;
489 // Transparent parameters are used to package up the intermediate results
490 // and pass them onto next clause
492 public class TransparentParameter : ImplicitLambdaParameter
495 const string ParameterNamePrefix = "<>__TranspIdent";
497 public TransparentParameter (Location loc)
498 : base (ParameterNamePrefix + counter++, loc)
504 // Transparent identifiers are stored in nested anonymous types, each type can contain
505 // up to 2 identifiers or 1 identifier and parent type.
507 public class TransparentIdentifiersScope
509 readonly string [] identifiers;
510 readonly TransparentIdentifiersScope parent;
511 readonly TransparentParameter parameter;
513 public TransparentIdentifiersScope (TransparentIdentifiersScope parent,
514 TransparentParameter parameter, string [] identifiers)
516 this.parent = parent;
517 this.parameter = parameter;
518 this.identifiers = identifiers;
521 public MemberAccess GetIdentifier (string name)
523 TransparentIdentifiersScope ident = FindIdentifier (name);
527 return new MemberAccess (CreateIndentifierNestingExpression (ident), name);
530 TransparentIdentifiersScope FindIdentifier (string name)
532 foreach (string s in identifiers) {
540 return parent.FindIdentifier (name);
543 Expression CreateIndentifierNestingExpression (TransparentIdentifiersScope end)
545 Expression expr = new SimpleName (parameter.Name, parameter.Location);
546 TransparentIdentifiersScope current = this;
547 while (current != end)
549 current = current.parent;
550 expr = new MemberAccess (expr, current.parameter.Name);
558 // Lambda expression block which contains transparent identifiers
560 class SelectorBlock : ToplevelBlock
562 readonly TransparentIdentifiersScope transparent_identifiers;
564 public SelectorBlock (Block block, Parameters parameters,
565 TransparentIdentifiersScope transparentIdentifiers, Location loc)
566 : base (block, parameters, loc)
568 this.transparent_identifiers = transparentIdentifiers;
571 public override Expression GetTransparentIdentifier (string name)
573 if (transparent_identifiers == null)
576 return transparent_identifiers.GetIdentifier (name);