/// <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> /// 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> /// Generate the code that we will use to access this array. Loop symantics in this framework are basically "foreach" rather than "for" - so /// we return an object that can be used to reference each array element. /// </summary> /// <param name="env"></param> /// <param name="context"></param> /// <param name="indexName"></param> /// <param name="popVariableContext"></param> /// <returns></returns> public Tuple <Expression, IDeclaredParameter> AddLoop(IGeneratedQueryCode env, ICodeContext context, CompositionContainer container) { /// /// First, we will need to know the length of this array /// var lenExpression = Expression.ArrayLength(_arrayExpression); var lenTranslation = ExpressionToCPP.GetExpression(lenExpression, env, context, container); /// /// Next, generate the expression that forms the basis of the index lookup. We don't /// translate this - that only gets to happen when one is actually looking at a final result. /// var loopVariable = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var indexExpression = Expression.MakeBinary(ExpressionType.ArrayIndex, _arrayExpression, loopVariable); /// /// Now the for loop statement! /// env.Add(new StatementForLoop(loopVariable, lenTranslation)); /// /// Return the index expression - the thing that can be used to replace all expressions and /// reference the item we are looping over. /// return(Tuple.Create <Expression, IDeclaredParameter>(indexExpression, loopVariable)); }
/// <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> /// Generate the code that we will use to access this array. Loop symantics in this framework are basically "foreach" rather than "for" - so /// we return an object that can be used to reference each array element. /// </summary> /// <param name="env"></param> /// <param name="context"></param> /// <param name="indexName"></param> /// <param name="popVariableContext"></param> /// <returns></returns> public Tuple<Expression, IDeclaredParameter> AddLoop(IGeneratedQueryCode env, ICodeContext context, CompositionContainer container) { /// /// First, we will need to know the length of this array /// var lenExpression = Expression.ArrayLength(_arrayExpression); var lenTranslation = ExpressionToCPP.GetExpression(lenExpression, env, context, container); /// /// Next, generate the expression that forms the basis of the index lookup. We don't /// translate this - that only gets to happen when one is actually looking at a final result. /// var loopVariable = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var indexExpression = Expression.MakeBinary(ExpressionType.ArrayIndex, _arrayExpression, loopVariable); /// /// Now the for loop statement! /// env.Add(new StatementForLoop(loopVariable, lenTranslation)); /// /// Return the index expression - the thing that can be used to replace all expressions and /// reference the item we are looping over. /// return Tuple.Create<Expression, IDeclaredParameter>(indexExpression, loopVariable); }
/// <summary> /// Actually try and process this! The count consisits of a count integer and something to increment it /// at its current spot. /// </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) { if (gc == null) throw new ArgumentNullException("CodeEnv must not be null!"); var c = resultOperator as CountResultOperator; if (c == null) throw new ArgumentNullException("resultOperator can only be a CountResultOperator and must not be null"); // // The accumulator where we will store the result. // var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); accumulator.SetInitialValue("0"); // // Use the Aggregate infrasturcutre to do the adding. This // has the advantage that it will correctly combine with // similar statements during query optimization. // var add = Expression.Add(accumulator, Expression.Constant((int)1)); var addResolved = ExpressionToCPP.GetExpression(add, gc, cc, container); gc.Add(new StatementAggregate(accumulator, addResolved)); return accumulator; }
/// <summary> /// Actually try and process this! The count consisits of a count integer and something to increment it /// at its current spot. /// </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) { if (gc == null) { throw new ArgumentNullException("CodeEnv must not be null!"); } var c = resultOperator as CountResultOperator; if (c == null) { throw new ArgumentNullException("resultOperator can only be a CountResultOperator and must not be null"); } // // The accumulator where we will store the result. // var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); accumulator.SetInitialValue("0"); // // Use the Aggregate infrasturcutre to do the adding. This // has the advantage that it will correctly combine with // similar statements during query optimization. // var add = Expression.Add(accumulator, Expression.Constant((int)1)); var addResolved = ExpressionToCPP.GetExpression(add, gc, cc, container); gc.Add(new StatementAggregate(accumulator, addResolved)); return(accumulator); }
/// <summary> /// Deal with an inline conditional expression (test ? ans1 : ans2). We can translate this directly to C++, fortunately! /// </summary> /// <param name="expression"></param> /// <returns></returns> /// <remarks> /// We turn this into a real if statement, rather than a fake if statement. This is to try to keep any code /// associated with the side that won't be executed, not being executed. /// Note that Resolver has some good code already to special case handle a bool result. /// </remarks> protected override Expression VisitConditional(ConditionalExpression expression) { var testExpression = expression.Test; var trueExpression = expression.IfTrue; var falseExpression = expression.IfFalse; // Run the test. var testBoolInCode = AssignExpreaaionToEvaluationIfNeededBool(_codeEnv, _codeContext, MEFContainer, testExpression); // Next, do the result cache. var resultInCode = DeclarableParameter.CreateDeclarableParameterExpression(expression.Type); _codeEnv.Add(resultInCode); _result = resultInCode; // Get the result if the test is true. var topScope = _codeEnv.CurrentScope; _codeEnv.Add(new Statements.StatementFilter(testBoolInCode)); _codeEnv.Add(new Statements.StatementAssign(resultInCode, GetExpression(trueExpression, _codeEnv, _codeContext, MEFContainer))); _codeEnv.CurrentScope = topScope; _codeEnv.Add(new Statements.StatementFilter(GetExpression(Expression.Not(testBoolInCode), _codeEnv, _codeContext, MEFContainer))); _codeEnv.Add(new Statements.StatementAssign(resultInCode, GetExpression(falseExpression, _codeEnv, _codeContext, MEFContainer))); _codeEnv.CurrentScope = topScope; // Result is set. Continue to process other items in the tree. return(expression); }
/// <summary> /// Parse the group by enum into an arrary. For us, this is a loop over the vector array of the second argument of /// the iterator that was created in the above array info factory. :-) /// </summary> /// <param name="expr"></param> /// <param name="gc"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <param name="ReGetIArrayInfo"></param> /// <returns></returns> public IArrayInfo GetIArrayInfo(Expression expr, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container, Func <Expression, IArrayInfo> ReGetIArrayInfo) { // // Make sure this is something we want // BaseGroupInfo groupObj; if (expr.Type.IsGenericType && expr.Type.GetGenericTypeDefinition() == typeof(IGrouping <int, int>).GetGenericTypeDefinition() && cc.LoopVariable.Type.IsGenericType && cc.LoopVariable.Type.GetGenericTypeDefinition() == typeof(GroupByType <int, int>).GetGenericTypeDefinition()) { var param = cc.LoopVariable as ConstantExpression; if (param == null) { return(null); } groupObj = param.Value as BaseGroupInfo; } else if (expr is ConstantExpression && expr.Type.IsGenericType && expr.Type.GetGenericTypeDefinition() == typeof(GroupByType <int, int>).GetGenericTypeDefinition()) { groupObj = (expr as ConstantExpression).Value as BaseGroupInfo; } else { return(null); } if (groupObj == null) { throw new InvalidOperationException("Group by type object has a null value - should never happen!"); } // // Ok, now code up the loop over the interior. // var s = new Statements.StatementLoopOverGroupItems(groupObj.GroupLoopStatement.GroupItemsReference.PerformAllSubstitutions(cc)); gc.Add(s); // // Finally, return the loop index variable. // We queue up a replacement as well - so we can make sure that when the original loop variable is referenced, we // are actually referencing the interior one. // var loopExpression = groupObj.TargetExpression; cc.Add(groupObj.TargetExpressionLoopVariable.RawValue, s.LoopItemIndex.AsExpression()); return(new SimpleLoopVarSetting(loopExpression, s.Counter)); }
/// <summary> /// Evaluate an expression. If the result is just DeclareableParameter, return that, otherwise make an assignment. /// </summary> /// <param name="ce"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <param name="exprToHandIn"></param> /// <returns></returns> private static DeclarableParameter AssignExpreaaionToEvaluationIfNeededBool(IGeneratedQueryCode ce, ICodeContext cc, CompositionContainer container, Expression exprToHandIn, IScopeInfo whereToDeclare = null) { IValue exprEvaluation = GetExpression(exprToHandIn, ce, cc, container); if (exprEvaluation is DeclarableParameter p) { // We can only return this if the variable is declared at the place we want it to be! var currentScope = ce.CurrentScope; try { if (whereToDeclare != null) { ce.CurrentScope = whereToDeclare; } if (ce.CodeBody.AllDeclaredVariables.Where(dp => dp == p).Any()) { return(p); } } finally { ce.CurrentScope = currentScope; } } // Create and assign an expression. var result = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); result.InitialValue = new ValSimple("false", typeof(bool)); if (whereToDeclare == null) { ce.Add(result); } else { var currentScope = ce.CurrentScope; ce.CurrentScope = whereToDeclare; ce.Add(result); ce.CurrentScope = currentScope; } ce.Add(new Statements.StatementAssign(result, exprEvaluation)); return(result); }
/// <summary> /// Actually add the loop to the code and return everything! /// </summary> /// <param name="env"></param> /// <param name="context"></param> /// <param name="container"></param> /// <returns></returns> public Tuple <Expression, IDeclaredParameter> AddLoop(IGeneratedQueryCode env, ICodeContext context, CompositionContainer container) { // Create the index variable! var loopVariable = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var floop = new Statements.StatementForLoop(loopVariable, _maxValue, _minValue); env.Add(floop); return(Tuple.Create(loopVariable as Expression, loopVariable as IDeclaredParameter)); }
/// <summary> /// Deal with the aggregate operator coming in here. /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <returns></returns> public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode _codeEnv, ICodeContext context, CompositionContainer container) { /// /// Basic code checks /// AggregateFromSeedResultOperator a = resultOperator as AggregateFromSeedResultOperator; if (a == null) { throw new ArgumentNullException("result Operator must not be null and must be of type AggregateFromSeedResultOperator!"); } if (a.Func.Parameters.Count != 1) { throw new InvalidOperationException("Aggregate only allows for a function with one parameters!"); } if (a.OptionalResultSelector != null) { throw new NotImplementedException("Can't do a selector function yet"); } // We need to declare a variable to hold the seed and its updates - the accumulator // We then need to write the code that does the update to the seed. // Finally, if there is a final function, we need to call that after the loop is done! var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(a.Seed.Type); var newGC = new GeneratedCode(blockShouldBeBraced: false); var newCC = new CodeContext(); accumulator.InitialValue = ExpressionToCPP.GetExpression(a.Seed, newGC, newCC, container); if (newGC.CodeBody.Statements.Count() > 0) { accumulator.InitialValueCode = newGC; } _codeEnv.QueueForTransferFromGC(newGC); /// /// Now, parse the lambda expression, doing a substitution with this guy! Note that the only argument is our /// accumulator - the other arguments have all been replaced with subqueryexpressions and the like! /// var p1 = context.Add(a.Func.Parameters[0].Name, accumulator); var funcResolved = ExpressionToCPP.GetExpression(a.Func.Body, _codeEnv, context, container); p1.Pop(); if (accumulator.RawValue != funcResolved.RawValue) { _codeEnv.Add(new Statements.StatementAggregate(accumulator, funcResolved)); } return(accumulator); }
/// <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); }
/// <summary> /// We are looking at a&&b or a||b. We wnat to make sure we evaluate b iff we need it, depending on the result of a. /// </summary> /// <param name="expr"></param> /// <param name="ce"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <returns></returns> /// <remarks> /// To prevent us from updating variables (which makes optmization harder), we will implement the code as follows for a&&b: /// bool_1 = false; bool_2 = false; bool_3 = false; /// bool_1 = a /// if (bool_1) bool_2 = b /// bool_3 = bool_1 && bool_2 ///</remarks> private static IValue GetExpressionForBoolAndOr(Expression expr, IGeneratedQueryCode ce, ICodeContext cc, CompositionContainer container) { // Svae to make sure we can get back. var outterScope = ce.CurrentScope; // Create a variable to hold the result of this test var resultBool3 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); resultBool3.InitialValue = new ValSimple("false", typeof(bool)); ce.Add(resultBool3); // Create and evaluate bool_1 var binaryExpression = expr as BinaryExpression; DeclarableParameter resultBool1 = AssignExpreaaionToEvaluationIfNeededBool(ce, cc, container, binaryExpression.Left); // Now, see if we need to evalute the right hand operand. if (expr.NodeType == ExpressionType.AndAlso) { ce.Add(new Statements.StatementFilter(resultBool1)); } else { var notYet = new ValSimple($"!{resultBool1.RawValue}", typeof(bool), new IDeclaredParameter[] { resultBool1 }); ce.Add(new Statements.StatementFilter(notYet)); } // Create and evaluate bool 1. var resultBool2 = AssignExpreaaionToEvaluationIfNeededBool(ce, cc, container, binaryExpression.Right, outterScope); ce.CurrentScope = outterScope; // Finally, evaluate bool3. var termEvaluation = expr.NodeType == ExpressionType.AndAlso ? $"{resultBool1.RawValue}&&{resultBool2.RawValue}" : $"{resultBool1.RawValue}||{resultBool2.RawValue}"; ce.Add(new Statements.StatementAssign(resultBool3, new ValSimple(termEvaluation, typeof(bool), new[] { resultBool1, resultBool2 }))); // Return the value we've now filled. return(resultBool3); }
/// <summary> /// If one of the helper functions needs to be parsed, we end up here. /// Note: this is a bit tricky, and, worse, this is not tested (too complex :-)). /// </summary> /// <param name="expr"></param> /// <param name="result"></param> /// <param name="gc"></param> /// <param name="context"></param> /// <returns></returns> public System.Linq.Expressions.Expression ProcessMethodCall(MethodCallExpression expr, IGeneratedQueryCode gc, ICodeContext context, CompositionContainer container) { if (expr == null) { throw new ArgumentNullException("expr"); } if (expr.Method.Name == "ApplyReturnFirst") { /// /// Load out the parameter names we are looking at so we cna do the translation. /// var parameters = expr.Method.GetParameters(); var action = RaiseLambda(expr.Arguments[2]); var methodGenericArguments = expr.Method.GetGenericArguments(); var actionType = typeof(Action <,>).MakeGenericType(new Type[] { methodGenericArguments[0], methodGenericArguments[1] }); var expressionGeneric = typeof(Expression <>).MakeGenericType(new Type[] { actionType }); var parameterSpec = expressionGeneric.GetProperty("Parameters"); var lambdaParameters = (parameterSpec.GetValue(action, null) as IEnumerable <ParameterExpression>).ToArray(); /// /// Next, do the lambda expression. Order of p1 and p2 is b/c we should make sure that it happens /// before any parameters are replaced! Note we parse the body of the lambda here! Parameters are defined and should /// correctly deal with any substitution in process. /// var p2 = context.Add(lambdaParameters[1].Name, expr.Arguments[1]); var p1 = context.Add(lambdaParameters[0].Name, expr.Arguments[0]); var statementBody = ExpressionToCPP.GetExpression(action.Body.Resolve(gc, context, container), gc, context, container); p1.Pop(); p2.Pop(); gc.Add(new Statements.StatementSimpleStatement(statementBody.RawValue, resultVars: new string[0] { }, dependentVars: statementBody.Dependants.Select(i => i.RawValue).ToArray())); /// /// Finally, what we will return if this is the last thing we are doing! /// return(expr.Arguments[0]); } else { throw new NotImplementedException("Helpers." + expr.Method.Name + " is not handled!"); } }
/// <summary> /// We want to print the results out to a file. /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <param name="_codeContext"></param> /// <param name="container"></param> /// <returns></returns> /// <remarks> /// We can handle several types of streams here: /// 1) a stream of double's - this is just one column. /// 2) A stream of Tuples /// 3) A stream of custom objects /// </remarks> public override Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { // Argument checking var asTTree = resultOperator as AsTTreeResultOperator; if (asTTree == null) { throw new ArgumentException("resultOperaton"); } // Declare the includes. gc.AddIncludeFile("<map>"); gc.AddIncludeFile("TSystem.h"); gc.AddIncludeFile("TFile.h"); gc.AddIncludeFile("TTree.h"); // If we were left to our own devices generating an output file, then make one up based on the tree name. var outputFile = asTTree.OutputFile != null ? asTTree.OutputFile : new FileInfo($"{asTTree.TreeName}.root"); // Generate a real filename. We are going to key the file by the cache key. Unfortunately, at this // point in the generation the cache key isn't known. So we have to have a 'promise' that can be used // for later when the code is actually generated. var outputFilePromise = GenerateUniqueFile(outputFile, cc); // Declare the TTree and the file we will be using! // Initialization is not important as we will over-write this directly. var stream = DeclarableParameter.CreateDeclarableParameterExpression(typeof(OutputTTreeFileType)); stream.InitialValue = new OutputTTreeFileType(outputFilePromise); // Open the file and declare the tree gc.AddInitalizationStatement(new StatementSimpleStatement(() => $"{stream.RawValue}.first = new TFile(\"<><>{outputFilePromise().FullName.AddCPPEscapeCharacters()}<><>\",\"RECREATE\")", dependentVars: new string[0], resultVars: new string[] { stream.RawValue })); gc.AddInitalizationStatement(new StatementSimpleStatement($"{stream.RawValue}.second = new TTree(\"{asTTree.TreeName}\", \"{asTTree.TreeTitle}\")", dependentVars: new string[0], resultVars: new string[] { stream.RawValue })); // Get the list of item values we are going to need here. List <Expression> itemValues = ExtractItemValueExpressions(queryModel); // We are just going to print out the line with the item in it. var itemAsValues = itemValues.Select(iv => ExpressionToCPP.GetExpression(iv, gc, cc, container)).ToArray(); var pstatement = new StatementFillTree(stream, itemAsValues.Zip(asTTree.HeaderColumns, (i, h) => Tuple.Create(i, h)).ToArray()); gc.Add(pstatement); // The return is a file path in the C# world. But here in C++, what should be returned? // We will use a string. return(stream); }
/// <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> /// See if we can't resolve a group-by object into a looper of some sort. /// </summary> /// <param name="expr"></param> /// <param name="gc"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <param name="ReGetIArrayInfo"></param> /// <returns></returns> public IArrayInfo GetIArrayInfo(Expression expr, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container, Func <Expression, IArrayInfo> ReGetIArrayInfo) { if (!expr.Type.IsGenericType || expr.Type.GetGenericTypeDefinition() != typeof(GroupByTypeTagEnum <int, int>).GetGenericTypeDefinition()) { return(null); } var param = expr as ConstantExpression; if (param == null) { return(null); } var groupObj = param.Value as BaseGroupInfo; if (groupObj == null) { throw new InvalidOperationException("Group object has a null value - should never happen!"); } // // Loop over the groups. groupIndex represents the actual group index. // var loopOverGroups = new Statements.StatementLoopOverGroups(groupObj.MapRecord); gc.Add(loopOverGroups); // // Finally, the loop index variable and we have to create the index object now, which is the grouping // (which can also be iterated over). // var t_return = typeof(GroupByType <int, int>).GetGenericTypeDefinition().MakeGenericType(param.Value.GetType().GetGenericArguments()); var ctor = t_return.GetConstructor(new Type[] { }); var o = ctor.Invoke(new object[] { }) as BaseGroupInfo; o.MapRecord = groupObj.MapRecord; o.TargetExpression = groupObj.TargetExpression; o.TargetExpressionLoopVariable = groupObj.TargetExpressionLoopVariable; o.GroupIndexVariable = loopOverGroups.IndexVariable; o.GroupLoopStatement = loopOverGroups; var loopVar = Expression.Constant(o); return(new SimpleLoopVarSetting(loopVar, loopOverGroups.IndexVariable)); }
/// <summary> /// We want to print the results out to a file. /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <param name="_codeContext"></param> /// <param name="container"></param> /// <returns></returns> /// <remarks> /// We can handle several types of streams here: /// 1) a stream of double's - this is just one column. /// 2) A stream of Tuples /// 3) A stream of custom objects /// </remarks> public override Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { // Argument checking var asCSV = resultOperator as AsCSVResultOperator; if (asCSV == null) { throw new ArgumentException("resultOperaton"); } // Declare the includes. gc.AddIncludeFile("<fstream>"); gc.AddIncludeFile("<iostream>"); // The output filename. How we do this is a little funny because we need the hash from the completely // done query, which isn't ready just yet. var outputFile = GenerateUniqueFile(asCSV.OutputFile, cc); var stream = DeclarableParameter.CreateDeclarableParameterExpression(typeof(OutputCSVTextFileType)); stream.InitialValue = new OutputCSVTextFileType(outputFile); var headerline = new StringBuilder(); bool first = true; foreach (var h in asCSV.HeaderColumns) { if (!first) { headerline.Append(","); } headerline.Append(h); first = false; } gc.AddInitalizationStatement(new Statements.StatementSimpleStatement($"{stream.RawValue} << \"{headerline.ToString()}\" << std::endl;", dependentVars: new string[0], resultVars: new string[] { stream.RawValue })); // Get the list of item values we are going to need here. List <Expression> itemValues = ExtractItemValueExpressions(queryModel); // We are just going to print out the line with the item in it. var itemAsValues = itemValues.Select(iv => ExpressionToCPP.GetExpression(iv, gc, cc, container)); var pstatement = new StatementCSVDump(stream, itemAsValues.ToArray()); gc.Add(pstatement); // The return is a file path in the C# world. But here in C++, what should be returned? // We will use a string. return(stream); }
/// <summary> /// We want to print the results out to a file. /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <param name="_codeContext"></param> /// <param name="container"></param> /// <returns></returns> /// <remarks> /// We can handle several types of streams here: /// 1) a stream of double's - this is just one column. /// 2) A stream of Tuples /// 3) A stream of custom objects /// </remarks> public override Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { // Argument checking var asCSV = resultOperator as AsCSVResultOperator; if (asCSV == null) throw new ArgumentException("resultOperaton"); // Declare the includes. gc.AddIncludeFile("<fstream>"); gc.AddIncludeFile("<iostream>"); // The output filename. How we do this is a little funny because we need the hash from the completely // done query, which isn't ready just yet. var outputFile = GenerateUniqueFile(asCSV.OutputFile, cc); var stream = DeclarableParameter.CreateDeclarableParameterExpression(typeof(OutputCSVTextFileType)); stream.InitialValue = new OutputCSVTextFileType(outputFile); var headerline = new StringBuilder(); bool first = true; foreach (var h in asCSV.HeaderColumns) { if (!first) { headerline.Append(", "); } headerline.Append(h); first = false; } gc.AddInitalizationStatement(new Statements.StatementSimpleStatement($"{stream.RawValue} << \"{headerline.ToString()}\" << std::endl;", dependentVars: new string[0], resultVars: new string[] { stream.RawValue })); // Get the list of item values we are going to need here. List<Expression> itemValues = ExtractItemValueExpressions(queryModel); // We are just going to print out the line with the item in it. var itemAsValues = itemValues.Select(iv => ExpressionToCPP.GetExpression(iv, gc, cc, container)); var pstatement = new StatementCSVDump(stream, itemAsValues.ToArray()); gc.Add(pstatement); // The return is a file path in the C# world. But here in C++, what should be returned? // We will use a string. return stream; }
/// <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> /// We want to print the results out to a file. /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <param name="_codeContext"></param> /// <param name="container"></param> /// <returns></returns> /// <remarks> /// We can handle several types of streams here: /// 1) a stream of double's - this is just one column. /// 2) A stream of Tuples /// 3) A stream of custom objects /// </remarks> public override Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { // Argument checking var asTTree = resultOperator as AsTTreeResultOperator; if (asTTree == null) throw new ArgumentException("resultOperaton"); // Declare the includes. gc.AddIncludeFile("<map>"); gc.AddIncludeFile("TSystem.h"); gc.AddIncludeFile("TFile.h"); gc.AddIncludeFile("TTree.h"); // If we were left to our own devices generating an output file, then make one up based on the tree name. var outputFile = asTTree.OutputFile != null ? asTTree.OutputFile : new FileInfo($"{asTTree.TreeName}.root"); // Declare the TTree and the file we will be using! // Initialization is not important as we will over-write this directly. var stream = DeclarableParameter.CreateDeclarableParameterExpression(typeof(OutputTTreeFileType)); stream.InitialValue = new OutputTTreeFileType(outputFile); // Generate a real filename. We are going to key the file by the cache key. Unfortunately, at this // point in the generation the cache key isn't known. So we have to have a 'promise' that can be used // for later when the code is actually generated. var outputFilePromise = GenerateUniqueFile(outputFile, cc); // Open the file and declare the tree gc.AddInitalizationStatement(new StatementSimpleStatement(() => $"{stream.RawValue}.first = new TFile(\"{outputFilePromise().FullName.AddCPPEscapeCharacters()}\",\"RECREATE\")", dependentVars: new string[0], resultVars: new string[] { stream.RawValue })); gc.AddInitalizationStatement(new StatementSimpleStatement($"{stream.RawValue}.second = new TTree(\"{asTTree.TreeName}\", \"{asTTree.TreeTitle}\")", dependentVars: new string[0], resultVars: new string[] { stream.RawValue })); // Get the list of item values we are going to need here. List<Expression> itemValues = ExtractItemValueExpressions(queryModel); // We are just going to print out the line with the item in it. var itemAsValues = itemValues.Select(iv => ExpressionToCPP.GetExpression(iv, gc, cc, container)).ToArray(); var pstatement = new StatementFillTree(stream, itemAsValues.Zip(asTTree.HeaderColumns, (i, h) => Tuple.Create(i, h)).ToArray()); gc.Add(pstatement); // The return is a file path in the C# world. But here in C++, what should be returned? // We will use a string. return stream; }
/// <summary> /// Run a sub-query for the user. The sub-query is a full-blown expression that /// must usually run in its own loop (or similar). /// </summary> /// <remarks> /// The query visitor must be re-invoked - this ends up being a full-blown /// parsing. /// </remarks> /// <param name="expression"></param> /// <returns></returns> protected override Expression VisitSubQuery(SubQueryExpression expression) { if (MEFContainer == null) { throw new InvalidOperationException("MEFContainer can't be null if we need to analyze a sub query!"); } QueryVisitor qv = new QueryVisitor(GeneratedCode, CodeContext, MEFContainer); MEFContainer.SatisfyImportsOnce(qv); /// /// Run it - since this result is out of this loop, we pop-back-out when done. /// var scope = GeneratedCode.CurrentScope; GeneratedCode.SetCurrentScopeAsResultScope(); qv.VisitQueryModel(expression.QueryModel); // // And the main index that was looked at we need to cache ourselves. // if (qv.MainIndexScope != null) { CodeContext.CacheVariableToEliminate(qv.MainIndexScope); } /// /// Two possible results from the sub-expression query, and how we proceed depends /// on what happened in the sub query /// /// 1. <returns a value> - an operator like Count() comes back from the sequence. /// it will get used in some later sequence (like # of jets in each event). So, /// we need to make sure it is declared and kept before it is used. The # that comes /// back needs to be used outside the scope we are sitting in - the one that we were at /// when we started this. Since this is a sub-query expression, the result isn't the final /// result, so we need to reset it so no one notices it. /// 2. <return a sequence> - this is weird - What we are actually doing here is putting the /// sequence into code. So the loop variable has been updated with the new sequence iterator /// value. But there isn't really a result! So the result will be null... /// var r = GeneratedCode.ResultValue; if (r != null) { GeneratedCode.CurrentScope = scope; if (r is IDeclaredParameter) { GeneratedCode.Add(r as IDeclaredParameter, false); } GeneratedCode.ResetResult(); return(r); } // // The fact that we returned null means we are dealing with a // sequence. There really is no translated version of this expression // in that case - so we will return null. If someone above is depending // on doing something with it they are going to run into some // trouble! return(null); }
/// <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> /// Build a code statement from the include files, the expression for the method call, and the generated lines of code. /// </summary> /// <param name="expr"></param> /// <param name="gc"></param> /// <param name="container"></param> /// <param name="includeFiles"></param> /// <param name="loc"></param> /// <returns></returns> public static IValue BuildCPPCodeStatement(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container, string[] includeFiles, string[] loc) { // Get include files in. if (includeFiles != null) { foreach (var inc in includeFiles) { gc.AddIncludeFile(inc); } } // Next, go after the lines of code. We have to first sort out what parameter names we are looking at, // and then force a translation of those parameters into simple values we can pass to the C++ code we // are going to pull back. var paramsTranslated = from p in expr.Arguments.Zip(expr.Method.GetParameters(), (arg, param) => Tuple.Create(arg, param)) select new { Name = p.Item2.Name, Translated = ExpressionToCPP.InternalGetExpression(p.Item1, gc, null, container) }; var paramLookup = paramsTranslated.ToDictionary(v => v.Name, v => v.Translated.ApplyParensIfNeeded()); // Parse out the list of variables that are used. We will be passing these up the line as needed // so that we can tell how to optimize things. var dependents = new HashSet<string>(FindDeclarableParameters.FindAll(expr).Select(e => e.RawValue)); // We also need a return variable. Since this can be multiple lines of code and we don't // know how the result will be used, we have to declare it up front... and pray they // use it correctly! :-) var cppResult = DeclarableParameter.CreateDeclarableParameterExpression(expr.Type); var cppStatement = new CPPCodeStatement(expr.Method, cppResult, loc, dependents); gc.Add(cppStatement); gc.Add(cppResult); paramLookup.Add(expr.Method.Name, cppResult.RawValue); var result = new ValSimple(cppResult.RawValue, expr.Type, DeclarableParameter.CreateDeclarableParameterExpression(cppResult.RawValue, expr.Type).AsArray()); // Make sure a result exists in here! This at least will prevent some bad C++ code from getting generated! var lookForResult = new Regex(string.Format(@"\b{0}\b", expr.Method.Name)); bool didReference = loc.Any(l => lookForResult.Match(l).Success); if (!didReference) throw new ArgumentException(string.Format("The C++ code attached to the method '{0}' doesn't seem to set a result.", expr.Method.Name)); // Figure out if there are any Unique variables. If there are, then we need to do // a replacement on them. var findUnique = new Regex(@"\b\w*Unique\b"); var varUniqueRequests = (from l in loc let matches = findUnique.Matches(l) from m in Enumerable.Range(0, matches.Count) select matches[m].Value).Distinct(); foreach (var varRepl in varUniqueRequests) { var uniqueName = varRepl.Substring(0, varRepl.Length - "Unique".Length); var uniqueTranslated = uniqueName + _uniqueCounter.ToString(); cppStatement.AddUniqueVariable(varRepl, uniqueTranslated); _uniqueCounter++; } // Add the parameters that need to be translated here. foreach (var paramName in paramLookup) { cppStatement.AddParamReplacement(paramName.Key, paramName.Value); } return result; }
/// <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> /// New a ROOT object. Make sure that it gets dtor'd! /// </summary> /// <param name="expression"></param> /// <param name="result"></param> /// <param name="gc"></param> /// <param name="context"></param> /// <param name="container"></param> /// <returns></returns> public Expression ProcessNew(NewExpression expression, out IValue result, IGeneratedQueryCode gc, CompositionContainer container) { /// /// Do checks /// if (gc == null) { throw new ArgumentException("gc"); } if (expression == null) { throw new ArgumentNullException("expression"); } /// /// Figure out the type. We can only get here if we get through ROOTNET.xxx /// result = null; string tname = expression.Type.FullName.Substring(8); if (tname[0] != 'N') { throw new ArgumentException(string.Format("Don't know how to translate to a ROOT type '{0}'", expression.Type.FullName)); } tname = tname.Substring(1); /// /// We assume the include file "just works" - this is ROOT, after all. But lets hope. /// This is something we might have to deal with later. :-) /// gc.AddIncludeFile(string.Format("{0}.h", tname)); /// /// Now, build the constructor, and add it to the statement list. /// var ctor = new StringBuilder(); var ctorName = expression.Type.CreateUniqueVariableName(); ctor.AppendFormat("{0} {1}", tname, ctorName); var argDep = AddMethodArguments(expression.Arguments, gc, container, ctor).ToArray(); gc.Add(new Statements.StatementSimpleStatement(ctor.ToString(), dependentVars: argDep.Select(i => i.RawValue).ToArray(), resultVars: new string[] { ctorName })); /// /// Now, everything in the C++ translation is a pointer, so we will /// not create a pointer to this guy. /// var ptrDecl = new StringBuilder(); var ptrName = expression.Type.CreateUniqueVariableName(); ptrDecl.AppendFormat("{0} *{1} = &{2}", tname, ptrName, ctorName); gc.Add(new Statements.StatementSimpleStatement(ptrDecl.ToString(), dependentVars: new string[] { ctorName }, resultVars: new string[] { ptrName })); /// /// That pointer is what we return for later use! /// result = new ValSimple(ptrName, expression.Type, argDep); return(expression); }
/// <summary> /// Build a code statement from the include files, the expression for the method call, and the generated lines of code. /// </summary> /// <param name="expr"></param> /// <param name="gc"></param> /// <param name="container"></param> /// <param name="includeFiles"></param> /// <param name="loc"></param> /// <returns></returns> public static IValue BuildCPPCodeStatement(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container, string[] includeFiles, string[] loc) { // Get include files in. if (includeFiles != null) { foreach (var inc in includeFiles) { gc.AddIncludeFile(inc); } } // Next, go after the lines of code. We have to first sort out what parameter names we are looking at, // and then force a translation of those parameters into simple values we can pass to the C++ code we // are going to pull back. var paramsTranslated = from p in expr.Arguments.Zip(expr.Method.GetParameters(), (arg, param) => Tuple.Create(arg, param)) select new { Name = p.Item2.Name, Translated = ExpressionToCPP.InternalGetExpression(p.Item1, gc, null, container) }; var paramLookup = paramsTranslated.ToDictionary(v => v.Name, v => v.Translated.ApplyParensIfNeeded()); // Parse out the list of variables that are used. We will be passing these up the line as needed // so that we can tell how to optimize things. var dependents = new HashSet <string>(FindDeclarableParameters.FindAll(expr).Select(e => e.RawValue)); // We also need a return variable. Since this can be multiple lines of code and we don't // know how the result will be used, we have to declare it up front... and pray they // use it correctly! :-) var cppResult = DeclarableParameter.CreateDeclarableParameterExpression(expr.Type); var cppStatement = new CPPCodeStatement(expr.Method, cppResult, loc, dependents); gc.Add(cppStatement); gc.Add(cppResult); paramLookup.Add(expr.Method.Name, cppResult.RawValue); var result = new ValSimple(cppResult.RawValue, expr.Type, DeclarableParameter.CreateDeclarableParameterExpression(cppResult.RawValue, expr.Type).AsArray()); // Make sure a result exists in here! This at least will prevent some bad C++ code from getting generated! var lookForResult = new Regex(string.Format(@"\b{0}\b", expr.Method.Name)); bool didReference = loc.Any(l => lookForResult.Match(l).Success); if (!didReference) { throw new ArgumentException(string.Format("The C++ code attached to the method '{0}' doesn't seem to set a result.", expr.Method.Name)); } // Figure out if there are any Unique variables. If there are, then we need to do // a replacement on them. var findUnique = new Regex(@"\b\w*Unique\b"); var varUniqueRequests = (from l in loc let matches = findUnique.Matches(l) from m in Enumerable.Range(0, matches.Count) select matches[m].Value).Distinct(); foreach (var varRepl in varUniqueRequests) { var uniqueName = varRepl.Substring(0, varRepl.Length - "Unique".Length); var uniqueTranslated = uniqueName + _uniqueCounter.ToString(); cppStatement.AddUniqueVariable(varRepl, uniqueTranslated); _uniqueCounter++; } // Add the parameters that need to be translated here. foreach (var paramName in paramLookup) { cppStatement.AddParamReplacement(paramName.Key, paramName.Value); } return(result); }
/// <summary> /// Write up the code for the any or the all /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="gc"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <returns></returns> public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { if (cc == null) throw new ArgumentNullException("cc"); if (gc == null) throw new ArgumentNullException("gc"); var all = resultOperator as AllResultOperator; var any = resultOperator as AnyResultOperator; /// /// Next, change the predicate into something that can be tested (as an if statement) /// For All: /// initial: aresult = true; /// if (aresult && !pred) { aresult = false; } /// For Any: /// initial: aresult = false; /// if (!aresult && pred) { aresult = true; } /// var aresult = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); IValue predicate = null; IValue predicateFastTest = null; string initialValue = ""; string markedValue = ""; if (all != null) { predicateFastTest = ExpressionToCPP.GetExpression(aresult, gc, cc, container); var notPredicate = Expression.Not(all.Predicate); predicate = ExpressionToCPP.GetExpression(notPredicate, gc, cc, container); initialValue = "true"; markedValue = "false"; } else { predicate = null; predicateFastTest = ExpressionToCPP.GetExpression(Expression.Not(aresult), gc, cc, container); initialValue = "false"; markedValue = "true"; } /// /// The result is a simple bool. This is what we will be handing back. /// aresult.SetInitialValue(initialValue); /// /// And the statements now. Instead of building up the code, we instead do a "global" statement. /// This makes it easier to re-combine later when we collapse queires. /// var ifstatement = new Statements.StatementAnyAllDetector(predicate, aresult, predicateFastTest, markedValue); gc.Add(ifstatement); /// /// Done! /// return aresult; }
/// <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> /// 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> /// 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> /// If one of the helper functions needs to be parsed, we end up here. /// Note: this is a bit tricky, and, worse, this is not tested (too complex :-)). /// </summary> /// <param name="expr"></param> /// <param name="result"></param> /// <param name="gc"></param> /// <param name="context"></param> /// <returns></returns> public System.Linq.Expressions.Expression ProcessMethodCall(MethodCallExpression expr, IGeneratedQueryCode gc, ICodeContext context, CompositionContainer container) { if (expr == null) throw new ArgumentNullException("expr"); if (expr.Method.Name == "ApplyReturnFirst") { /// /// Load out the parameter names we are looking at so we cna do the translation. /// var parameters = expr.Method.GetParameters(); var action = RaiseLambda(expr.Arguments[2]); var methodGenericArguments = expr.Method.GetGenericArguments(); var actionType = typeof(Action<,>).MakeGenericType(new Type[] { methodGenericArguments[0], methodGenericArguments[1] }); var expressionGeneric = typeof(Expression<>).MakeGenericType(new Type[] { actionType }); var parameterSpec = expressionGeneric.GetProperty("Parameters"); var lambdaParameters = (parameterSpec.GetValue(action, null) as IEnumerable<ParameterExpression>).ToArray(); /// /// Next, do the lambda expression. Order of p1 and p2 is b/c we should make sure that it happens /// before any parameters are replaced! Note we parse the body of the lambda here! Parameters are defined and should /// correctly deal with any substitution in process. /// var p2 = context.Add(lambdaParameters[1].Name, expr.Arguments[1]); var p1 = context.Add(lambdaParameters[0].Name, expr.Arguments[0]); var statementBody = ExpressionToCPP.GetExpression(action.Body.Resolve(gc, context, container), gc, context, container); p1.Pop(); p2.Pop(); gc.Add(new Statements.StatementSimpleStatement(statementBody.RawValue, resultVars: new string[0] { }, dependentVars: statementBody.Dependants.Select(i => i.RawValue).ToArray())); /// /// Finally, what we will return if this is the last thing we are doing! /// return expr.Arguments[0]; } else { throw new NotImplementedException("Helpers." + expr.Method.Name + " is not handled!"); } }
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> /// Actually add the loop to the code and return everything! /// </summary> /// <param name="env"></param> /// <param name="context"></param> /// <param name="container"></param> /// <returns></returns> public Tuple<Expression, IDeclaredParameter> AddLoop(IGeneratedQueryCode env, ICodeContext context, CompositionContainer container) { // Create the index variable! var loopVariable = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var floop = new Statements.StatementForLoop(loopVariable, _maxValue, _minValue); env.Add(floop); return Tuple.Create(loopVariable as Expression, loopVariable as IDeclaredParameter); }
/// <summary> /// Write up the code for the any or the all /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="gc"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <returns></returns> public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { if (cc == null) { throw new ArgumentNullException("cc"); } if (gc == null) { throw new ArgumentNullException("gc"); } var all = resultOperator as AllResultOperator; var any = resultOperator as AnyResultOperator; /// /// Next, change the predicate into something that can be tested (as an if statement) /// For All: /// initial: aresult = true; /// if (aresult && !pred) { aresult = false; } /// For Any: /// initial: aresult = false; /// if (!aresult && pred) { aresult = true; } /// var aresult = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); IValue predicate = null; IValue predicateFastTest = null; string initialValue = ""; string markedValue = ""; if (all != null) { predicateFastTest = ExpressionToCPP.GetExpression(aresult, gc, cc, container); var notPredicate = Expression.Not(all.Predicate); predicate = ExpressionToCPP.GetExpression(notPredicate, gc, cc, container); initialValue = "true"; markedValue = "false"; } else { predicate = null; predicateFastTest = ExpressionToCPP.GetExpression(Expression.Not(aresult), gc, cc, container); initialValue = "false"; markedValue = "true"; } /// /// The result is a simple bool. This is what we will be handing back. /// aresult.SetInitialValue(initialValue); /// /// And the statements now. Instead of building up the code, we instead do a "global" statement. /// This makes it easier to re-combine later when we collapse queires. /// var ifstatement = new Statements.StatementAnyAllDetector(predicate, aresult, predicateFastTest, markedValue); gc.Add(ifstatement); /// /// Done! /// return(aresult); }
/// <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; }
/// <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); }