Interpreter

Jim's Pages => Java Pages => Interpreter

This program was inspired by an article at Java Developer's Journal, but the code is all original except for a few regular expression patterns.  I did this little project as a way to explore reflection and Test Driven Development.

Interpreter executes commands that can instantiate and exercise Java classes. The Ractor program is provided as the minimal interactive UI. In Ractor, a user enters commands in a the scripting language and Interpreter executes the commands. You could make more elaborate programs that integrate Interpreter into an application GUI for testing or debugging.

The command syntax is:

[varname] = [object.] method [ arg [, arg ] ]

If varname is provided, the results of the command (if not a void method) are stored in a variable of that name. The result is also always stored in a variable called result. The result is always returned as an Object from Interpreter.interpret()

If object is provided, the command is executed on a previously instantiated object stored in a variable of that name. If object is not provided, the method is executed on the interpreter itself. Interpreter methods are listed below.

The method is the only required bit of syntax.

The scripting syntax to create a new object is not standard Java. It uses the object.method syntax as classname.new. The keyword new tells the Interpreter that Object is a classname instead of a variable.

The argument list is optional. There are no parens. Arguments are comma delimited. Escape any commas in the argument values as \, Note that you don't have to escape other characters, even quotes in quoted strings.

Argument Types

The interpreter goes through several steps to determine the argument type.

  1. If Java casting syntax is used, take the type from the cast. This overrides a variable's actual type or an entered value.
  2. If we don't have a type yet and the argument is a variable, take the type from the variable.
  3. If we don't have a type yet and the argument is a value, attempt to infer the type from common Java notation. Numeric types all allow a leading minus sign.
    1. Digits => int
    2. Digits with trailing L => long
    3. Digits with decimal point or trailing F => float
    4. Digits with scientific notation and/or trailing D => double
    5. true or false => boolean
    6. Wrapped in double quotes => String
    7. Wrapped in single quotes => char
  4. Expand synonyms for convenience when casting and creating. Currently Object, String, Long, Float, Double, Boolean and StringBuffer are synonyms for the fully qualified name java.lang.etc. The Parameter class lets you add other synonyms for your own types.

When dealing with a value (not a variable) the interpreter creates an object of the correct type. That requires the correct type to have a constructor with a single String argument. (Character does not have such a constructor, but Interpreter takes care of that for you.) So you can cast a value argument to any type that has a String constructor. You can cast a variable argument to any superclass or implemented Interface.

The value null can be used to pass a null argument. It must be cast to a type, e.g. (String)null.

Interpreter uses strict type matching on method calls. The cast or inferred types must match the method definition exactly. See how that forces some casting in the examples below.

Interpreter Script Methods

The following methods may be invoked on the Interpreter in script commands by omitting the object portion of the command syntax.

Interpreter Public Java Methods

The following methods may be invoked on the Interpreter object by other Java objects. The calling application may preload a set of variables that the user might explore or manipulate, or to retrieve results when done.

The Argument class has the following methods to manipulate synonyms:

Example Ractor Session

The following lines were captured from a Ractor session. I added the numbers for the detailed discussion following. When Ractor displays "Ractor: Result=" it is showing the value returned by the Interpreter.interpret( command ) call. Lines that do not start with Ractor: are printed by the interpreter or by the methods invoked on other classes.

01 Ractor: Enter command: s = String.new "Say "Hello" to Ractor!"
02 Ractor: Result=Say "Hello" to Ractor!
03 Ractor: Enter command: sb = StringBuffer.new
04 Ractor: Result=
05 Ractor: Enter command: sb.append "Hello\,"
06 Ractor: Result=Hello,
07 Ractor: Enter command: sb.append " Momma!"
08 Ractor: Result=Hello, Momma!
09 Ractor: Enter command: get (Object)sb
10 Ractor: Result=Hello, Momma!
11 Ractor: Enter command: print (Object)sb
12 Hello, Momma!
13 Ractor: Result=Hello, Momma!
14 Ractor: Enter command: list
15 Variable Name=result  Class=java.lang.StringBuffer
16 Variable Name=s  Class=java.lang.String
17 Variable Name=sb  Class=java.lang.StringBuffer
18 Ractor: Result=null
19 Ractor: Enter command: b = s.substring 5, 9
20 Ractor: Result=Hell
21 Ractor: Enter command:
22 Ractor: Done 
  1. Creates a new String. The synonym filled in java.lang.String. Notice that the outside set of quotes identifies a String, but we don't have to escape the quotes inside.
  2. Ractor displays the return value from the Interpreter.
  3. Creates a new StringBuffer as variable sb. Again the synonym provides java.lang on the classname.
  4. The result is empty as sb is an empty string buffer.
  5. Appends a string to the StringBuffer. I have to escape the comma because I separate arguments by commas before I pattern-match them for types. This works so naturally most of the time, I didn't want to mess it up by only finding commas outside quoted strings.
  6. Shows the results
  7. Appends another string. Note the leading space within the quoted string was retained.
  8. Shows the result as expected by Johnny Bravo's momma.
  9. The internal get method. I have to cast the variable to Object because that's what get expects. The synonym provides java.lang
  10. Ractor displays what it got
  11. The internal print command, cast to Object again.
  12. Interpreter printed this line
  13. Ractor displays what it got
  14. The internal list command
  15. The automatic variable result contains the latest result
  16. s is still a String
  17. sb is still a StringBuffer
  18. Ractor displays what it got: nothing!
  19. Shows a method with two integer arguments
  20. Where you go on the way to learning reflection
  21. Empty command ends Ractor
  22. We gone, bye bye.