884 lines
22 KiB
C#
884 lines
22 KiB
C#
//
|
|
// linq.cs: support for query expressions
|
|
//
|
|
// Authors: Marek Safar (marek.safar@gmail.com)
|
|
//
|
|
// Dual licensed under the terms of the MIT X11 or GNU GPL
|
|
//
|
|
// Copyright 2007-2008 Novell, Inc
|
|
// Copyright 2011 Xamarin Inc
|
|
//
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Mono.CSharp.Linq
|
|
{
|
|
public class QueryExpression : AQueryClause
|
|
{
|
|
public QueryExpression (AQueryClause start)
|
|
: base (null, null, start.Location)
|
|
{
|
|
this.next = start;
|
|
}
|
|
|
|
public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parentParameter)
|
|
{
|
|
return next.BuildQueryClause (ec, lSide, parentParameter);
|
|
}
|
|
|
|
protected override Expression DoResolve (ResolveContext ec)
|
|
{
|
|
int counter = QueryBlock.TransparentParameter.Counter;
|
|
|
|
Expression e = BuildQueryClause (ec, null, null);
|
|
if (e != null)
|
|
e = e.Resolve (ec);
|
|
|
|
//
|
|
// Reset counter in probing mode to ensure that all transparent
|
|
// identifier anonymous types are created only once
|
|
//
|
|
if (ec.IsInProbingMode)
|
|
QueryBlock.TransparentParameter.Counter = counter;
|
|
|
|
return e;
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { throw new NotSupportedException (); }
|
|
}
|
|
}
|
|
|
|
public abstract class AQueryClause : ShimExpression
|
|
{
|
|
protected class QueryExpressionAccess : MemberAccess
|
|
{
|
|
public QueryExpressionAccess (Expression expr, string methodName, Location loc)
|
|
: base (expr, methodName, loc)
|
|
{
|
|
}
|
|
|
|
public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc)
|
|
: base (expr, methodName, typeArguments, loc)
|
|
{
|
|
}
|
|
|
|
public override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
|
|
{
|
|
ec.Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " +
|
|
"Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?",
|
|
name);
|
|
}
|
|
}
|
|
|
|
protected class QueryExpressionInvocation : Invocation, OverloadResolver.IErrorHandler
|
|
{
|
|
public QueryExpressionInvocation (QueryExpressionAccess expr, Arguments arguments)
|
|
: base (expr, arguments)
|
|
{
|
|
}
|
|
|
|
protected override MethodGroupExpr DoResolveOverload (ResolveContext rc)
|
|
{
|
|
using (rc.Set (ResolveContext.Options.QueryClauseScope)) {
|
|
return mg.OverloadResolve (rc, ref arguments, this, OverloadResolver.Restrictions.None);
|
|
}
|
|
}
|
|
|
|
protected override Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr)
|
|
{
|
|
ec.Report.Error (1979, loc,
|
|
"Query expressions with a source or join sequence of type `dynamic' are not allowed");
|
|
return null;
|
|
}
|
|
|
|
#region IErrorHandler Members
|
|
|
|
bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
|
|
{
|
|
var emg = mg as ExtensionMethodGroupExpr;
|
|
var type = emg == null ? mg.InstanceExpression : emg.ExtensionExpression;
|
|
|
|
ec.Report.SymbolRelatedToPreviousError (best);
|
|
ec.Report.SymbolRelatedToPreviousError (ambiguous);
|
|
ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
|
|
best.Name, type.Type.GetSignatureForError ());
|
|
return true;
|
|
}
|
|
|
|
bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
|
|
{
|
|
var ms = (MethodSpec) best;
|
|
TypeSpec source_type = ms.Parameters.ExtensionMethodType;
|
|
if (source_type != null) {
|
|
Argument a = arguments[0];
|
|
|
|
if (TypeManager.IsGenericType (source_type) && InflatedTypeSpec.ContainsTypeParameter (source_type)) {
|
|
TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments);
|
|
tic.OutputTypeInference (rc, a.Expr, source_type);
|
|
if (tic.FixAllTypes (rc)) {
|
|
source_type = source_type.GetDefinition ().MakeGenericType (rc, tic.InferredTypeArguments);
|
|
}
|
|
}
|
|
|
|
if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) {
|
|
rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
|
|
best.Name, a.Type.GetSignatureForError ());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (best.Name == "SelectMany") {
|
|
rc.Report.Error (1943, loc,
|
|
"An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
|
|
arguments[0].GetSignatureForError ());
|
|
} else {
|
|
rc.Report.Error (1942, loc,
|
|
"An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
|
|
best.Name.ToLowerInvariant (), best.Name);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public AQueryClause next;
|
|
public QueryBlock block;
|
|
|
|
protected AQueryClause (QueryBlock block, Expression expr, Location loc)
|
|
: base (expr)
|
|
{
|
|
this.block = block;
|
|
this.loc = loc;
|
|
}
|
|
|
|
protected override void CloneTo (CloneContext clonectx, Expression target)
|
|
{
|
|
base.CloneTo (clonectx, target);
|
|
|
|
AQueryClause t = (AQueryClause) target;
|
|
|
|
if (block != null)
|
|
t.block = (QueryBlock) clonectx.LookupBlock (block);
|
|
|
|
if (next != null)
|
|
t.next = (AQueryClause) next.Clone (clonectx);
|
|
}
|
|
|
|
protected override Expression DoResolve (ResolveContext ec)
|
|
{
|
|
return expr.Resolve (ec);
|
|
}
|
|
|
|
public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
|
|
{
|
|
Arguments args = null;
|
|
CreateArguments (ec, parameter, ref args);
|
|
lSide = CreateQueryExpression (lSide, args);
|
|
if (next != null) {
|
|
parameter = CreateChildrenParameters (parameter);
|
|
|
|
Select s = next as Select;
|
|
if (s == null || s.IsRequired (parameter))
|
|
return next.BuildQueryClause (ec, lSide, parameter);
|
|
|
|
// Skip transparent select clause if any clause follows
|
|
if (next.next != null)
|
|
return next.next.BuildQueryClause (ec, lSide, parameter);
|
|
}
|
|
|
|
return lSide;
|
|
}
|
|
|
|
protected virtual Parameter CreateChildrenParameters (Parameter parameter)
|
|
{
|
|
// Have to clone the parameter for any children use, it carries block sensitive data
|
|
return parameter.Clone ();
|
|
}
|
|
|
|
protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
|
|
{
|
|
args = new Arguments (2);
|
|
|
|
LambdaExpression selector = new LambdaExpression (loc);
|
|
|
|
block.SetParameter (parameter);
|
|
selector.Block = block;
|
|
selector.Block.AddStatement (new ContextualReturn (expr));
|
|
|
|
args.Add (new Argument (selector));
|
|
}
|
|
|
|
protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
|
|
{
|
|
return new QueryExpressionInvocation (
|
|
new QueryExpressionAccess (lSide, MethodName, loc), arguments);
|
|
}
|
|
|
|
protected abstract string MethodName { get; }
|
|
|
|
public AQueryClause Next {
|
|
set {
|
|
next = value;
|
|
}
|
|
}
|
|
|
|
public AQueryClause Tail {
|
|
get {
|
|
return next == null ? this : next.Tail;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// A query clause with an identifier (range variable)
|
|
//
|
|
public abstract class ARangeVariableQueryClause : AQueryClause
|
|
{
|
|
sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
|
|
{
|
|
public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter)
|
|
: base (initializer, parameter.Name, parameter.Location)
|
|
{
|
|
}
|
|
|
|
protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
|
|
{
|
|
ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
|
|
Name, initializer);
|
|
}
|
|
}
|
|
|
|
class RangeParameterReference : ParameterReference
|
|
{
|
|
Parameter parameter;
|
|
|
|
public RangeParameterReference (Parameter p)
|
|
: base (null, p.Location)
|
|
{
|
|
this.parameter = p;
|
|
}
|
|
|
|
protected override Expression DoResolve (ResolveContext ec)
|
|
{
|
|
pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter);
|
|
return base.DoResolve (ec);
|
|
}
|
|
}
|
|
|
|
protected RangeVariable identifier;
|
|
|
|
protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
|
|
: base (block, expr, loc)
|
|
{
|
|
this.identifier = identifier;
|
|
}
|
|
|
|
public RangeVariable Identifier {
|
|
get {
|
|
return identifier;
|
|
}
|
|
}
|
|
|
|
public FullNamedExpression IdentifierType { get; set; }
|
|
|
|
protected Invocation CreateCastExpression (Expression lSide)
|
|
{
|
|
return new QueryExpressionInvocation (
|
|
new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null);
|
|
}
|
|
|
|
protected override Parameter CreateChildrenParameters (Parameter parameter)
|
|
{
|
|
return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ());
|
|
}
|
|
|
|
protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init)
|
|
{
|
|
var args = new List<AnonymousTypeParameter> (2);
|
|
|
|
//
|
|
// The first argument is the reference to the parameter
|
|
//
|
|
args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location));
|
|
|
|
//
|
|
// The second argument is the linq expression
|
|
//
|
|
args.Add (new RangeAnonymousTypeParameter (init, name));
|
|
|
|
//
|
|
// Create unique anonymous type
|
|
//
|
|
return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location);
|
|
}
|
|
|
|
protected virtual RangeVariable GetIntoVariable ()
|
|
{
|
|
return identifier;
|
|
}
|
|
}
|
|
|
|
public sealed class RangeVariable : INamedBlockVariable
|
|
{
|
|
Block block;
|
|
|
|
public RangeVariable (string name, Location loc)
|
|
{
|
|
Name = name;
|
|
Location = loc;
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public Block Block {
|
|
get {
|
|
return block;
|
|
}
|
|
set {
|
|
block = value;
|
|
}
|
|
}
|
|
|
|
public bool IsDeclared {
|
|
get {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public bool IsParameter {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public Location Location { get; private set; }
|
|
|
|
public string Name { get; private set; }
|
|
|
|
#endregion
|
|
|
|
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
|
|
{
|
|
//
|
|
// We know the variable name is somewhere in the scope. This generates
|
|
// an access expression from current block
|
|
//
|
|
var pb = rc.CurrentBlock.ParametersBlock;
|
|
while (true) {
|
|
if (pb is QueryBlock) {
|
|
for (int i = pb.Parameters.Count - 1; i >= 0; --i) {
|
|
var p = pb.Parameters[i];
|
|
if (p.Name == Name)
|
|
return pb.GetParameterReference (i, loc);
|
|
|
|
Expression expr = null;
|
|
var tp = p as QueryBlock.TransparentParameter;
|
|
while (tp != null) {
|
|
if (expr == null)
|
|
expr = pb.GetParameterReference (i, loc);
|
|
else
|
|
expr = new TransparentMemberAccess (expr, tp.Name);
|
|
|
|
if (tp.Identifier == Name)
|
|
return new TransparentMemberAccess (expr, Name);
|
|
|
|
if (tp.Parent.Name == Name)
|
|
return new TransparentMemberAccess (expr, Name);
|
|
|
|
tp = tp.Parent as QueryBlock.TransparentParameter;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pb == block)
|
|
return null;
|
|
|
|
pb = pb.Parent.ParametersBlock;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class QueryStartClause : ARangeVariableQueryClause
|
|
{
|
|
public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc)
|
|
: base (block, identifier, expr, loc)
|
|
{
|
|
block.AddRangeVariable (identifier);
|
|
}
|
|
|
|
public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
|
|
{
|
|
if (IdentifierType != null)
|
|
expr = CreateCastExpression (expr);
|
|
|
|
if (parameter == null)
|
|
lSide = expr;
|
|
|
|
return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location));
|
|
}
|
|
|
|
protected override Expression DoResolve (ResolveContext ec)
|
|
{
|
|
Expression e = BuildQueryClause (ec, null, null);
|
|
return e.Resolve (ec);
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { throw new NotSupportedException (); }
|
|
}
|
|
}
|
|
|
|
public class GroupBy : AQueryClause
|
|
{
|
|
Expression element_selector;
|
|
QueryBlock element_block;
|
|
|
|
public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc)
|
|
: base (block, keySelector, loc)
|
|
{
|
|
//
|
|
// Optimizes clauses like `group A by A'
|
|
//
|
|
if (!elementSelector.Equals (keySelector)) {
|
|
this.element_selector = elementSelector;
|
|
this.element_block = elementBlock;
|
|
}
|
|
}
|
|
|
|
public Expression SelectorExpression {
|
|
get {
|
|
return element_selector;
|
|
}
|
|
}
|
|
|
|
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
|
|
{
|
|
base.CreateArguments (ec, parameter, ref args);
|
|
|
|
if (element_selector != null) {
|
|
LambdaExpression lambda = new LambdaExpression (element_selector.Location);
|
|
|
|
element_block.SetParameter (parameter.Clone ());
|
|
lambda.Block = element_block;
|
|
lambda.Block.AddStatement (new ContextualReturn (element_selector));
|
|
args.Add (new Argument (lambda));
|
|
}
|
|
}
|
|
|
|
protected override void CloneTo (CloneContext clonectx, Expression target)
|
|
{
|
|
GroupBy t = (GroupBy) target;
|
|
if (element_selector != null) {
|
|
t.element_selector = element_selector.Clone (clonectx);
|
|
t.element_block = (QueryBlock) element_block.Clone (clonectx);
|
|
}
|
|
|
|
base.CloneTo (clonectx, t);
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "GroupBy"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class Join : SelectMany
|
|
{
|
|
QueryBlock inner_selector, outer_selector;
|
|
|
|
public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc)
|
|
: base (block, lt, inner, loc)
|
|
{
|
|
this.outer_selector = outerSelector;
|
|
this.inner_selector = innerSelector;
|
|
}
|
|
|
|
public QueryBlock InnerSelector {
|
|
get {
|
|
return inner_selector;
|
|
}
|
|
}
|
|
|
|
public QueryBlock OuterSelector {
|
|
get {
|
|
return outer_selector;
|
|
}
|
|
}
|
|
|
|
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
|
|
{
|
|
args = new Arguments (4);
|
|
|
|
if (IdentifierType != null)
|
|
expr = CreateCastExpression (expr);
|
|
|
|
args.Add (new Argument (expr));
|
|
|
|
outer_selector.SetParameter (parameter.Clone ());
|
|
var lambda = new LambdaExpression (outer_selector.StartLocation);
|
|
lambda.Block = outer_selector;
|
|
args.Add (new Argument (lambda));
|
|
|
|
inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location));
|
|
lambda = new LambdaExpression (inner_selector.StartLocation);
|
|
lambda.Block = inner_selector;
|
|
args.Add (new Argument (lambda));
|
|
|
|
base.CreateArguments (ec, parameter, ref args);
|
|
}
|
|
|
|
protected override void CloneTo (CloneContext clonectx, Expression target)
|
|
{
|
|
Join t = (Join) target;
|
|
t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx);
|
|
t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx);
|
|
base.CloneTo (clonectx, t);
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "Join"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class GroupJoin : Join
|
|
{
|
|
readonly RangeVariable into;
|
|
|
|
public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner,
|
|
QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc)
|
|
: base (block, lt, inner, outerSelector, innerSelector, loc)
|
|
{
|
|
this.into = into;
|
|
}
|
|
|
|
protected override RangeVariable GetIntoVariable ()
|
|
{
|
|
return into;
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "GroupJoin"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class Let : ARangeVariableQueryClause
|
|
{
|
|
public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
|
|
: base (block, identifier, expr, loc)
|
|
{
|
|
}
|
|
|
|
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
|
|
{
|
|
expr = CreateRangeVariableType (ec, parameter, identifier, expr);
|
|
base.CreateArguments (ec, parameter, ref args);
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "Select"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class Select : AQueryClause
|
|
{
|
|
public Select (QueryBlock block, Expression expr, Location loc)
|
|
: base (block, expr, loc)
|
|
{
|
|
}
|
|
|
|
//
|
|
// For queries like `from a orderby a select a'
|
|
// the projection is transparent and select clause can be safely removed
|
|
//
|
|
public bool IsRequired (Parameter parameter)
|
|
{
|
|
SimpleName sn = expr as SimpleName;
|
|
if (sn == null)
|
|
return true;
|
|
|
|
return sn.Name != parameter.Name;
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "Select"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
|
|
}
|
|
|
|
public class SelectMany : ARangeVariableQueryClause
|
|
{
|
|
public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
|
|
: base (block, identifier, expr, loc)
|
|
{
|
|
}
|
|
|
|
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
|
|
{
|
|
if (args == null) {
|
|
if (IdentifierType != null)
|
|
expr = CreateCastExpression (expr);
|
|
|
|
base.CreateArguments (ec, parameter.Clone (), ref args);
|
|
}
|
|
|
|
Expression result_selector_expr;
|
|
QueryBlock result_block;
|
|
|
|
var target = GetIntoVariable ();
|
|
var target_param = new ImplicitLambdaParameter (target.Name, target.Location);
|
|
|
|
//
|
|
// When select follows use it as a result selector
|
|
//
|
|
if (next is Select) {
|
|
result_selector_expr = next.Expr;
|
|
|
|
result_block = next.block;
|
|
result_block.SetParameters (parameter, target_param);
|
|
|
|
next = next.next;
|
|
} else {
|
|
result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location));
|
|
|
|
result_block = new QueryBlock (block.Parent, block.StartLocation);
|
|
result_block.SetParameters (parameter, target_param);
|
|
}
|
|
|
|
LambdaExpression result_selector = new LambdaExpression (Location);
|
|
result_selector.Block = result_block;
|
|
result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
|
|
|
|
args.Add (new Argument (result_selector));
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "SelectMany"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class Where : AQueryClause
|
|
{
|
|
public Where (QueryBlock block, Expression expr, Location loc)
|
|
: base (block, expr, loc)
|
|
{
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "Where"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class OrderByAscending : AQueryClause
|
|
{
|
|
public OrderByAscending (QueryBlock block, Expression expr)
|
|
: base (block, expr, expr.Location)
|
|
{
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "OrderBy"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class OrderByDescending : AQueryClause
|
|
{
|
|
public OrderByDescending (QueryBlock block, Expression expr)
|
|
: base (block, expr, expr.Location)
|
|
{
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "OrderByDescending"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class ThenByAscending : OrderByAscending
|
|
{
|
|
public ThenByAscending (QueryBlock block, Expression expr)
|
|
: base (block, expr)
|
|
{
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "ThenBy"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
public class ThenByDescending : OrderByDescending
|
|
{
|
|
public ThenByDescending (QueryBlock block, Expression expr)
|
|
: base (block, expr)
|
|
{
|
|
}
|
|
|
|
protected override string MethodName {
|
|
get { return "ThenByDescending"; }
|
|
}
|
|
|
|
public override object Accept (StructuralVisitor visitor)
|
|
{
|
|
return visitor.Visit (this);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Implicit query block
|
|
//
|
|
public class QueryBlock : ParametersBlock
|
|
{
|
|
//
|
|
// Transparent parameters are used to package up the intermediate results
|
|
// and pass them onto next clause
|
|
//
|
|
public sealed class TransparentParameter : ImplicitLambdaParameter
|
|
{
|
|
public static int Counter;
|
|
const string ParameterNamePrefix = "<>__TranspIdent";
|
|
|
|
public readonly Parameter Parent;
|
|
public readonly string Identifier;
|
|
|
|
public TransparentParameter (Parameter parent, RangeVariable identifier)
|
|
: base (ParameterNamePrefix + Counter++, identifier.Location)
|
|
{
|
|
Parent = parent;
|
|
Identifier = identifier.Name;
|
|
}
|
|
|
|
public static void Reset ()
|
|
{
|
|
Counter = 0;
|
|
}
|
|
}
|
|
|
|
public QueryBlock (Block parent, Location start)
|
|
: base (parent, ParametersCompiled.EmptyReadOnlyParameters, start, Flags.CompilerGenerated)
|
|
{
|
|
}
|
|
|
|
public void AddRangeVariable (RangeVariable variable)
|
|
{
|
|
variable.Block = this;
|
|
TopBlock.AddLocalName (variable.Name, variable, true);
|
|
}
|
|
|
|
public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
|
|
{
|
|
TopBlock.Report.Error (1931, variable.Location,
|
|
"A range variable `{0}' conflicts with a previous declaration of `{0}'",
|
|
name);
|
|
}
|
|
|
|
public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
|
|
{
|
|
TopBlock.Report.Error (1930, variable.Location,
|
|
"A range variable `{0}' has already been declared in this scope",
|
|
name);
|
|
}
|
|
|
|
public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
|
|
{
|
|
TopBlock.Report.Error (1948, loc,
|
|
"A range variable `{0}' conflicts with a method type parameter",
|
|
name);
|
|
}
|
|
|
|
public void SetParameter (Parameter parameter)
|
|
{
|
|
base.parameters = new ParametersCompiled (parameter);
|
|
base.parameter_info = new ParameterInfo[] {
|
|
new ParameterInfo (this, 0)
|
|
};
|
|
}
|
|
|
|
public void SetParameters (Parameter first, Parameter second)
|
|
{
|
|
base.parameters = new ParametersCompiled (first, second);
|
|
base.parameter_info = new ParameterInfo[] {
|
|
new ParameterInfo (this, 0),
|
|
new ParameterInfo (this, 1)
|
|
};
|
|
}
|
|
}
|
|
|
|
sealed class TransparentMemberAccess : MemberAccess
|
|
{
|
|
public TransparentMemberAccess (Expression expr, string name)
|
|
: base (expr, name)
|
|
{
|
|
}
|
|
|
|
public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
|
|
{
|
|
rc.Report.Error (1947, loc,
|
|
"A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value",
|
|
Name);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|