Back


Customizing Linguist

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

	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
Linguist compiler V1.00
Copyright (c) 1998 by Linguist Software

   4:
   5:    put 5 into N
   6:    double N
Line 6: 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:

//	BKDouble.java

package basic.keyword;

import compiler.*;
import runtime.*;
import linguist.*;
import linguist.condition.*;
import linguist.handler.*;
import linguist.keyword.*;
import linguist.value.*;
import basic.handler.*;

/******************************************************************************
   double {variable}
*/
public class BKDouble extends LKHandler
{
   public LHHandler handleKeyword() throws LLError
   {
      getNextToken();
      if (isSymbol())
      {
         LHHandler variable=getHandler();
         if (!(variable instanceof BHVariable))inappropriateType();
         return new BHDouble(getCurrentLino(),(BHVariable)variable);
      }
      return null;
   }
	
   /***************************************************************************
      Return the keyword string.
   */
   public String getKeyword()
   {
      return "double";
   }

   /***************************************************************************
      Return a list of other words used in this handler.
   */
   public String[] getOtherWords()
   {
      return null;
   }

   /***************************************************************************
      Return a summary of this keyword.
   */
   protected String getSummary()
   {
      return "Double a variable.";
   }

   /***************************************************************************
      Return a description of this keyword.
   */
   public String getDescription()
   {
      return LLError.noDescription();
   }
}

The name of the class is important, as explained elsewhere. Because we're adding to the basic package we call this file BKDouble (Basic Keyword Double). The class extends LKHandler, the root of all keyword handlers. Taking its methods one by one:

   public LHHandler handleKeyword() throws LLError
   {
      getNextToken();
      if (isSymbol())
      {
         LHHandler variable=getHandler();
         if (!(variable instanceof BHVariable))inappropriateType();
         return new BHDouble(getCurrentLino(),(BHVariable)variable);
      }
      return null;
   }

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 returns the handler for the variable. 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 BHVariable) 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 (X)LErrors class (where (X) 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 BHDouble, which we haven't yet written, so read on.

   /***************************************************************************
      Return the keyword string.
   */
   public String getKeyword()
   {
      return "double";
   }

This method is provided for use by a color-syntax script editor. Because there are no standard keywords, an editor won't know which words to color, so it looks in the keyword handler itself to find out. We are developing such an editor.

   /***************************************************************************
      Return a list of other words used in this handler.
   */
   public String[] getOtherWords()
   {
      return null;
   }

This method is also for a color-syntax editor. Where extra words are used by a command, these should be listed in a String array.

   /***************************************************************************
      Return a summary of this keyword.
   */
   protected String getSummary()
   {
      return "Double a variable.";
   }

This method is provided for use by a documentation system, probably associated with a script editor. Selecting a word will cause its one-line description to be shown in a status bar somewhere. Nothing has yet been written for this.

   /***************************************************************************
      Return a description of this keyword.
   */
   public String getDescription()
   {
      return LLError.noDescription();
   }

This method is also provided for use by a future documentation system, as an extended description of the keyword and its use.

The New Runtime Handler

Now we'll create the runtime handler for the double command; it looks like this:

// BHDouble.java

package basic.handler;

import runtime.*;
import linguist.*;
import linguist.condition.*;
import linguist.handler.*;
import linguist.keyword.*;
import linguist.value.*;

/******************************************************************************
   Double a variable.
*/
public class BHDouble extends LHHandler
{
   private BHVariable variable;		// the variable to double

   public BHDouble(int line,BHVariable variable)
   {
      this.line=line;
      this.variable=variable;
   }

   public int execute() throws LRError
   {
      variable.setValue(variable.getValue()*2);
      return pc+1;
   }
}

A variable handler is somewhat 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.

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 BHVariable 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 V1.00
Copyright (c) 1998 by Linguist Software

Compilation of Test.ls finished - no error(s) detected.
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.

Adding a new 'value'

Adding a new package


Back