The expression parsers shown in this chapter are quite useful in a variety of applications because they enable you to offer expanded functionality without significant effort on your part. Consider the situation in which your program requests that the user enter a numeric value. For example, an application might ask the user to enter the number of copies of a document to print. Normally, you would simply display a text field, wait for input, and then convert that text into its internal numeric format. This simple approach would allow the user to enter a value, such as 100. However, what if the user wanted to print 72 copies for each of 9 departments? The user would need to manually compute that product and then enter the value 648 into the text field. However, if you use the parser to compute the value obtained from the text field, then the user could enter 9*72, and no manual computation would be required. The ability to parse and evaluate a numeric expression can add a sophisticated, professional feel to even the simplest application. Try using the parser to handle numeric input for one of your applications.
As mentioned early on in this chapter, only minimal error checking is performed by the parser. You might want to add detailed error reporting. For example, you could highlight the point in the expression at which an error was detected. This would allow the user to find and correct a syntax error.
As the parser now stands it can evaluate only numeric expressions. However, with a few additions, it is possible to evaluate other types of expressions, such as strings, spatial coordinates, or complex numbers. For example, to allow the parser to evaluate strings, you must make the following changes:
1. Define a new token type calledSTRING.
2. EnhancegetToken( )so that it recognizes strings.
3. Add a new case insideatom( )that handlesSTRINGtokens.
After implementing these steps, the parser could handle string expressions like these: a = "one"
b = "two" c = a + b
CHAPTER
3
Implementing Language
Interpreters in Java
by Herb Schildt
39
ApDev TIGHT /The Art of Java / Schildt/Holmes / 222971-3 /
P:\010Comp\ApDev\971-3\ch03.vp Monday, July 07, 2003 5:17:47 PM
Color profile: Generic CMYK printer profile Composite Default screen
H
ave you ever wanted to create your own computer language? If you're like most programmers, you probably have. Frankly, the idea of being able to create, control, enhance, and modify your own computer language is very appealing. However, few programmers realize how easy, and enjoyable, it can be. Be assured that the development of a full-featured compiler, such as Java, is a major undertaking, but the creation of a language interpreter is a much simpler task.Although both interpreters and compilers take as input the source code for a program, what they do with that source code differs significantly. A compiler converts the source code of a program into an executable form. Often, as is the case with a language like C++, this executable form consists of actual CPU instructions that are directly executed by the computer. In other cases, the output of a compiler is a portable intermediate form, which is then executed by a runtime system. This is the way Java works. In Java, this intermediate code is called bytecode.
An interpreter works in a completely different way. It reads the source code to a program, executing each statement as it is encountered. Thus, an interpreter does not translate the source code into object code. Instead, the interpreter directly executes the program. Although program execution via an interpreter is slower than when the same program is executed in its compiled form, interpreters are still commonly used in programming for the following reasons.
First, they can provide a truly interactive environment in which program execution can be paused and resumed through user interaction. Such an interactive environment is helpful in robotics, for example. Second, because of the nature of language interpreters, they are especially well suited for interactive debugging. Third, interpreters are excellent for “script languages,” such as query languages for databases. Fourth, they allow the same program to run on a variety of different platforms. Only the interpreter's runtime package must be implemented for each new environment.
Sometimes the terminterpreteris used in situations other than those just described. For example, the original Java runtime system was called abytecode interpreter. This isnotthe same type of interpreter developed in this chapter. The Java runtime system provides an execution environment for bytecode, which is a highly optimized set of portable machine instructions. Thus, the Java runtime system does not operate on source code, but on portable machine code. This is why the Java runtime system is called the Java Virtual Machine.
In addition to being an interesting and useful piece of code, the interpreter developed in this chapter serves a second purpose: it demonstrates the streamlined elegance of the Java language. Like the parser in Chapter 2, the language interpreter is a “pure code” example. It is also a fairly sophisticated program. The ease by which the interpreter can be implemented in Java gives testimony to Java's versatility. Moreover, the transparency of the code shows the expressive power of the Java syntax and libraries.