/// <summary>
        /// Take the incoming stream of items, and send them along! :-)
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="_codeEnv"></param>
        /// <param name="_codeContext"></param>
        /// <param name="container"></param>
        public void ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            //
            // Some basic checks on the input.
            //

            if (cc == null)
            {
                throw new ArgumentNullException("cc");
            }
            if (gc == null)
            {
                throw new ArgumentNullException("gc");
            }
            if (cc.LoopVariable == null)
            {
                throw new ArgumentNullException("No defined loop variable!");
            }

            //
            // Get the indexer that is being used to access things. We will just push that onto a temp vector of int's. That will be
            // a list of the items that we want to come back and look at. That said, once done we need to pop-up one level in our
            // depth.
            //

            var arrayRecord = DeclarableParameter.CreateDeclarableParameterArrayExpression(typeof(int));

            gc.AddOutsideLoop(arrayRecord);

            var recordIndexStatement = new Statements.StatementRecordIndicies(ExpressionToCPP.GetExpression(cc.LoopIndexVariable.AsExpression(), gc, cc, container), arrayRecord);

            gc.Add(recordIndexStatement);

            gc.Pop();

            //
            // Now, we go down one loop and run over the pairs with a special loop.
            //

            var index1        = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            var index2        = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            var indexIterator = new Statements.StatementPairLoop(arrayRecord, index1, index2);

            gc.Add(indexIterator);

            //
            // Finally, build the resulting loop variable. For now it is just a tuple, which is basically the formed expression we started with,
            // but with the other index properties. Other bits will have to do the translation for us. :-)
            //

            var item1 = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index1);
            var item2 = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index2);

            var tupleType = typeof(Tuple <,>).MakeGenericType(cc.LoopVariable.Type, cc.LoopVariable.Type);
            var newTuple  = Expression.New(tupleType.GetConstructor(new Type[] { cc.LoopVariable.Type, cc.LoopVariable.Type }), item1, item2);

            cc.SetLoopVariable(newTuple, null);
        }
Пример #2
0
        /// <summary>
        /// Process the grouping operator. We have to sort through the items, group them, and then
        /// create an object we can be translated later to access the items or the Key. We need to return
        /// an IEnumerable<IGrouping<TKey, TElement>>... As a result, this is one of those operators that has
        /// a fair amount of implementation in other parts of the re-linq structure.
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="gc"></param>
        /// <param name="cc"></param>
        /// <param name="container"></param>
        /// <returns></returns>
        /// <remarks>
        /// re-linq blog post that shows the format of the query we are dealing with: https://www.re-motion.org/blogs/mix/2009/09/01/re-linq-how-to-support-ldquogroup-intordquo-with-aggregates
        /// Not as useful:
        /// Code for the result operator (including in-memory execution): https://svn.re-motion.org/svn/Remotion/trunk/Relinq/Core/Clauses/ResultOperators/GroupResultOperator.cs
        /// Unit tests for the result operator: https://www.re-motion.org/fisheye/browse/~raw,r=17871/Remotion/trunk/Remotion/Data/Linq.UnitTests/Linq/SqlBackend/SqlPreparation/ResultOperatorHandlers/GroupResultOperatorHandlerTest.cs
        /// </remarks>
        System.Linq.Expressions.Expression IQVScalarResultOperator.ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            //
            // Basic checks
            //

            var groupOp = resultOperator as GroupResultOperator;

            if (groupOp == null)
            {
                throw new ArgumentNullException("resultOperator");
            }

            if (!groupOp.KeySelector.Type.IsNumberType())
            {
                throw new InvalidOperationException(string.Format("Don't know how to group by type '{0}'.", groupOp.KeySelector.Type.Name));
            }

            //
            // First, record all the indicies and the values. This is what we are going to be grouping.
            //

            var mapRecord = DeclarableParameter.CreateDeclarableParameterMapExpression(groupOp.KeySelector.Type, typeof(int).MakeArrayType());

            gc.AddOutsideLoop(mapRecord);

            var savePairValues = new StatementRecordPairValues(mapRecord,
                                                               ExpressionToCPP.GetExpression(groupOp.KeySelector, gc, cc, container),
                                                               ExpressionToCPP.GetExpression(cc.LoopIndexVariable.AsExpression(), gc, cc, container));

            gc.Add(savePairValues);

            gc.Pop();

            //
            // Now create the object that will be handed back for later parsing. This should contain the info that is needed to do the
            // actual looping over the groups when it is requested.
            //

            var t_return = typeof(GroupByTypeTagEnum <int, int>).GetGenericTypeDefinition().MakeGenericType(new Type[] { groupOp.KeySelector.Type, groupOp.ElementSelector.Type });
            var ctor     = t_return.GetConstructor(new Type[] { });
            var o        = ctor.Invoke(new object[] { }) as BaseGroupInfo;

            o.MapRecord                    = mapRecord;
            o.TargetExpression             = groupOp.ElementSelector;
            o.TargetExpressionLoopVariable = cc.LoopIndexVariable;

            return(Expression.Constant(o, t_return));
        }
        /// <summary>
        /// Take the incoming stream of items, and send them along! :-)
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="_codeEnv"></param>
        /// <param name="_codeContext"></param>
        /// <param name="container"></param>
        public void ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            //
            // Some basic checks on the input.
            //

            if (cc == null)
                throw new ArgumentNullException("cc");
            if (gc == null)
                throw new ArgumentNullException("gc");
            if (cc.LoopVariable == null)
                throw new ArgumentNullException("No defined loop variable!");

            //
            // Get the indexer that is being used to access things. We will just push that onto a temp vector of int's. That will be
            // a list of the items that we want to come back and look at. That said, once done we need to pop-up one level in our
            // depth.
            //

            var arrayRecord = DeclarableParameter.CreateDeclarableParameterArrayExpression(typeof(int));
            gc.AddOutsideLoop(arrayRecord);

            var recordIndexStatement = new Statements.StatementRecordIndicies(ExpressionToCPP.GetExpression(cc.LoopIndexVariable.AsExpression(), gc, cc, container), arrayRecord);
            gc.Add(recordIndexStatement);

            gc.Pop();

            //
            // Now, we go down one loop and run over the pairs with a special loop.
            //

            var index1 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            var index2 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            var indexIterator = new Statements.StatementPairLoop(arrayRecord, index1, index2);
            gc.Add(indexIterator);

            //
            // Finally, build the resulting loop variable. For now it is just a tuple, which is basically the formed expression we started with,
            // but with the other index properties. Other bits will have to do the translation for us. :-)
            //

            var item1 = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index1);
            var item2 = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index2);

            var tupleType = typeof(Tuple<,>).MakeGenericType(cc.LoopVariable.Type, cc.LoopVariable.Type);
            var newTuple = Expression.New(tupleType.GetConstructor(new Type[] { cc.LoopVariable.Type, cc.LoopVariable.Type }), item1, item2);
            cc.SetLoopVariable(newTuple, null);
        }
Пример #4
0
        /// <summary>
        /// For a root variable we create a special variable which holds onto the initial value, and
        /// also will get loaded at the correct time.
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="codeEnv"></param>
        /// <returns></returns>
        public IValue ProcessConstantReference(ConstantExpression expr, IGeneratedQueryCode codeEnv, CompositionContainer container)
        {
            ///
            /// The value is a reference that will do the loading.
            ///

            var rootObject = expr.Value as ROOTNET.Interface.NTNamed;

            if (rootObject == null)
            {
                throw new ArgumentException("the object to be stored must derive from NTNamed! It is of type '" + expr.Value.GetType().Name + "' which does not appear to derive from TNamed.");
            }

            //
            // Queue this object for transfer, get a "unique" name back. This will also double check
            // to see if the object is already up there read to be queued.
            //

            var varNameForTransport = codeEnv.QueueForTransfer(rootObject);

            //
            // Now, we need to generate an IValue for the object that can be used in our expression parsing.
            // When in the middle of a tight loop, since finding the object is a "slow" linear lookup, we will cache it in a static
            // variable. This isn't so pretty when there is a one-time initialization, but it shouldn't add too much.
            //

            var staticCache = new ROOTObjectStaticHolder(rootObject.GetType(), rootObject);

            staticCache.DeclareAsStatic = true;
            staticCache.InitialValue    = new ValSimple("nullptr", staticCache.Type);
            codeEnv.Add(staticCache);

            codeEnv.Add(new Statements.StatementFilter(new ValSimple($"{staticCache.RawValue} == nullptr", typeof(bool), new IDeclaredParameter[] { staticCache })));
            var CPPType = rootObject.GetType().AsCPPType();
            var val     = new ROOTObjectCopiedValue(varNameForTransport, rootObject.GetType(), CPPType, rootObject.Name, rootObject.Title);

            codeEnv.Add(new Statements.StatementAssign(staticCache, val));
            codeEnv.Pop();

            // And the rest of the code should use the static cache.
            return(staticCache);
        }
Пример #5
0
        public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel,
                                                IGeneratedQueryCode gc, ICodeContext cc,
                                                CompositionContainer container)
        {
            if (resultOperator == null)
            {
                throw new ArgumentNullException("resultOperator");
            }
            if (cc == null)
            {
                throw new ArgumentNullException("CodeContext can't be null");
            }

            // Determine the type of the result operator we are processing and
            // anything we need to know about it.
            Type sumType;

            sumType = cc.LoopVariable.Type;
            bool doAverage = false;

            if (resultOperator is SumResultOperator)
            {
                doAverage = false;
            }
            else
            {
                doAverage = true;
            }

            // We only know how to sum basic types
            if (!sumType.IsNumberType())
            {
                throw new InvalidOperationException(string.Format("Do not know how to generate C++ to sum type {0}.", sumType.Name));
            }

            var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(sumType);

            accumulator.SetInitialValue("0");

            // Sum and average are a alike in that we are going to add everything we see up.
            var add         = Expression.Add(accumulator, cc.LoopVariable);
            var addResolved = ExpressionToCPP.GetExpression(add, gc, cc, container);

            gc.Add(new StatementAggregate(accumulator, addResolved));

            // If this is a sum no further work needs to happen.
            if (!doAverage)
            {
                return(accumulator);
            }

            // If this is a average then we need to add a simple count.
            var counter = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));

            counter.SetInitialValue("0");
            var incbyone = Expression.Add(counter, Expression.Constant(1));

            gc.Add(new StatementAggregate(counter, ExpressionToCPP.GetExpression(incbyone, gc, cc, container)));

            // Next, we have to delcare the counter and the accumulator. These are now both temprorary variables.
            if (cc.LoopIndexVariable == null)
            {
                throw new AverageNotAllowedAtTopLevelException("Attempt to use Average at top level, accross events. Not currently implemented.");
            }
            gc.AddOutsideLoop(counter);
            gc.AddOutsideLoop(accumulator);

            // It is an error to average a sequence with no elements. So we need to throw a C++ exception. We need to pop up out of the loop in order
            // to do this.
            // http://msdn.microsoft.com/en-us/library/bb354760.aspx (for specs on Average on this).

            var testForSomething = Expression.Equal(counter, Expression.Constant(0));

            gc.AddAtResultScope(new StatementThrowIfTrue(ExpressionToCPP.GetExpression(testForSomething, gc, cc, container), "Can't take an average of a null sequence"));

            var returnType   = DetermineAverageReturnType(sumType);
            var faccumulator = Expression.Convert(accumulator, returnType);
            var fcount       = Expression.Convert(counter, returnType);
            var divide       = Expression.Divide(faccumulator, fcount);

            // We are done with this calculation, so pop up and out.
            gc.Pop();

            return(divide);
        }
Пример #6
0
        /// <summary>
        /// Process the First/last. This means adding a pointer (or not if we are looking at a plane type) and
        /// then filling it till it is full or filling it till the loop is done. Bomb out if we are asked to at the end!!
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="_codeEnv"></param>
        /// <returns></returns>
        public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel,
                                                IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            ///
            /// First, do data normalization
            ///

            var asFirst = resultOperator as FirstResultOperator;
            var asLast  = resultOperator as LastResultOperator;

            if (asFirst == null && asLast == null)
            {
                throw new ArgumentNullException("First/Last operator must be either first or last, and not null!");
            }

            bool isFirst       = asFirst != null;
            bool bombIfNothing = true;

            if (isFirst)
            {
                bombIfNothing = !asFirst.ReturnDefaultWhenEmpty;
            }
            else
            {
                bombIfNothing = !asLast.ReturnDefaultWhenEmpty;
            }

            //
            // Figure out if we need to cache the result:
            //  - simple variable which has a default value which can be used later on.
            //      like a double, etc.
            //  - We actually allow for a default variable.
            //

            bool cacheResult = cc.LoopVariable.Type.IsNumberType();

            cacheResult = cacheResult && !bombIfNothing;

            //
            // Next, make sure we are looping over something. This had better be an array we are looking at!
            //

            if (cc.LoopIndexVariable == null)
            {
                throw new InvalidOperationException(string.Format("Can't apply First operator when we aren't looping over some well formed array '{0}'", cc.LoopVariable.ToString()));
            }
            var indexExpr = cc.LoopIndexVariable;

            //
            // We need to hold onto either the first or the last item here, so we create a statement that holds nnto the
            // first or the last time. It also has to mark the thing as valid! It will break when it is done.
            // While the bool can be used later on to get at the exception we might be throwing, the actual
            // result may be used much further on down. To protect against that, we set the array index to be -1,
            // and then hope there is a crash later on! :-)
            //
            // It is possible that this is part of a dual selection. For example, if you are interested in the jet that has the closest track, and the
            // loop is constructed over the jets first, and then the tracks. This First will likely be for a track index, but we will be looking up the
            // track later. So we need to record both the jet and track index. To get the other indicies, we just look for all loop variables between here and
            // the result scope.
            //

            var valueWasSeen = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool));
            var indexSeen    = DeclarableParameter.CreateDeclarableParameterExpression(indexExpr.Type);

            if (indexSeen.Type.IsNumberType())
            {
                indexSeen.SetInitialValue("-1");
            }

            gc.AddAtResultScope(valueWasSeen);
            gc.AddAtResultScope(indexSeen);

            var rv = new Statements.StatementRecordValue(indexSeen, indexExpr, valueWasSeen, isFirst);

            gc.Add(rv);

            foreach (var v in gc.GetUsedQuerySourceVariables(rv, indexExpr))
            {
                var saver = DeclarableParameter.CreateDeclarableParameterExpression(v.Type);
                gc.AddAtResultScope(saver);
                rv.AddNewSaver(saver, v);
                cc.Add(v.RawValue, saver);
            }

            gc.Pop(true);

            if (bombIfNothing)
            {
                var test = ExpressionToCPP.GetExpression(Expression.Not(valueWasSeen), gc, cc, container);
                gc.Add(new Statements.StatementThrowIfTrue(test, string.Format("First/Last predicate executed on a null sequence: {0}", queryModel.ToString())));
            }

            //
            // Finally, we need the new expression. For this we basically just ask for the translated expression. We
            // also add a substitution for later on for more complex expressions.
            //

            var firstlastValue = cc.LoopVariable;

            cc.Add(indexExpr.RawValue, indexSeen);

            Debug.WriteLine("First/Last: {0} for QM {1}", indexSeen.ToString(), queryModel.ToString());

            // Reset the expression we are looking at in the loop.
            var newIndexExpr = firstlastValue.ReplaceSubExpression(indexExpr.AsExpression(), indexSeen);

            cc.SetLoopVariable(newIndexExpr, indexSeen);

            if (cacheResult)
            {
                //
                // Set the default value
                //

                var actualValue = DeclarableParameter.CreateDeclarableParameterExpression(cc.LoopVariable.Type);
                actualValue.SetInitialValue("0");

                //
                // If everything went well, then we can do the assignment. Otherwise, we leave
                // it as above (having the default value).
                //

                gc.Add(new Statements.StatementFilter(valueWasSeen));
                gc.Add(new Statements.StatementAssign(actualValue,
                                                      ExpressionToCPP.GetExpression(firstlastValue, gc, cc, container)));
                gc.Pop();

                return(actualValue);
            }
            else
            {
                // No need to cache the result - so no need to add extra code.
                return(newIndexExpr);
            }
        }
Пример #7
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();
        }
Пример #8
0
        /// <summary>
        /// Add the code to do the pair-wise loop.
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="_codeEnv"></param>
        /// <param name="_codeContext"></param>
        /// <param name="container"></param>
        public void ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            var ro = resultOperator as PairWiseAllResultOperator;
            if (ro == null)
                throw new ArgumentNullException("Result operator is not of PairWiseAll type");

            //
            // First, record all the good indicies for this array
            // 

            var arrayRecord = DeclarableParameter.CreateDeclarableParameterArrayExpression(typeof(int));
            gc.AddOutsideLoop(arrayRecord);

            var recordIndexStatement = new StatementRecordIndicies(ExpressionToCPP.GetExpression(cc.LoopIndexVariable.AsExpression(), gc, cc, container), arrayRecord);
            gc.Add(recordIndexStatement);

            gc.Pop();

            ///
            /// Next, we create a loop that will mark all the guys as "good" that
            /// the pair-wise function. Hopefully the statement below will be efficient and
            /// not double-try anything! The lambda we've been passed we have to evaluate - twice -
            /// for each, and pass it as a "test" to the statement. It will be some horrendus expression
            /// I suppose!
            /// 

            var passAll = DeclarableParameter.CreateDeclarableParameterArrayExpression(typeof(bool));
            gc.Add(passAll);
            var index1 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            var index2 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));

            var index1Lookup = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index1); //Expression.ArrayIndex(array, index1);
            var index2Lookup = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index2);//Expression.ArrayIndex(array, index2);

            var callLambda = Expression.Invoke(ro.Test,
                index1Lookup,
                index2Lookup
                );

            var xcheck = new Statements.StatementCheckLoopPairwise(arrayRecord,
                index1, index2, passAll);
            gc.Add(xcheck);
            var test = new Statements.StatementTestLoopPairwise(
                passAll,
                ExpressionToCPP.GetExpression(callLambda, gc, cc, container));
            gc.Add(test);
            gc.Pop();

            //
            // Ok, the result of that will be the array we have here is now filled with the
            // "proper" stuff. That is - we have "true" in everthing that is good. So we will
            // now just loop over that and apply the index as needed.
            //

            var goodIndex = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            gc.Add(goodIndex);
            var loopOverGood = new Statements.StatementLoopOverGood(arrayRecord, passAll, goodIndex);
            gc.Add(loopOverGood);

            cc.SetLoopVariable(cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), goodIndex), goodIndex);
        }
Пример #9
0
        /// <summary>
        /// Add the code to do the pair-wise loop.
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="_codeEnv"></param>
        /// <param name="_codeContext"></param>
        /// <param name="container"></param>
        public void ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            var ro = resultOperator as PairWiseAllResultOperator;

            if (ro == null)
            {
                throw new ArgumentNullException("Result operator is not of PairWiseAll type");
            }

            //
            // First, record all the good indicies for this array
            //

            var arrayRecord = DeclarableParameter.CreateDeclarableParameterArrayExpression(typeof(int));

            gc.AddOutsideLoop(arrayRecord);

            var recordIndexStatement = new StatementRecordIndicies(ExpressionToCPP.GetExpression(cc.LoopIndexVariable.AsExpression(), gc, cc, container), arrayRecord);

            gc.Add(recordIndexStatement);

            gc.Pop();

            ///
            /// Next, we create a loop that will mark all the guys as "good" that
            /// the pair-wise function. Hopefully the statement below will be efficient and
            /// not double-try anything! The lambda we've been passed we have to evaluate - twice -
            /// for each, and pass it as a "test" to the statement. It will be some horrendus expression
            /// I suppose!
            ///

            var passAll = DeclarableParameter.CreateDeclarableParameterArrayExpression(typeof(bool));

            gc.Add(passAll);
            var index1 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            var index2 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));

            var index1Lookup = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index1); //Expression.ArrayIndex(array, index1);
            var index2Lookup = cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), index2); //Expression.ArrayIndex(array, index2);

            var callLambda = Expression.Invoke(ro.Test,
                                               index1Lookup,
                                               index2Lookup
                                               );

            var xcheck = new Statements.StatementCheckLoopPairwise(arrayRecord,
                                                                   index1, index2, passAll);

            gc.Add(xcheck);
            var test = new Statements.StatementTestLoopPairwise(
                passAll,
                ExpressionToCPP.GetExpression(callLambda, gc, cc, container));

            gc.Add(test);
            gc.Pop();

            //
            // Ok, the result of that will be the array we have here is now filled with the
            // "proper" stuff. That is - we have "true" in everthing that is good. So we will
            // now just loop over that and apply the index as needed.
            //

            var goodIndex = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));

            gc.Add(goodIndex);
            var loopOverGood = new Statements.StatementLoopOverGood(arrayRecord, passAll, goodIndex);

            gc.Add(loopOverGood);

            cc.SetLoopVariable(cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), goodIndex), goodIndex);
        }
Пример #10
0
        /// <summary>
        /// Process the First/last. This means adding a pointer (or not if we are looking at a plane type) and
        /// then filling it till it is full or filling it till the loop is done. Bomb out if we are asked to at the end!!
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="_codeEnv"></param>
        /// <returns></returns>
        public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel,
            IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            ///
            /// First, do data normalization
            /// 

            var asFirst = resultOperator as FirstResultOperator;
            var asLast = resultOperator as LastResultOperator;

            if (asFirst == null && asLast == null)
            {
                throw new ArgumentNullException("First/Last operator must be either first or last, and not null!");
            }

            bool isFirst = asFirst != null;
            bool bombIfNothing = true;
            if (isFirst)
            {
                bombIfNothing = !asFirst.ReturnDefaultWhenEmpty;
            }
            else
            {
                bombIfNothing = !asLast.ReturnDefaultWhenEmpty;
            }

            //
            // Figure out if we need to cache the result:
            //  - simple variable which has a default value which can be used later on.
            //      like a double, etc.
            //  - We actually allow for a default variable.
            //

            bool cacheResult = cc.LoopVariable.Type.IsNumberType();
            cacheResult = cacheResult && !bombIfNothing;

            //
            // Next, make sure we are looping over something. This had better be an array we are looking at!
            //

            if (cc.LoopIndexVariable == null)
            {
                throw new InvalidOperationException(string.Format("Can't apply First operator when we aren't looping over some well formed array '{0}'", cc.LoopVariable.ToString()));
            }
            var indexExpr = cc.LoopIndexVariable;

            //
            // We need to hold onto either the first or the last item here, so we create a statement that holds nnto the
            // first or the last time. It also has to mark the thing as valid! It will break when it is done.
            // While the bool can be used later on to get at the exception we might be throwing, the actual
            // result may be used much further on down. To protect against that, we set the array index to be -1,
            // and then hope there is a crash later on! :-)
            //
            // It is possible that this is part of a dual selection. For example, if you are interested in the jet that has the closest track, and the
            // loop is constructed over the jets first, and then the tracks. This First will likely be for a track index, but we will be looking up the
            // track later. So we need to record both the jet and track index. To get the other indicies, we just look for all loop variables between here and
            // the result scope.
            //

            var valueWasSeen = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool));
            var indexSeen = DeclarableParameter.CreateDeclarableParameterExpression(indexExpr.Type);
            if (indexSeen.Type.IsNumberType())
                indexSeen.SetInitialValue("-1");

            gc.AddAtResultScope(valueWasSeen);
            gc.AddAtResultScope(indexSeen);

            var rv = new Statements.StatementRecordValue(indexSeen, indexExpr, valueWasSeen, isFirst);
            gc.Add(rv);

            foreach (var v in gc.GetUsedQuerySourceVariables(rv, indexExpr))
            {
                var saver = DeclarableParameter.CreateDeclarableParameterExpression(v.Type);
                gc.AddAtResultScope(saver);
                rv.AddNewSaver(saver, v);
                cc.Add(v.RawValue, saver);
            }

            gc.Pop(true);

            if (bombIfNothing)
            {
                var test = ExpressionToCPP.GetExpression(Expression.Not(valueWasSeen), gc, cc, container);
                gc.Add(new Statements.StatementThrowIfTrue(test, string.Format("First/Last predicate executed on a null sequence: {0}", queryModel.ToString())));
            }

            //
            // Finally, we need the new expression. For this we basically just ask for the translated expression. We
            // also add a substitution for later on for more complex expressions.
            //

            var firstlastValue = cc.LoopVariable;
            cc.Add(indexExpr.RawValue, indexSeen);

            Debug.WriteLine("First/Last: {0} for QM {1}", indexSeen.ToString(), queryModel.ToString());

            // Reset the expression we are looking at in the loop.
            var newIndexExpr = firstlastValue.ReplaceSubExpression(indexExpr.AsExpression(), indexSeen);
            cc.SetLoopVariable(newIndexExpr, indexSeen);

            if (cacheResult)
            {
                //
                // Set the default value
                //

                var actualValue = DeclarableParameter.CreateDeclarableParameterExpression(cc.LoopVariable.Type);
                actualValue.SetInitialValue("0");

                //
                // If everything went well, then we can do the assignment. Otherwise, we leave
                // it as above (having the default value).
                //

                gc.Add(new Statements.StatementFilter(valueWasSeen));
                gc.Add(new Statements.StatementAssign(actualValue,
                    ExpressionToCPP.GetExpression(firstlastValue, gc, cc, container)));
                gc.Pop();

                return actualValue;
            }
            else
            {
                // No need to cache the result - so no need to add extra code.
                return newIndexExpr;
            }
        }
Пример #11
0
        public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel,
            IGeneratedQueryCode gc, ICodeContext cc,
            CompositionContainer container)
        {
            if (resultOperator == null)
                throw new ArgumentNullException("resultOperator");
            if (cc == null)
                throw new ArgumentNullException("CodeContext can't be null");

            //
            // Determine the type of the result operator we are processing and
            // anything we need to know about it.
            //

            Type sumType;
            sumType = cc.LoopVariable.Type;
            bool doAverage = false;

            if (resultOperator is SumResultOperator)
            {
                doAverage = false;
            }
            else
            {
                doAverage = true;
            }

            //
            // We only know how to sum basic types
            //

            if (!sumType.IsNumberType())
            {
                throw new InvalidOperationException(string.Format("Do not know how to generate C++ to sum type {0}.", sumType.Name));
            }

            var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(sumType);
            accumulator.SetInitialValue("0");

            //
            // Now, in the loop we are currently in, we do the "add".
            //

            var add = Expression.Add(accumulator, cc.LoopVariable);

            var addResolved = ExpressionToCPP.GetExpression(add, gc, cc, container);
            gc.Add(new StatementAggregate(accumulator, addResolved));

            //
            // The sum will just be this accumulator - so return it.
            //

            if (!doAverage)
                return accumulator;

            //
            // If this is a average then we need to add a simple count on. Further, we need to declare
            // everything we are going to need for later.
            //

            var counter = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
            counter.SetInitialValue("0");
            gc.AddOutsideLoop(counter);
            gc.AddOutsideLoop(accumulator);
            var incbyone = Expression.Add(counter, Expression.Constant(1));
            gc.Add(new StatementAggregate(counter, ExpressionToCPP.GetExpression(incbyone, gc, cc, container)));

            // It is an error to average a sequence with no elements. So we need to throw a C++ exception. We need to pop up out of the loop in order
            // to do this.
            // http://msdn.microsoft.com/en-us/library/bb354760.aspx (for specs on Average on this).

            var testForSomething = Expression.Equal(counter, Expression.Constant(0));
            gc.AddAtResultScope(new StatementThrowIfTrue(ExpressionToCPP.GetExpression(testForSomething, gc, cc, container), "Can't take an average of a null sequence"));

            var returnType = DetermineAverageReturnType(sumType);
            var faccumulator = Expression.Convert(accumulator, returnType);
            var fcount = Expression.Convert(counter, returnType);
            var divide = Expression.Divide(faccumulator, fcount);

            // We are done with this calculation, so pop up and out.
            gc.Pop();

            return divide;
        }