Skip to content

Commit 6a948b7

Browse files
committedApr 9, 2018
Separated some code into their own files and removed unused UI elements.
1 parent c69ebd0 commit 6a948b7

25 files changed

+29030
-775
lines changed
 

‎gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
junitVersion=4.12
2-
loggerVersion=1.0.13
2+
loggerVersion=1.0.13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package se.student.liu.jessp088.vm;
2+
3+
public interface DebugListener {
4+
/** Called before {@link VirtualMachine#execute(Bytecode)} or
5+
* {@link VirtualMachine#debug(Bytecode)} is called but after state has been set to
6+
* {@link VMState#EXECUTING} or {@link VMState#DEBUGGING}.
7+
*
8+
* @param vm
9+
* the virtual machine */
10+
void beforeExecution(VirtualMachine vm);
11+
12+
/** Called after {@link VirtualMachine#execute(Bytecode)} or
13+
* {@link VirtualMachine#debug(Bytecode)} is called and after state has been set to
14+
* {@link VMState#END_SUCCESS}, {@link VMState#END_ERROR}, or {@link VMState#END_USER}.
15+
*
16+
* @param vm
17+
* the virtual machine */
18+
void afterExecution(VirtualMachine vm);
19+
20+
/** Called before the virtual machine handles an instruction. Calling
21+
* {@link VirtualMachine#stopExecution()} in this method is guaranteed to stop the execution of
22+
* the virtual machine before the instruction is handled.
23+
*
24+
* @param vm
25+
* the virtual machine */
26+
void beforeInstruction(VirtualMachine vm);
27+
28+
/** Called after the virtual machine handles an instruction. If execution of the virtual machine
29+
* is paused or stopped this method is <b>not</b> guaranteed to be called before the next
30+
* {@link DebugListener#beforeExecution(VirtualMachine)} is called. This method is guaranteed to
31+
* be called even if the virtual machine gets a <b>non-critical</b> error during execution.
32+
*
33+
* @param vm
34+
* the virtual machine */
35+
void afterInstruction(VirtualMachine vm);
36+
37+
/** Called after the state of the virtual machine changes.
38+
*
39+
* @param vm
40+
* the virtual machine
41+
* @see VirtualMachine#getCurrentState()
42+
* @see VirtualMachine#getPreviousState() */
43+
void onStateChanged(VirtualMachine vm);
44+
}

‎src/main/java/se/student/liu/jessp088/vm/DefaultVirtualMachine.java

+28-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package se.student.liu.jessp088.vm;
22

3-
import static se.student.liu.jessp088.vm.VirtualMachine.VMState.*;
3+
import static se.student.liu.jessp088.vm.VMState.*;
44

55
import java.util.ArrayList;
66
import java.util.List;
@@ -11,9 +11,7 @@
1111
import se.student.liu.jessp088.vm.instructions.Instruction;
1212
import se.student.liu.jessp088.vm.instructions.InstructionException;
1313

14-
15-
public class DefaultVirtualMachine implements VirtualMachine
16-
{
14+
public class DefaultVirtualMachine implements VirtualMachine {
1715
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultVirtualMachine.class);
1816

1917
private final Stack stack;
@@ -39,14 +37,13 @@ public DefaultVirtualMachine(final int maxStackSize, final int maxVariableSize)
3937
previousState = state = END_SUCCESS;
4038
}
4139

42-
/**
43-
* {@inheritDoc}
40+
/** {@inheritDoc}
4441
* <p>
4542
* The {@link DefaultVirtualMachine} implementation is an <b>infinite loop</b> that can only be
4643
* broken by calling {@link DefaultVirtualMachine#stopExecution()} or if the code execution is
4744
* stopped either by error or by finishing successfully.
4845
* </p>
49-
*/
46+
*/
5047
@Override
5148
public void execute(final Bytecode code) throws IllegalStateException {
5249
if (!isStopped()) {
@@ -80,15 +77,17 @@ private void runInternal(final Bytecode code, final boolean debug) {
8077
}
8178
// catch listener pause or stop
8279
if (!isRunning()) {
83-
LOGGER.debug("Virtual machine was stopped by a listener before processing could begin.");
80+
LOGGER.debug(
81+
"Virtual machine was stopped by a listener before processing could begin.");
8482
return;
8583
}
8684

8785
while (currentCode.hasNext()) {
8886
if (wasAtBreakpoint) {
8987
// We don't want to break on the same line twice
9088
wasAtBreakpoint = false;
91-
LOGGER.trace("Skipping breakpoint on pointer {} (already executed).", currentCode.getPtr());
89+
LOGGER.trace("Skipping breakpoint on pointer {} (already executed).",
90+
currentCode.getPtr());
9291
} else if (debug && isBreakpointAt(currentCode.getPtr())) {
9392
wasAtBreakpoint = true;
9493
LOGGER.debug("Hit breakpoint at pointer {}.", currentCode.getPtr());
@@ -100,20 +99,22 @@ private void runInternal(final Bytecode code, final boolean debug) {
10099

101100
beforeInstruction();
102101
if (!isRunning()) {
103-
LOGGER.debug("Virtual machine stopped by listener before instruction could be processed.");
102+
LOGGER.debug(
103+
"Virtual machine stopped by listener before instruction could be processed.");
104104
// Revert to previous instruction or we will skip an instruction
105105
code.setPtr(code.getPtr() - 1);
106106
LOGGER.trace("Reverting instruction pointer.");
107107
return;
108108
}
109109

110110
try {
111-
LOGGER.debug("Processing instruction {}: {}.", currentCode.getPtr() - 1, currentInstruction);
111+
LOGGER.trace("Processing instruction {}: {}.", currentCode.getPtr() - 1,
112+
currentInstruction);
112113
currentInstruction.process(stack, code, variables);
113114
afterInstruction();
114115
} catch (final InstructionException e) {
115-
this.error = String
116-
.format("Error executing instruction %s on line %s: %s", currentInstruction, getCurrentLineNumber(), e);
116+
this.error = String.format("Error executing instruction %s on line %s: %s",
117+
currentInstruction, getCurrentLineNumber(), e);
117118
LOGGER.error(error);
118119

119120
currentCode.setPtr(0);
@@ -155,7 +156,8 @@ public void resumeExecution() {
155156
}
156157

157158
if (!wasRunning()) {
158-
LOGGER.error("Cannot resume from state {} when previous state was {}!", state, previousState);
159+
LOGGER.error("Cannot resume from state {} when previous state was {}!", state,
160+
previousState);
159161
final String format = "Cannot resume from state %s when previous state was %s!";
160162
throw new IllegalStateException(String.format(format, state, previousState));
161163
}
@@ -208,7 +210,8 @@ public void addBreakpoint(final int instructionPtr) {
208210
if (!isBreakpointAt(instructionPtr)) {
209211
LOGGER.trace("Adding breakpoint at {}", instructionPtr);
210212
breakpoints.add(instructionPtr);
211-
} else LOGGER.trace("Already a breakpoint at {}.", instructionPtr);
213+
} else
214+
LOGGER.trace("Already a breakpoint at {}.", instructionPtr);
212215
}
213216

214217
@Override
@@ -217,7 +220,13 @@ public void removeBreakpoint(final int instructionPtr) {
217220
LOGGER.trace("Removing breakpoint at {}", instructionPtr);
218221
// Must cast otherwise it uses the wrong remove() method
219222
breakpoints.remove((Integer) instructionPtr);
220-
} else LOGGER.trace("No breakpoint at {} to remove.", instructionPtr);
223+
} else
224+
LOGGER.trace("No breakpoint at {} to remove.", instructionPtr);
225+
}
226+
227+
@Override
228+
public void removeAllBreakpoints() {
229+
breakpoints.clear();
221230
}
222231

223232
@Override
@@ -311,7 +320,9 @@ private void onStateChanged() {
311320

312321
private void setState(final VMState state) {
313322
if (this.state == state) {
314-
LOGGER.trace("Attempt to set state to {} when virtual machine was already in that state!", state);
323+
LOGGER.trace(
324+
"Attempt to set state to {} when virtual machine was already in that state!",
325+
state);
315326
return;
316327
}
317328

‎src/main/java/se/student/liu/jessp088/vm/Stack.java

+47-62
Original file line numberDiff line numberDiff line change
@@ -3,109 +3,100 @@
33
import java.util.stream.Collectors;
44
import java.util.stream.IntStream;
55

6-
/**
7-
* A stack of integers.
6+
/** A stack of integers.
87
*
9-
* @author Charanor
10-
*/
11-
public class Stack
12-
{
8+
* @author Charanor */
9+
public class Stack {
1310
private int[] stack;
1411
private int stackPtr;
1512

16-
/**
17-
* Creates a new Stack object with the specified max stack size.
13+
/** Creates a new Stack object with the specified max stack size.
1814
*
19-
* @param maxStackSize
20-
*/
15+
* @param maxStackSize */
2116
public Stack(final int maxStackSize) {
2217
stack = new int[maxStackSize];
2318
}
2419

25-
/**
26-
* Creates a new Stack object that is an exact copy (all elements and pointer are the same) of
20+
/** Creates a new Stack object that is an exact copy (all elements and pointer are the same) of
2721
* the supplied Stack parameter.
2822
*
29-
* @param stack the stack to copy.
30-
*/
23+
* @param stack
24+
* the stack to copy. */
3125
public Stack(final Stack stack) {
3226
this.stack = new int[stack.getMaxSize()];
3327
System.arraycopy(stack.stack, 0, this.stack, 0, stack.getMaxSize());
3428
this.stackPtr = stack.stackPtr;
3529
}
3630

37-
/**
38-
* Push a value to the top of the stack.
31+
/** Push a value to the top of the stack.
3932
*
40-
* @param value the value
33+
* @param value
34+
* the value
4135
*
42-
* @throws IndexOutOfBoundsException if the stack is full
43-
*/
36+
* @throws IndexOutOfBoundsException
37+
* if the stack is full */
4438
public void push(final int value) throws IndexOutOfBoundsException {
45-
if (stackPtr >= stack.length) throw new IndexOutOfBoundsException("Trying to push past max stack size!");
39+
if (stackPtr >= stack.length)
40+
throw new IndexOutOfBoundsException("Trying to push past max stack size!");
4641
stack[stackPtr++] = value;
4742
}
4843

49-
/**
50-
* Remove and return the top value of the stack.
44+
/** Remove and return the top value of the stack.
5145
*
5246
* @return the value
53-
* @throws IndexOutOfBoundsException if the stack is empty.
54-
*/
47+
* @throws IndexOutOfBoundsException
48+
* if the stack is empty. */
5549
public int pop() throws IndexOutOfBoundsException {
56-
if (stackPtr <= 0) throw new IndexOutOfBoundsException("Trying to pop from an empty stack!");
50+
if (stackPtr <= 0)
51+
throw new IndexOutOfBoundsException("Trying to pop from an empty stack!");
5752
return stack[--stackPtr];
5853
}
5954

60-
/**
61-
* Returns the top value of the stack without removing it.
55+
/** Returns the top value of the stack without removing it.
6256
*
63-
* @return the value
64-
*/
57+
* @return the value */
6558
public int peek() throws IndexOutOfBoundsException {
66-
if (stackPtr < 1) throw new IndexOutOfBoundsException("Trying to peek from an empty stack!");
59+
if (stackPtr < 1)
60+
throw new IndexOutOfBoundsException("Trying to peek from an empty stack!");
6761
return stack[stackPtr - 1];
6862
}
6963

70-
/**
71-
* Returns the first value beneath the top of the stack.
64+
/** Returns the first value beneath the top of the stack.
7265
*
7366
* @return the value
74-
* @throws IndexOutOfBoundsException if the stack has fewer than 2 elements.
75-
*/
67+
* @throws IndexOutOfBoundsException
68+
* if the stack has fewer than 2 elements. */
7669
public int deepPeek() throws IndexOutOfBoundsException {
77-
if (stackPtr < 1) throw new IndexOutOfBoundsException("Cannot deep peek a stack with fewer than 2 elements!");
78-
return stack[stackPtr - 1];
70+
if (stackPtr < 2) throw new IndexOutOfBoundsException(
71+
"Cannot deep peek a stack with fewer than 2 elements!");
72+
return stack[stackPtr - 2];
7973
}
8074

8175
/** Clears the stack setting the stack pointer to 0. */
8276
public void clear() {
8377
stackPtr = 0;
8478
}
8579

86-
/**
87-
* Increases the max size of the stack by <code>size</code>.
80+
/** Increases the max size of the stack by <code>size</code>.
8881
*
89-
* @param size the size
90-
*/
82+
* @param size
83+
* the size */
9184
public void expand(final int size) {
9285
sizeTo(getMaxSize() + size);
9386
}
9487

95-
/**
96-
* Reduces the max size of the stack by <code>size</code>.
88+
/** Reduces the max size of the stack by <code>size</code>.
9789
*
98-
* @param size the size
99-
*/
90+
* @param size
91+
* the size */
10092
public void shrink(final int size) {
10193
sizeTo(getMaxSize() - size);
10294
}
10395

104-
/**
105-
* Sets the max size of the stack to <code>size</code>
96+
/** Sets the max size of the stack to <code>size</code>.
10697
*
107-
* @param size the size
108-
*/
98+
* @param size
99+
* the size */
109100
public void sizeTo(final int size) {
110101
final int[] tmp = new int[size];
111102
System.arraycopy(stack, 0, tmp, 0, Math.min(size, stack.length));
@@ -127,34 +118,28 @@ public boolean isEmpty() {
127118
return stackPtr <= 0;
128119
}
129120

130-
/**
131-
* Returns the contents of the stack. Identical to {@link Stack#toDecimalString()}.
121+
/** Returns the contents of the stack. Identical to {@link Stack#toDecimalString()}.
132122
*
133-
* @return the string representation.
134-
*/
123+
* @return the string representation. */
135124
@Override
136125
public String toString() {
137126
return toDecimalString();
138127
}
139128

140-
/**
141-
* Returns the contents of the stack represented as decimal numbers. Example:
129+
/** Returns the contents of the stack represented as decimal numbers. Example:
142130
* <code>[5, 10, 252]</code>.
143131
*
144-
* @return the string representation.
145-
*/
132+
* @return the string representation. */
146133
public String toDecimalString() {
147134
return IntStream.of(stack).limit(stackPtr).boxed().collect(Collectors.toList()).toString();
148135
}
149136

150-
/**
151-
* Returns the contents of the stack represented as hexadecimal numbers. Example:
137+
/** Returns the contents of the stack represented as hexadecimal numbers. Example:
152138
* <code>[0x05, 0x0A, 0xFC]</code>.
153139
*
154-
* @return the string representation.
155-
*/
140+
* @return the string representation. */
156141
public String toHexString() {
157-
return IntStream.of(stack).limit(stackPtr).mapToObj(v -> String.format("0x%02X", v)).collect(Collectors.toList())
158-
.toString();
142+
return IntStream.of(stack).limit(stackPtr).mapToObj(v -> String.format("0x%02X", v))
143+
.collect(Collectors.toList()).toString();
159144
}
160145
}
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,75 @@
11
package se.student.liu.jessp088.vm;
22

3+
import se.student.liu.jessp088.vm.instructions.Instruction;
4+
import se.student.liu.jessp088.vm.instructions.Literal;
5+
import se.student.liu.jessp088.vm.instructions.arithmetic.Add;
6+
import se.student.liu.jessp088.vm.instructions.arithmetic.Div;
7+
import se.student.liu.jessp088.vm.instructions.arithmetic.Mod;
8+
import se.student.liu.jessp088.vm.instructions.arithmetic.Mul;
9+
import se.student.liu.jessp088.vm.instructions.arithmetic.Sub;
10+
import se.student.liu.jessp088.vm.instructions.control.Cmp;
11+
import se.student.liu.jessp088.vm.instructions.control.Jmp;
12+
import se.student.liu.jessp088.vm.instructions.control.JmpEQ;
13+
import se.student.liu.jessp088.vm.instructions.control.JmpGE;
14+
import se.student.liu.jessp088.vm.instructions.control.JmpGT;
15+
import se.student.liu.jessp088.vm.instructions.control.JmpLE;
16+
import se.student.liu.jessp088.vm.instructions.control.JmpLT;
17+
import se.student.liu.jessp088.vm.instructions.control.JmpNE;
18+
import se.student.liu.jessp088.vm.instructions.control.subroutine.Break;
19+
import se.student.liu.jessp088.vm.instructions.control.subroutine.Call;
20+
import se.student.liu.jessp088.vm.instructions.control.subroutine.Return;
21+
import se.student.liu.jessp088.vm.instructions.data.Discard;
22+
import se.student.liu.jessp088.vm.instructions.data.Dup;
23+
import se.student.liu.jessp088.vm.instructions.data.Load;
24+
import se.student.liu.jessp088.vm.instructions.data.Store;
25+
import se.student.liu.jessp088.vm.instructions.data.Swap;
26+
import se.student.liu.jessp088.vm.instructions.meta.Free;
27+
import se.student.liu.jessp088.vm.instructions.meta.Malloc;
28+
import se.student.liu.jessp088.vm.instructions.meta.SizeTo;
29+
330
/** All instructions that the VM can process.
431
*
532
* @author Charanor */
633
public enum VMInstruction {
734
// Arithmetic instructions
8-
LITERAL(1),
9-
ADD(0),
10-
SUB(0),
11-
DIV(0),
12-
MUL(0),
13-
MOD(0),
35+
LITERAL(1, Literal.class),
36+
ADD(0, Add.class),
37+
SUB(0, Sub.class),
38+
DIV(0, Div.class),
39+
MUL(0, Mul.class),
40+
MOD(0, Mod.class),
1441

1542
// Control Flow Instructions
16-
CMP(0),
17-
JMP(1),
18-
JMPGT(1),
19-
JMPLT(1),
20-
JMPEQ(1),
21-
JMPNE(1),
22-
JMPGE(1),
23-
JMPLE(1),
24-
CALL(1),
25-
BREAK(0),
26-
RETURN(0),
43+
CMP(0, Cmp.class),
44+
JMP(1, Jmp.class),
45+
JMPGT(1, JmpGT.class),
46+
JMPLT(1, JmpLT.class),
47+
JMPEQ(1, JmpEQ.class),
48+
JMPNE(1, JmpNE.class),
49+
JMPGE(1, JmpGE.class),
50+
JMPLE(1, JmpLE.class),
51+
CALL(1, Call.class),
52+
BREAK(0, Break.class),
53+
RETURN(0, Return.class),
2754

2855
// Data Transfer Instructions
29-
DUP(0),
30-
SWAP(0),
31-
LOAD(1),
32-
STORE(1),
33-
DISCARD(0),
56+
DUP(0, Dup.class),
57+
SWAP(0, Swap.class),
58+
LOAD(1, Load.class),
59+
STORE(1, Store.class),
60+
DISCARD(0, Discard.class),
3461

3562
// Meta Control Instructions
36-
MALLOC(1),
37-
FREE(1),
38-
SIZETO(1),;
63+
MALLOC(1, Malloc.class),
64+
FREE(1, Free.class),
65+
SIZETO(1, SizeTo.class),;
3966

4067
public final int numArguments;
68+
public final Class<? extends Instruction> instructionClass;
4169

42-
private VMInstruction(final int numArguments) {
70+
private VMInstruction(final int numArguments,
71+
final Class<? extends Instruction> instructionClass) {
4372
this.numArguments = numArguments;
73+
this.instructionClass = instructionClass;
4474
}
4575
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package se.student.liu.jessp088.vm;
2+
3+
/** The types of state a {@link VirtualMachine} can be in at one specific time.
4+
*
5+
* @author Charanor */
6+
public enum VMState {
7+
/** The virtual machine is currently executing code normally. */
8+
EXECUTING(true, false, false),
9+
/** The virtual machine is currently debugging code. */
10+
DEBUGGING(true, false, false),
11+
/** The code executed and ended successfully. */
12+
END_SUCCESS(false, false, true),
13+
/** The code executed and ended in an error. */
14+
END_ERROR(false, false, true),
15+
/** The execution was stopped by a user. */
16+
END_USER(false, false, true),
17+
/** The virtual machine hit a breakpoint and paused execution. */
18+
PAUSE_BREAKPOINT(false, true, false),
19+
/** The execution was paused by a user. */
20+
PAUSE_USER(false, true, false);
21+
22+
public final boolean running, paused, stopped;
23+
24+
private VMState(final boolean running, final boolean paused, final boolean stopped) {
25+
this.running = running;
26+
this.paused = paused;
27+
this.stopped = stopped;
28+
}
29+
}

‎src/main/java/se/student/liu/jessp088/vm/Variables.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package se.student.liu.jessp088.vm;
22

33
import java.util.Arrays;
4+
import java.util.Iterator;
45

5-
public class Variables {
6+
public class Variables implements Iterable<Integer> {
67
private final int[] variables;
78
private int numVariables;
89

@@ -42,4 +43,21 @@ public void clear() {
4243
public String toString() {
4344
return Arrays.toString(Arrays.copyOf(variables, numVariables));
4445
}
46+
47+
@Override
48+
public Iterator<Integer> iterator() {
49+
return new Iterator<Integer>() {
50+
int idx = 0;
51+
52+
@Override
53+
public boolean hasNext() {
54+
return idx < variables.length;
55+
}
56+
57+
@Override
58+
public Integer next() {
59+
return variables[idx++];
60+
}
61+
};
62+
}
4563
}

‎src/main/java/se/student/liu/jessp088/vm/VirtualMachine.java

+77-174
Original file line numberDiff line numberDiff line change
@@ -2,73 +2,68 @@
22

33
import se.student.liu.jessp088.vm.instructions.Instruction;
44

5-
/**
6-
* An object representing a virtual machine that can run and debug bytecode given to it. It is also
7-
* responsible for containing the stack and any variables used by the code.
5+
/** An object representing a virtual machine that can run and debug {@link Bytecode} given to it. It
6+
* is also responsible for containing the {@link Stack} and {@link Variables} used by the code.
87
*
98
* @author Charanor
10-
*/
11-
public interface VirtualMachine
12-
{
13-
/**
14-
* Executes the code in the {@link Bytecode} object. Calling this method when the virtual
15-
* machine is running or paused will cause an {@link IllegalStateException} to be thrown.
9+
*
10+
* @see Bytecode
11+
* @see Stack
12+
* @see Variables */
13+
public interface VirtualMachine {
14+
/** Executes the code in the {@link Bytecode} object. Calling this method when the virtual
15+
* machine is not stopped will cause an {@link IllegalStateException} to be thrown.
16+
*
17+
* @param code
18+
* the code to execute.
1619
*
17-
* @param code the code to execute.
20+
* @throws IllegalStateException
21+
* if called when {@link VirtualMachine} is not stopped.
1822
*
19-
* @throws IllegalStateException if called when {@link VirtualMachine} is running or paused.
20-
*/
23+
* @see VirtualMachine#isStopped() */
2124
void execute(Bytecode code) throws IllegalStateException;
2225

23-
/**
24-
* Debugs the code in the {@link Bytecode} object. Calling this method when the virtual machine
25-
* is running or paused will cause an {@link IllegalStateException} to be thrown.
26+
/** Debugs the code in the {@link Bytecode} object. Calling this method when the virtual machine
27+
* is not stopped will cause an {@link IllegalStateException} to be thrown.
2628
*
27-
* @param code the code to debug.
29+
* @param code
30+
* the code to debug.
2831
*
29-
* @throws IllegalStateException if called when {@link VirtualMachine} is running or paused.
30-
*/
32+
* @throws IllegalStateException
33+
* if called when {@link VirtualMachine} is not stopped.
34+
*
35+
* @see VirtualMachine#isStopped() */
3136
void debug(Bytecode code) throws IllegalStateException;
3237

33-
/**
34-
* Pauses the execution of the virtual machine. The exact effects of pausing is
38+
/** Pauses the execution of the virtual machine. The exact effects of pausing is
3539
* <b>implementation specific</b> and is allowed to depend on the current and previous
3640
* {@link VMState}. The only guarantee made is that pausing prohibits any more
3741
* {@link Instruction}s from being processed until {@link VirtualMachine#resumeExecution()} is
38-
* called. If the virtual machine is not running this method does nothing.
39-
*/
42+
* called. If the virtual machine is not running this method does nothing. */
4043
void pauseExecution();
4144

42-
/**
43-
* Resumes the execution of the virtual machine. If the virtual machine is not paused this
44-
* method does nothing.
45-
*/
45+
/** Resumes the execution of the virtual machine. If the virtual machine is not paused this
46+
* method does nothing. */
4647
void resumeExecution();
4748

48-
/**
49-
* Stops the execution of the virtual machine. The exact effects of stopping is
49+
/** Stops the execution of the virtual machine. The exact effects of stopping is
5050
* <b>implementation specific</b> and is allowed to depend on the current and previous
5151
* {@link VMState}. The only guarantee made is that stopping prohibits any more
5252
* {@link Instruction}s from being processed until {@link VirtualMachine#execute(Bytecode)} or
5353
* {@link VirtualMachine#debug(Bytecode)} is called again and that the program counter is reset.
54-
* If the virtual machine is not running this method does nothing.
55-
*/
54+
* If the virtual machine is not running this method does nothing. */
5655
void stopExecution();
5756

58-
/**
59-
* Gets the current {@link VMState} of the virtual machine. The default state is
57+
/** Gets the current {@link VMState} of the virtual machine. The default state is
6058
* {@link VMState#END_SUCCESS}.
6159
*
62-
* @return the current state.
63-
*/
60+
* @return the current state. */
6461
VMState getCurrentState();
6562

66-
/**
67-
* Gets the previous {@link VMState} of the virtual machine. The default state is
63+
/** Gets the previous {@link VMState} of the virtual machine. The default state is
6864
* {@link VMState#END_SUCCESS}.
6965
*
70-
* @return the previous state.
71-
*/
66+
* @return the previous state. */
7267
VMState getPreviousState();
7368

7469
/** @return true if the virtual machine is currently running; false otherwise. */
@@ -101,23 +96,20 @@ default boolean wasStopped() {
10196
return getPreviousState().stopped;
10297
}
10398

104-
/**
105-
* Adds a {@link DebugListener} to listen for debug events from this virtual machine.
99+
/** Adds a {@link DebugListener} to listen for debug events from this virtual machine.
106100
*
107-
* @param listener the listener
108-
*/
101+
* @param listener
102+
* the listener */
109103
void addDebugListener(DebugListener listener);
110104

111-
/**
112-
* Removes a previously registered {@link DebugListener} from this virtual machine. If the
105+
/** Removes a previously registered {@link DebugListener} from this virtual machine. If the
113106
* listener is not registered this method does nothing.
114107
*
115-
* @param listener the listener
116-
*/
108+
* @param listener
109+
* the listener */
117110
void removeDebugListener(DebugListener listener);
118111

119-
/**
120-
* Adds a breakpoint at the specified instruction pointer. A breakpoint is automatically
112+
/** Adds a breakpoint at the specified instruction pointer. A breakpoint is automatically
121113
* triggered and does not require external interference. The exact effects of a breakpoint is
122114
* <b>implementation specific</b>. The only guarantees made are:
123115
* <ul>
@@ -136,179 +128,90 @@ default boolean wasStopped() {
136128
* the instruction that the breakpoint stopped on and is thus <b>undefined behavior</b>.
137129
* </p>
138130
*
139-
* @param instructionPtr the pointer to break at
140-
*/
131+
* @param instructionPtr
132+
* the pointer to break at */
141133
void addBreakpoint(int instructionPtr);
142134

143-
/**
144-
* Removes a breakpoint at the specified instruction pointer. If there is no breakpoint at the
135+
/** Removes a breakpoint at the specified instruction pointer. If there is no breakpoint at the
145136
* specified instruction pointer this method does nothing.
146137
*
147-
* @param instructionPtr the pointer to remove the break from
148-
*/
138+
* @param instructionPtr
139+
* the pointer to remove the break from */
149140
void removeBreakpoint(int instructionPtr);
150141

151-
/**
152-
* Returns if there is a breakpoint at the specified instruction pointer.
142+
/** Removes all breakpoints. */
143+
void removeAllBreakpoints();
144+
145+
/** Returns if there is a breakpoint at the specified instruction pointer.
153146
*
154-
* @param instructionPtr the pointer
147+
* @param instructionPtr
148+
* the pointer
155149
*
156-
* @return true if there is a breakpoint; false otherwise or if instructionPtr is invalid.
157-
*/
150+
* @return true if there is a breakpoint; false otherwise or if instructionPtr is invalid. */
158151
boolean isBreakpointAt(int instructionPtr);
159152

160-
/**
161-
* Gets the current {@link Instruction}. Whether or not the instruction has already been
153+
/** Gets the current {@link Instruction}. Whether or not the instruction has already been
162154
* executed when this call is made is <b>implementation specific</b>.
163155
*
164-
* @return the current {@link Instruction}
165-
*/
156+
* @return the current {@link Instruction} */
166157
Instruction getCurrentInstruction();
167158

168-
/**
169-
* Gets the instruction pointer pointing to the index of the {@link Instruction} from
159+
/** Gets the instruction pointer pointing to the index of the {@link Instruction} from
170160
* {@link VirtualMachine#getCurrentInstruction()} in a {@link Bytecode} object.
171161
*
172-
* @return the pointer
173-
*/
162+
* @return the pointer */
174163
int getCurrentInstructionPtr();
175164

176-
/**
177-
* Gets the line number of the {@link Instruction} from
165+
/** Gets the line number of the {@link Instruction} from
178166
* {@link VirtualMachine#getCurrentInstruction()}.
179167
*
180-
* @return the line number
181-
*/
168+
* @return the line number */
182169
int getCurrentLineNumber();
183170

184-
/**
185-
* Converts an instruction pointer to the line number where the instruction can be found.
171+
/** Converts an instruction pointer to the line number where the instruction can be found.
186172
*
187-
* @param ptr the pointer
173+
* @param ptr
174+
* the pointer
188175
*
189176
* @return the line number
190-
* @throws IllegalArgumentException if there is no line corresponding to the pointer <b>or</b> if the pointer is
191-
* invalid.
192-
*/
177+
* @throws IllegalArgumentException
178+
* if there is no line corresponding to the pointer <b>or</b> if the pointer is
179+
* invalid. */
193180
int convertPtrToLine(int ptr) throws IllegalArgumentException;
194181

195-
/**
196-
* Converts a line number to the instruction pointer found on the line number.
182+
/** Converts a line number to the instruction pointer found on the line number.
197183
*
198-
* @param line the line
184+
* @param line
185+
* the line
199186
*
200187
* @return the instruction pointer
201-
* @throws IllegalArgumentException if there is no pointer corresponding to the line given.
202-
*/
188+
* @throws IllegalArgumentException
189+
* if there is no pointer corresponding to the line given. */
203190
int convertLineToPtr(int line) throws IllegalArgumentException;
204191

205-
/**
206-
* Gets the errors the virtual machine has experienced. This method is <b>only</b> guaranteed
192+
/** Gets the errors the virtual machine has experienced. This method is <b>only</b> guaranteed
207193
* to return an accurate error message when the virtual machine is stopped. Otherwise the return
208194
* value is <b>implementation specific</b> and is allowed to be <code>null</code>.
209195
*
210-
* @return the current error message(s). May be null.
211-
*/
196+
* @return the current error message(s). May be null. */
212197
String getError();
213198

214-
/**
215-
* Checks if the virtual machine currently has any errors. This method is <b>only</b>
199+
/** Checks if the virtual machine currently has any errors. This method is <b>only</b>
216200
* guaranteed to return an accurate value when the virtual machine is stopped. Otherwise the
217201
* return value is <b>implementation specific</b>.
218202
*
219-
* @return if the {@link VirtualMachine} currently has any errors.
220-
*/
203+
* @return if the {@link VirtualMachine} currently has any errors. */
221204
boolean hasErrors();
222205

223-
/**
224-
* Returns a copy of the current {@link Stack} instance contained in this
206+
/** Returns a copy of the current {@link Stack} instance contained in this
225207
* {@link VirtualMachine}.
226208
*
227-
* @return the stack.
228-
*/
209+
* @return the stack. */
229210
Stack getStack();
230211

231-
/**
232-
* Returns a copy of the current {@link Variables} instance contained in this
212+
/** Returns a copy of the current {@link Variables} instance contained in this
233213
* {@link VirtualMachine}.
234214
*
235-
* @return the variables.
236-
*/
215+
* @return the variables. */
237216
Variables getVariables();
238-
239-
/**
240-
* The types of state the {@link VirtualMachine} can be in at one specific time.
241-
*
242-
* @author Charanor
243-
*/
244-
public static enum VMState
245-
{
246-
/** The {@link VirtualMachine} is currently executing code normally. */
247-
EXECUTING(true, false, false), /** The {@link VirtualMachine} is currently debugging code. */
248-
DEBUGGING(true, false, false), /** The code executed and ended successfully. */
249-
END_SUCCESS(false, false, true), /**
250-
* The code executed and ended in an error accessible by {@link VirtualMachine#getError()}
251-
* .
252-
*/
253-
END_ERROR(false, false, true), /** The execution was stopped by a user. */
254-
END_USER(false, false, true), /** The execution hit a breakpoint and paused execution. */
255-
PAUSE_BREAKPOINT(false, true, false), /** The execution was paused by a user. */
256-
PAUSE_USER(false, true, false);
257-
258-
public final boolean running, paused, stopped;
259-
260-
private VMState(final boolean running, final boolean paused, final boolean stopped) {
261-
this.running = running;
262-
this.paused = paused;
263-
this.stopped = stopped;
264-
}
265-
}
266-
267-
public static interface DebugListener
268-
{
269-
/**
270-
* Called before {@link VirtualMachine#execute(Bytecode)} or
271-
* {@link VirtualMachine#debug(Bytecode)} is called but after state has been set to
272-
* {@link VMState#EXECUTING} or {@link VMState#DEBUGGING}.
273-
*
274-
* @param vm the virtual machine
275-
*/
276-
void beforeExecution(VirtualMachine vm);
277-
278-
/**
279-
* Called after {@link VirtualMachine#execute(Bytecode)} or
280-
* {@link VirtualMachine#debug(Bytecode)} is called and after state has been set to
281-
* {@link VMState#END_SUCCESS}, {@link VMState#END_ERROR}, or {@link VMState#END_USER}.
282-
*
283-
* @param vm the virtual machine
284-
*/
285-
void afterExecution(VirtualMachine vm);
286-
287-
/**
288-
* Called before the {@link VirtualMachine} handles an {@link Instruction}. Calling
289-
* {@link VirtualMachine#stopExecution()} is guaranteed to stop the execution of the
290-
* {@link VirtualMachine} before the Instruction is handled.
291-
*
292-
* @param vm the virtual machine
293-
*/
294-
void beforeInstruction(VirtualMachine vm);
295-
296-
/**
297-
* Called after the {@link VirtualMachine} handles an {@link Instruction}. If execution of
298-
* the {@link VirtualMachine} is paused or stopped this method is <b>not</b> guaranteed to
299-
* be called before the next {@link DebugListener#beforeExecution(VirtualMachine)} is
300-
* called. This method is guaranteed to be called even if the {@link VirtualMachine} gets a
301-
* <b>non-critical</b> error during execution.
302-
*
303-
* @param vm the virtual machine
304-
*/
305-
void afterInstruction(VirtualMachine vm);
306-
307-
/**
308-
* Called after the state of the {@link VirtualMachine} changes.
309-
*
310-
* @param vm the virtual machine
311-
*/
312-
void onStateChanged(VirtualMachine vm);
313-
}
314217
}

‎src/main/java/se/student/liu/jessp088/vm/instructions/Literal.java

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package se.student.liu.jessp088.vm.instructions;
22

33
public class Literal extends OneArgInstruction {
4-
54
public Literal(final int arg) {
65
super(arg);
76
}

‎src/main/java/se/student/liu/jessp088/vm/instructions/OneArgInstruction.java

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ protected OneArgInstruction(final int arg) {
77
this.arg = arg;
88
}
99

10+
public int getArg() {
11+
return arg;
12+
}
13+
1014
@Override
1115
public String toString() {
1216
return super.toString() + " " + arg;

‎src/main/java/se/student/liu/jessp088/vm/instructions/TwoArgInstruction.java

+8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ protected TwoArgInstruction(final int left, final int right) {
99
this.right = right;
1010
}
1111

12+
public int getArg1() {
13+
return left;
14+
}
15+
16+
public int getArg2() {
17+
return right;
18+
}
19+
1220
@Override
1321
public String toString() {
1422
return super.toString() + " " + left + " " + right;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package se.student.liu.jessp088.vm.parsing.suppliers;
2+
3+
import java.lang.reflect.InvocationTargetException;
4+
5+
import se.student.liu.jessp088.vm.VMInstruction;
6+
import se.student.liu.jessp088.vm.instructions.Instruction;
7+
8+
public class ReflectionSupplier implements InstructionSupplier {
9+
@Override
10+
public Instruction getInstruction(final VMInstruction instruction, final int... inputArgs)
11+
throws IllegalArgumentException {
12+
final Class<?>[] paramTypes = new Class<?>[instruction.numArguments];
13+
for (int i = 0; i < paramTypes.length; i++)
14+
paramTypes[i] = int.class;
15+
16+
// Must be of type Object[] because int[] will cause an argument mismatch.
17+
final Object[] args = new Object[instruction.numArguments];
18+
for (int i = 0; i < args.length; i++)
19+
args[i] = inputArgs[i];
20+
21+
try {
22+
return instruction.instructionClass.getConstructor(paramTypes).newInstance(args);
23+
} catch (InstantiationException | IllegalAccessException | InvocationTargetException
24+
| NoSuchMethodException | SecurityException e) {
25+
throw new IllegalArgumentException("Could not create instruction " + instruction, e);
26+
}
27+
}
28+
}

‎src/main/java/se/student/liu/jessp088/vm/ui/GUI.java

+181-45
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,40 @@
1313
import java.io.IOException;
1414
import java.nio.file.Files;
1515
import java.util.List;
16-
17-
import javax.swing.*;
16+
import java.util.concurrent.ExecutorService;
17+
import java.util.concurrent.Executors;
18+
19+
import javax.swing.BorderFactory;
20+
import javax.swing.Icon;
21+
import javax.swing.ImageIcon;
22+
import javax.swing.JComponent;
23+
import javax.swing.JFileChooser;
24+
import javax.swing.JFrame;
25+
import javax.swing.JLabel;
26+
import javax.swing.JMenu;
27+
import javax.swing.JMenuBar;
28+
import javax.swing.JMenuItem;
29+
import javax.swing.JPanel;
30+
import javax.swing.JScrollPane;
31+
import javax.swing.JSeparator;
32+
import javax.swing.JSplitPane;
33+
import javax.swing.JTabbedPane;
34+
import javax.swing.JTable;
35+
import javax.swing.JTextArea;
36+
import javax.swing.KeyStroke;
37+
import javax.swing.SwingConstants;
1838
import javax.swing.border.LineBorder;
1939
import javax.swing.filechooser.FileNameExtensionFilter;
2040
import javax.swing.table.DefaultTableModel;
2141
import javax.swing.text.BadLocationException;
2242

2343
import se.student.liu.jessp088.vm.Bytecode;
44+
import se.student.liu.jessp088.vm.DebugListener;
2445
import se.student.liu.jessp088.vm.DefaultVirtualMachine;
46+
import se.student.liu.jessp088.vm.Variables;
2547
import se.student.liu.jessp088.vm.VirtualMachine;
48+
import se.student.liu.jessp088.vm.instructions.Instruction;
49+
import se.student.liu.jessp088.vm.instructions.data.Store;
2650
import se.student.liu.jessp088.vm.parsing.Lexer;
2751
import se.student.liu.jessp088.vm.parsing.Parser;
2852
import se.student.liu.jessp088.vm.parsing.Token;
@@ -38,6 +62,7 @@ public class GUI {
3862
private static final Icon DEBUG_ICON = new ImageIcon(GUI.class.getResource("/debug_16.png"));
3963
private static final Icon CONSOLE_ICON = new ImageIcon(GUI.class.getResource("/pc_16.png"));
4064
private static final Icon VARIABLES_ICON = new ImageIcon(GUI.class.getResource("/var_16.png"));
65+
private static final Icon STACK_ICON = new ImageIcon(GUI.class.getResource("/stack_16.png"));
4166
private static final Icon PROGRAM_ICON = new ImageIcon(GUI.class.getResource("/vm_16.png"));
4267
private static final Icon RESUME_ICON = new ImageIcon(GUI.class.getResource("/resume_16.png"));
4368
private static final Icon PAUSE_ICON = new ImageIcon(GUI.class.getResource("/pause_16.png"));
@@ -46,14 +71,12 @@ public class GUI {
4671

4772
private static final Font CODE_FONT = new Font("Consolas", Font.PLAIN, 12);
4873

74+
private final ExecutorService executor = Executors.newSingleThreadExecutor();
75+
4976
private Lexer lexer;
5077
private Parser parser;
5178
private VirtualMachine vm;
5279

53-
// Debugging state
54-
private Bytecode parseResult;
55-
private boolean isDebugging;
56-
5780
private JFrame frame;
5881
private JFileChooser fc;
5982
private JTabbedPane programTabs;
@@ -63,6 +86,11 @@ public class GUI {
6386

6487
private final int maxStackSize = DEFAULT_MAX_STACK_SIZE;
6588
private final int maxVariableSize = DEFAULT_MAX_VARIABLE_SIZE;
89+
private JMenuItem resumeOption;
90+
private JMenuItem pauseOption;
91+
private JMenuItem stopOption;
92+
private JTextArea stackTextArea;
93+
private JLabel lineInfoLabel;
6694

6795
/** Launch the application. */
6896
public static void main(final String[] args) {
@@ -145,20 +173,26 @@ private void initialize() {
145173
final JSeparator separator = new JSeparator();
146174
runMenu.add(separator);
147175

148-
final JMenuItem resumeOption = new JMenuItem("Resume Execution");
176+
resumeOption = new JMenuItem("Resume Execution");
177+
resumeOption.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_MASK));
149178
resumeOption.setEnabled(false);
150179
resumeOption.setIcon(RESUME_ICON);
151-
resumeOption.addActionListener(this::resumeDebug);
180+
resumeOption.addActionListener(this::resumeExecution);
152181
runMenu.add(resumeOption);
153182

154-
final JMenuItem pauseOption = new JMenuItem("Pause Execution");
183+
pauseOption = new JMenuItem("Pause Execution");
184+
pauseOption.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_MASK));
155185
pauseOption.setEnabled(false);
156186
pauseOption.setIcon(PAUSE_ICON);
187+
pauseOption.addActionListener(this::pauseExecution);
157188
runMenu.add(pauseOption);
158189

159-
final JMenuItem stopOption = new JMenuItem("Stop Execution");
190+
stopOption = new JMenuItem("Stop Execution");
191+
stopOption.setAccelerator(
192+
KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
160193
stopOption.setEnabled(false);
161194
stopOption.setIcon(STOP_ICON);
195+
stopOption.addActionListener(this::stopExecution);
162196
runMenu.add(stopOption);
163197

164198
final JSeparator separator_1 = new JSeparator();
@@ -173,6 +207,19 @@ private void initialize() {
173207
final JMenuItem addBreakpointOption = new JMenuItem("Add Breakpoint at Cursor");
174208
addBreakpointOption.addActionListener(this::addBreakpointAtCursor);
175209
debugMenu.add(addBreakpointOption);
210+
211+
final JMenuItem clearConsoleOption = new JMenuItem("Clear console");
212+
clearConsoleOption.addActionListener(e -> consoleTextArea.setText(""));
213+
214+
final JMenuItem removeBreakpointOption = new JMenuItem("Remove Breakpoint at Cursor");
215+
removeBreakpointOption.addActionListener(this::removeBreakpointAtCursor);
216+
debugMenu.add(removeBreakpointOption);
217+
218+
final JMenuItem removeAllOption = new JMenuItem("Remove all Breakpoints");
219+
removeAllOption.addActionListener(e -> vm.removeAllBreakpoints());
220+
debugMenu.add(removeAllOption);
221+
debugMenu.add(clearConsoleOption);
222+
176223
configurationOption.addActionListener(e -> {
177224
final RunConfigDialog dialog = new RunConfigDialog(frame);
178225

@@ -187,7 +234,6 @@ private void initialize() {
187234
programExtrasSplitter.setOrientation(JSplitPane.VERTICAL_SPLIT);
188235

189236
programTabs = new JTabbedPane(SwingConstants.TOP);
190-
programExtrasSplitter.setLeftComponent(programTabs);
191237

192238
// final JTextArea codeTextArea = new JTextArea();
193239
// codeTextArea.setBorder(new LineBorder(Color.GRAY));
@@ -204,6 +250,7 @@ private void initialize() {
204250
programExtrasSplitter.setRightComponent(extrasTabs);
205251

206252
consoleTextArea = new JTextArea();
253+
consoleTextArea.setEditable(false);
207254
consoleTextArea.setBorder(new LineBorder(Color.GRAY));
208255
consoleTextArea.setFont(CODE_FONT);
209256
final JScrollPane consoleScrollPane = new JScrollPane(consoleTextArea);
@@ -219,16 +266,87 @@ private void initialize() {
219266

220267
final JScrollPane variableScrollPane = new JScrollPane(variableTable);
221268
extrasTabs.addTab("Variables", VARIABLES_ICON, variableScrollPane, null);
269+
270+
stackTextArea = new JTextArea();
271+
stackTextArea.setEditable(false);
272+
final JScrollPane stackScrollPane = new JScrollPane(stackTextArea);
273+
extrasTabs.addTab("Stack", STACK_ICON, stackScrollPane, null);
274+
275+
// Redirect logging to GUI console
276+
new MessageConsole(consoleTextArea).redirectOut();
277+
278+
final JPanel codePanel = new JPanel();
279+
programExtrasSplitter.setLeftComponent(codePanel);
280+
codePanel.setLayout(new BorderLayout(0, 0));
281+
codePanel.add(programTabs);
282+
283+
lineInfoLabel = new JLabel("Line: 0, Column: 0");
284+
codePanel.add(lineInfoLabel, BorderLayout.SOUTH);
285+
286+
// Listener to add variables to table
287+
vm.addDebugListener(new DebugListener() {
288+
@Override
289+
public void beforeExecution(final VirtualMachine vm) {
290+
final int numRows = variableTable.getModel().getRowCount();
291+
for (int i = 0; i < numRows; i++) {
292+
variableTable.getModel().setValueAt(null, i, 0);
293+
variableTable.getModel().setValueAt(null, i, 1);
294+
}
295+
296+
stackTextArea.setText("");
297+
}
298+
299+
@Override
300+
public void afterExecution(final VirtualMachine vm) {
301+
resumeOption.setEnabled(false);
302+
pauseOption.setEnabled(false);
303+
stopOption.setEnabled(false);
304+
}
305+
306+
@Override
307+
public void beforeInstruction(final VirtualMachine vm) {
308+
}
309+
310+
@Override
311+
public void afterInstruction(final VirtualMachine vm) {
312+
stackTextArea.setText(vm.getStack().toString());
313+
314+
final Instruction ins = vm.getCurrentInstruction();
315+
if (!(ins instanceof Store)) return;
316+
final Store store = (Store) ins;
317+
final int varIdx = store.getArg();
318+
319+
final Variables variables = vm.getVariables();
320+
variableTable.getModel().setValueAt(varIdx, varIdx, 0);
321+
variableTable.getModel().setValueAt(variables.load(varIdx), varIdx, 1);
322+
}
323+
324+
@Override
325+
public void onStateChanged(final VirtualMachine vm) {
326+
resumeOption.setEnabled(vm.isPaused());
327+
pauseOption.setEnabled(vm.isRunning());
328+
stopOption.setEnabled(vm.isPaused() || vm.isRunning());
329+
}
330+
});
222331
}
223332

224333
private void openNewProgramTab(final String tabName, final String tabContents) {
225-
final JTextArea codeTextArea = new JTextArea(tabContents);
226-
codeTextArea.setBorder(BorderFactory.createLineBorder(Color.GRAY));
227-
codeTextArea.setFont(CODE_FONT);
334+
final JTextArea text = new JTextArea(tabContents);
335+
text.setBorder(BorderFactory.createLineBorder(Color.GRAY));
336+
text.setFont(CODE_FONT);
337+
text.addCaretListener(e -> {
338+
try {
339+
final int lineNumber = lineNumberAtCursor();
340+
final int column = text.getCaretPosition()
341+
- text.getLineStartOffset(lineNumber - 1);
342+
lineInfoLabel.setText("Line: " + lineNumber + ", Column: " + column);
343+
} catch (final Exception ignored) {
344+
}
345+
});
228346

229-
final TextLineNumber lineNumbers = new TextLineNumber(codeTextArea);
230-
final JScrollPane codeScrollPane = new JScrollPane(codeTextArea);
231-
codeScrollPane.setRowHeaderView(lineNumbers);
347+
final TextLineNumber lineNumbers = new TextLineNumber(text);
348+
final JScrollPane codeScrollPane = new JScrollPane(text);
349+
// codeScrollPane.setRowHeaderView(lineNumbers);
232350

233351
programTabs.addTab(tabName, PROGRAM_ICON, codeScrollPane, null);
234352
}
@@ -288,36 +406,33 @@ private void openFile(final ActionEvent e) {
288406
private void runCodeInOpenTab(final ActionEvent e) {
289407
final Bytecode result = parseCodeInOpenTab();
290408
if (result == null) return;
291-
// if (!vm.execute(result)) {
292-
// addConsoleMessage("Execution of bytecode failed with error
293-
// %s.", vm.getError());
294-
// } else {
295-
// addConsoleMessage("Execution of bytecode successful. End
296-
// state: %s.", vm);
297-
// }
409+
410+
executor.execute(() -> {
411+
vm.stopExecution();
412+
vm.execute(result);
413+
});
298414
}
299415

300416
private void debugCodeInOpenTab(final ActionEvent e) {
301-
parseResult = parseCodeInOpenTab();
302-
if (parseResult == null) return;
417+
final Bytecode result = parseCodeInOpenTab();
418+
if (result == null) return;
303419

304-
isDebugging = true;
420+
executor.execute(() -> {
421+
vm.stopExecution();
422+
vm.debug(result);
423+
});
424+
}
425+
426+
private void resumeExecution(final ActionEvent e) {
427+
vm.resumeExecution();
428+
}
305429

306-
resumeDebug(e);
430+
private void pauseExecution(final ActionEvent e) {
431+
vm.pauseExecution();
307432
}
308433

309-
private void resumeDebug(final ActionEvent e) {
310-
// if (vm.isDebugFinished()) {
311-
// addConsoleMessage("Execution of bytecode successful. End
312-
// state: %s.", vm);
313-
// isDebugging = false;
314-
// return;
315-
// } else if (!vm.execute(parseResult.code,
316-
// parseResult.instructionToLineNumber)) {
317-
// addConsoleMessage("Execution of bytecode failed with error
318-
// %s.", vm.getError());
319-
// isDebugging = false;
320-
// }
434+
private void stopExecution(final ActionEvent e) {
435+
vm.stopExecution();
321436
}
322437

323438
private void closeCurrentProgramTab(final ActionEvent e) {
@@ -331,15 +446,36 @@ private void closeCurrentProgramTab(final ActionEvent e) {
331446
}
332447

333448
private void addBreakpointAtCursor(final ActionEvent e) {
449+
final int lineNumber = lineNumberAtCursor();
450+
if (lineNumber == -1) {
451+
addConsoleMessage("Could not add breakpoint at cursor, invalid line.");
452+
return;
453+
}
454+
455+
vm.addBreakpoint(vm.convertLineToPtr(lineNumber));
456+
addConsoleMessage("Setting breakpoint at line %s.", lineNumber);
457+
}
458+
459+
private void removeBreakpointAtCursor(final ActionEvent e) {
460+
final int lineNumber = lineNumberAtCursor();
461+
if (lineNumber == -1) {
462+
addConsoleMessage("Could not remove breakpoint at cursor, invalid line.");
463+
return;
464+
}
465+
466+
vm.removeBreakpoint(vm.convertLineToPtr(lineNumber));
467+
addConsoleMessage("Removing breakpoint at line %s.", lineNumber);
468+
}
469+
470+
private int lineNumberAtCursor() {
334471
try {
335472
final JTextArea text = getCurrentProgramTextArea();
336-
if (text == null) return;
473+
if (text == null) return -1;
337474
final int carretOffset = text.getCaretPosition();
338-
final int lineNumber = text.getLineOfOffset(carretOffset);
339-
// vm.setBreakpoint(lineNumber);
340-
addConsoleMessage("Setting breakpoint at line %s.", lineNumber);
475+
return text.getLineOfOffset(carretOffset) + 1;
341476
} catch (final BadLocationException e1) {
342-
addConsoleMessage("Could not add breakpoint at cursor. Error: %s.", e1);
477+
addConsoleMessage("Could not fetch line number at cursor. Error: %s.", e1);
478+
return -1;
343479
}
344480
}
345481

‎src/main/java/se/student/liu/jessp088/vm/ui/LabeledTextArea.java

-55
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package se.student.liu.jessp088.vm.ui;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.io.PrintStream;
5+
6+
import javax.swing.text.BadLocationException;
7+
import javax.swing.text.Document;
8+
import javax.swing.text.JTextComponent;
9+
10+
/*
11+
* Create a simple console to display text messages.
12+
*
13+
* Messages can be directed here from different sources. Each source can
14+
* have its messages displayed in a different color.
15+
*
16+
* Messages can either be appended to the console or inserted as the first
17+
* line of the console
18+
*
19+
* You can limit the number of lines to hold in the Document.
20+
*/
21+
public class MessageConsole {
22+
private final JTextComponent textComponent;
23+
private final Document document;
24+
25+
public MessageConsole(final JTextComponent textComponent) {
26+
this.textComponent = textComponent;
27+
this.document = textComponent.getDocument();
28+
textComponent.setEditable(false);
29+
}
30+
31+
/* Redirect the output from the standard output to the console using the default text color and
32+
* null PrintStream */
33+
public void redirectOut() {
34+
final ConsoleOutputStream cos = new ConsoleOutputStream();
35+
System.setOut(new PrintStream(cos, true));
36+
}
37+
38+
/* Class to intercept output from a PrintStream and add it to a Document. The output can
39+
* optionally be redirected to a different PrintStream. The text displayed in the Document can
40+
* be color coded to indicate the output source. */
41+
class ConsoleOutputStream extends ByteArrayOutputStream {
42+
private final String EOL = System.getProperty("line.separator");
43+
private final StringBuffer buffer = new StringBuffer(80);
44+
private boolean isFirstLine;
45+
46+
/* Override this method to intercept the output text. Each line of text output will actually
47+
* involve invoking this method twice: a) for the actual text message b) for the newLine
48+
* string The message will be treated differently depending on whether the line will be
49+
* appended or inserted into the Document */
50+
@Override
51+
public void flush() {
52+
final String message = toString();
53+
if (message.length() == 0) return;
54+
handleAppend(message);
55+
reset();
56+
}
57+
58+
/* We don't want to have blank lines in the Document. The first line added will simply be
59+
* the message. For additional lines it will be: newLine + message */
60+
private void handleAppend(final String message) {
61+
// This check is needed in case the text in the Document has been
62+
// cleared. The buffer may contain the EOL string from the previous
63+
// message.
64+
65+
if (document.getLength() == 0) buffer.setLength(0);
66+
67+
buffer.append(message);
68+
if (!EOL.equals(message)) clearBuffer();
69+
}
70+
71+
/* The message and the newLine have been added to the buffer in the appropriate order so we
72+
* can now update the Document and send the text to the optional PrintStream. */
73+
private void clearBuffer() {
74+
// In case both the standard out and standard err are being redirected
75+
// we need to insert a newline character for the first line only
76+
77+
if (isFirstLine && document.getLength() != 0) {
78+
buffer.insert(0, "\n");
79+
}
80+
81+
isFirstLine = false;
82+
final String line = buffer.toString();
83+
84+
try {
85+
final int offset = document.getLength();
86+
document.insertString(offset, line, null);
87+
textComponent.setCaretPosition(document.getLength());
88+
} catch (final BadLocationException ignored) {
89+
}
90+
91+
buffer.setLength(0);
92+
}
93+
}
94+
}

‎src/main/java/se/student/liu/jessp088/vm/ui/TextLineNumber.java

+355-370
Large diffs are not rendered by default.

‎src/main/resources/logback.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<configuration>
2+
<configuration scan="true">
33
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
44
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
5-
<level>INFO</level>
5+
<level>DEBUG</level>
66
</filter>
77

88
<encoder>

‎src/main/resources/stack_16.png

476 Bytes
Loading

‎src/main/resources/stack_32.png

1.1 KB
Loading

‎src/main/resources/version.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
#Sun Apr 01 23:06:39 CEST 2018
1+
#Mon Apr 09 15:30:50 CEST 2018
22
version=0.0.1-SNAPSHOT

‎src/test/java/se/student/liu/jessp088/vm/ParserTest.java

+7-14
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,7 @@
22

33
import static org.junit.Assert.assertTrue;
44
import static org.junit.Assert.fail;
5-
import static se.student.liu.jessp088.vm.parsing.TokenType.DEFINE;
6-
import static se.student.liu.jessp088.vm.parsing.TokenType.DEFTAG;
7-
import static se.student.liu.jessp088.vm.parsing.TokenType.EOF;
8-
import static se.student.liu.jessp088.vm.parsing.TokenType.EQUALS;
9-
import static se.student.liu.jessp088.vm.parsing.TokenType.IDENTIFIER;
10-
import static se.student.liu.jessp088.vm.parsing.TokenType.LEFTBRACKET;
11-
import static se.student.liu.jessp088.vm.parsing.TokenType.NEXTOP;
12-
import static se.student.liu.jessp088.vm.parsing.TokenType.NUMBER;
13-
import static se.student.liu.jessp088.vm.parsing.TokenType.RIGHTBRACKET;
5+
import static se.student.liu.jessp088.vm.parsing.TokenType.*;
146

157
import java.util.ArrayList;
168
import java.util.List;
@@ -25,14 +17,14 @@
2517
import se.student.liu.jessp088.vm.parsing.Token;
2618
import se.student.liu.jessp088.vm.parsing.TokenType;
2719
import se.student.liu.jessp088.vm.parsing.exceptions.ParserException;
20+
import se.student.liu.jessp088.vm.parsing.suppliers.ReflectionSupplier;
2821

29-
public class ParserTest
30-
{
22+
public class ParserTest {
3123
private Parser parser;
3224

3325
@Before
34-
public void setUp() throws Exception {
35-
parser = new Parser();
26+
public void setUp() {
27+
parser = new Parser(new ReflectionSupplier());
3628
}
3729

3830
@Test
@@ -71,7 +63,8 @@ public void test() {
7163
try {
7264
code = parser.parse(tokens);
7365
} catch (final ParserException e) {
74-
fail("Parsing of tokens " + tokens + " failed! Reason: " + e.getMessage());
66+
fail("Parsing of tokens " + tokens + " failed! Reason: " + e);
67+
e.printStackTrace();
7568
return;
7669
}
7770

‎src/test/java/se/student/liu/jessp088/vm/TestExampleFile.java

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import org.junit.Test;
1313

14-
import se.student.liu.jessp088.vm.VirtualMachine.VMState;
1514
import se.student.liu.jessp088.vm.parsing.Lexer;
1615
import se.student.liu.jessp088.vm.parsing.Parser;
1716
import se.student.liu.jessp088.vm.parsing.Token;

‎src/test/java/se/student/liu/jessp088/vm/VirtualMachineTest.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,14 @@
1010
import org.junit.Before;
1111
import org.junit.Test;
1212

13-
import se.student.liu.jessp088.vm.VirtualMachine.DebugListener;
14-
import se.student.liu.jessp088.vm.VirtualMachine.VMState;
1513
import se.student.liu.jessp088.vm.instructions.Instruction;
1614
import se.student.liu.jessp088.vm.instructions.Literal;
1715
import se.student.liu.jessp088.vm.instructions.arithmetic.Sub;
1816
import se.student.liu.jessp088.vm.instructions.control.Jmp;
1917
import se.student.liu.jessp088.vm.instructions.data.Load;
2018
import se.student.liu.jessp088.vm.instructions.data.Store;
2119

22-
public class VirtualMachineTest
23-
{
20+
public class VirtualMachineTest {
2421
private Bytecode code;
2522
private VirtualMachine vm;
2623

‎src/test/resources/count_primes.vm

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ literal 0
5555
mul
5656
load #n
5757
cmp
58-
jmpGT check_mod_i_exit
58+
jmp_GT check_mod_i_exit
5959
dup
6060
load #n
6161
swap

‎uml.mdj

+28,047
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.