Curriculum Programmable RPN Calculator
From Real Software Documentation
Aim
We make the calculator from the last lesson programmable with RBScript. This is an interesting exercise, since RBScript’s ability to communicate with other parts of the same program was initially fairly limited.
About RBScript
RBScript is a class that can run Real Studio programs. These programs are loaded into an RBScript object as text, and are then compiled and run. This means you can make your program modifiable by the end user.
Note that what is available to RBScript is actually a large subset of the full Real Basic language. See the built-in Language Reference for details.
At this point, you should read through the built-in Language Reference’s information about RBScript.
The Challenge
The challenge in using RBScript is that it only offers a very simple way of communicating with the rest of the program. The code that is running in an RBScript program has access to almost the full REALbasic programming language, but none of the built-in Real Studio objects. There are just two commands that an RBScript program can use to communicate with the rest of the program:
- The Input function, which takes a single string argument and calls the RBScript object’s Input event handler, which receives the same string as an argument. The event handler can return a string, which becomes the result of the function in RBScript.
- The Print command, which takes a single string argument and calls the RBScript object’s Print event handler, which receives the string as an argument.
RBScript has no ability to call any part of the program it is running in, or indeed any other way to communicate with that program, other than through these two commands.
The challenge is that we want to provide the ability to “add RBScript code to a button”, and to extend RBScript’s abilities so that it supports some suitable extra commands for communicating with the calculator’s stack. We will implement two functions (Pop and Size) and size methods (Clear, Drop, Dup, Over, Swap, and Push).
At this point, you should stop and think about how you can let the end user write a script like this:
Push Pop * Pop
when RBScript doesn’t provide these stack commands.
Hint: You will need to add extra code to the RBScript that the user provides, containing implementations of all of the stack commands. These commands will need to communicate with the stack through the Print and Input commands.
The Project
- Open the project RBScriptCalculator.
What we will do is define a Protocol for the RBScript code to communicate with the rest of the program. A protocol is a set of rules by which everybody agrees to behave. In this case, we will define a set of messages that can be sent through the Input and Event commands in RBScript, and their associated events in the RBScript object, which will implement the stack manipulation commands we need. Then, we will add extra code to the RBScript provided by the user, which implements methods to handle the protocol for each action. To the end user, it will appear as though these methods are built-in to RBScript.
- Open the code editor for the StackScript class.
- Examine the Input and Print event handlers.
The protocol is very straightforward. For example, if we receive the string “Pop” from the RBScript, we pop the top value from the stack and return it.
The most complex command is Push, which needs to support receiving an argument from the RBScript. What we do is separate the value from the command with a space.
- Now examine the code called from the constructor in this class.
To simplify adding the extra methods to the RBScript, we build up a string through some auxiliary methods. We begin by noticing that there are just three types of functions or methods we need to add to the string we are storing in TheScriptHeader, which will be added to the user’s RBScript code:
* Methods with no arguments;
* Methods with one argument; and
* Functions with no arguments
We notice that all of these do the same thing: they pass their name out through either the Input or Print commands; if there is an argument, that is passed through the same command, separated from the name with a space.
So we create three auxiliary methods to generate method or function declarations, given that method or function’s name, and we just call the appropriate auxiliary method for each method or function we need to add to TheScriptHeader. Notice that we have an AddFunction1 that we never use; it’s just there for completeness, in case we want to extend this project at some point. Other than all this stuff, there are getter and setter methods for the script itself, a SetStack method (so this class can be a StackOperation) and that’s about it.
- Look through the RBScriptButton and ButtonEditWindow, to see how that all works.
- Run the project, Alt+click (Option-click on Macintosh) on one of the blank buttons and add a script to it.
For example:Dup
Push Pop * Pop
This is a great project for extension:
- There is no error handling, which makes it very easy to cause the program to generate an unhandled exception. Add enough error handling so there is no way for the user to cause an unhandled exception. You should probably ignore some errors, and display the most useful command you can in the case of others. Note that the RBScript object has a property to tell you which line it is executing. It would be nice to tell the user which line in his RBScript caused an error.
- Add extra commands for the users to call. Commands to read and write data from files would be useful; and
- A more ambitious exercise would be to have the calculator save the scripts in its buttons when it quits, and reload them when it is launched. The PreferencesFolder function is very useful for this…
