Ejemplo n.º 1
0
            /// <summary>
            /// Run a sub-query for the user. The sub-query is a full-blown expression that
            /// must usually run in its own loop (or similar).
            /// </summary>
            /// <remarks>
            /// The query visitor must be re-invoked - this ends up being a full-blown
            /// parsing.
            /// </remarks>
            /// <param name="expression"></param>
            /// <returns></returns>
            protected override Expression VisitSubQuery(SubQueryExpression expression)
            {
                if (MEFContainer == null)
                {
                    throw new InvalidOperationException("MEFContainer can't be null if we need to analyze a sub query!");
                }

                QueryVisitor qv = new QueryVisitor(GeneratedCode, CodeContext, MEFContainer);

                MEFContainer.SatisfyImportsOnce(qv);

                ///
                /// Run it - since this result is out of this loop, we pop-back-out when done.
                ///

                var scope = GeneratedCode.CurrentScope;

                GeneratedCode.SetCurrentScopeAsResultScope();
                qv.VisitQueryModel(expression.QueryModel);

                //
                // And the main index that was looked at we need to cache ourselves.
                //

                if (qv.MainIndexScope != null)
                {
                    CodeContext.CacheVariableToEliminate(qv.MainIndexScope);
                }

                ///
                /// Two possible results from the sub-expression query, and how we proceed depends
                /// on what happened in the sub query
                ///
                /// 1. <returns a value> - an operator like Count() comes back from the sequence.
                ///    it will get used in some later sequence (like # of jets in each event). So,
                ///    we need to make sure it is declared and kept before it is used. The # that comes
                ///    back needs to be used outside the scope we are sitting in - the one that we were at
                ///    when we started this. Since this is a sub-query expression, the result isn't the final
                ///    result, so we need to reset it so no one notices it.
                /// 2. <return a sequence> - this is weird - What we are actually doing here is putting the
                ///    sequence into code. So the loop variable has been updated with the new sequence iterator
                ///    value. But there isn't really a result! So the result will be null...
                ///

                var r = GeneratedCode.ResultValue;

                if (r != null)
                {
                    GeneratedCode.CurrentScope = scope;
                    if (r is IDeclaredParameter)
                    {
                        GeneratedCode.Add(r as IDeclaredParameter, false);
                    }
                    GeneratedCode.ResetResult();
                    return(r);
                }

                //
                // The fact that we returned null means we are dealing with a
                // sequence. There really is no translated version of this expression
                // in that case - so we will return null. If someone above is depending
                // on doing something with it they are going to run into some
                // trouble!

                return(null);
            }
Ejemplo n.º 2
0
        /// <summary>
        /// Cache the result of a query model into a function.
        /// </summary>
        /// <param name="queryModel"></param>
        /// <param name="qmSource"></param>
        private void VisitQueryModelCache(QueryModel queryModel, IQMFunctionSource qmSource)
        {
            // If we already have the answer for this cache, then we should just re-call the routine.
            if (qmSource.StatementBlock != null)
            {
                Debug.WriteLine("Using previously cached QM result");
                GenerateQMFunctionCall(qmSource);
                return;
            }
            Debug.WriteLine("Cache: Gathering Data");
            Debug.Indent();

            // Since we don't have it cached, we need to re-run things, and carefully watch for
            // everything new that shows up. What shows up will be what we declare as the function
            // body.
            var currentScope      = _codeEnv.CurrentScope;
            var topLevelStatement = new StatementInlineBlock();

            _codeEnv.Add(topLevelStatement);
            _codeEnv.SetCurrentScopeAsResultScope();

            // If this variable has been cached, then return it. Otherwise, mark the cache as filled.
            _codeEnv.Add(new StatementFilter(qmSource.CacheVariableGood));
            _codeEnv.Add(new StatementReturn(qmSource.CacheVariable));
            _codeEnv.Pop();
            _codeEnv.Add(new StatementAssign(qmSource.CacheVariableGood, new ValSimple("true", typeof(bool), null)));

            // Now, run the code to process the query model!

            VisitQueryModelNoCache(queryModel);

            // The result is treated differently depending on it being a sequence or a single value.
            if (qmSource.IsSequence)
            {
                // Push the good values into our cache object.
                if (!(_codeContext.LoopIndexVariable is IDeclaredParameter))
                {
                    throw new InvalidOperationException("Can't deal with anythign that isn't a loop var");
                }
                _codeEnv.Add(new StatementRecordIndicies(ExpressionToCPP.GetExpression(_codeContext.LoopVariable, _codeEnv, _codeContext, MEFContainer), qmSource.CacheVariable));

                // Remember what the loop index variable is, as we are going to need it when we generate the return function!
                qmSource.SequenceVariable(_codeContext.LoopIndexVariable, _codeContext.LoopVariable);
            }
            else
            {
                // This is a specific result. Save just the result and return it.
                // Grab the result, cache it, and return it.
                var rtnExpr = ExpressionToCPP.GetExpression(_codeEnv.ResultValue, _codeEnv, _codeContext, MEFContainer);
                topLevelStatement.Add(new StatementAssign(qmSource.CacheVariable, rtnExpr));

                // If the return is a declared parameter, then it must be actually defined somewhere (we normally don't).
                var declParam = _codeEnv.ResultValue as IDeclaredParameter;
                if (declParam != null)
                {
                    topLevelStatement.Add(declParam, false);
                }
            }

            // Always return the proper value...
            topLevelStatement.Add(new StatementReturn(qmSource.CacheVariable));

            // Now extract the block of code and put it in the function block.
            _codeEnv.CurrentScope = currentScope;
            qmSource.SetCodeBody(topLevelStatement);

            // Reset our state and remove the function code. And put in the function call in its place.
            _codeEnv.Remove(topLevelStatement);
            GenerateQMFunctionCall(qmSource);

            Debug.Unindent();
        }