JTB GJ1.0 is the special version of JTB for
GJ. It generates syntax trees that work with GJ.
This document describes the difference between this special edition and
the standard JTB. You are required to know JTB 1.2, before you start
working on JTB GJ1.0. If you are not yet familiar with JTB, please
see the Documentation page for the specifications
of JTB 1.2.
What's GJ
GJ
is a design that extends the Java programming language with the generic
types and methods. For example, GJ can support the following codes:
interface Collection<A> {
public void add(A x);
public Iterator<A> iterator();
}
The main benefit of GJ
over the current Java programming language lies in the added expressiveness
and safety that stems from making type parameters explicit and making type
casts implicit. GJ
is designed to be fully backwards compatible with the current Java language,
and in particular, one can retrofit existing library classes with generic
interfaces without changing their codes. For details about GJ and
downloading GJ compiler, please go to GJ
web page.
JTB GJ1.0
JTB GJ1.0 was upgraded from JTB 1.2. It preserves the structure of
JTB 1.2 and still generates Visitor.java interface and DepthFirstVisitor.java
class that support visitors without return value. Printer and Scheme
Tree Builder toolkits still subclass DepthFirstVisitor.
However, the following have been changed in order to utilize generic types
and methods provided by GJ:
-
Node classes in syntaxtree directory
public interface Node extends java.io.Serializable {
public void accept(visitor.Visitor v);
public <R,A> R accept(visitor.GJVisitor<R,A>
v, A argu);
}
Every syntax tree node generated by JTB GJ1.0 implements the above
interface. The second accept() method uses generic types
provided by GJ. You don't need to change the syntax tree nodes created
by JTB.
-
GJVisitor.java interface and GJDepthFirst.java class
JTB GJ1.0 doesn't generate ObjectVisitor interface and ObjectDepthFirst
class any longer. Instead, it will generate GJVisitor
interface and GJDepthFirst class in the visitor directory.
GJVisitor
looks like:
public interface GJVisitor<R,A> {
public R visit(XXXX n, A argu);
public R visit(YYYY n, A argu);
...
}
XXXX and YYYY stand for different names of the productions in your
grammar. R and A are type parameters. GJDepthFirst
is the default implementation of GJVisitor. It simply passes
the argument on while visiting each node of the syntax tree. Each
visit()
method in GJDepthFirst will return null as default. The
structure of GJDepthFirst is:
public class GJDepthFirst<R,A> implements GJVisitor<R,A>
{
public R visit(XXXX n, A argu) {
R _ret=null;
n.f0.accept(this, argu);
...
return _ret;
}
...
}
You may notice that GJDepthFirst still keeps type parameters.
You can specify those type parameters by making your own visitor extand
GJDepthFirst.
For further information, please see the example
on this page.
Note: All visitor classes must implement the Visitor or
GJVisitor
interface either directly or by subclassing a class which does so (such
as DepthFirstVisitor or GJDepthFirst).
Download
Requirements for JTB GJ1.0:
Instructions to install JTB GJ1.0:
-
Download jtbgj10.class. NOTE:
On Windows machines Netscape may save the file as "jtbgj10.exe".
Simply rename the file to jtbgj10.class.
-
Execute this class using a Java 1.1 (or higher) virtual machine.
For example, on Unix, the command would be:
-
Follow the on-screen instructions to complete installation.
Using JTB GJ1.0
Run JTB GJ1.0 on your .jj grammar file with the command:
% jtbgj yourfile.jj
Generate your parser by running JavaCC on jtb.out.jj
with the command:
% javacc jtb.out.jj
Use classes, generated by JTB GJ1.0 and JavaCC, to design
your own program according to GJ specifications, then compile it on GJ
compiler:
% gjc filename.java
GJ compiler will generate .class files executable on Java
Virtual Machine, so you can run your program by:
% java filename
Example
This section contains a very simple example of how to use GJVisitor
and GJDepthFirst. It uses Java1.1.jj
(Java grammar that includes the Java 1.1 language extensions provided by
JavaCC)
as the grammar file. The function of this example is to count how
many tokens, including <EOF>, are in the input Java file.
First run JTB GJ1.0 on Java1.1.jj,
then JavaCC on jtb.out.jj:
% jtbgj Java1.1.jj
% javacc jtb.out.jj
Download Main.java to
the directory where you run JTB and JavaCC, and download MyInt.java
and Counter.java
to the visitor directory generated by JTB. Finally, compile
Main.java
with GJ
compiler:
% gjc Main.java
Now you can run the example on any Java source file by:
% java Main < inputfile.java
It will tell you how many tokens are there in inputfile.java. The
following paragraphs will go through the source files of this example and
show you how generic types are specified in visitors. Let's start
from MyInt.java:
package visitor;
public class MyInt {
public MyInt(int n) { count=n; }
public int count;
}
An object of MyInt class will be passed on as an argument during
the syntax tree traversal. The reason I put MyInt in the
visitor
package is that both Main class and Counter class need
it. The next is Counter.java:
package visitor;
import syntaxtree.*;
import java.util.*;
public class Counter extends GJDepthFirst<MyInt,MyInt> {
public MyInt visit(NodeToken n, MyInt argu) {
argu.count++;
return argu;
}
}
Counter is a visitor that extends GJDepthFirst.
It specifies the type parameters by GJDepthFirst<MyInt,MyInt>.
The first MyInt stands for the return type, and the second MyInt
is the type of the argument. In this example, the return value is
useless. What Counter does is to increase count
field of the object of MyInt. An object of MyInt
will be passed on as the argument, so at the end of the syntax tree traversal,
it will record the total number of the tokens. The last file is Main.java:
import syntaxtree.*;
import visitor.*;
public class Main {
public static void main(String [] args) {
try {
Node root = new
JavaParser(System.in).CompilationUnit();
System.out.println("Program
parsed successfully");
MyInt re=new MyInt(0);
root.accept(new
Counter(),re);
System.out.println("Total
tokens: " + re.count + "\n");
}
catch (ParseException e) {
System.out.println(e.toString());
}
}
}
There's nothing special about Main.java. It simply calls
the parser (the syntax tree is constructed automatically during parsing)
and starts the visitor. This example doesn't have to be implemented
this way. The whole purpose is to demonstrate the usage of GJDepthFirst
and GJVisitor.
Comparison
Let's rewrite the above Counter.java with ObjectDepthFirst
class generated by JTB 1.2 and standard Java programming language:
package visitor;
import syntaxtree.*;
import java.util.*;
public class Counter extends ObjectDepthFirst {
public Object visit(NodeToken n, Object argu) {
(MyInt)argu.count++;
return argu;
}
}
The only difference is that with GJ, there is no type casting at all.
You will find GJ extremely uesful when your visitor is complex and you
try to pass parameters and get return values. All the mismatch types
can be found at compile-time in GJ.
Advanced Example
Here is an advanced example of how to use GJVisitor and GJDepthFirst:
Expression_GJ.jar
This example transforms an infix expression to a prefix expression.
It uses the Calc1.jj grammar provided with the JavaCC distribution,
version 1.0. Some changes were made to the original grammar file
to fit in with JTBGJ 1.0. For more information, please read the README
file included in the above example package. As a comparison, you
can go to the Examples page,
and see how to achieve the same goal using standard Java.
Still have questions? Suggestions on improving this document?
Feel free to mail Wanjun Wang or
Jens
Palsberg. |