Implementing a moldable Stack Machine
Motivation
Stacks occur everywhere in computer science. They underly RPN calculators and are also the basic of stack-based languages like Forth.
The idea of this exercise is to build a stack machine with views of the state of the stack and the history of its execution, and actions to update the state of the stack and to perform various operations.
See also
Implementing a stack machine is a similar tutorial that focuses more on implementing the machine in an example-driven way, where each example also serves as a test case.
There is also an Exercism exercise to implement an evaluator for a subset of Forth. The stack machine could also be used as a starting point for such an evaluator
Tasks
Protoype the Stack Machine
Create a StackMachine
instance as a Moldable Object.
StackMachine new
Create the class using the fixit wrench and inspect it. You'll need to specify a Package, and optional a tag. Since it's a domain object, use the tag Model
.
Consider making it a subclass of CollectionValueHolder
so it can serve as an MVC model.
Initialize it with a stack
slot that holds an OrderedCollection
instance.
Add basic mutator methods
From the Contextual Playground experiment with updating the stack
state. Extract a method to push values onto the stack. Perhaps call the new method push:
or !
.
Also add pop
method.
Create some examples
Create a new class, StackMachineExamples
. Its tag should be Examples
.
Each of its methods will return an Example Object.
StackMachineExamples new empty
This one should just return a new StackMachine
. Create the method from the Contextual Playground, and then add some assertions. You'll probablu want to add methods like isEmpty
and top
so you can make some nice tests.
If you want to see examples of example methods, inspect the result of this query.
#gtExample gtPragmas & GtLudoGame package gtPackageMatches
Add further examples with one or more values pushed or popped. Be sure to compose examples from earlier examples.
(Have a look at the Examples Map view of GtLudoRecordingGameExamples
to see how examples can be composed.)
Add a basic view
We'd like a view of the state of the stack examples. Start with a Simple View, for example, a forwarding view to the stack slot.
Here are lots of examples of such forwarding views.
#gtView gtPragmas & #forward gtSenders
Update the view automatically
We want the view of the stack state to be updated when the state changes. This means that mutating operations should emit an Announcement that is listened to by the view. This can be done by defining your own announcements, or you can leverage the CollectionValueHolder
functionality.
In the first case, see GtLudoRecordingGame>>#gtMovesFor:
as an example of a Ludo game view that is updated when GtLudoMoveRecorded
is generated by GtLudoRecordingGame>>#notifyMoveRecorded
.
In the second case, ValueChanged
is generated by NewValueHolder
and its subclasses, and then listened for in views such as SPLContext>>#gtContinuationFor:
in the SPL example.
Verify that multiple Inspector views on the same Stack machine are updated when the state mutates.
Add calculator operations
Add arithmatic operations like +
, -
etc. that you would expect on a calculator.
Add a Custom Action for each operation.
Build a GUI view
Build a calculator view with buttons for entering digits and performing operations. Have a look at the Memory Game and the Ludo Game for inspiration in developing a simple grid layout with interaction.
Support back-in-time views
We would like to see a view of the history of StackMachine operations, and be able to explore previous states and the operations performed. This means that each StackMachine state becomes an immutable value, and every "mutating" operation actually produces a new state, which is logged in the history.
For inspiration, have a look at GtLudoRecordingGame
and SPLContext
. For examples, see GtLudoRecordingGameExamples
and SPLCaseStudyExamples
.
Extend the stack machine to support a simplified Forth
See the Forth exercism description.