using System; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.Problems.ProgramSynthesis { /// /// An iteration instruction that executes the top item on the CODE stack a number of times that depends on the /// top two integers, while also pushing the loop counter onto the INTEGER stack for possible access during the /// execution of the body of the loop. The top integer is the "destination index" and the second integer is the "current index." /// Last the code and the integer arguments are saved locally and popped. Then the integers are compared. If the integers are equal /// then the current index is pushed onto the INTEGER stack and the code (which is the "body" of the loop) is pushed onto the EXEC stack /// for subsequent execution. If the integers are not equal then the current index will still be pushed onto the INTEGER stack but two /// items will be pushed onto the EXEC stack -- first a recursive call to CODE.DO*RANGE (with the same code and destination index, /// but with a current index that has been either incremented or decremented by 1 to be closer to the destination index) and then the body /// code. Note that the range is inclusive of both endpoints; a call with integer arguments 3 and 5 will cause its body to be executed /// 3 times, with the loop counter having the values 3, 4, and 5. Note also that one can specify a loop that "counts down" by providing a /// destination index that is less than the specified current index. /// [Serializable] [StorableClass] [PushExpression( StackTypes.Code, "CODE.DO*RANGE", "An iteration instruction that executes the top item on the CODE stack a number of times that depends on the top two integers, while also pushing the loop counter onto the INTEGER stack for possible access during the execution of the body of the loop.", StackTypes.Integer)] public class CodeDoRangeExpression : StatelessExpression { public CodeDoRangeExpression() { } [StorableConstructor] protected CodeDoRangeExpression(bool deserializing) : base(deserializing) { } public override bool IsNoop(IInternalPushInterpreter interpreter) { return interpreter.IntegerStack.Count < 2 || interpreter.CodeStack.IsEmpty; } public override void Eval(IInternalPushInterpreter interpreter) { var toDo = interpreter.CodeStack.Pop(); var currentIndex = interpreter.IntegerStack[1]; var destinationIndex = interpreter.IntegerStack[0]; interpreter.IntegerStack.Pop(); if (currentIndex != destinationIndex) { var incrementor = currentIndex < destinationIndex ? 1 : -1; var continuation = interpreter.PoolContainer.ExpressionListPool.Get(); var pool = interpreter.PoolContainer.GetStatefulExpressionPool(); continuation.Add(this); continuation.Add(toDo); continuation.Add(ExpressionTable.GetStatelessExpression()); continuation.Add(IntegerPushExpression.Create(pool, destinationIndex)); continuation.Add(IntegerPushExpression.Create(pool, currentIndex + incrementor)); var program = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, continuation); interpreter.ExecStack.Push(program); } interpreter.ExecStack.Push(toDo); } } /// /// An iteration instruction that executes the top item on the EXEC stack a number of times that depends on the top two /// integers, while also pushing the loop counter onto the INTEGER stack for possible access during the execution of the body of /// the loop. This is similar to CODE.DO*COUNT except that it takes its code argument from the EXEC stack. The top integer is the /// "destination index" and the second integer is the "current index." Last the code and the integer arguments are /// saved locally and popped. Then the integers are compared. If the integers are equal then the current index is pushed onto the /// INTEGER stack and the code (which is the "body" of the loop) is pushed onto the EXEC stack for subsequent execution. If the /// integers are not equal then the current index will still be pushed onto the INTEGER stack but two items will be pushed onto the EXEC /// stack -- first a recursive call to EXEC.DO*RANGE (with the same code and destination index, but with a current index that /// has been either incremented or decremented by 1 to be closer to the destination index) and then the body code. Note that the /// range is inclusive of both endpoints; a call with integer arguments 3 and 5 will cause its body to be executed 3 times, with /// the loop counter having the values 3, 4, and 5. Note also that one can specify a loop that "counts down" by providing a /// destination index that is less than the specified current index. /// [Serializable] [StorableClass] [PushExpression( stackType: StackTypes.Exec, name: "EXEC.DO*RANGE", description: "An iteration instruction that executes the top item on the EXEC stack a number of times that depends on the top two integers, while also pushing the loop counter onto the INTEGER stack for possible access during the execution of the body of the loop.", additionalStackDependencies: StackTypes.Integer, requiredBlockCount: 1)] public class ExecDoRangeExpression : StatelessExpression { public ExecDoRangeExpression() { } [StorableConstructor] protected ExecDoRangeExpression(bool deserializing) : base(deserializing) { } public override bool IsNoop(IInternalPushInterpreter interpreter) { return interpreter.IntegerStack.Count < 2 || interpreter.ExecStack.IsEmpty; } public override void Eval(IInternalPushInterpreter interpreter) { var currentIndex = interpreter.IntegerStack[1]; var destinationIndex = interpreter.IntegerStack[0]; interpreter.IntegerStack.Pop(); if (currentIndex == destinationIndex) return; var toDo = interpreter.ExecStack.Top; var incrementor = currentIndex < destinationIndex ? 1 : -1; var continuation = interpreter.PoolContainer.ExpressionListPool.Get(); var pool = interpreter.PoolContainer.GetStatefulExpressionPool(); continuation.Add(toDo); continuation.Add(this); continuation.Add(IntegerPushExpression.Create(pool, destinationIndex)); continuation.Add(IntegerPushExpression.Create(pool, currentIndex + incrementor)); interpreter.ExecStack.Top = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, continuation); interpreter.ExecStack.Push(toDo); } } }