|
||||
The Bugs Interpreter Part 2 assignment is a particularly difficult one. As I see it, there are two main sources of difficulty:
Parser for the Bugs language.
Given a String, it will produce the Abstract Syntax Tree (AST).Bug class
ThreadMaster and Worker
classes that demonstrate how to coordinate Threads.How do you put all these pieces together?
| What the user does | What happens |
|---|---|
| The user writes a program in the Bugs language, using his or her favorite text editor. | Nothing yet. |
| The user starts our Bugs interpreter, possibly by double-clicking on a program named Bugs. | The GUI appears. It currently has buttons labeled New, Step, Run, Pause, and Reset. Since we don't yet have a program loaded, these buttons should all be disabled. |
| The user looks for a Load... menu item. | This is the usual way of loading a file. We do have a File menu, we should add a Load... command to it. |
| The user loads in a Bugs program. | The GUI should ask the Parser to parse the program. If there are syntax errors, it should notify the user in as much detail possible. If the program is correct, the Step and Run buttons should be enabled. The New button made sense when the GUI created a model (the bouncing ball). It doesn't seem to make sense now, so it should be eliminated. What else can be done to prepare the program? The Allbugs section declares variables and functions, and these can be extracted and put into hash maps. The named Bugs can be created (and given their respective ASTs), and put into another hash map. The maps I have are:
Interpreter. Ideally, one GUI action should call one model method,
but it doesn't actually hurt to call several. |
| The user clicks the Run button. | The GUI tells the interpreter to execute the
program. The interpreter has to create a Thread for each bug (if it
hasn't done so already), and start them. It needs to use code extremely
similar to that in ThreadManager to
coordinate the Bugs (and each bug has to have code extremely similar to
that in Worker). You should study
this code.A bug may execute as many "statements" as it likes, but only one "action". After an action, it needs another work permit to continue. Actions draw something on the screen. |
| The user clicks the Pause button. | The Bugs should pause execution as soon as possible. Effectively, this means that the interpreter should check a "blocked" flag that tells it to stop handing out "work permits". |
| The user clicks the Step button. | The interpreter should hand out one and only one more round of work permits each time the Step button is clicked. |
| The user clicks the Run button again. | The "blocked" flag should be cleared, and the interpreter should resume handing out work permits. It does not do all the work required for the first click of the Run button. |
| [Not a user action:] A bug reaches the end of its program and the Thread dies. | The interpreter should notice the Thread death, and thereafter hand out work permits only to live bugs, not dead ones. If/when all Threads die, the interpreter has nothing more to do. |
| The user clicks the Reset button. | The interpreter should stop any remaining bugs, and return to the state it was in just after the Bugs program was loaded. |
| The user loads another program. | All current activity should cease, and the new program should be processed the same way the previous one was. |
The MVC is usually the best pattern to use when writing a GUI-based program. How do we divide the above work into these three parts?
The Controller: The GUI class has all the controls on it, so it is most naturally part of the Controller. The Controller might also contain other classes for interacting with the user, but in this assignment there isn't much more.
The Model: The Model should do all the actual work of the application, so it will include the
Parser, theBugclass (for interpreting the code of individual bugs), theInterpreterclass (for controlling and coordinating the bugs--which also makes it a handy place for keeping the variables and functions declared in Allbugs). A few other classes are included in the Model, such asTree,Token, andSyntaxException.The View: It will turn out that there is some work involved in preparing results to display, so it helps to have a separate
Viewclass to do that work. Since the display happens in the large central JPanel of the GUI, a really good way to do this is to write a View class that extends JPanel, and put the View object (rather than a plain old JPanel) into the GUI.
The use case and the MVC pattern, taken together, give us some strong hints about the design.
The GUI: This is your Controller. When Load... is chosen, a program has to be read in, parsed, and partly interpreted. The GUI should ask the user for the File; then it could either read in the File and give the text to the Interpreter, or it could give the text to the Parser and give the resultant AST to the Interpreter. When Run or Step is chosen, it should tell the Interpreter to run or to single step.
The Interpreter: The Interpreter class is the "main" class
of the Model. The constructor should be given an AST for a <program> (or
the text of a program to parse into an AST), and it should construct the
needed maps (Allbugs variables, Allbugs functions, and Bugs). Later, when
told to Run or Step,
it should start() all the Bugs (if they haven't already been
started), and hand out "work permits" to either run continuously or single
step.
Quick Java review. The wait() method causes a program to pause right where it is, and wait until it gets a notification. The notifyAll()
method sends a notification to all waiting bugs. Since you cannot
control which bugs get notified, when a waiting bug gets notified, it
usually has to perform some other check to see whether it can proceed,
or whether it has to wait some more. These methods are only allowed in synchronized code. |
The Bugs: Each Bug runs in a separate Thread. The constructor for a Bug needs to be given the AST for that bug, so that it has something to interpret. It also needs to be given a reference to the Interpreter that started it up, because it will have to go to that interpreter to get work permits, and to ask for variables and functions declared in the Allbugs part, and to ask for other Bugs (so it can read certain variables from them, such as Fred.x).
Getting the interpreter to create all the Bugs, and to coordinate their activities, is a fairly complex problem. The GUI just adds complexity, and makes testing that much harder. If you are using MVC, the Model part is completely independent of the Controller and View. This is a huge advantage, because you don't need the GUI to test the Model. All you need is a simple |
The View. You might think that a Bug, when it moves, draws a line directly on the display. This seems obvious, but it's horribly wrong, and causes more problems than I have the energy to write about. Instead I'll just remind you that a Bug, which is part of the Model, should be completely independent of the View.
Here's what it does instead. When a bug "draws a line" (with a move, moveto, or line
command), it really just creates an object representing that line, and
puts it in a list of "things that should be drawn." In my code I have a
simple class called Command that holds the x and y
coordinates of the two endpoints of a single line, and the color that
the line should be. Each bug could keep its own list of commands, but I
found it slightly simpler to have just one list, in the Interpreter class.
The View class extends JPanel, and is the large area in the center of the GUI. When its paint(Graphics g)
method is called, it paints all the lines in the list of Commands. Then
it draws something to represent the individual bugs, at each bugs
current position. It does this 25 times a second (once every 40 ms.),
regardless of what the bugs are doing or not doing. This is done in a
completely independent Thread, created by a javax.swing.Timer object that I created when I first started the program and made a View object; the Timer calls repaint() every 40 milliseconds.
A note on the speed control slider in the GUI: It does not control how often things are drawn. Instead, it controls how long the Interpreter should
sleepbefore it tries to hand out more work permits.