public QueryUnboundLambdaState(UnboundLambda unbound, Binder binder, RangeVariableMap rangeVariableMap, ImmutableArray <RangeVariableSymbol> parameters, LambdaBodyResolver bodyResolver)
     : base(unbound, binder)
 {
     this.parameters       = parameters;
     this.bodyResolver     = bodyResolver;
     this.rangeVariableMap = rangeVariableMap;
 }
 public QueryUnboundLambdaState(UnboundLambda unbound, Binder binder, RangeVariableMap rangeVariableMap, ImmutableArray<RangeVariableSymbol> parameters, LambdaBodyResolver bodyResolver)
     : base(unbound, binder)
 {
     this.parameters = parameters;
     this.bodyResolver = bodyResolver;
     this.rangeVariableMap = rangeVariableMap;
 }
예제 #3
0
        UnboundLambda MakePairLambda(CSharpSyntaxNode node, QueryTranslationState state, RangeVariableSymbol x1, RangeVariableSymbol x2)
        {
            LambdaBodyResolver resolver = (LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag d) =>
            {
                var x1Expression = new BoundParameter(node, lambdaSymbol.Parameters[0])
                {
                    WasCompilerGenerated = true
                };
                var x2Expression = new BoundParameter(node, lambdaSymbol.Parameters[1])
                {
                    WasCompilerGenerated = true
                };
                var construction = MakePair(node, x1.Name, x1Expression, x2.Name, x2Expression, state, d);
                return(lambdaBodyBinder.CreateBlockFromExpression(ImmutableArray <LocalSymbol> .Empty, construction, node, d));
            };
            var result = MakeQueryUnboundLambda(state.RangeVariableMap(), Args(x1, x2), node, resolver);

            state.rangeVariable = state.TransparentRangeVariable(this);
            state.AddTransparentIdentifier(x1.Name);
            var x2m = state.allRangeVariables[x2];

            x2m[x2m.Count - 1] = x2.Name;
            return(result);
        }
예제 #4
0
        UnboundLambda MakeQueryUnboundLambda(RangeVariableMap qvm, ImmutableArray <RangeVariableSymbol> parameters, CSharpSyntaxNode node, LambdaBodyResolver resolver)
        {
            var state  = new QueryUnboundLambdaState(null, this, qvm, parameters, resolver);
            var lambda = new UnboundLambda(node, state, false)
            {
                WasCompilerGenerated = true
            };

            state.SetUnboundLambda(lambda);
            return(lambda);
        }
예제 #5
0
 UnboundLambda MakeQueryUnboundLambda(RangeVariableMap qvm, RangeVariableSymbol parameter, CSharpSyntaxNode node, LambdaBodyResolver resolver)
 {
     return(MakeQueryUnboundLambda(qvm, Args(parameter), node, resolver));
 }
예제 #6
0
        void ReduceLet(LetClauseSyntax let, QueryTranslationState state, DiagnosticBag diagnostics)
        {
            // A query expression with a let clause
            //     from x in e
            //     let y = f
            //     ...
            // is translated into
            //     from * in ( e ) . Select ( x => new { x , y = f } )
            //     ...
            var x = state.rangeVariable;

            // We use a slightly different translation strategy.  We produce
            //     from * in ( e ) . Select ( x => new Pair<X,Y>(x, f) )
            // Where X is the type of x, and Y is the type of the expression f.
            // Subsequently, x (or members of x, if it is a transparent identifier)
            // are accessed as TRID.Item1 (or members of that), and y is accessed
            // as TRID.Item2, where TRID is the compiler-generated identifier used
            // to represent the transparent identifier in the result.
            LambdaBodyResolver resolver = (LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag d) =>
            {
                var xExpression = new BoundParameter(let, lambdaSymbol.Parameters[0])
                {
                    WasCompilerGenerated = true
                };

                lambdaBodyBinder = new ScopedExpressionBinder(lambdaBodyBinder, let.Expression);
                var            yExpression   = lambdaBodyBinder.BindValue(let.Expression, d, BindValueKind.RValue);
                SourceLocation errorLocation = new SourceLocation(let.SyntaxTree, new TextSpan(let.Identifier.SpanStart, let.Expression.Span.End - let.Identifier.SpanStart));
                if (!yExpression.HasAnyErrors && !yExpression.HasExpressionType())
                {
                    MessageID id = MessageID.IDS_NULL;
                    if (yExpression.Kind == BoundKind.UnboundLambda)
                    {
                        id = ((UnboundLambda)yExpression).MessageID;
                    }
                    else if (yExpression.Kind == BoundKind.MethodGroup)
                    {
                        id = MessageID.IDS_MethodGroup;
                    }
                    else
                    {
                        Debug.Assert(yExpression.IsLiteralNull(), "How did we successfully bind an expression without a type?");
                    }

                    Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, id.Localize());
                    yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(yExpression), CreateErrorType());
                }
                else if (!yExpression.HasAnyErrors && yExpression.Type.SpecialType == SpecialType.System_Void)
                {
                    Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Type);
                    yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(yExpression), yExpression.Type);
                }

                var construction = MakePair(let, x.Name, xExpression, let.Identifier.ValueText, yExpression, state, d);
                return(lambdaBodyBinder.CreateBlockFromExpression(lambdaBodyBinder.Locals, construction, let, d));
            };
            var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, let.Expression, resolver);

            state.rangeVariable = state.TransparentRangeVariable(this);
            state.AddTransparentIdentifier(x.Name);
            var y = state.AddRangeVariable(this, let.Identifier, diagnostics);

            state.allRangeVariables[y].Add(let.Identifier.ValueText);
            var invocation = MakeQueryInvocation(let, state.fromExpression, "Select", lambda, diagnostics);

            state.fromExpression = MakeQueryClause(let, invocation, y, invocation);
        }
예제 #7
0
            UnboundLambda MakeQueryUnboundLambda(QueryVariableMap qvm, ReadOnlyArray <QueryVariableSymbol> parameters, SyntaxNode node, LambdaBodyResolver resolver)
            {
                var state  = new QueryUnboundLambdaState(null, this, qvm, parameters, resolver);
                var lambda = new UnboundLambda(node, SyntaxTree, state, false);

                state.SetUnboundLambda(lambda);
                return(lambda);
            }
예제 #8
0
            void ReduceQuery(QueryTranslationState state, DiagnosticBag diagnostics)
            {
                var topClause = state.clauses.Pop();

                if (topClause.Kind == SyntaxKind.WhereClause)
                {
                    // A query expression with a where clause
                    //     from x in e
                    //     where f
                    //     …
                    // is translated into
                    //     from x in ( e ) . Where ( x => f )
                    var where = topClause as WhereClauseSyntax;
                    var lambda     = MakeQueryUnboundLambda(state.QueryVariableMap(), state.queryVariable, where.Condition);
                    var invocation = MakeInvocation(where, state.fromExpression, "Where", lambda, diagnostics);
                    state.fromExpression = new BoundQueryClause(
                        syntax: where,
                        syntaxTree: SyntaxTree,
                        value: invocation,
                        definedSymbol: null,
                        queryMethod: invocation.Method,
                        castMethod: null,
                        type: invocation.Type);
                }
                else if (topClause.Kind == SyntaxKind.JoinClause && state.clauses.IsEmpty() && state.selectOrGroup.Kind == SyntaxKind.SelectClause)
                {
                    var join     = topClause as JoinClauseSyntax;
                    var select   = state.selectOrGroup as SelectClauseSyntax;
                    var joinArgs = ArrayBuilder <BoundExpression> .GetInstance();

                    var          e2         = BindValue(join.InExpression, BindValueKind.RValue, diagnostics);
                    MethodSymbol castMethod = null;
                    if (join.TypeOpt != null)
                    {
                        // A join clause that explicitly specifies a range variable type
                        //     join T x in e on k1 equals k2
                        // is translated into
                        //     join x in ( e ) . Cast < T > ( ) on k1 equals k2
                        var castType   = BindType(join.TypeOpt, diagnostics);
                        var invocation = MakeInvocation(join, e2, "Cast", join.TypeOpt, castType, diagnostics);
                        castMethod = invocation.Method;
                        e2         = invocation;
                    }

                    joinArgs.Add(e2);
                    var lambda1 = MakeQueryUnboundLambda(state.QueryVariableMap(), state.queryVariable, join.LeftExpression);
                    joinArgs.Add(lambda1);
                    var x2      = state.AddQueryVariable(this, join.Identifier);
                    var lambda2 = MakeQueryUnboundLambda(state.QueryVariableMap(x2), x2, join.RightExpression); // TODO: ensure no other query variables in scope but x2.
                    joinArgs.Add(lambda2);
                    BoundExpression result;
                    if (join.IntoOpt == null)
                    {
                        // A query expression with a join clause without an into followed by a select clause
                        //     from x1 in e1
                        //     join x2 in e2 on k1 equals k2
                        //     select v
                        // is translated into
                        //     ( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v )
                        var lambda3 = MakeQueryUnboundLambda(state.QueryVariableMap(), Args(state.queryVariable, x2), select.Expression);
                        // TODO: lambda3's body should be surrounded by a BoundQueryClause for the select clause.
                        joinArgs.Add(lambda3);
                        var invocation = MakeInvocation(join, state.fromExpression, "Join", joinArgs.ToReadOnlyAndFree(), diagnostics);
                        result = new BoundQueryClause(
                            syntax: join,
                            syntaxTree: SyntaxTree,
                            value: invocation,
                            definedSymbol: x2,
                            queryMethod: invocation.Method,
                            castMethod: castMethod,
                            type: invocation.Type);
                    }
                    else
                    {
                        // A query expression with a join clause with an into followed by a select clause
                        //     from x1 in e1
                        //     join x2 in e2 on k1 equals k2 into g
                        //     select v
                        // is translated into
                        //     ( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
                        var g = state.AddQueryVariable(this, join.IntoOpt.Identifier);
                        // binder.queryVariable = g; // TODO: where to record the info symbol?
                        var lambda3 = MakeQueryUnboundLambda(state.QueryVariableMap(), Args(state.queryVariable, g), select.Expression);
                        // TODO: lambda3's body should be surrounded by a BoundQueryClause for the select clause.
                        joinArgs.Add(lambda3);
                        var invocation   = MakeInvocation(join, state.fromExpression, "GroupJoin", joinArgs.ToReadOnlyAndFree(), diagnostics);
                        var newArguments = Args(invocation.Arguments[0], invocation.Arguments[1], invocation.Arguments[2],
                                                new BoundQueryClause( // record the into clause's symbol in the bound tree
                                                    syntax: join.IntoOpt,
                                                    syntaxTree: SyntaxTree,
                                                    value: invocation.Arguments[3],
                                                    definedSymbol: g,
                                                    queryMethod: null,
                                                    castMethod: null,
                                                    type: invocation.Arguments[3].Type));
                        invocation = invocation.Update(
                            receiverOpt: invocation.ReceiverOpt,
                            method: invocation.Method,
                            arguments: newArguments);
                        result = new BoundQueryClause(
                            syntax: join,
                            syntaxTree: SyntaxTree,
                            value: invocation,
                            definedSymbol: x2,
                            queryMethod: invocation.Method,
                            castMethod: castMethod,
                            type: invocation.Type);
                    }

                    state.Clear(); // this completes the whole query
                    state.fromExpression = result;
                }
                else if (topClause.Kind == SyntaxKind.OrderByClause)
                {
                    // A query expression with an orderby clause
                    //     from x in e
                    //     orderby k1 , k2 , … , kn
                    //     …
                    // is translated into
                    //     from x in ( e ) .
                    //     OrderBy ( x => k1 ) .
                    //     ThenBy ( x => k2 ) .
                    //     … .
                    //     ThenBy ( x => kn )
                    //     …
                    // If an ordering clause specifies a descending direction indicator,
                    // an invocation of OrderByDescending or ThenByDescending is produced instead.
                    var    orderby    = topClause as OrderByClauseSyntax;
                    bool   first      = true;
                    Symbol lastMethod = null;
                    foreach (var ordering in orderby.Orderings)
                    {
                        string methodName = (first ? "OrderBy" : "ThenBy") + (ordering.Kind == SyntaxKind.DescendingOrdering ? "Descending" : "");
                        var    lambda     = MakeQueryUnboundLambda(state.QueryVariableMap(), state.queryVariable, ordering.Expression);
                        var    invocation = MakeInvocation(ordering, state.fromExpression, methodName, lambda, diagnostics);
                        lastMethod           = invocation.Method;
                        state.fromExpression = new BoundQueryClause(
                            syntax: ordering, syntaxTree: SyntaxTree, value: invocation,
                            definedSymbol: null, queryMethod: invocation.Method, castMethod: null, type: invocation.Type);
                        first = false;
                    }

                    state.fromExpression = new BoundQueryClause(
                        syntax: orderby, syntaxTree: SyntaxTree, value: state.fromExpression,
                        definedSymbol: null, queryMethod: lastMethod, castMethod: null, type: state.fromExpression.Type);
                }
                else if (topClause.Kind == SyntaxKind.FromClause && state.clauses.IsEmpty() && state.selectOrGroup.Kind == SyntaxKind.SelectClause)
                {
                    // A query expression with a second from clause followed by a select clause
                    //     from x1 in e1
                    //     from x2 in e2
                    //     select v
                    // is translated into
                    //     ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )
                    var             fromClause = topClause as FromClauseSyntax;
                    var             select     = state.selectOrGroup as SelectClauseSyntax;
                    var             x1         = state.queryVariable;
                    TypeSymbol      castType   = fromClause.TypeOpt == null ? null : BindType(fromClause.TypeOpt, diagnostics);
                    BoundExpression lambda1    = MakeQueryUnboundLambda(state.QueryVariableMap(), x1, fromClause.Expression, fromClause.TypeOpt, castType);
                    // TODO: wrap the bound version of lambda1 in a BoundQueryClause for the from clause defining  x2.
                    var             x2      = state.AddQueryVariable(this, fromClause.Identifier);
                    BoundExpression lambda2 = MakeQueryUnboundLambda(state.QueryVariableMap(), Args(x1, x2), select.Expression);
                    var             result  = MakeInvocation(fromClause, state.fromExpression, "SelectMany", Args(lambda1, lambda2), diagnostics);
                    // TODO: extract the Cast<T>() operation from the bound version of lambda1 (this invocation's first argument) and store it in e2Binder.castMethod
                    state.Clear();
                    state.fromExpression = new BoundQueryClause(
                        syntax: select, syntaxTree: SyntaxTree, value: result,
                        definedSymbol: null, queryMethod: result.Method, castMethod: null, type: result.Type);
                }
                else if (topClause.Kind == SyntaxKind.LetClause)
                {
                    var let = topClause as LetClauseSyntax;
                    var x   = state.queryVariable;

                    // A query expression with a let clause
                    //     from x in e
                    //     let y = f
                    //     …
                    // is translated into
                    //     from * in ( e ) . Select ( x => new { x , y = f } )
                    //     …

                    // We use a slightly different translation strategy.  We produce
                    //     from * in ( e ) . Select ( x => new Pair<X,Y>(x, f) )
                    // Where X is the type of x, and Y is the type of the expression f.
                    // Subsequently, x (or members of x, if it is a transparent identifier)
                    // are accessed as TRID.Item1 (or members of that), and y is accessed
                    // as TRID.Item2, where TRID is the compiler-generated identifier used
                    // to represent the transparent identifier in the result.  We place
                    // this mapping into the state and then, subsequently, into the binder
                    // for any further clauses.
                    LambdaBodyResolver resolver = (LambdaSymbol lambdaSymbol, ExecutableCodeBinder lambdaBodyBinder, DiagnosticBag d) =>
                    {
                        var xExpression  = new BoundParameter(let, lambdaBodyBinder.SyntaxTree, lambdaSymbol.Parameters[0]);
                        var yExpression  = lambdaBodyBinder.BindValue(let.Expression, BindValueKind.RValue, d);
                        var construction = MakePair(let, xExpression, yExpression, d);
                        return(lambdaBodyBinder.WrapExpressionLambdaBody(construction, let, d));
                    };
                    var lambda = MakeQueryUnboundLambda(state.QueryVariableMap(), x, let.Expression, resolver);
                    state.queryVariable = state.TransparentQueryVariable(this);
                    var invocation = MakeInvocation(let, state.fromExpression, "Select", lambda, diagnostics);
                    state.AddTransparentIdentifier("Item1");
                    var y = state.AddQueryVariable(this, let.Identifier);
                    state.allQueryVariables[y].Add("Item2");
                    state.fromExpression = new BoundQueryClause(
                        syntax: let, syntaxTree: SyntaxTree, value: invocation,
                        definedSymbol: y, queryMethod: invocation.Method, castMethod: null, type: invocation.Type);
                }
                else
                {
                    diagnostics.Add(ErrorCode.ERR_NotYetImplementedInRoslyn, Location(topClause), "query expression");
                    var result = state.fromExpression; // short circuit any remaining reductions
                    state.Clear();
                    state.fromExpression = result;
                }
            }