示例#1
0
        /// <summary>
        /// Someone is accessing a method on our ROOT object. We do the translation to C++ here.
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="result"></param>
        /// <param name="gc"></param>
        /// <returns></returns>
        /// <remarks>Static methods and instance methods are both handled correctly.</remarks>
        public IValue CodeMethodCall(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container)
        {
            var           objRef = ExpressionToCPP.InternalGetExpression(expr.Object, gc, null, container);
            StringBuilder bld    = new StringBuilder();

            if (expr.Method.IsStatic && objRef != null)
            {
                throw new ArgumentException(string.Format("Call to ROOT instance method '{0}' where the instance is null", expr.Method.Name));
            }
            if (!expr.Method.IsStatic && objRef == null)
            {
                throw new ArgumentException(string.Format("Call to ROOT static method '{0}' where the instance is not null", expr.Method.Name));
            }

            //
            // Code up the local invocation or the static invocation to the method
            //

            if (objRef != null)
            {
                bld.AppendFormat("{0}.{1}", objRef.AsObjectReference(), expr.Method.Name);
            }
            else
            {
                bld.AppendFormat("{0}::{1}", expr.Method.DeclaringType.Name.Substring(1), expr.Method.Name);
            }

            //
            // Put in the arguments
            //

            var argDep = AddMethodArguments(expr.Arguments, gc, container, bld);

            return(new ValSimple(bld.ToString(), expr.Type, objRef == null ? argDep : objRef.Dependants.Concat(argDep)));
        }
示例#2
0
        /// <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);
        }
        public void TestApplyReturnFirstMethodCall()
        {
            ///
            /// Get the method we need to get! :-)
            ///

            var clsGeneric = typeof(Helpers).GetMethod("ApplyReturnFirst");
            var cls        = clsGeneric.MakeGenericMethod(new Type[] { typeof(ROOTNET.NTH1F), typeof(double) });
            Expression <Action <ROOTNET.NTH1F, double> > applyIt = (h, item) => h.Fill(item);
            MethodCallExpression mc = Expression.Call(cls, Expression.Parameter(typeof(ROOTNET.NTH1F), "myhist"), Expression.Constant(10.2), applyIt);

            ///
            /// Now do the actual call
            ///

            GeneratedCode gc     = new GeneratedCode();
            var           result = ExpressionToCPP.GetExpression(mc, gc, null, MEFUtilities.MEFContainer);

            ///
            /// And check the results!
            ///

            Assert.AreEqual(typeof(ROOTNET.NTH1F), result.Type, "incorrect result type");
            Assert.AreEqual("myhist", result.RawValue, "didn't get back the accumulator!");

            Assert.AreEqual(2, gc.CodeBody.Statements.Count(), "Expected a statement body to do the filling!");
            Assert.IsInstanceOfType(gc.CodeBody.Statements.First(), typeof(LINQToTTreeLib.Statements.StatementAssign), "incorrect statement saved");
            var statement = gc.CodeBody.Statements.First() as LINQToTTreeLib.Statements.StatementAssign;

            Assert.AreEqual("(*myhist).Fill(10.2)", statement.Expression.RawValue, "incorrect fill statement");
        }
        /// <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);
        }
示例#5
0
        /// <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>
        /// Called late to replace a constant expression of this type. By the time we get here these should not exist!
        /// The expression holder can't hold anything interesting (like parameters) - by the time we are here
        /// it is too late to do the parsing.
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="codeEnv"></param>
        /// <param name="container"></param>
        /// <returns></returns>
        public IValue ProcessConstantReference(ConstantExpression expr, IGeneratedQueryCode codeEnv, CompositionContainer container)
        {
            var holder = expr.Value as IExpressionHolder;

            if (holder == null)
            {
                throw new InvalidOperationException("Can't get at the interface to get at the expression.");
            }

            var e = holder.HeldExpression;

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

            var groupOp = resultOperator as GroupResultOperator;

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

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

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

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

            gc.AddOutsideLoop(mapRecord);

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

            gc.Add(savePairValues);

            gc.Pop();

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

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

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

            return(Expression.Constant(o, t_return));
        }
示例#9
0
        /// <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);
        }
示例#10
0
        /// <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);
        }
示例#11
0
        private static IValue RunArrayLengthOnExpression(Expression arrayLenLambda, Type expectedType)
        {
            MEFUtilities.AddPart(new QVResultOperators());
            MEFUtilities.AddPart(new ROCount());
            MEFUtilities.AddPart(new TypeHandlerCache());
            MEFUtilities.AddPart(new TypeHandlerTranslationClass());
            GeneratedCode gc = new GeneratedCode();
            CodeContext   cc = new CodeContext();

            MEFUtilities.Compose(new QueryVisitor(gc, cc, MEFUtilities.MEFContainer));

            var result = ExpressionToCPP.GetExpression(arrayLenLambda, gc, cc, MEFUtilities.MEFContainer);

            Assert.IsNotNull(result, "result");
            Assert.AreEqual(expectedType, result.Type, "result type");
            return(result);
        }
示例#12
0
        /// <summary>
        /// Convert something to a double. We don't actually do anything as long as this is an expression that we
        /// can naturally convert (int, float, etc.).
        ///
        /// We are expecting an expressio nthat is ToDouble(Convert()), so if we can't see the convert, then we bail.
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="result"></param>
        /// <param name="gc"></param>
        /// <param name="context"></param>
        /// <param name="container"></param>
        /// <returns></returns>
        private static IValue ProcessToDouble(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container)
        {
            var srcExpr = expr.Arguments[0];

            if (srcExpr.NodeType != ExpressionType.Convert)
            {
                throw new NotImplementedException("Expecting a Convert expression inside the call to Convert.ToDouble");
            }
            var cvtExpr = srcExpr as UnaryExpression;

            var result = ExpressionToCPP.InternalGetExpression(cvtExpr.Operand, gc, null, container);

            if (!result.Type.IsNumberType())
            {
                throw new NotImplementedException("Do not know how to convert '" + srcExpr.Type.Name + "' to a double!");
            }

            return(result);
        }
示例#13
0
        /// <summary>
        /// Add the arguments for call
        /// </summary>
        /// <param name="args"></param>
        /// <param name="gc"></param>
        /// <param name="context"></param>
        /// <param name="container"></param>
        /// <param name="builtArgs"></param>
        /// <returns>List of dependent variables used in the arguments</returns>
        private static IEnumerable <IDeclaredParameter> AddMethodArguments(System.Collections.ObjectModel.ReadOnlyCollection <Expression> args, IGeneratedQueryCode gc, CompositionContainer container, StringBuilder builtArgs)
        {
            builtArgs.Append("(");
            bool first      = true;
            var  dependents = Enumerable.Empty <IDeclaredParameter>();

            foreach (var a in args)
            {
                if (!first)
                {
                    builtArgs.Append(",");
                }
                first = false;
                var e = ExpressionToCPP.GetExpression(a, gc, null, container);
                builtArgs.Append(e.CastToType(a));
                dependents = dependents.Concat(e.Dependants);
            }
            builtArgs.Append(")");
            return(dependents);
        }
示例#14
0
        /// <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);
        }
示例#15
0
        /// <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);
        }
示例#16
0
        public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel,
                                                IGeneratedQueryCode gc, ICodeContext cc,
                                                CompositionContainer container)
        {
            if (resultOperator == null)
            {
                throw new ArgumentNullException("resultOperator");
            }
            if (cc == null)
            {
                throw new ArgumentNullException("CodeContext can't be null");
            }

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

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

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

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

            var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(sumType);

            accumulator.SetInitialValue("0");

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

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

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

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

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

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

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

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

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

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

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

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

            return(divide);
        }
示例#17
0
        /// <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>
        /// Translate the method call
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="result"></param>
        /// <param name="gc"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public IValue CodeMethodCall(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container)
        {
            Init();

            ///
            /// First see if we can't locate the method call that at least matches in names
            ///

            var matchingMethodNames = from kt in _knownTypes
                                      where kt.Name == expr.Method.DeclaringType.Name
                                      from m in kt.Methods
                                      where m.Name == expr.Method.Name
                                      select new
            {
                theType   = kt,
                theMethod = m
            };

            ///
            /// Next, match with the arguments
            ///

            var matchingMethod = from m in matchingMethodNames
                                 where m.theMethod.Arguments.Length == expr.Arguments.Count
                                 where m.theMethod.Arguments.Zip(expr.Arguments, (us, them) => new Tuple <KnownTypeInfo.MechodArg, Expression>(us, them)).All(apair => apair.Item1.Type == apair.Item2.Type.FullName)
                                 select m;

            ///
            /// Ok, at this point, we should have only one guy. If we have more then just choose the first
            ///

            var method = matchingMethod.FirstOrDefault();

            if (method == null)
            {
                throw new ArgumentException("Could not find a matching method to translate for the call " + expr.ToString());
            }

            ///
            /// And now translate the call
            ///

            StringBuilder rawValue = new StringBuilder();

            rawValue.Append(method.theMethod.CPPName);
            rawValue.Append("(");
            bool first      = true;
            var  dependents = Enumerable.Empty <IDeclaredParameter>();

            foreach (var arg in expr.Arguments.Zip(method.theMethod.Arguments, (m, a) => Tuple.Create(m, a)))
            {
                if (!first)
                {
                    rawValue.Append(",");
                }
                first = false;
                var e = ExpressionToCPP.InternalGetExpression(arg.Item1, gc, null, container);
                rawValue.AppendFormat("({0}){1}", arg.Item2.CPPType, e.RawValue);
                dependents = dependents.Concat(e.Dependants);
            }
            rawValue.Append(")");

            var result = new ValSimple(rawValue.ToString(), expr.Type, dependents);

            ///
            /// Include files
            ///

            foreach (var ifile in method.theMethod.IncludeFiles)
            {
                gc.AddIncludeFile(ifile);
            }

            ///
            /// We aren't re-writing this expression, so just return it.
            ///

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

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

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

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

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

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

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

            cacheResult = cacheResult && !bombIfNothing;

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

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

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

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

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

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

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

            gc.Add(rv);

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

            gc.Pop(true);

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

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

            var firstlastValue = cc.LoopVariable;

            cc.Add(indexExpr.RawValue, indexSeen);

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

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

            cc.SetLoopVariable(newIndexExpr, indexSeen);

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

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

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

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

                return(actualValue);
            }
            else
            {
                // No need to cache the result - so no need to add extra code.
                return(newIndexExpr);
            }
        }
示例#21
0
        /// <summary>
        /// Add the code to do the pair-wise loop.
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="_codeEnv"></param>
        /// <param name="_codeContext"></param>
        /// <param name="container"></param>
        public void ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container)
        {
            var ro = resultOperator as PairWiseAllResultOperator;

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

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

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

            gc.AddOutsideLoop(arrayRecord);

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

            gc.Add(recordIndexStatement);

            gc.Pop();

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

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

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

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

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

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

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

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

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

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

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

            gc.Add(loopOverGood);

            cc.SetLoopVariable(cc.LoopVariable.ReplaceSubExpression(cc.LoopIndexVariable.AsExpression(), goodIndex), goodIndex);
        }
示例#22
0
        /// <summary>
        /// Sort the current stream of the query. To do this we run through all the results, sort them,
        /// and then start a new loop.
        /// </summary>
        /// <param name="ordering"></param>
        /// <param name="queryModel"></param>
        /// <param name="orderByClause"></param>
        /// <param name="index"></param>
        public override void VisitOrdering(Ordering ordering, QueryModel queryModel, OrderByClause orderByClause, int index)
        {
            //
            // Only number types can be sorted.
            //

            if (!ordering.Expression.Type.IsNumberType())
            {
                throw new InvalidOperationException(string.Format("Don't know how to sort query by type '{0}'.", ordering.Expression.Type.Name));
            }

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

            var mapRecord = DeclarableParameter.CreateDeclarableParameterMapExpression(ordering.Expression.Type, _codeContext.LoopIndexVariable.Type.MakeArrayType());

            _codeEnv.AddOutsideLoop(mapRecord);

            var savePairValues = new StatementRecordPairValues(mapRecord,
                                                               ExpressionToCPP.GetExpression(ordering.Expression, _codeEnv, _codeContext, MEFContainer),
                                                               ExpressionToCPP.GetExpression(_codeContext.LoopIndexVariable.AsExpression(), _codeEnv, _codeContext, MEFContainer));

            _codeEnv.Add(savePairValues);

            var otherSavers = _codeEnv.GetUsedQuerySourceVariables(savePairValues, _codeContext.LoopIndexVariable)
                              .Select(v =>
            {
                var mr = DeclarableParameter.CreateDeclarableParameterMapExpression(ordering.Expression.Type, v.Type.MakeArrayType());
                _codeEnv.AddOutsideLoop(mr);
                savePairValues.AddSaver(mr, v);
                return(Tuple.Create(v, mr));
            })
                              .ToArray();

            // Get back to the results level now, where we do the sorting!

            _codeEnv.PopToResultsLevel();

            //
            // Now, we need to sort and loop over the variables in the map. This is a bit of a messy
            // multi-line statement, and it is a compound statement.
            //

            var sortAndRunLoop = new StatementLoopOverSortedPairValue(mapRecord, ordering.OrderingDirection == OrderingDirection.Asc);

            _codeEnv.Add(sortAndRunLoop);

            var pindex = sortAndRunLoop.IndexVariable;
            var lv     = _codeContext.LoopIndexVariable.RawValue;

            _codeContext.Add(lv, pindex);

            foreach (var savers in otherSavers)
            {
                var newVarName = sortAndRunLoop.RestoreOtherSaver(savers.Item2);
                _codeContext.Add(savers.Item1.RawValue, newVarName);
            }

            _codeContext.SetLoopVariable(_codeContext.LoopVariable.ReplaceSubExpression(_codeContext.LoopIndexVariable.AsExpression(), pindex), pindex);
        }
示例#23
0
 /// <summary>
 /// Deal with a where clause - we need to create an if statement that deals with the expression we are testing!
 /// </summary>
 /// <param name="whereClause"></param>
 /// <param name="queryModel"></param>
 /// <param name="index"></param>
 public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index)
 {
     _codeEnv.Add(new Statements.StatementFilter(ExpressionToCPP.GetExpression(whereClause.Predicate, _codeEnv, _codeContext, MEFContainer)));
 }
示例#24
0
        /// <summary>
        /// Cache the result of a query model into a function.
        /// </summary>
        /// <param name="queryModel"></param>
        /// <param name="qmSource"></param>
        private void VisitQueryModelCache(QueryModel queryModel, IQMFunctionSource qmSource)
        {
            // If we already have the answer for this cache, then we should just re-call the routine.
            if (qmSource.StatementBlock != null)
            {
                Debug.WriteLine("Using previously cached QM result");
                GenerateQMFunctionCall(qmSource);
                return;
            }
            Debug.WriteLine("Cache: Gathering Data");
            Debug.Indent();

            // Since we don't have it cached, we need to re-run things, and carefully watch for
            // everything new that shows up. What shows up will be what we declare as the function
            // body.
            var currentScope      = _codeEnv.CurrentScope;
            var topLevelStatement = new StatementInlineBlock();

            _codeEnv.Add(topLevelStatement);
            _codeEnv.SetCurrentScopeAsResultScope();

            // If this variable has been cached, then return it. Otherwise, mark the cache as filled.
            _codeEnv.Add(new StatementFilter(qmSource.CacheVariableGood));
            _codeEnv.Add(new StatementReturn(qmSource.CacheVariable));
            _codeEnv.Pop();
            _codeEnv.Add(new StatementAssign(qmSource.CacheVariableGood, new ValSimple("true", typeof(bool), null)));

            // Now, run the code to process the query model!

            VisitQueryModelNoCache(queryModel);

            // The result is treated differently depending on it being a sequence or a single value.
            if (qmSource.IsSequence)
            {
                // Push the good values into our cache object.
                if (!(_codeContext.LoopIndexVariable is IDeclaredParameter))
                {
                    throw new InvalidOperationException("Can't deal with anythign that isn't a loop var");
                }
                _codeEnv.Add(new StatementRecordIndicies(ExpressionToCPP.GetExpression(_codeContext.LoopVariable, _codeEnv, _codeContext, MEFContainer), qmSource.CacheVariable));

                // Remember what the loop index variable is, as we are going to need it when we generate the return function!
                qmSource.SequenceVariable(_codeContext.LoopIndexVariable, _codeContext.LoopVariable);
            }
            else
            {
                // This is a specific result. Save just the result and return it.
                // Grab the result, cache it, and return it.
                var rtnExpr = ExpressionToCPP.GetExpression(_codeEnv.ResultValue, _codeEnv, _codeContext, MEFContainer);
                topLevelStatement.Add(new StatementAssign(qmSource.CacheVariable, rtnExpr));

                // If the return is a declared parameter, then it must be actually defined somewhere (we normally don't).
                var declParam = _codeEnv.ResultValue as IDeclaredParameter;
                if (declParam != null)
                {
                    topLevelStatement.Add(declParam, false);
                }
            }

            // Always return the proper value...
            topLevelStatement.Add(new StatementReturn(qmSource.CacheVariable));

            // Now extract the block of code and put it in the function block.
            _codeEnv.CurrentScope = currentScope;
            qmSource.SetCodeBody(topLevelStatement);

            // Reset our state and remove the function code. And put in the function call in its place.
            _codeEnv.Remove(topLevelStatement);
            GenerateQMFunctionCall(qmSource);

            Debug.Unindent();
        }