001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2004-2007 Robert Grimm
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * version 2 as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
017: * USA.
018: */
019: package xtc.lang;
020:
021: import java.lang.reflect.Method;
022:
023: import java.io.File;
024: import java.io.IOException;
025: import java.io.Reader;
026:
027: import xtc.tree.Node;
028: import xtc.tree.Visitor;
029: import xtc.tree.ParseTreePrinter;
030: import xtc.tree.ParseTreeStripper;
031: import xtc.tree.Printer;
032:
033: import xtc.util.Runtime;
034: import xtc.util.SymbolTable;
035: import xtc.util.Tool;
036:
037: import xtc.parser.Result;
038: import xtc.parser.ParseException;
039:
040: /**
041: * The Java tool.
042: *
043: * <h4>Parser Runtime Classes</h4>
044: *
045: * The Java parsers supported by this tool are generated by
046: * <i>Rats!</i>, JavaCC, and ANTLR, respectively. Each of these
047: * parsers requires a set of runtime classes. The runtime classes for
048: * the <i>Rats!</i> generated parser are (obviously) part of xtc. The
049: * runtime classes for the JavaCC generated parser are contained in
050: * the {@link xtc.lang.javacc} package. However, while the actual
051: * parser generated by ANTLR is contained in the {@link
052: * xtc.lang.antlr} paackage, the runtime classes are not.
053: * Consequently, running this tool requires access to the
054: * <code>antlr.jar</code> file that is part of ANTLR's distribution.
055: * In particular, <code>antlr.jar</code> must either be in the local
056: * classpath (when running this tool within the development tree) or
057: * in the same directory as the <code>xtc.jar</code> file (when
058: * running this tool from the binary distribution). In the latter
059: * case, xtc's JAR file manifest already contains the appropriate
060: * classpath declaration.
061: *
062: * @author Robert Grimm
063: * @version $Revision: 1.37 $
064: */
065: public class Java extends Tool {
066:
067: /** The method for printing the memoization table's profile. */
068: protected Method profile;
069:
070: /** The method for printing the parser's memoization table. */
071: protected Method dump;
072:
073: /** Create a new Java tool. */
074: public Java() {
075: /* Nothing to do. */
076: }
077:
078: public String getName() {
079: return "xtc Java Tool";
080: }
081:
082: public String getCopy() {
083: return "(C) 2004-2007 IBM, Robert Grimm, and NYU";
084: }
085:
086: public String getExplanation() {
087: return "By default, this tool simply parses Java sources using the Rats!-"
088: + "generated recognizer. If the ast option is specified, it also "
089: + "generates an AST, which can then be further processed and printed. "
090: + "If the pt option is specified, it also embeds all formatting in "
091: + "the resulting AST. For performance comparisons, this tool can "
092: + "optionally use ANTLR- or JavaCC-generated recognizers and parsers. "
093: + "Any ASTs generated by these parsers cannot be processed.";
094: }
095:
096: public void init() {
097: super .init();
098: runtime
099: .bool("memoProfile", "printMemoProfile", false,
100: "Print profile of parser's memoization table.")
101: .bool("memoTable", "printMemoTable", false,
102: "Print parser's memoization table.")
103: .bool("tiger", "optionTiger", false,
104: "Process Java 5 aka Tiger.")
105: .bool("ast", "optionAST", false,
106: "Use parser that builds abstract syntax tree.")
107: .bool("parsetree", "optionParseTree", false,
108: "Use parser that builds parse tree.")
109: .bool("strip", "optionStrip", false,
110: "Strip any annotations from the AST before printing.")
111: .bool("simplifyAST", "optionSimplifyAST", false,
112: "Simplify the AST.")
113: .bool("printAST", "printAST", false,
114: "Print the AST in generic form.")
115: .bool(
116: "locateAST",
117: "optionLocateAST",
118: false,
119: "Include location information when printing the AST in "
120: + "generic form.")
121: .bool("printSource", "printSource", false,
122: "Print the AST in Java source form.")
123: .bool("antlr", "optionANTLR", false,
124: "Use an ANTLR-generated parser.")
125: .bool("javacc", "optionJavaCC", false,
126: "Use a JavaCC-generated parser.")
127: .bool("analyze", "optionAnalyze", false,
128: "Analyze the program's AST.")
129: .bool("printSymbolTable", "printSymbolTable", false,
130: "Print the program's symbol table.")
131: .bool("g", "debug", false,
132: "Ignored; provided for compatibility with jacks.")
133: .bool("deprecated", "deprecated", false,
134: "Ignored; provided for compatibility with jacks.");
135: }
136:
137: public void prepare() {
138: super .prepare();
139: if (runtime.test("printMemoProfile")) {
140: if (runtime.test("optionANTLR")
141: || runtime.test("optionJavaCC")) {
142: runtime
143: .error("memoization profile only available for Rats!-generated "
144: + "parser");
145: } else {
146: // Ensure the parser has a profile(Printer) method.
147: Class klass;
148: String name;
149: if (runtime.test("optionAST")) {
150: klass = JavaParser.class;
151: name = "Java parser";
152: } else if (runtime.test("optionParseTree")) {
153: klass = JavaReader.class;
154: name = "Java reader";
155: } else {
156: klass = JavaRecognizer.class;
157: name = "Java recognizer";
158: }
159: try {
160: profile = klass.getMethod("profile",
161: new Class[] { Printer.class });
162: } catch (NoSuchMethodException x) {
163: runtime.error(name
164: + " generated without profile attribute");
165: } catch (SecurityException x) {
166: runtime.error("unable to access " + name
167: + "'s profile() method");
168: }
169: }
170: }
171: if (runtime.test("printMemoTable")) {
172: if (runtime.test("optionANTLR")
173: || runtime.test("optionJavaCC")) {
174: runtime
175: .error("memoization table only available for Rats!-generated "
176: + "parser");
177: } else {
178: // Ensure the parser has a dump(Printer) method.
179: Class klass;
180: String name;
181: if (runtime.test("optionAST")) {
182: klass = JavaParser.class;
183: name = "Java parser";
184: } else if (runtime.test("optionParseTree")) {
185: klass = JavaReader.class;
186: name = "Java reader";
187: } else {
188: klass = JavaRecognizer.class;
189: name = "Java recognizer";
190: }
191: try {
192: dump = klass.getMethod("dump",
193: new Class[] { Printer.class });
194: } catch (NoSuchMethodException x) {
195: runtime.error(name
196: + " generated without dump attribute");
197: } catch (SecurityException x) {
198: runtime.error("unable to access " + name
199: + "'s dump() method");
200: }
201: }
202: }
203: if (runtime.test("optionTiger")) {
204: if (runtime.test("optionANTLR")) {
205: runtime
206: .error("antlr option incompatible with tiger option");
207: } else if (runtime.test("optionJavaCC")) {
208: runtime
209: .error("javacc option incompatible with tiger option");
210: }
211: if ((!runtime.test("optionAST"))
212: && (!runtime.test("optionParseTree"))) {
213: runtime
214: .error("tiger option requires ast or parseTree option");
215: }
216: }
217: if (runtime.test("optionANTLR") && runtime.test("optionJavaCC")) {
218: runtime
219: .error("antlr option incompatible with javacc option");
220: }
221: if (runtime.test("optionParseTree")) {
222: if (runtime.test("optionANTLR")) {
223: runtime
224: .error("antlr option incompatible with pt option");
225: }
226: if (runtime.test("optionJavaCC")) {
227: runtime
228: .test("javacc option incompatible with pt option");
229: }
230: }
231: if (runtime.test("optionStrip")
232: || runtime.test("optionSimplifyAST")
233: || runtime.test("printAST")
234: || runtime.test("printSource")) {
235: if ((!runtime.test("optionAST"))
236: && (!runtime.test("optionParseTree"))) {
237: runtime
238: .error("processing of AST requires ast or pt option");
239: } else if (runtime.test("optionANTLR")) {
240: runtime
241: .error("AST built by ANTLR-generated parser cannot be printed");
242: } else if (runtime.test("optionJavaCC")) {
243: runtime
244: .error("AST built by JavaCC-generated parser cannot be printed");
245: }
246: }
247: if (runtime.test("optionLocateAST")) {
248: if (!runtime.test("printAST")) {
249: runtime
250: .error("locateAST option requires printAST option");
251: }
252: }
253: if (runtime.test("optionAnalyze")
254: && !runtime.test("optionSimplifyAST"))
255: runtime.error("type checker only works on simplified AST;"
256: + "run with -ast -simplifyAST -analyze");
257: }
258:
259: public File locate(String name) throws IOException {
260: File file = super .locate(name);
261: if (Integer.MAX_VALUE < file.length()) {
262: throw new IllegalArgumentException(file
263: + ": file too large");
264: }
265: return file;
266: }
267:
268: public Node parse(Reader in, File file) throws IOException,
269: ParseException {
270: if (runtime.test("optionJavaCC")) { // ============================ JavaCC
271: if (runtime.test("optionAST")) {
272: xtc.lang.javacc.JavaTreeParser parser = new xtc.lang.javacc.JavaTreeParser(
273: in);
274: try {
275: parser.CompilationUnit();
276: } catch (xtc.lang.javacc.ParseException x) {
277: runtime.error();
278: System.err.println(x.getMessage());
279: }
280:
281: } else {
282: xtc.lang.javacc.JavaParser parser = new xtc.lang.javacc.JavaParser(
283: in);
284: try {
285: parser.CompilationUnit();
286: } catch (xtc.lang.javacc.ParseException x) {
287: runtime.error();
288: System.err.println(x.getMessage());
289: }
290: }
291: return null;
292:
293: } else if (runtime.test("optionANTLR")) { // ====================== ANTLR
294: if (runtime.test("optionAST")) {
295: xtc.lang.antlr.JavaTreeLexer lexer = new xtc.lang.antlr.JavaTreeLexer(
296: in);
297: xtc.lang.antlr.JavaTreeRecognizer parser = new xtc.lang.antlr.JavaTreeRecognizer(
298: lexer);
299: try {
300: parser.compilationUnit();
301: } catch (Exception x) {
302: runtime.error();
303: System.err.println(x.getMessage());
304: }
305: parser.getAST();
306:
307: } else {
308: xtc.lang.antlr.JavaLexer lexer = new xtc.lang.antlr.JavaLexer(
309: in);
310: xtc.lang.antlr.JavaRecognizer parser = new xtc.lang.antlr.JavaRecognizer(
311: lexer);
312: try {
313: parser.compilationUnit();
314: } catch (Exception x) {
315: runtime.error();
316: System.err.println(x.getMessage());
317: }
318: }
319: return null;
320:
321: } else { // ======================================================= Rats!
322: if (runtime.test("optionTiger")
323: && runtime.test("optionParseTree")) {
324: JavaFiveReader parser = new JavaFiveReader(in, file
325: .toString(), (int) file.length());
326: Result result = parser.pCompilationUnit(0);
327: printMemoInfo(parser, file);
328: return (Node) parser.value(result);
329:
330: } else if (runtime.test("optionTiger")
331: && runtime.test("optionAST")) {
332: JavaFiveParser parser = new JavaFiveParser(in, file
333: .toString(), (int) file.length());
334: Result result = parser.pCompilationUnit(0);
335: printMemoInfo(parser, file);
336: return (Node) parser.value(result);
337:
338: } else if (runtime.test("optionParseTree")) {
339: JavaReader parser = new JavaReader(in, file.toString(),
340: (int) file.length());
341: Result result = parser.pCompilationUnit(0);
342: printMemoInfo(parser, file);
343: return (Node) parser.value(result);
344:
345: } else if (runtime.test("optionAST")) {
346: JavaParser parser = new JavaParser(in, file.toString(),
347: (int) file.length());
348: Result result = parser.pCompilationUnit(0);
349: printMemoInfo(parser, file);
350: return (Node) parser.value(result);
351:
352: } else {
353: JavaRecognizer parser = new JavaRecognizer(in, file
354: .toString(), (int) file.length());
355: Result result = parser.pCompilationUnit(0);
356: printMemoInfo(parser, file);
357: return (Node) parser.value(result);
358: }
359: }
360: }
361:
362: /**
363: * Print the specified parser's memoization profile and table.
364: *
365: * @param parser The parser.
366: * @param file The file.
367: */
368: private void printMemoInfo(Object parser, File file) {
369: if (runtime.test("printMemoProfile")) {
370: try {
371: profile.invoke(parser,
372: new Object[] { runtime.console() });
373: } catch (Exception x) {
374: runtime.error(file + ": " + x.getMessage());
375: }
376: runtime.console().flush();
377: }
378:
379: if (runtime.test("printMemoTable")) {
380: try {
381: dump.invoke(parser, new Object[] { runtime.console() });
382: } catch (Exception x) {
383: runtime.error(file + ": " + x.getMessage());
384: }
385: runtime.console().flush();
386: }
387: }
388:
389: public void process(Node node) {
390: // Simplify the AST.
391: if (runtime.test("optionSimplifyAST")) {
392: node = (Node) new JavaAstSimplifier().dispatch(node);
393: }
394:
395: if (runtime.test("optionAnalyze")) {
396: // Make sure the classpath is set.
397: if (!runtime.hasValue(Runtime.INPUT_DIRECTORY))
398: runtime.setValue(Runtime.INPUT_DIRECTORY, new File(
399: System.getProperty("user.dir")));
400:
401: // Perform type checking.
402: final SymbolTable table = new SymbolTable();
403: new JavaAnalyzer(runtime, table).dispatch(node);
404:
405: // Print the symbol table.
406: if (runtime.test("printSymbolTable")) {
407: final Visitor visitor = runtime.console().visitor();
408: try {
409: table.root().dump(runtime.console());
410: } finally {
411: runtime.console().register(visitor);
412: }
413: runtime.console().flush();
414: }
415: }
416:
417: // Strip the AST.
418: if (runtime.test("optionStrip")) {
419: node = (Node) new ParseTreeStripper().dispatch(node);
420: }
421:
422: // Print the AST.
423: if (runtime.test("printAST")) {
424: runtime.console().format(node,
425: runtime.test("optionLocateAST")).pln().flush();
426: }
427:
428: // Print the source.
429: if (runtime.test("printSource")) {
430: if (runtime.test("optionParseTree")
431: && (!runtime.test("optionStrip"))) {
432: new ParseTreePrinter(runtime.console()).dispatch(node);
433: } else {
434: new JavaPrinter(runtime.console()).dispatch(node);
435: }
436: runtime.console().flush();
437: }
438: }
439:
440: /**
441: * Run the tool with the specified command line arguments.
442: *
443: * @param args The command line arguments.
444: */
445: public static void main(String[] args) {
446: new Java().run(args);
447: }
448:
449: }
|