16.5 Building Commands


 
Building Parsers with Java
By Steven  John  Metsker

Table of Contents
Chapter  16.   Parsing an Imperative Language

    Content

Imperative languages such as Sling let a user write a script, or program, that tells the computer what to do. When you create a parser for an imperative language, a good approach is to follow the command pattern [Gamma et al.]. The intent of this pattern is to encapsulate a request. The Sling parser builds a command object that contains the user's request in a Java object. An advantage of building a command from an imperative script is that this approach divides your user's task into two phases.

Users, when programming in your language, will spend much of their time trying to arrive at a syntactically correct input that your parser can recognize. Simple typographic errors as well as deeper misunderstandings about the rules of your language will usually be a significant barrier for users. It is advisable to have your program follow along with the state your user is in. First, help the user to enter syntactically correct input by issuing informative messages about why the parser cannot parse the input text. Issue no output and perform no commands until your parser successfully comprehends the user's complete input.

Once users get past the hurdle of entering input that your parser can recognize, their task is to verify that the program does the right thing. For example, a syntactically correct program for Sling might produce a picture that surprises users. Now they will struggle with the meaning, or semantics, of the program, a different struggle from that of entering recognizable input. An advisable approach is to assemble a Command object as your parser recognizes the user's request, and then execute the command.

A command for a typical imperative language script is a composition of other commands. The command that represents the entire script usually contains branching and looping commands along with commands that pertain only to the specific context of the language. Figure 16.14 shows a package of reusable commands. When you create an imperative language, you can combine these classes with your own subclasses of Command that are specific to the goals of your language.

Figure 16.14. The imperative package. This package contains a collection of subclasses of the abstract Command class. Each subclass encapsulates a request that a calling object can execute at any time.

graphics/16fig14.gif

These classes by themselves cannot create really interesting composite commands. For that you must add new commands that pertain to a context. The Sling environment adds new commands for assigning a function to a variable and for adding a function to a function collection. But to show how the commands can be composed , the next section gives a simple example that uses only the classes in sjm.imperative .

16.5.1 A Command Example

The commands in sjm.imperative allow simple input and output. Today's users typically expect graphical user interfaces for any programming language. However, you can use the PrintlnCommand and ReadCommand classes to create simple examples of composite commands.

Most of the classes in sjm.imperative rely on classes and interfaces from the package sjm.engine . Classes in the Command hierarchy must use variables that they can evaluate on demand, and the classes in sjm.engine fill this need. For example, a PrintlnCommand object can print any instance of Term . Here is a program that builds a for command and then executes the command:

 package sjm.examples.imperative;  import sjm.engine.*; import sjm.imperative.*; /**  * This class shows a simple composition of commands from  * <code>sjm.imperative</code>.  */ public class ShowCommand { public static void main(String[] args) {     Fact go          = new Fact("go!");     PrintlnCommand p = new PrintlnCommand(go);     Variable i       = new Variable("i");     ForCommand f     = new ForCommand(i, 1, 5, p);     f.execute(); } } 

This program wraps the string "go!" in a Fact because the PrintlnCommand class expects Term from sjm.engine as its argument. The Term interface allows PrintlnCommand to accept an argument that might be either a variable or a value.

The main() method of ShowCommand creates a variable i and a ForCommand f . The ForCommand object encapsulates a request to step i from 1 to 5, executing the command p each time. Running ShowCommand prints:

 go!  go! go! go! go! 

The power of the code is this: You have separated a composite command from its execution. You can execute the command any time in any thread on any computer to which you can pass the ForCommand object. Imperative language parsers can use the power of the command pattern to separate the parsing of a composite command from the execution of the command.

16.5.2 AssignmentCommand

An AssignmentCommand object class holds an object of class Evaluation from sjm.engine , and executes it upon receiving an execute() command. Figure 16.15 shows the AssignmentCommand class.

Figure 16.15. The AssignmentCommand class. An AssignmentCommand object holds an Assignment object, which it executes in response to the execute() method.

graphics/16fig15.gif

The AssignmentCommand class makes it possible to store an assignment for later execution. An AssignmentCommand object can appear inside another command, where it can be executed repeatedly. For example, the following program composes a ForCommand object that has an AssignmentCommand object as its body:

 package sjm.examples.imperative;  import sjm.engine.*; import sjm.imperative.*; /**  * This class provides an example of the assignment command.  *  * The <code>main</code> method of this class creates  * a variable "x" and pre-assigns it the value 0. Then the  * method creates a "for" command that encapsulates:  *  * <blockquote><pre>  *  *     for (int i = 1; i <= 4; i++) {  *         x = x * 10 + 1;  *     }  *  * </pre></blockquote>  *  * The method executes the "for" command, leaving x with the  * value 1111.0.  */ public class ShowAssignmentCommand { public static void main(String[] args) {     Variable x = new Variable("x");     x.unify(new NumberFact(0));     // *(x, 10.0)     ArithmeticOperator op1 =         new ArithmeticOperator('*', x, new NumberFact(10));     // +(*(x, 10.0), 1.0)     ArithmeticOperator op2 =         new ArithmeticOperator('+', op1, new NumberFact(1));     // #(x, +(*(x, 10.0), 1.0))     AssignmentCommand ac =         new AssignmentCommand(new Evaluation(x, op2));     ForCommand f =         new ForCommand(new Variable("i"), 1, 4, ac);     f.execute();     System.out.println(x); } } 

Running this program prints the following:

 1111.0 

The program creates the variable x and initializes its value to 1 . This initialization is outside any command object, although the program might equivalently establish the initialization as another AssignmentCommand . The program establishes two operators: one that multiplies x by 10 and another one that adds 1 to the first operator. The program creates an AssignmentCommand object that effectively stores the request x= x * 10 + 1; . The program places the assignment command in a for command, executes it, and displays the value of x .

16.5.3 CommandSequence

This class contains a sequence of other commands, which is a basic building block of composition. Looping and branching commands such as if , while , and for can execute a CommandSequence object that contains any number of other commands. Figure 16.16 shows the CommandSequence class.

Figure 16.16. The CommandSequence class. This class provides the basic ability to create a command as a sequence of other commands.

graphics/16fig16.gif

16.5.4 ForCommand

A ForCommand object executes some other command in a loop, iterating a given variable from a from value to a to value. Figure 16.17 shows ForCommand .

Figure 16.17. The ForCommand class. Most of the flexibility in the ForCommand class comes from its variety of constructors. The first constructor is fundamental; the other constructors use the first to construct a for command that iterates a given variable between two values.

graphics/16fig17.gif

The ForCommand class retains four parameters that the constructors receive or establish. These parameters are a setup command, a condition, an end command, and a body command. The execute() method executes a for loop, essentially executing

 for (setup; condition; endCommand){       bodyCommand;  } 

16.5.5 IfCommand

This command mimics a normal if statement, such as

 if (x > 7) {      // body to execute if condition is true }else {     // body to execute if condition is false } 

Figure 16.18 shows the IfCommand class.

Figure 16.18. The IfCommand class. When asked to execute() , an IfCommand object evaluates its condition and then asks either its ifCommand or elseCommand to execute.

graphics/16fig18.gif

16.5.6 NullCommand

This command does nothing, and that can provide exactly the right effect in some cases. For example, an if command with no given else uses a NullCommand object for its else command.

16.5.7 PrintlnCommand

This command, when executed, prints the value of a term provided in the constructor. This object can be either a structure (usually a fact) or a variable. Figure 16.19 shows the PrintlnCommand class.

Figure 16.19. The PrintlnCommand class. The second constructor in the figure allows the creator to specify the output stream; the default output stream is System.out .

graphics/16fig19.gif

16.5.8 ReadCommand

This command, when executed, reads a string and assigns it to a supplied variable. Figure 16.20 shows the ReadCommand class.

Figure 16.20. The ReadCommand class. A ReadCommand object encapsulates a request to accept input from a given source, which defaults to System.in .

graphics/16fig20.gif

16.5.9 WhileCommand

A WhileCommand object mimics a normal while loop, executing a given command in a loop as long as some condition holds true. Figure 16.21 shows WhileCommand .

Figure 16.21. The WhileCommand class. A WhileCommand , when asked to execute() , checks its condition, executes its command, and repeats this loop as long as the condition's value is true.

graphics/16fig21.gif

16.5.10 Commands Summary

Command classes let you compose a request and execute it later. This is important to parsers because it is advantageous to parse a user's program first to verify that the program is syntactically correct. Another advantage of constructing a composite command that encapsulates a user's program is that you may need to execute the program many times.

The Sling development environment parses a user's program into a command only when the program changes. The environment reexecutes the command each time a slider moves without reparsing the program text. If your language includes variable elements that may change after a user's program is parsed, it is especially important to build a composite command and reexecute it when the variables change.

The commands described in this section are only the beginning. You will almost always add new subclasses of Command to support imperative elements and functions that are relevant to your application.


   
Top


Building Parsers with Java
Building Parsers With Javaв„ў
ISBN: 0201719622
EAN: 2147483647
Year: 2000
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net