/// <summary> /// Generate a function call statement for the cached function we are going to emit. /// </summary> /// <param name="qmSource"></param> /// <returns></returns> private void GenerateQMFunctionCall(IQMFunctionSource qmSource) { // Assemble what we need for the sequence call. if (qmSource.Arguments.Any()) { throw new NotImplementedException("Can only deal with internal functions with no arguments."); } // NOTE: If we add parameters then we will have to fix up dependencies below. var call = string.Format("{0} ()", qmSource.Name); if (qmSource.IsSequence) { // For the sequence we get the resulting vector array. var cvar = DeclarableParameter.CreateDeclarableParameterExpression(qmSource.ResultType); _codeEnv.Add(cvar); _codeEnv.Add(new StatementAssign(cvar, new ValSimple(call, qmSource.ResultType, null))); // Now, do a loop over it. var loopVar = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); _codeEnv.Add(new StatementForLoop(loopVar, new ValSimple(string.Format("{0}.size()", cvar.RawValue), typeof(int), new IDeclaredParameter[] { cvar }))); // Finally, we setup the loop index variables to match what they did when we ran the function. var oldLoopIndex = qmSource.OldLoopIndexVariable; Expression oldLoopVariable = qmSource.OldLoopExpression; var arrayLookup = Expression.ArrayIndex(cvar, loopVar); _codeContext.SetLoopVariable(arrayLookup, loopVar); } else { // For the non-sequence this just returns a value that we need. _codeEnv.SetResult(Expression.Parameter(qmSource.ResultType, call)); } }
/// <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> /// Given array info, code a loop over it. /// </summary> /// <param name="query">The query this loop is associated with</param> /// <param name="arrayRef">The reference to the array</param> /// <remarks>Will add the query to the code context to forward to the variable that is being dealt with here.</remarks> public static IVariableScopeHolder CodeLoopOverArrayInfo(this IArrayInfo arrayRef, IQuerySource query, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { var indexVar = arrayRef.AddLoop(gc, cc, container); if (indexVar == null) return null; /// /// Next, make sure the index variable can be used for later references! /// var result = cc.Add(query, indexVar.Item1); cc.SetLoopVariable(indexVar.Item1, indexVar.Item2); return result; }
/// <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> /// Given array info, code a loop over it. /// </summary> /// <param name="query">The query this loop is associated with</param> /// <param name="arrayRef">The reference to the array</param> /// <remarks>Will add the query to the code context to forward to the variable that is being dealt with here.</remarks> public static IVariableScopeHolder CodeLoopOverArrayInfo(this IArrayInfo arrayRef, IQuerySource query, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { var indexVar = arrayRef.AddLoop(gc, cc, container); if (indexVar == null) { return(null); } /// /// Next, make sure the index variable can be used for later references! /// var result = cc.Add(query, indexVar.Item1); cc.SetLoopVariable(indexVar.Item1, indexVar.Item2); return(result); }
/// <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); } }
/// <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> /// 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; } }