/// <summary> /// Implement the skipping. We have a main limitation: we currently know only how to implement integer skipping. /// We implement with "if" statements to support composability, even if it means running longer in the end... /// We actually return nothing when goes - we aren't really a final result the way "Count" is. /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <returns></returns> public void ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode codeEnv, ICodeContext codeContext, CompositionContainer container) { /// /// Quick checks to make sure /// if (codeEnv == null) throw new ArgumentNullException("codeEnv cannot be null"); var take = resultOperator as TakeResultOperator; var skip = resultOperator as SkipResultOperator; if (take == null && skip == null) { throw new ArgumentNullException("resultOperator must not be null and must represent either a take or a skip operation!"); } if (take != null && take.Count.Type != typeof(int)) throw new ArgumentException("Take operator count must be an integer!"); if (skip != null && skip.Count.Type != typeof(int)) throw new ArgumentException("Skip operator count must be an integer!"); // If this is a "global" take, then we need to declare the variable a bit specially. // Global: we have a limit on the number of objects that goes across events. We test this by seeing if this // is a sub-query that is registered (or not). var isGlobalTake = codeContext.IsInTopLevelQueryModel(queryModel); // Now, we create a count variable and that is how we will tell if we are still skipping or // taking. It must be declared in the current block, before our current code! :-) var counter = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int), otherDependencies: codeContext.LoopIndexVariable.Return<IDeclaredParameter>()); if (isGlobalTake) { counter.DeclareAsStatic = true; codeEnv.Add(counter); } else { codeEnv.AddOutsideLoop(counter); } var comparison = StatementIfOnCount.ComparisonOperator.LessThanEqual; IValue limit = null; if (skip != null) { comparison = StatementIfOnCount.ComparisonOperator.GreaterThan; limit = ExpressionToCPP.GetExpression(skip.Count, codeEnv, codeContext, container); } else { limit = ExpressionToCPP.GetExpression(take.Count, codeEnv, codeContext, container); } codeEnv.Add(new StatementIfOnCount(counter, limit, comparison)); /// /// We are particularly fortunate here. We don't have to update the Loop variable - whatever it is, is /// still the right one! Normally we'd have to futz with the LoopVariable in code context because we /// were iterating over something new. :-) Easy! /// }
/// <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); }
/// <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); }
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); }
/// <summary> /// Implement the skipping. We have a main limitation: we currently know only how to implement integer skipping. /// We implement with "if" statements to support composability, even if it means running longer in the end... /// We actually return nothing when goes - we aren't really a final result the way "Count" is. /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <returns></returns> public void ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode codeEnv, ICodeContext codeContext, CompositionContainer container) { /// /// Quick checks to make sure /// if (codeEnv == null) { throw new ArgumentNullException("codeEnv cannot be null"); } var take = resultOperator as TakeResultOperator; var skip = resultOperator as SkipResultOperator; if (take == null && skip == null) { throw new ArgumentNullException("resultOperator must not be null and must represent either a take or a skip operation!"); } if (take != null && take.Count.Type != typeof(int)) { throw new ArgumentException("Take operator count must be an integer!"); } if (skip != null && skip.Count.Type != typeof(int)) { throw new ArgumentException("Skip operator count must be an integer!"); } // If this is a "global" take, then we need to declare the variable a bit specially. // Global: we have a limit on the number of objects that goes across events. We test this by seeing if this // is a sub-query that is registered (or not). var isGlobalTake = codeContext.IsInTopLevelQueryModel(queryModel); // Now, we create a count variable and that is how we will tell if we are still skipping or // taking. It must be declared in the current block, before our current code! :-) var counter = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int), otherDependencies: codeContext.LoopIndexVariable.Return <IDeclaredParameter>()); if (isGlobalTake) { counter.DeclareAsStatic = true; codeEnv.Add(counter); } else { codeEnv.AddOutsideLoop(counter); } var comparison = StatementIfOnCount.ComparisonOperator.LessThanEqual; IValue limit = null; if (skip != null) { comparison = StatementIfOnCount.ComparisonOperator.GreaterThan; limit = ExpressionToCPP.GetExpression(skip.Count, codeEnv, codeContext, container); } else { limit = ExpressionToCPP.GetExpression(take.Count, codeEnv, codeContext, container); } codeEnv.Add(new StatementIfOnCount(counter, limit, comparison)); /// /// We are particularly fortunate here. We don't have to update the Loop variable - whatever it is, is /// still the right one! Normally we'd have to futz with the LoopVariable in code context because we /// were iterating over something new. :-) Easy! /// }
/// <summary> /// Code up the min/max result operators. We run the loop out, and then /// we return the result whatever it is. We only work when the type is /// something simple we can deal with! /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <param name="_codeContext"></param> /// <param name="container"></param> /// <returns></returns> public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { /// /// Some argument checking /// if (cc == null) throw new ArgumentNullException("cc"); if (gc == null) throw new ArgumentNullException("gc"); if (gc.Depth == 1) throw new ArgumentException("The Max/Min operators can't be used as result operators for a query - they can only be used in a sub-query"); /// /// Is it min or max? /// var minOperator = resultOperator as MinResultOperator; var maxOperator = resultOperator as MaxResultOperator; if (minOperator == null && maxOperator == null) throw new InvalidOperationException("Should always have min or max operator!"); bool doMax = maxOperator != null; bool returnDefaultValue = false; if (doMax) returnDefaultValue = maxOperator.ReturnDefaultWhenEmpty; else returnDefaultValue = minOperator.ReturnDefaultWhenEmpty; /// /// Next, look at the type of the current result that is running. /// var valueExpr = queryModel.SelectClause.Selector; if (!TimeCanBeCompared(valueExpr.Type)) throw new ArgumentException(string.Format("I don't know how to fix the min or max of a sequence of '{0}'s", cc.LoopVariable.Type.Name)); /// /// Now, declare two variables, one bool which gets set when we get the first value, /// and the other to hold the min/max value! Note that we initalize the variable to /// the proper type. We don't declare minmax holder - as it may end up be used /// externally. /// var vIsFilled = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); vIsFilled.InitialValue = new ValSimple("false", typeof(bool), null); var vMaxMin = DeclarableParameter.CreateDeclarableParameterExpression(valueExpr.Type); vMaxMin.InitialValue = new ValSimple("0", typeof(int), null); gc.AddOutsideLoop(vIsFilled); /// /// The expression we want to mimize or maximize /// var exprToMinOrMaximize = ExpressionToCPP.GetExpression(valueExpr, gc, cc, container); /// /// Now, we just have to put the x-checks in there. /// var ifStatement = new Statements.StatementMinMaxTest(vIsFilled, vMaxMin, exprToMinOrMaximize, doMax); gc.Add(ifStatement); return vMaxMin; }
/// <summary> /// Sort the current stream of the query. To do this we run through all the results, sort them, /// and then start a new loop. /// </summary> /// <param name="ordering"></param> /// <param name="queryModel"></param> /// <param name="orderByClause"></param> /// <param name="index"></param> public override void VisitOrdering(Ordering ordering, QueryModel queryModel, OrderByClause orderByClause, int index) { // // Only number types can be sorted. // if (!ordering.Expression.Type.IsNumberType()) { throw new InvalidOperationException(string.Format("Don't know how to sort query by type '{0}'.", ordering.Expression.Type.Name)); } // // First, record all the indicies and the values. This is what we are going to be sorting. // var mapRecord = DeclarableParameter.CreateDeclarableParameterMapExpression(ordering.Expression.Type, _codeContext.LoopIndexVariable.Type.MakeArrayType()); _codeEnv.AddOutsideLoop(mapRecord); var savePairValues = new StatementRecordPairValues(mapRecord, ExpressionToCPP.GetExpression(ordering.Expression, _codeEnv, _codeContext, MEFContainer), ExpressionToCPP.GetExpression(_codeContext.LoopIndexVariable.AsExpression(), _codeEnv, _codeContext, MEFContainer)); _codeEnv.Add(savePairValues); var otherSavers = _codeEnv.GetUsedQuerySourceVariables(savePairValues, _codeContext.LoopIndexVariable) .Select(v => { var mr = DeclarableParameter.CreateDeclarableParameterMapExpression(ordering.Expression.Type, v.Type.MakeArrayType()); _codeEnv.AddOutsideLoop(mr); savePairValues.AddSaver(mr, v); return(Tuple.Create(v, mr)); }) .ToArray(); // Get back to the results level now, where we do the sorting! _codeEnv.PopToResultsLevel(); // // Now, we need to sort and loop over the variables in the map. This is a bit of a messy // multi-line statement, and it is a compound statement. // var sortAndRunLoop = new StatementLoopOverSortedPairValue(mapRecord, ordering.OrderingDirection == OrderingDirection.Asc); _codeEnv.Add(sortAndRunLoop); var pindex = sortAndRunLoop.IndexVariable; var lv = _codeContext.LoopIndexVariable.RawValue; _codeContext.Add(lv, pindex); foreach (var savers in otherSavers) { var newVarName = sortAndRunLoop.RestoreOtherSaver(savers.Item2); _codeContext.Add(savers.Item1.RawValue, newVarName); } _codeContext.SetLoopVariable(_codeContext.LoopVariable.ReplaceSubExpression(_codeContext.LoopIndexVariable.AsExpression(), pindex), pindex); }
/// <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); }
/// <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); }
/// <summary> /// Code up the min/max result operators. We run the loop out, and then /// we return the result whatever it is. We only work when the type is /// something simple we can deal with! /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <param name="_codeContext"></param> /// <param name="container"></param> /// <returns></returns> public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { /// /// Some argument checking /// if (cc == null) { throw new ArgumentNullException("cc"); } if (gc == null) { throw new ArgumentNullException("gc"); } if (gc.Depth == 1) { throw new ArgumentException("The Max/Min operators can't be used as result operators for a query - they can only be used in a sub-query"); } /// /// Is it min or max? /// var minOperator = resultOperator as MinResultOperator; var maxOperator = resultOperator as MaxResultOperator; if (minOperator == null && maxOperator == null) { throw new InvalidOperationException("Should always have min or max operator!"); } bool doMax = maxOperator != null; bool returnDefaultValue = false; if (doMax) { returnDefaultValue = maxOperator.ReturnDefaultWhenEmpty; } else { returnDefaultValue = minOperator.ReturnDefaultWhenEmpty; } /// /// Next, look at the type of the current result that is running. /// var valueExpr = queryModel.SelectClause.Selector; if (!TimeCanBeCompared(valueExpr.Type)) { throw new ArgumentException(string.Format("I don't know how to fix the min or max of a sequence of '{0}'s", cc.LoopVariable.Type.Name)); } /// /// Now, declare two variables, one bool which gets set when we get the first value, /// and the other to hold the min/max value! Note that we initalize the variable to /// the proper type. We don't declare minmax holder - as it may end up be used /// externally. /// var vIsFilled = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); vIsFilled.InitialValue = new ValSimple("false", typeof(bool), null); var vMaxMin = DeclarableParameter.CreateDeclarableParameterExpression(valueExpr.Type); vMaxMin.InitialValue = new ValSimple("0", typeof(int), null); gc.AddOutsideLoop(vIsFilled); /// /// The expression we want to mimize or maximize /// var exprToMinOrMaximize = ExpressionToCPP.GetExpression(valueExpr, gc, cc, container); /// /// Now, we just have to put the x-checks in there. /// var ifStatement = new Statements.StatementMinMaxTest(vIsFilled, vMaxMin, exprToMinOrMaximize, doMax); gc.Add(ifStatement); return(vMaxMin); }
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; }