/// <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);
        }
예제 #3
0
        /// <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));
        }
예제 #4
0
        /// <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!
            /// 
        }
예제 #5
0
        /// <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);
        }
예제 #6
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;
        }
예제 #7
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);
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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));
        }
예제 #10
0
        /// <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));
            }
예제 #12
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);
        }
예제 #13
0
        /// <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);
        }
예제 #14
0
        /// <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);
        }
예제 #15
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!");
            }
        }
예제 #16
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);
        }
예제 #17
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));
        }
예제 #18
0
        /// <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));
        }
예제 #19
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);
        }
예제 #20
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;
        }
예제 #21
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);
        }
예제 #22
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");

            // 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;
        }
예제 #23
0
            /// <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);
            }
예제 #24
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);
        }
예제 #25
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;
        }
예제 #26
0
        /// <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!
            ///
        }
예제 #27
0
        /// <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);
        }
예제 #28
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);
        }
예제 #29
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;
        }
예제 #30
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;
            }
        }
예제 #31
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();
        }
예제 #32
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);
            }
        }
예제 #33
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!");
            }
        }
예제 #34
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);
        }
            /// <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);
            }
예제 #36
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);
        }
예제 #37
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;
        }
예제 #38
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");

            //
            // 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;
        }
예제 #39
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);
        }