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 NewValueHolder subclass: #CollectionValueHolder instanceVariableNames: '' classVariableNames: '' package: 'NewValueHolder-Core-Base' so it can serve as an MVC model.

Initialize it with a stack slot that holds an OrderedCollection SequenceableCollection subclass: #OrderedCollection instanceVariableNames: 'array firstIndex lastIndex' classVariableNames: '' package: 'Collections-Sequenceable-Ordered' 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 GtLudoGameExamples subclass: #GtLudoRecordingGameExamples instanceVariableNames: '' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Examples' 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 NewValueHolder subclass: #CollectionValueHolder instanceVariableNames: '' classVariableNames: '' package: 'NewValueHolder-Core-Base' functionality.

In the first case, see GtLudoRecordingGame>>#gtMovesFor: gtMovesFor: aView <gtView> ^ (aView columnedList) title: 'Moves'; priority: 40; items: [ self moves ]; column: 'Index' text: [ :eachItem :eachIndex | eachIndex asRopedText foreground: Color gray ] width: 45; column: 'Roll' text: [ :move | move roll ]; column: 'Player' text: [ :move | move player ]; column: 'Token' text: [ :move | move token ]; updateWhen: GtLudoMoveRecorded in: self announcer as an example of a Ludo game view that is updated when GtLudoMoveRecorded Announcement subclass: #GtLudoMoveRecorded instanceVariableNames: '' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Announcements' is generated by GtLudoRecordingGame>>#notifyMoveRecorded notifyMoveRecorded announcer announce: GtLudoMoveRecorded new .

In the second case, ValueChanged Announcement subclass: #ValueChanged instanceVariableNames: 'newValue oldValue' classVariableNames: '' package: 'System-Model-Announcements' is generated by NewValueHolder Model subclass: #NewValueHolder instanceVariableNames: 'lock value' classVariableNames: '' package: 'NewValueHolder-Core-Base' and its subclasses, and then listened for in views such as SPLContext>>#gtContinuationFor: gtContinuationFor: aView <gtView> ^ aView textEditor title: 'Continuation'; priority: 30; aptitude: BrGlamorousCodeEditorAptitude; text: [ self continuation printString asRopedText ]; actionUpdateButton; actionButtonIcon: BrGlamorousIcons right tooltip: 'Step' action: [ :aButton :aTab | self updateProgramIfChanged: aButton phlow textViewContent asString. self step ]; actionButtonIcon: BrGlamorousIcons tohere tooltip: 'Run' action: [ :aButton :aTab | self updateProgramIfChanged: aButton phlow textViewContent asString. self run ]; updateWhen: ValueChanged in: self stateHolder announcer 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 GtLudoGame subclass: #GtLudoRecordingGame instanceVariableNames: 'moves' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Model' and SPLContext Object subclass: #SPLContext instanceVariableNames: 'environment outputHolder traceHolder nodeHolder' classVariableNames: '' package: 'GToolkit-Demo-SPL-Interpreter' . For examples, see GtLudoRecordingGameExamples GtLudoGameExamples subclass: #GtLudoRecordingGameExamples instanceVariableNames: '' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Examples' and SPLCaseStudyExamples SPLContextExamples subclass: #SPLCaseStudyExamples instanceVariableNames: '' classVariableNames: '' package: 'GToolkit-Demo-SPL-Examples' .

Extend the stack machine to support a simplified Forth