Back
To show you how easy it is to create custom scripts, here's an example. Suppose we want to add a command to the basic package that will double the value of a variable. The first thing is to write a test script:
! Test.ls package basic variable N put 5 into N double N prompt N exit
If you try to compile this script, this is what happens:
C:\Linguist>java LS Test.ls
Pass 1 of C:\Linguist\Test.ls 4: 5: variable N 6: 7: put 5 into N 8: double N Line 8 of C:\Linguist\Test.ls: I can't process the command starting with 'double'. C:\Linguist>
because there's no handler for the keyword double. So let's create one.
The New Keyword Handler
First we'll create a keyword handler for the double command; it looks like this:
// BasicKDouble.java package net.eclecity.linguist.basic.keyword; import net.eclecity.linguist.main.*; import net.eclecity.linguist.runtime.*; import net.eclecity.linguist.main.*; import net.eclecity.linguist.condition.*; import net.eclecity.linguist.handler.*; import net.eclecity.linguist.keyword.*; import net.eclecity.linguist.value.*; import net.eclecity.linguist.basic.handler.*; /****************************************************************************** double {variable} */ public class BasicKDouble extends LKHandler { public LHHandler handleKeyword() throws LLError { getNextToken(); if (isSymbol()) { LHHandler variable=getHandler(); if (!(variable instanceof BasicHVariable))inappropriateType(); return new BasicHDouble(getCurrentLino(),(BasicHVariable)variable); } return null; } }
The name of the class is important, as explained elsewhere. Because we're adding to the basic package we call this file BasicKDouble (Basic Keyword Double). The class extends LKHandler, the root of all keyword handlers. It only has one method, handleKeyword(). This method is called by the Linguist compiler when it encounters the word double in the script. The first thing it does is ask the compiler for the next token from the script. This is expected to be a variable, so it asks the compiler to go look it up in the symbol table, which stores the handler for the variable inside the compiler class. If the variable is not present the method returns a null handler, which tells the compiler that the keyword handler was unable to process the command but couldn't be sure it was an error. This allows the compiler to try another package in case that also handles double.
If the symbol is present we ask for the handler that was in the symbol table. Note that most of the work is being done by calls to methods in the compiler; most of LKHandler in fact consists of these calls, to avoid the need to subclass the compiler itself.
We now check the type of the handler. If it's not a simple variable (a BasicHVariable) we throw one of the standard errors. You can alternatively throw a new error of your own choosing, but you are recommended to keep the text of the message in the appropriate (Xxxx)LErrors class (where (Xxxx) is the package identifier) to keep the whole thing language-independent.
If the variable handler is the correct type we can now return a runtime handler. In this case it's a BasicHDouble, which we haven't yet written, so read on.
The New Runtime Handler
Now we'll create the runtime handler for the double command; it looks like this:
// BasicHDouble.java package net.eclecity.linguist.basic.handler; import net.eclecity.linguist.runtime.*; import net.eclecity.linguist.main.*; import net.eclecity.linguist.condition.*; import net.eclecity.linguist.handler.*; import net.eclecity.linguist.keyword.*; import net.eclecity.linguist.value.*; /****************************************************************************** Double a variable. */ public class BasicHDouble extends LHHandler { private BasicHVariable variable; // the variable to double public BasicHDouble(int line,BasicHVariable variable) { this.line=line; this.variable=variable; } public int execute() throws LRError { variable.setValue(variable.getValue()*2); return pc+1; } }
A variable handler is a little more complex that this, so I'll save that for explanation elsewhere. The job of this handler is to double the variable whose handler has been provided. All runtime handlers subclass LHHandler and provide a constructor and an execute() method. The constructor is always passed the script line number, for use by the debugger; all other parameters depend on what the handler is to do. Here we have a variable from the symbol table; this is saved in a private location. All handlers are pre-initialized in this way; you can pass as much information as you like as long as all objects are Serializable. This excludes things like Images, although you can if necessary extract their contents to another object before serializing (and the graphics package provides a means of preloading images at compile time).
The execute() method is called when the program reaches that point executing the compiled script. Here you do the doubling operation by reading the current value of the variable, doubling it and putting it back. You can alternatively duck the issue as follows:
public int execute() throws LRError { variable.double(); return pc+1; }
This avoids having to know how to double a variable; we simply ask the variable to double itself. The technique is more object-oriented and comes into its own on more complex operations, especially where the underlying functionality is itself a package provided by someone else. It also (with a small amount of extra work) enables the double handler to be used for more than one type of object, with each one doing its own doubling.
If you choose to ask the variable to double itself you'll need to provide a new public method in BasicHVariable that doubles its current value:
public void double() throws LRError { setValue(getValue()*2); }
Finally, when we recompile and run our test script it performs as expected:
C:\Linguist>java LS Test.ls -r
Linguist compiler VX.xx Copyleft (c) 2000 by Linguist Software Pass 1 of C:\Linguist\Test.ls Pass 2 of C:\Linguist\Test.ls Compilation of C:\Linguist\Test.ls finished - no error(s) detected. Output file 'C:\Linguist\Test.lrun' written. 10
C:\Linguist>
As you can see, adding extra keywords to your script language is pretty simple. We provide elsewhere a list of important points you will need to be aware of as you implement more complex features, plus a description of all the methods the compiler provides to make the job as easy as possible.