/// <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> /// 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> /// 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(); }
/// <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(); }