示例#1
0
        /// <summary>
        /// Return all statements after this one
        /// </summary>
        /// <param name="statement"></param>
        /// <returns></returns>
        internal static IEnumerable <IStatement> AllStatementsAfter(this IStatement statement)
        {
            var parent = statement.FindCompoundParent();

            if (parent != null)
            {
                var statements = parent.Statements.SkipWhile(s => s != statement).Skip(1);

                foreach (var s in statements)
                {
                    yield return(s);
                }

                // Now, go up one.
                foreach (var s in statement.Parent.AllStatementsAfter())
                {
                    yield return(s);
                }
            }
        }
示例#2
0
        /// <summary>
        /// Return all statements before this one. we march to the start of each block, and then up, and then all ones previous to that. Each time we go
        /// up a level we ignore that one.
        /// </summary>
        /// <param name="statement"></param>
        /// <returns></returns>
        internal static IEnumerable <IStatement> AllStatementsPrevious(this IStatement statement)
        {
            var parent = statement.FindCompoundParent();

            if (parent != null)
            {
                var statements = parent.Statements.TakeWhile(s => s != statement).Reverse();

                foreach (var s in statements)
                {
                    yield return(s);
                }

                // Now, go up one.
                foreach (var s in statement.Parent.AllStatementsPrevious())
                {
                    yield return(s);
                }
            }
        }
        /// <summary>
        /// sToPop is a member of block, and can be or is the first statement. We see if we can pop it up
        /// a level, and then start a scan for an equivalent statement, marching up the list. If we
        /// find an equivalent statement, we will perform the combination and removal.
        /// </summary>
        /// <param name="block">The block of statements that holds sToPop</param>
        /// <param name="sToPop">The statement to try to up level.</param>
        /// <param name="previousStatements">Statements before this one in this block. This block might not actually contain this statement, in which case we must have this!</param>
        /// <param name="followingStatements">Statements after this one in this block. This block might not actually contain this statement, in which case we must have this!</param>
        /// <returns>True if something was done to the statement, false if we aborted for whatever reason.</returns>
        private static bool FindEquivalentAboveAndCombine(IStatementCompound block, IStatement sToPop,
            IEnumerable<IStatement> previousStatements = null,
            IEnumerable<IStatement> followingStatements = null,
            IStatement betweenStatement = null
            )
        {
            // Can we combine these guys? This is when one sits in the other.
            if (!(block is IStatementLoop) && block.TryCombineStatement(sToPop, new BlockRenamer(sToPop.Parent.FindBookingParent(), block.FindBookingParent())))
            {
                sToPop.FindCompoundParent().Remove(sToPop);
                return false;
            }


            // If we can't get data flow information about a statement, then we can't do anything.
            var sInfo = sToPop as ICMStatementInfo;
            if (sInfo == null)
            {
                return false;
            }

            // For this next step we need to fetch the list of statements above us. Either it has
            // been supplied to us, or we will have to generate it.
            if (previousStatements == null)
            {
                previousStatements = block.Statements.TakeWhile(s => s != sInfo).Reverse();
                followingStatements = block.Statements.SkipWhile(s => s != sInfo).Skip(1);
                betweenStatement = sToPop;
            }

            // Make sure we can get the statement to the top of the block. As we move it
            // forward, we want to also see if we can combine it in a straight-up way
            // with each statement as we go by it.
            bool madeItToTheFront = true;
            foreach (var prevStatement in previousStatements)
            {
                if (MakeStatmentsEquivalent(prevStatement, sToPop))
                {
                    return true;
                }
                if (!StatementCommutes(prevStatement, sToPop))
                {
                    madeItToTheFront = false;
                }
            }

            // Next, lets see if there isn't a statement *after* this one that we can combine it with. However,
            // to do this, we have to move the statements *after* forward previous to this one. So a little painful.
            // No need to do this if we working at the level of the statement: this ground will automatically be covered
            // later in the loop.
            if (betweenStatement != sToPop && betweenStatement is ICMCompoundStatementInfo)
            {
                foreach (var followStatement in followingStatements)
                {
                    if (followStatement is ICMStatementInfo)
                    {
                        // Can we commute this statement from where it is to before the statement we are working on?
                        if (StatementCommutes(followStatement, followingStatements.TakeWhile(f => f != followStatement).Reverse()))
                        {
                            // Next is the tricky part. We are now sitting one down from the block that contains
                            // the sToPop statement. Can we move it up above the block? If the statements are the same,
                            // then we know it is ok to move it pass all the contents of the block (otherwise we would not be here).
                            // But what if it is an if statement, and the if statement depends on something in sToPop? Then
                            // we can't move it.
                            var betweenAsBlock = betweenStatement as ICMCompoundStatementInfo;
                            if (betweenAsBlock.CommutesWithGatingExpressions(followStatement as ICMStatementInfo))
                            {
                                if (MakeStatmentsEquivalent(followStatement, sToPop))
                                {
                                    // To keep continuity and unitarity, this follow statement now has to be moved before the betweenStatement!
                                    var parent = followStatement.Parent as IStatementCompound;
                                    parent.Remove(followStatement);
                                    parent.AddBefore(followStatement, betweenStatement);
                                    return true;
                                }
                            }
                        }
                    }
                }
            }

            // Now the only option left is to pop it up one level. We can do that only if we were able to
            // shift the statement all the way to the front.
            if (!madeItToTheFront)
            {
                return false;
            }

            // The statement can be moved to the top of the block, and isn't the same as
            // anything else we passed. Can we pull it out one level?
            // The key to answering this is: are all the variables it needs defined at the next
            // level up? And if not, are the missing ones simply declared down here and need to be moved up?
            var nParent = block.Parent.FindBookingParent();
            if (nParent == null)
            {
                return false;
            }

            var sDependent = sInfo.DependentVariables;
            var availAtParent = nParent.AllDeclaredVariables.Select(n => n.RawValue).Intersect(sDependent);
            IEnumerable<string> declaredInBlock = Enumerable.Empty<string>();
            if (block is IBookingStatementBlock)
            {
                declaredInBlock = (block as IBookingStatementBlock).DeclaredVariables.Select(np => np.RawValue).Intersect(sDependent);
            }
            if ((availAtParent.Count() + declaredInBlock.Count()) != sDependent.Count())
            {
                return false;
            }

            // If this there is a variable declared in the block internally, then we can't lift it up and out.
            // Also make sure that we can lift it past if there is a gating expression.
            if (block is ICMCompoundStatementInfo)
            {
                var compoundInfo = block as ICMCompoundStatementInfo;
                if (compoundInfo.InternalResultVarialbes.Select(p => p.RawValue).Intersect(sDependent).Any())
                {
                    return false;
                }
                if (!compoundInfo.CommutesWithGatingExpressions(sInfo))
                {
                    return false;
                }
            }

            // If we are going to try to lift past a loop, we have to make sure the statement is idempotent.
            if (block is IStatementLoop && !StatementIdempotent(sToPop))
            {
                return false;
            }

            // And the next figure out where we are in the list of statements.
            var nPrevStatements = nParent.Statements.TakeWhile(ps => ps != block).Reverse();
            var nFollowStatements = nParent.Statements.SkipWhile(ps => ps != block).Skip(1);

            // And repeat one level up with some tail recursion!
            var statementMoved = FindEquivalentAboveAndCombine(nParent, sToPop, nPrevStatements, nFollowStatements, block);

            // There is one other thing to try. If we couldn't move it above us (e.g. statementMoved is false), it could be
            // we can leave the statement here, rather than in its original location.
            if (!statementMoved)
            {
                var parentsToBlock = sToPop
                    .WalkParents(false)
                    .TakeWhile(s => s != block)
                    .Concat(new IStatementCompound[] { block }).ToArray();

                // If no lifting out of some statement between us and the statement, then don't do it.
                var parentsNotOK = parentsToBlock
                    .Where(s => s is ICMCompoundStatementInfo)
                    .Cast<ICMCompoundStatementInfo>()
                    .Where(s => !s.AllowNormalBubbleUp);
                if (parentsNotOK.Any())
                {
                    return false;
                }

                // The next thing we have to double check is that we can do the lifting, and nothing we are going to
                // lift is going to impact some variable.
                var dependents = sInfo.DependentVariables;
                var dependentAffected = parentsToBlock
                    .Select(p => p.CheckForVariableAsInternalResult(dependents))
                    .Where(t => t);
                if (dependentAffected.Any())
                {
                    return false;
                }

                return MoveStatement(sToPop, block);
            }

            return statementMoved;
        }
        /// <summary>
        /// sToPop is a member of block, and can be or is the first statement. We see if we can pop it up
        /// a level, and then start a scan for an equivalent statement, marching up the list. If we
        /// find an equivalent statement, we will perform the combination and removal.
        /// </summary>
        /// <param name="block">The block of statements that holds sToPop</param>
        /// <param name="sToPop">The statement to try to up level.</param>
        /// <param name="previousStatements">Statements before this one in this block. This block might not actually contain this statement, in which case we must have this!</param>
        /// <param name="followingStatements">Statements after this one in this block. This block might not actually contain this statement, in which case we must have this!</param>
        /// <returns>True if something was done to the statement, false if we aborted for whatever reason.</returns>
        private static bool FindEquivalentAboveAndCombine(IStatementCompound block, IStatement sToPop,
                                                          IEnumerable <IStatement> previousStatements  = null,
                                                          IEnumerable <IStatement> followingStatements = null,
                                                          IStatement betweenStatement = null
                                                          )
        {
            // Can we combine these guys? This is when one sits in the other.
            if (!(block is IStatementLoop) && block.TryCombineStatement(sToPop, new BlockRenamer(sToPop.Parent.FindBookingParent(), block.FindBookingParent())))
            {
                sToPop.FindCompoundParent().Remove(sToPop);
                return(false);
            }


            // If we can't get data flow information about a statement, then we can't do anything.
            var sInfo = sToPop as ICMStatementInfo;

            if (sInfo == null)
            {
                return(false);
            }

            // For this next step we need to fetch the list of statements above us. Either it has
            // been supplied to us, or we will have to generate it.
            if (previousStatements == null)
            {
                previousStatements  = block.Statements.TakeWhile(s => s != sInfo).Reverse();
                followingStatements = block.Statements.SkipWhile(s => s != sInfo).Skip(1);
                betweenStatement    = sToPop;
            }

            // Make sure we can get the statement to the top of the block. As we move it
            // forward, we want to also see if we can combine it in a straight-up way
            // with each statement as we go by it.
            bool madeItToTheFront = true;

            foreach (var prevStatement in previousStatements)
            {
                if (MakeStatmentsEquivalent(prevStatement, sToPop))
                {
                    return(true);
                }
                if (!StatementCommutes(prevStatement, sToPop))
                {
                    madeItToTheFront = false;
                }
            }

            // Next, lets see if there isn't a statement *after* this one that we can combine it with. However,
            // to do this, we have to move the statements *after* forward previous to this one. So a little painful.
            // No need to do this if we working at the level of the statement: this ground will automatically be covered
            // later in the loop.
            if (betweenStatement != sToPop && betweenStatement is ICMCompoundStatementInfo)
            {
                foreach (var followStatement in followingStatements)
                {
                    if (followStatement is ICMStatementInfo)
                    {
                        // Can we commute this statement from where it is to before the statement we are working on?
                        if (StatementCommutes(followStatement, followingStatements.TakeWhile(f => f != followStatement).Reverse()))
                        {
                            // Next is the tricky part. We are now sitting one down from the block that contains
                            // the sToPop statement. Can we move it up above the block? If the statements are the same,
                            // then we know it is ok to move it pass all the contents of the block (otherwise we would not be here).
                            // But what if it is an if statement, and the if statement depends on something in sToPop? Then
                            // we can't move it.
                            var betweenAsBlock = betweenStatement as ICMCompoundStatementInfo;
                            if (betweenAsBlock.CommutesWithGatingExpressions(followStatement as ICMStatementInfo))
                            {
                                if (MakeStatmentsEquivalent(followStatement, sToPop))
                                {
                                    // To keep continuity and unitarity, this follow statement now has to be moved before the betweenStatement!
                                    var parent = followStatement.Parent as IStatementCompound;
                                    parent.Remove(followStatement);
                                    parent.AddBefore(followStatement, betweenStatement);
                                    return(true);
                                }
                            }
                        }
                    }
                }
            }

            // Now the only option left is to pop it up one level. We can do that only if we were able to
            // shift the statement all the way to the front.
            if (!madeItToTheFront)
            {
                return(false);
            }

            // The statement can be moved to the top of the block, and isn't the same as
            // anything else we passed. Can we pull it out one level?
            // The key to answering this is: are all the variables it needs defined at the next
            // level up? And if not, are the missing ones simply declared down here and need to be moved up?
            var nParent = block.Parent.FindBookingParent();

            if (nParent == null)
            {
                return(false);
            }

            var sDependent    = sInfo.DependentVariables;
            var availAtParent = nParent.AllDeclaredVariables.Select(n => n.RawValue).Intersect(sDependent);
            IEnumerable <string> declaredInBlock = Enumerable.Empty <string>();

            if (block is IBookingStatementBlock)
            {
                declaredInBlock = (block as IBookingStatementBlock).DeclaredVariables.Select(np => np.RawValue).Intersect(sDependent);
            }
            if ((availAtParent.Count() + declaredInBlock.Count()) != sDependent.Count())
            {
                return(false);
            }

            // If this there is a variable declared in the block internally, then we can't lift it up and out.
            // Also make sure that we can lift it past if there is a gating expression.
            if (block is ICMCompoundStatementInfo)
            {
                var compoundInfo = block as ICMCompoundStatementInfo;
                if (compoundInfo.InternalResultVarialbes.Select(p => p.RawValue).Intersect(sDependent).Any())
                {
                    return(false);
                }
                if (!compoundInfo.CommutesWithGatingExpressions(sInfo))
                {
                    return(false);
                }
            }

            // If we are going to try to lift past a loop, we have to make sure the statement is idempotent.
            if (block is IStatementLoop && !StatementIdempotent(sToPop))
            {
                return(false);
            }

            // And the next figure out where we are in the list of statements.
            var nPrevStatements   = nParent.Statements.TakeWhile(ps => ps != block).Reverse();
            var nFollowStatements = nParent.Statements.SkipWhile(ps => ps != block).Skip(1);

            // And repeat one level up with some tail recursion!
            var statementMoved = FindEquivalentAboveAndCombine(nParent, sToPop, nPrevStatements, nFollowStatements, block);

            // There is one other thing to try. If we couldn't move it above us (e.g. statementMoved is false), it could be
            // we can leave the statement here, rather than in its original location.
            if (!statementMoved)
            {
                var parentsToBlock = sToPop
                                     .WalkParents(false)
                                     .TakeWhile(s => s != block)
                                     .Concat(new IStatementCompound[] { block }).ToArray();

                // If no lifting out of some statement between us and the statement, then don't do it.
                var parentsNotOK = parentsToBlock
                                   .Where(s => s is ICMCompoundStatementInfo)
                                   .Cast <ICMCompoundStatementInfo>()
                                   .Where(s => !s.AllowNormalBubbleUp);
                if (parentsNotOK.Any())
                {
                    return(false);
                }

                // The next thing we have to double check is that we can do the lifting, and nothing we are going to
                // lift is going to impact some variable.
                var dependents        = sInfo.DependentVariables;
                var dependentAffected = parentsToBlock
                                        .Select(p => p.CheckForVariableAsInternalResult(dependents))
                                        .Where(t => t);
                if (dependentAffected.Any())
                {
                    return(false);
                }

                return(MoveStatement(sToPop, block));
            }

            return(statementMoved);
        }