Browse Source

basisschleifen fertig

Kolja Strohm 6 years ago
parent
commit
371aababb1

+ 9 - 10
src/animation/Memory.java

@@ -1,6 +1,5 @@
 package animation;
 
-import java.util.Iterator;
 import java.util.Stack;
 
 import animation.StackFrame.FrameType;
@@ -44,9 +43,9 @@ public class Memory {
             this.global.set( name, value );
         else
         {
-        	Iterator<StackFrame> iterator = stack.iterator();
-        	while (iterator.hasNext()) {
-        	   StackFrame stackF = iterator.next();
+        	int index = stack.size() - 1;
+        	while( index >= 0 ) {
+        	   StackFrame stackF = stack.get( index-- );
         	   if( stackF.isDefined( name ) )
         	   {
         		   stackF.set( name, value );
@@ -64,9 +63,9 @@ public class Memory {
             return this.global.get( name );
         else
         {
-        	Iterator<StackFrame> iterator = stack.iterator();
-        	while (iterator.hasNext()) {
-        	   StackFrame stackF = iterator.next();
+        	int index = stack.size() - 1;
+        	while( index >= 0 ) {
+        	   StackFrame stackF = stack.get( index-- );
         	   if( stackF.isDefined( name ) )
         		   return stackF.get( name );
         	   if( stackF.getType() == FrameType.FUNCTION )
@@ -80,9 +79,9 @@ public class Memory {
     {
     	if( global )
     		return this.global.isDefined( name );
-    	Iterator<StackFrame> iterator = stack.iterator();
-    	while (iterator.hasNext()) {
-    	   StackFrame stackF = iterator.next();
+    	int index = stack.size() - 1;
+    	while( index >= 0 ) {
+    	   StackFrame stackF = stack.get( index-- );
     	   if( stackF.isDefined( name ) )
     		   return true;
     	   if( stackF.getType() == FrameType.FUNCTION )

+ 68 - 58
src/animation/PseudoCodeNode.java

@@ -229,69 +229,79 @@ public class PseudoCodeNode extends DefaultMutableTreeNode {
     {
         if( currentCodeLine == -1 )
         {
-            ControlFlow cf = code.runForward( m );
-            switch( cf.getStatus() )
-            {
-            case ControlFlow.STEP_INTO:
-            	function = true;
-            	StackFrame tmp = m.removeFrame();
-            	if( children != null )
-            	{
-            		for( Object c : children )
-            			((PseudoCodeNode)c).writeToStack( m );
-            	}
-            	m.addFrame( tmp );
-                switch( stepInto( m ) )
-                {
-				case BREAKPOINT:
-					return CodeStatus.BREAKPOINT;
-				case FINISHED:
-					currentCodeLine = -1;
-                    switch( setSelected( true ) )
-                    {
-					case SKIP:
-						return forwardStepOverIntern( m );
-					case STOP:
-						return CodeStatus.BREAKPOINT;
-					default:
-						break;
-                    }
-				case UNFINISHED:
-					return CodeStatus.UNFINISHED;
-                }
-            case ControlFlow.STEP_OVER:
-            	if( function )
-            	{
-                	if( children != null )
-                	{
-                		for( Object c : children )
-                			((PseudoCodeNode)c).loadFromStack( m );
-                	}
-            	}
-                return CodeStatus.FINISHED;
-            case ControlFlow.CALL:
-    			alg.addActiveFunction( cf.getFunction() );
-    			setSelected( false );
-    			m.declare( "callback", this, true );
-    			switch( cf.getFunction().setSelected( true ) )
-    			{
-				case CONTINUE:
-					break;
-				case SKIP:
-					switch( cf.getFunction().forwardStepOverIntern( m ) )
-        			{
+			if( !m.isDefined( "line_" + nodeId + "_call", false ) )
+			{
+	            ControlFlow cf = code.runForward( m );
+	            switch( cf.getStatus() )
+	            {
+	            case ControlFlow.STEP_INTO:
+	            	function = true;
+	            	StackFrame tmp = m.removeFrame();
+	            	if( children != null )
+	            	{
+	            		for( Object c : children )
+	            			((PseudoCodeNode)c).writeToStack( m );
+	            	}
+	            	m.addFrame( tmp );
+	                switch( stepInto( m ) )
+	                {
 					case BREAKPOINT:
 						return CodeStatus.BREAKPOINT;
 					case FINISHED:
+						currentCodeLine = -1;
+	                    switch( setSelected( true ) )
+	                    {
+						case SKIP:
+							return forwardStepOverIntern( m );
+						case STOP:
+							return CodeStatus.BREAKPOINT;
+						default:
+							break;
+	                    }
 					case UNFINISHED:
 						return CodeStatus.UNFINISHED;
-        			}
-					break;
-				case STOP:
-					return CodeStatus.BREAKPOINT;
-    			}
-    			return CodeStatus.UNFINISHED;
-            }
+	                }
+	            case ControlFlow.STEP_OVER:
+	            	if( function )
+	            	{
+	                	if( children != null )
+	                	{
+	                		for( Object c : children )
+	                			((PseudoCodeNode)c).loadFromStack( m );
+	                	}
+	            	}
+	                return CodeStatus.FINISHED;
+	            case ControlFlow.CALL:
+	    			alg.addActiveFunction( cf.getFunction() );
+	    			m.declare( "line_" + nodeId + "_call", true, false );
+	    			setSelected( false );
+	    			m.declare( "callback", this, true );
+	    			switch( cf.getFunction().setSelected( true ) )
+	    			{
+					case CONTINUE:
+						break;
+					case SKIP:
+						switch( cf.getFunction().forwardStepOverIntern( m ) )
+	        			{
+						case BREAKPOINT:
+							return CodeStatus.BREAKPOINT;
+						case FINISHED:
+							setSelected( true );
+						case UNFINISHED:
+							return CodeStatus.UNFINISHED;
+	        			}
+						break;
+					case STOP:
+						return CodeStatus.BREAKPOINT;
+	    			}
+	    			return CodeStatus.UNFINISHED;
+	            }
+			}
+			else
+			{
+				m.undeclare( "line_" + nodeId + "_call", false );
+				return CodeStatus.FINISHED;
+			}
         }
         else
         {

+ 7 - 43
src/bk/BKNodePlacement.java

@@ -11,6 +11,7 @@ import animation.Memory;
 import animation.PseudoCodeNode;
 import animation.StackFrame;
 import animation.StackFrame.FrameType;
+import codelines.FunctionCall;
 import codelines.FunctionDefinition;
 import graph.LayeredGraphNode;
 import lib.TextLayoutHelper;
@@ -44,7 +45,7 @@ public class BKNodePlacement extends AnimatedAlgorithm {
     public BKNodePlacement(AnimationController controller, LayeredGraphNode graph, JFrame view) {
         super(controller, graph, view);
         state = State.CONFLICTS;
-        conftion = new ConflictDetection( graph, this );
+        conftion = new ConflictDetection( this );
         layouts = new ExtremalLayoutCalc[ 4 ];
         layouts[ 0 ] = new ExtremalLayoutCalc( graph, this );
         layouts[ 1 ] = new ExtremalLayoutCalc( graph, this );
@@ -67,55 +68,18 @@ public class BKNodePlacement extends AnimatedAlgorithm {
 
 			@Override
 			public ControlFlow runForward(Memory m) {
-				if( !m.isDefined( "line_" + lineId + "_call", false ) )
-				{
-					m.declare( "line_" + lineId + "_call", true, false );
-					m.declare( "param1", graph, true );
-					actions.push( (Memory mem) -> {
-						mem.undeclare( "param1", true );
-						mem.undeclare( "line_" + lineId + "_call", false );
-						return new ControlFlow( ControlFlow.STEP_OVER );
-					} );
+				m.declare( "param1", graph, true );
+				actions.push( (Memory mem) -> {
 					return new ControlFlow( mainFunction );
-				}
-				else
-				{
-					actions.push( (Memory mem) -> {
-						return new ControlFlow( mainFunction );
-					} );
-					return new ControlFlow( ControlFlow.STEP_OVER );
-				}
+				} );
+				return new ControlFlow( mainFunction );
 			}
         	
         }, this );
         root.setSelected( true );
     	
         PseudoCodeNode conflictDetectionFunction = conftion.createPseudocodeTree( tree );
-        PseudoCodeNode node1 = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call detectConflicts( graph )", vars ), tree, new CodeLine() {
-
-			@Override
-			public ControlFlow runForward(Memory m) {
-				if( !m.isDefined( "line_" + lineId + "_call", false ) )
-				{
-					m.declare( "line_" + lineId + "_call", true, false );
-					m.declare( "param1", m.read( "graph", false ), true );
-					actions.push( (Memory mem) -> {
-						mem.undeclare( "param1", true );
-						mem.undeclare( "line_" + lineId + "_call", false );
-						return new ControlFlow( ControlFlow.STEP_OVER );
-					} );
-					return new ControlFlow( conflictDetectionFunction );
-				}
-				else
-				{
-					actions.push( (Memory mem) -> {
-						return new ControlFlow( conflictDetectionFunction );
-					} );
-					return new ControlFlow( ControlFlow.STEP_OVER );
-				}
-			}
-        	
-        }, this );
+        PseudoCodeNode node1 = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call detectConflicts( graph )", vars ), tree, new FunctionCall( conflictDetectionFunction, new String[]{ "graph" } ), this );
         root.add( mainFunction );
         mainFunction.add( node1 );
         root.add( conflictDetectionFunction );

+ 46 - 40
src/bk/ConflictDetection.java

@@ -1,7 +1,7 @@
 package bk;
 
 import java.util.ArrayList;
-import java.util.Stack;
+import java.util.List;
 
 import javax.swing.JTree;
 
@@ -11,12 +11,12 @@ import animation.CodeLine;
 import animation.ControlFlow;
 import animation.Memory;
 import animation.PseudoCodeNode;
-import animation.PseudoCodeNode.CodeAction;
-import animation.StackFrame;
-import animation.StackFrame.FrameType;
 import codelines.DeclareVariable;
+import codelines.ForEachLoop;
 import codelines.ForLoop;
 import codelines.FunctionDefinition;
+import codelines.IfLoop;
+import codelines.WhileLoop;
 import graph.LayeredGraphEdge;
 import graph.LayeredGraphNode;
 import lib.TextLayoutHelper;
@@ -29,11 +29,9 @@ import lib.TextLayoutHelper;
  */
 public class ConflictDetection implements AlgorithmStage {
 
-    private LayeredGraphNode graph;
     private AnimatedAlgorithm alg;
 
-    public ConflictDetection(LayeredGraphNode graph, AnimatedAlgorithm alg) {
-        this.graph = graph;
+    public ConflictDetection( AnimatedAlgorithm alg ) {
         this.alg = alg;
     }
 
@@ -80,44 +78,52 @@ public class ConflictDetection implements AlgorithmStage {
         outerLoop.add( line );
         PseudoCodeNode innerLoop = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "for l1=0 to |L[i+1]|-1 do", vars ), tree, new ForLoop( "l1", "0", "end_" ), alg );
         outerLoop.add( innerLoop );
-        PseudoCodeNode ifNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if l1==|L[i+1]|-1 or L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree, new CodeLine() {
+        PseudoCodeNode ifNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if l1==|L[i+1]|-1 or L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree, new IfLoop() {
 			@Override
-			public ControlFlow runForward(Memory m) {
-				if( m.isDefined( "line_" + lineId + "_inside", false ) )
-				{
-					StackFrame old = m.removeFrame();
-					actions.push( (Memory mem) -> {
-						mem.addFrame( old );
-		            	return new ControlFlow( ControlFlow.STEP_INTO );
-					} );
-	                return new ControlFlow( ControlFlow.STEP_OVER );
-				}
-				else
-				{
-		            if( m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1).size() == m.<Integer>read( "l1", false ) || 
-		            		incidentToInnerSegmentBetweenLiPlusOneAndLi( m ) ) {
-		            	m.addFrame( new StackFrame( FrameType.LOOP ) );
-		            	m.declare( "line_" + lineId + "_inside", true, false );
-		            	actions.push( (Memory mem) -> {
-		            		mem.removeFrame();
-		            		return new ControlFlow( ControlFlow.STEP_OVER );
-		            	} );
-		            	return new ControlFlow( ControlFlow.STEP_INTO );
-		            } else {
-		            	m.declare( "line_" + lineId + "_inside", false, false );
-		            	actions.push( (Memory mem) -> {
-		            		mem.undeclare( "line_" + lineId + "_inside", false );
-			                return new ControlFlow( ControlFlow.STEP_OVER );
-		            	} );
-		                return new ControlFlow( ControlFlow.STEP_OVER );
-		            }
-				}
+			protected boolean condition(Memory m) {
+	            return m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1).size() == m.<Integer>read( "l1", false ) || 
+	            		incidentToInnerSegmentBetweenLiPlusOneAndLi( m );
 			}
         }, alg );
         innerLoop.add( ifNode );
+        line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = |L[i]|-1;", vars ), tree, new DeclareVariable<Integer>( "k1" ) {
+			@Override
+			protected Integer value(Memory m) {
+				return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", false ).get( m.read( "i", false ) ).size() - 1;
+			}
+        }, alg );
+        ifNode.add( line );
+        PseudoCodeNode innerIfNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree, new IfLoop() {
+			@Override
+			protected boolean condition(Memory m) {
+				return incidentToInnerSegmentBetweenLiPlusOneAndLi( m );
+			}
+		}, alg );
+        ifNode.add( innerIfNode );
+        
+        line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = pos(pred(L[i+1][l1])[0]);", vars ), tree, new DeclareVariable<Integer>( "k1" ) {
+			@Override
+			protected Integer value(Memory m) {
+				return m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.read( "i", false ) ).indexOf(
+						m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1 ).get( m.read( "l1", false ) ).getSortedIncomingEdges().get( 0 ).getSources().get( 0 ) );
+			}
+        }, alg );
+        innerIfNode.add( line );
+        PseudoCodeNode whileLoop = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "while l <= l1 do", vars ), tree, new WhileLoop() {
+			@Override
+			protected boolean condition( Memory m ) {
+				return m.<Integer>read( "l", false ) <= m.<Integer>read( "l1", false );
+			}
+        }, alg );
+        ifNode.add( whileLoop );
+        PseudoCodeNode foreach = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "foreach v in pred(L[i+1][l]) do", vars ), tree, new ForEachLoop<LayeredGraphEdge>( "v" ) {
+			@Override
+			protected List<LayeredGraphEdge> list(Memory m) {
+				return m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1 ).get( m.read( "l", false ) ).getIncomingEdges();
+			}
+        }, alg );
+        whileLoop.add( foreach );
         /*
-        lines[ 4 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = |L[i]|-1;", vars ), tree );
-        lines[ 3 ].add( lines[ 4 ] );
         lines[ 5 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree );
         lines[ 3 ].add( lines[ 5 ] );
         lines[ 6 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = pos(pred(L[i+1][l1])[0]);", vars ), tree );

+ 5 - 5
src/codelines/DeclareVariable.java

@@ -4,15 +4,13 @@ import animation.CodeLine;
 import animation.ControlFlow;
 import animation.Memory;
 
-public class DeclareVariable <T> extends CodeLine {
+public abstract class DeclareVariable <T> extends CodeLine {
 
 	private boolean oldExists;
-	private T val;
 	private String name;
 	
-	public DeclareVariable( String name, T value )
+	public DeclareVariable( String name )
 	{
-		this.val = value;
 		this.name = name;
 	}
 
@@ -21,7 +19,7 @@ public class DeclareVariable <T> extends CodeLine {
 		
 		oldExists = m.isDefined( name, false );
 		T oldVal = m.read( name, false );
-		m.declare( name, val, false );
+		m.declare( name, value( m ), false );
 		actions.push( (Memory mem) -> {
 			if( !oldExists )
 				mem.undeclare( name, false );
@@ -31,4 +29,6 @@ public class DeclareVariable <T> extends CodeLine {
 		});
 		return new ControlFlow( ControlFlow.STEP_OVER );
 	}
+	
+	abstract protected T value( Memory m );
 }

+ 8 - 8
src/codelines/ForEachLoop.java

@@ -8,14 +8,12 @@ import animation.Memory;
 import animation.StackFrame;
 import animation.StackFrame.FrameType;
 
-public class ForEachLoop <T> extends CodeLine {
+public abstract class ForEachLoop <T> extends CodeLine {
 
-	String listVar;
 	String loopVar;
 	
-	public ForEachLoop( String listName, String varName )
+	public ForEachLoop( String varName )
 	{
-		this.listVar = listName;
 		this.loopVar = varName;
 	}
 	
@@ -27,7 +25,7 @@ public class ForEachLoop <T> extends CodeLine {
 			m.declare( "line_" + lineId + "_index", 0, false );
 			declared = true;
 		}
-		if( m.<Integer>read( "line_" + lineId + "_index", false ) > m.<List<T>>read( listVar, false ).size() - 1 ) // prove if the loop has finished
+		if( m.<Integer>read( "line_" + lineId + "_index", false ) > list( m ).size() - 1 ) // prove if the loop has finished
 		{
 			m.undeclare( "line_" + lineId + "_index", false );
 			actions.push( (Memory mem) -> {
@@ -38,7 +36,7 @@ public class ForEachLoop <T> extends CodeLine {
 		if( declared )
 		{
 			m.addFrame( new StackFrame( FrameType.LOOP ) );
-			m.declare( loopVar, m.<List<T>>read( listVar, false ).get( m.read( "line_" + lineId + "_index", false ) ), false ); // set loop variable
+			m.declare( loopVar, list( m ).get( m.read( "line_" + lineId + "_index", false ) ), false ); // set loop variable
 			actions.push( (Memory mem) -> {
 				mem.removeFrame();
 				mem.undeclare( "line_" + lineId + "_index", false );
@@ -49,7 +47,7 @@ public class ForEachLoop <T> extends CodeLine {
 		{
 			int oldIndex = m.<Integer>read( "line_" + lineId + "_index", false );
 			m.write( "line_" + lineId + "_index", oldIndex + 1, false ); // count index down
-			if( m.<Integer>read( "line_" + lineId + "_index", false ) > m.<List<T>>read( listVar, false ).size() - 1 ) // prove if loop was finished
+			if( m.<Integer>read( "line_" + lineId + "_index", false ) > list( m ).size() - 1 ) // prove if loop was finished
 			{
 				StackFrame sf = m.removeFrame(); // remove loop stack
 				m.undeclare( "line_" + lineId + "_index", false );
@@ -62,7 +60,7 @@ public class ForEachLoop <T> extends CodeLine {
 			}
 			StackFrame old = m.removeFrame(); // fresh stack frame for loop body
 			m.addFrame( new StackFrame( FrameType.LOOP ) );
-			m.write( loopVar, m.<List<T>>read( listVar, false ).get( m.read( "line_" + lineId + "_index", false ) ), false ); // update loop variable
+			m.write( loopVar, list( m ).get( m.read( "line_" + lineId + "_index", false ) ), false ); // update loop variable
 			actions.push( (Memory mem) -> {
 				mem.removeFrame();
 				mem.addFrame( old );
@@ -72,4 +70,6 @@ public class ForEachLoop <T> extends CodeLine {
 		}
 		return new ControlFlow( ControlFlow.STEP_INTO );
 	}
+	
+	abstract protected List<T> list( Memory m );
 }

+ 39 - 0
src/codelines/FunctionCall.java

@@ -0,0 +1,39 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.PseudoCodeNode;
+
+public class FunctionCall extends CodeLine {
+
+	private PseudoCodeNode target;
+	private String[] params;
+	
+	public FunctionCall( PseudoCodeNode function, String[] params )
+	{
+		this.target = function;
+		this.params = params;
+	}
+	
+	@Override
+	public ControlFlow runForward(Memory m) {
+		int index = 1;
+		for( String p : params )
+		{
+			m.declare( "param" + index, m.read( p, false ), true );
+			index++;
+		}
+		actions.push( (Memory mem) -> {
+			int ind = 1;
+			for( @SuppressWarnings("unused") String p : params )
+			{
+				m.undeclare( "param" + ind, true );
+				ind++;
+			}
+			return new ControlFlow( target );
+		} );
+		return new ControlFlow( target );
+	}
+
+}

+ 49 - 0
src/codelines/IfLoop.java

@@ -0,0 +1,49 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.StackFrame.FrameType;
+
+/**
+ * Yey, IF is a LOOP.
+ * @author kolja
+ *
+ */
+public abstract class IfLoop extends CodeLine {
+
+	@Override
+	public ControlFlow runForward(Memory m) {
+		if( m.isDefined( "line_" + lineId + "_inside", false ) )
+		{
+			StackFrame old = m.removeFrame();
+			actions.push( (Memory mem) -> {
+				mem.addFrame( old );
+            	return new ControlFlow( ControlFlow.STEP_INTO );
+			} );
+            return new ControlFlow( ControlFlow.STEP_OVER );
+		}
+		else
+		{
+            if( condition( m ) ) {
+            	m.addFrame( new StackFrame( FrameType.LOOP ) );
+            	m.declare( "line_" + lineId + "_inside", true, false );
+            	actions.push( (Memory mem) -> {
+            		mem.removeFrame();
+            		return new ControlFlow( ControlFlow.STEP_OVER );
+            	} );
+            	return new ControlFlow( ControlFlow.STEP_INTO );
+            } else {
+            	m.declare( "line_" + lineId + "_inside", false, false );
+            	actions.push( (Memory mem) -> {
+            		mem.undeclare( "line_" + lineId + "_inside", false );
+	                return new ControlFlow( ControlFlow.STEP_OVER );
+            	} );
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+		}
+	}
+
+	abstract protected boolean condition( Memory m );
+}

+ 61 - 0
src/codelines/WhileLoop.java

@@ -0,0 +1,61 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.StackFrame.FrameType;
+
+public abstract class WhileLoop extends CodeLine {
+
+	@Override
+	public ControlFlow runForward(Memory m) {
+		boolean declared = false; // prove if it is the first step in the loop
+		if( !m.isSomewhereDefined( "line_" + lineId + "_index", false ) )
+		{ // first loop step
+			m.declare( "line_" + lineId + "_index", 0, false );
+			declared = true;
+		}
+		if( !condition( m ) ) // prove if the loop has finished
+		{
+			m.undeclare( "line_" + lineId + "_index", false );
+			actions.push( (Memory mem) -> {
+				return new ControlFlow( ControlFlow.STEP_OVER ); // loop was not called so nothing to reverse
+			});
+			return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
+		}
+		if( declared )
+		{
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.undeclare( "line_" + lineId + "_index", false );
+				return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
+			} );
+		}
+		else
+		{
+			if( !condition( m ) ) // prove if loop was finished
+			{
+				StackFrame sf = m.removeFrame(); // remove loop stack
+				m.undeclare( "line_" + lineId + "_index", false );
+				actions.push( (Memory mem) -> {
+					mem.declare( "line_" + lineId + "_index", 0, false );
+					mem.addFrame( sf ); // restore last loop stack
+					return new ControlFlow( ControlFlow.STEP_INTO ); // step into the loop body
+				});
+				return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
+			}
+			StackFrame old = m.removeFrame(); // fresh stack frame for loop body
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.addFrame( old );
+				return new ControlFlow( ControlFlow.STEP_INTO );
+			});
+		}
+		return new ControlFlow( ControlFlow.STEP_INTO );
+	}
+
+	abstract protected boolean condition( Memory m );
+}