13
13
import java .io .IOException ;
14
14
import java .nio .file .Files ;
15
15
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 ;
18
38
import javax .swing .border .LineBorder ;
19
39
import javax .swing .filechooser .FileNameExtensionFilter ;
20
40
import javax .swing .table .DefaultTableModel ;
21
41
import javax .swing .text .BadLocationException ;
22
42
23
43
import se .student .liu .jessp088 .vm .Bytecode ;
44
+ import se .student .liu .jessp088 .vm .DebugListener ;
24
45
import se .student .liu .jessp088 .vm .DefaultVirtualMachine ;
46
+ import se .student .liu .jessp088 .vm .Variables ;
25
47
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 ;
26
50
import se .student .liu .jessp088 .vm .parsing .Lexer ;
27
51
import se .student .liu .jessp088 .vm .parsing .Parser ;
28
52
import se .student .liu .jessp088 .vm .parsing .Token ;
@@ -38,6 +62,7 @@ public class GUI {
38
62
private static final Icon DEBUG_ICON = new ImageIcon (GUI .class .getResource ("/debug_16.png" ));
39
63
private static final Icon CONSOLE_ICON = new ImageIcon (GUI .class .getResource ("/pc_16.png" ));
40
64
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" ));
41
66
private static final Icon PROGRAM_ICON = new ImageIcon (GUI .class .getResource ("/vm_16.png" ));
42
67
private static final Icon RESUME_ICON = new ImageIcon (GUI .class .getResource ("/resume_16.png" ));
43
68
private static final Icon PAUSE_ICON = new ImageIcon (GUI .class .getResource ("/pause_16.png" ));
@@ -46,14 +71,12 @@ public class GUI {
46
71
47
72
private static final Font CODE_FONT = new Font ("Consolas" , Font .PLAIN , 12 );
48
73
74
+ private final ExecutorService executor = Executors .newSingleThreadExecutor ();
75
+
49
76
private Lexer lexer ;
50
77
private Parser parser ;
51
78
private VirtualMachine vm ;
52
79
53
- // Debugging state
54
- private Bytecode parseResult ;
55
- private boolean isDebugging ;
56
-
57
80
private JFrame frame ;
58
81
private JFileChooser fc ;
59
82
private JTabbedPane programTabs ;
@@ -63,6 +86,11 @@ public class GUI {
63
86
64
87
private final int maxStackSize = DEFAULT_MAX_STACK_SIZE ;
65
88
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 ;
66
94
67
95
/** Launch the application. */
68
96
public static void main (final String [] args ) {
@@ -145,20 +173,26 @@ private void initialize() {
145
173
final JSeparator separator = new JSeparator ();
146
174
runMenu .add (separator );
147
175
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 ));
149
178
resumeOption .setEnabled (false );
150
179
resumeOption .setIcon (RESUME_ICON );
151
- resumeOption .addActionListener (this ::resumeDebug );
180
+ resumeOption .addActionListener (this ::resumeExecution );
152
181
runMenu .add (resumeOption );
153
182
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 ));
155
185
pauseOption .setEnabled (false );
156
186
pauseOption .setIcon (PAUSE_ICON );
187
+ pauseOption .addActionListener (this ::pauseExecution );
157
188
runMenu .add (pauseOption );
158
189
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 ));
160
193
stopOption .setEnabled (false );
161
194
stopOption .setIcon (STOP_ICON );
195
+ stopOption .addActionListener (this ::stopExecution );
162
196
runMenu .add (stopOption );
163
197
164
198
final JSeparator separator_1 = new JSeparator ();
@@ -173,6 +207,19 @@ private void initialize() {
173
207
final JMenuItem addBreakpointOption = new JMenuItem ("Add Breakpoint at Cursor" );
174
208
addBreakpointOption .addActionListener (this ::addBreakpointAtCursor );
175
209
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
+
176
223
configurationOption .addActionListener (e -> {
177
224
final RunConfigDialog dialog = new RunConfigDialog (frame );
178
225
@@ -187,7 +234,6 @@ private void initialize() {
187
234
programExtrasSplitter .setOrientation (JSplitPane .VERTICAL_SPLIT );
188
235
189
236
programTabs = new JTabbedPane (SwingConstants .TOP );
190
- programExtrasSplitter .setLeftComponent (programTabs );
191
237
192
238
// final JTextArea codeTextArea = new JTextArea();
193
239
// codeTextArea.setBorder(new LineBorder(Color.GRAY));
@@ -204,6 +250,7 @@ private void initialize() {
204
250
programExtrasSplitter .setRightComponent (extrasTabs );
205
251
206
252
consoleTextArea = new JTextArea ();
253
+ consoleTextArea .setEditable (false );
207
254
consoleTextArea .setBorder (new LineBorder (Color .GRAY ));
208
255
consoleTextArea .setFont (CODE_FONT );
209
256
final JScrollPane consoleScrollPane = new JScrollPane (consoleTextArea );
@@ -219,16 +266,87 @@ private void initialize() {
219
266
220
267
final JScrollPane variableScrollPane = new JScrollPane (variableTable );
221
268
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
+ });
222
331
}
223
332
224
333
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
+ });
228
346
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);
232
350
233
351
programTabs .addTab (tabName , PROGRAM_ICON , codeScrollPane , null );
234
352
}
@@ -288,36 +406,33 @@ private void openFile(final ActionEvent e) {
288
406
private void runCodeInOpenTab (final ActionEvent e ) {
289
407
final Bytecode result = parseCodeInOpenTab ();
290
408
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
+ });
298
414
}
299
415
300
416
private void debugCodeInOpenTab (final ActionEvent e ) {
301
- parseResult = parseCodeInOpenTab ();
302
- if (parseResult == null ) return ;
417
+ final Bytecode result = parseCodeInOpenTab ();
418
+ if (result == null ) return ;
303
419
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
+ }
305
429
306
- resumeDebug (e );
430
+ private void pauseExecution (final ActionEvent e ) {
431
+ vm .pauseExecution ();
307
432
}
308
433
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 ();
321
436
}
322
437
323
438
private void closeCurrentProgramTab (final ActionEvent e ) {
@@ -331,15 +446,36 @@ private void closeCurrentProgramTab(final ActionEvent e) {
331
446
}
332
447
333
448
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 () {
334
471
try {
335
472
final JTextArea text = getCurrentProgramTextArea ();
336
- if (text == null ) return ;
473
+ if (text == null ) return - 1 ;
337
474
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 ;
341
476
} 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 ;
343
479
}
344
480
}
345
481
0 commit comments