001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2005-2007 New York University
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:
020: package xtc.xform;
021:
022: import java.io.BufferedReader;
023: import java.io.File;
024: import java.io.FileNotFoundException;
025: import java.io.FileReader;
026: import java.io.IOException;
027: import java.io.Reader;
028:
029: import java.lang.reflect.Method;
030: import java.lang.reflect.Constructor;
031: import java.lang.reflect.InvocationTargetException;
032:
033: import java.util.List;
034: import java.util.Iterator;
035:
036: import xtc.lang.JavaParser;
037: import xtc.lang.JavaPrinter;
038: import xtc.lang.CParser;
039: import xtc.lang.CPrinter;
040:
041: import xtc.parser.Result;
042: import xtc.parser.ParseException;
043:
044: import xtc.tree.GNode;
045: import xtc.tree.Node;
046: import xtc.tree.Printer;
047:
048: import xtc.util.Tool;
049: import xtc.util.Statistics;
050:
051: /**
052: * The driver for parsing and printing XForm.
053: *
054: * @author Joe Pamer
055: * @author Laune Harris
056: * @author Robert Grimm
057: * @version $Revision: 1.33 $
058: */
059: public class Driver extends Tool {
060:
061: /**
062: * Initialize this driver. This method declares this drivers's
063: * command line options. The following options are available:<ul>
064: *
065: * <li>a boolean option <code>optionC</code> for parsing
066: * with XTC's CParser,</li>
067: * <li>a boolean option <code>optionJava</code> for parsing
068: * with XTC's JavaParser,</li>
069: * <li>a word option <code>parser</code> specifying an
070: * arbitrary parser,</li>
071: * <li>a word option <code>unit</code> specifiying the
072: * program unit,</li>
073: * <li>a boolean option <code>printC</code> for printing post-query
074: * source with XTC's CPrinter,</li>
075: * <li>a boolean option <code>printJava</code> for printing post-query
076: * source with XTC's JavaPrinter,</li>
077: * <li>a boolean option <code>printer</code> for specifying the printer for
078: * post-query source,</li>
079: * <li>a boolean option <code>preAST</code> for printing the
080: * pre-query AST,</li>
081: * <li>a boolean option <code>postAST</code> for printing the
082: * post-query AST,</li>
083: * <li>a boolean option <code>queryAST</code> for printing the XForm
084: * query's AST,</li>
085: * <li>a boolean option <code>queryVal</code> for printing
086: * query's result,</li>
087: * <li>a boolean option <code>debug</code> for printing stack traces on
088: * runtime errors</li>
089: * </ul>
090: */
091: public void init() {
092: super .init();
093:
094: runtime
095: .bool("c", "optionC", false,
096: "Parse source file using XTC's CParser")
097: .bool("java", "optionJava", false,
098: "Parse source file using XTC's JavaParser")
099: .word("parser", "optionParser", false,
100: "Parse source file with parser. Eg. \"xtc.lang.NewJavaParser\"")
101: .word("unit", "optionUnit", false,
102: "Specify source file's program unit. Eg. \"compilationUnit\"")
103: .bool("printC", "optionCPrinter", false,
104: "Print post-query source with XTC's CPrinter")
105: .bool("printJava", "optionJavaPrinter", false,
106: "Print post-query source with XTC's JavaPrinter")
107: .word("printer", "optionPrinter", false,
108: "Print post-query source with printer.")
109: .bool("preAST", "optionPreAST", false,
110: "Print the AST of the pre-query source")
111: .bool("postAST", "optionPostAST", false,
112: "Print the AST of the post-query source")
113: .bool("queryAST", "optionQueryAST", false,
114: "Print the query's AST")
115: .bool("queryVal", "optionQueryValue", false,
116: "Print the value of the query.")
117: .bool("debug", "optionDebug", false,
118: "Print a stack trace if a runtime error occurs");
119: }
120:
121: /**
122: * Run this Driver with the command line arguments
123: * @param args The command line arguments.
124: */
125: public void run(String[] args) {
126: init();
127: runtime.console().p(getName()).p(", v. ").p(getVersion()).p(
128: ", ").pln().pln(getCopy()).flush();
129:
130: //Print the tool description and exit if there are no arguments.
131: if (0 == args.length) {
132: runtime
133: .console()
134: .pln()
135: .pln(
136: "Usage: <option>* <xform-file-name> <source-file-name>")
137: .pln().pln("Options are:");
138: runtime.printOptions();
139:
140: String explanation = getExplanation();
141: if (null != explanation) {
142: runtime.console().pln().wrap(0, explanation).pln();
143: }
144: runtime.console().pln().flush();
145: runtime.exit();
146: }
147:
148: // Process the command line arguments.
149: int index = runtime.process(args);
150: if (index >= args.length) {
151: runtime.error("no file names specified");
152: }
153:
154: prepare();
155:
156: //Stop if there have been errors.
157: if (runtime.seenError()) {
158: runtime.exit();
159: }
160:
161: String xformName = args[index++];
162: Query query = parseXform(xformName);
163: if (runtime.test("optionQueryAST")) {
164: runtime.console().pln().format(query.ast).pln().flush();
165: }
166:
167: boolean measure = runtime.test("optionPerformance");
168: boolean doGC = runtime.test("optionGC");
169: int warmUp = measure ? runtime.getInt("runsWarmUp") : 0;
170: int total = measure ? runtime.getInt("runsTotal") : 1;
171: Statistics time = measure ? new Statistics() : null;
172: Statistics fileSizes = measure ? new Statistics() : null;
173: Statistics latencies = measure ? new Statistics() : null;
174:
175: if (measure) {
176: runtime.console().p(
177: "Legend: file, size, time (ave, med, stdev), ")
178: .pln().flush();
179: }
180:
181: while (index < args.length) {
182: String sourceName = args[index++];
183: if (runtime.test("optionVerbose")) {
184: runtime.console().p("Parsing " + sourceName).pln();
185: }
186:
187: //open and parse the sourcefile
188: Node ast = null;
189: Reader in = null;
190: File sourcefile = null;
191: try {
192: sourcefile = locate(sourceName);
193: in = new BufferedReader(new FileReader(sourcefile));
194: } catch (IllegalArgumentException x) {
195: runtime.error(x.getMessage());
196: } catch (FileNotFoundException x) {
197: runtime.error(x.getMessage());
198: runtime.exit();
199: } catch (IOException x) {
200: if (null == x.getMessage()) {
201: runtime.error(": I/O error");
202: } else {
203: runtime.error(": " + x.getMessage());
204: }
205: } catch (Throwable x) {
206: x.printStackTrace();
207: runtime.error();
208: }
209:
210: try {
211: ast = parse(in, sourcefile);
212: } catch (ParseException x) {
213: runtime.error(x.getMessage());
214: } catch (Throwable x) {
215: runtime.error();
216: x.printStackTrace();
217: }
218:
219: if (runtime.test("optionPreAST")) {
220: runtime.console().pln().format(ast).pln().flush();
221: }
222:
223: //run the query
224: GNode result_ast = null;
225: List<Object> query_value = null;
226: Engine engine = new Engine();
227:
228: if (runtime.test("optionVerbose")) {
229: runtime.console().p("Performing query.").pln().flush();
230: }
231:
232: if (measure) {
233: time.reset();
234: }
235: for (int i = 0; i < total; i++) {
236: if (doGC) {
237: System.gc();
238: }
239:
240: //begin timing
241: long startTime = 0;
242:
243: if (measure) {
244: startTime = System.currentTimeMillis();
245: }
246:
247: try {
248: query_value = engine
249: .run(query, (GNode) ast.strip());
250: result_ast = engine.getASTRoot();
251: } catch (Throwable x) {
252: while (null != x.getCause()) {
253: x = x.getCause();
254: }
255:
256: if (runtime.test("optionDebug")) {
257: x.printStackTrace();
258: }
259: return;
260: }
261:
262: if (measure) {
263: //timing ends
264: long endTime = System.currentTimeMillis();
265: if (i >= warmUp) {
266: time.add(endTime - startTime);
267: }
268: }
269: }
270: //Collect performance data for this file's runs
271: if (measure) {
272: long fileSize = sourcefile.length();
273: double latency = time.mean();
274:
275: fileSizes.add(fileSize / 1024.0);
276: latencies.add(latency);
277:
278: runtime.console().p(sourceName).p(' ').p(
279: Statistics.round(fileSize / 1024.0)).p(' ').p(
280: Statistics.round(latency)).p(' ').pln().flush();
281: }
282:
283: if (runtime.test("optionQueryValue")) {
284: printSequence(query_value);
285: runtime.console().pln().flush();
286: }
287: process(result_ast);
288: }
289:
290: // Print overall statistics, if requested.
291: if (measure) {
292: double throughput = 1000.0 / Statistics.fitSlope(fileSizes,
293: latencies);
294: runtime.console().pln().p("Throughput : ").p(
295: Statistics.round(throughput)).pln().flush();
296: }
297: }
298:
299: /*
300: * Open and parse the XForm file and create query
301: * @param filename The name of the xform query file
302: * @return The created query
303: */
304: public Query parseXform(String filename) {
305: if (runtime.test("optionVerbose")) {
306: runtime.console().p("Parsing " + filename).pln().flush();
307: }
308:
309: BufferedReader xform_in = null;
310: File xformfile = null;
311: try {
312: xformfile = locate(filename);
313: xform_in = new BufferedReader(new FileReader(xformfile));
314:
315: } catch (IllegalArgumentException x) {
316: runtime.error(x.getMessage());
317: runtime.exit();
318: } catch (FileNotFoundException x) {
319: runtime.error(x.getMessage());
320: runtime.exit();
321: } catch (IOException x) {
322: if (null == x.getMessage()) {
323: runtime.error(": I/O error");
324: } else {
325: runtime.error(": " + x.getMessage());
326: }
327: runtime.exit();
328: } catch (Throwable x) {
329: runtime.error();
330: if (runtime.test("optionDebug")) {
331: x.printStackTrace();
332: }
333: runtime.exit();
334: }
335:
336: Query query = null;
337: try {
338: query = new Query(xform_in);
339: } catch (IllegalArgumentException iae) {
340: runtime.error("Error: XForm query is malformed.");
341: }
342: return query;
343: }
344:
345: /*
346: * Parse the source file using the parser specified by the command line
347: * options (-parser, -c, -java)
348: * @param in The reader for the source file
349: * @param file The source file's file object
350: * @return The source file's AST root
351: */
352: public Node parse(Reader in, File file) throws IOException,
353: ParseException {
354: if (runtime.test("optionJava")) {
355: JavaParser parser = new JavaParser(in, file.toString(),
356: (int) file.length());
357: return (GNode) parser.value(parser.pCompilationUnit(0));
358:
359: } else if (runtime.test("optionC")) {
360: CParser parser = new CParser(in, file.toString(),
361: (int) file.length());
362: return (GNode) parser.value(parser.pTranslationUnit(0));
363:
364: } else if (null != runtime.getValue("optionParser")) {
365: String name = (String) runtime.getValue("optionParser");
366: String unit = (String) runtime.getValue("optionUnit");
367:
368: if (null == unit) {
369: runtime.error("-parser option requires -unit option");
370: }
371:
372: unit = "p" + unit;
373: try {
374: Class<?> klass = Class.forName(name);
375: Constructor<?> klassConst = klass
376: .getConstructor(new Class[] { Reader.class,
377: String.class, int.class });
378: Long l = new Long(file.length());
379: Object parser = klassConst
380: .newInstance(new Object[] { in,
381: file.toString(),
382: new Integer(l.intValue()) });
383: Method meth = klass.getMethod(unit,
384: new Class[] { int.class });
385:
386: if (null == meth) {
387: runtime.error("unit does not match any in " + name);
388: }
389: Result result = (Result) (meth.invoke(parser,
390: new Object[] { new Integer(0) }));
391: if (result.hasValue()) {
392: return (GNode) result.semanticValue();
393: }
394: } catch (ClassNotFoundException e) {
395: runtime.error("Unable to find class " + name);
396: } catch (ExceptionInInitializerError e) {
397: runtime.error("Unable to initialise " + name);
398: } catch (NoSuchMethodException e) {
399: runtime.error("Method " + unit + " not found");
400: } catch (InstantiationException e) {
401: runtime.error("Unable to instantiate " + name);
402: } catch (IllegalAccessException e) {
403: runtime.error("Unable to access method" + unit);
404: } catch (InvocationTargetException e) {
405: runtime.error("Invocation error on method " + unit);
406: }
407: }
408: return null;
409: }
410:
411: /*
412: * Handle post query command line requests
413: * @param node The AST's root node
414: */
415: public void process(Node node) {
416: GNode root = (GNode) node;
417: if (runtime.test("optionPostAST")) {
418: runtime.console().pln().format(root).pln().flush();
419: }
420: if (runtime.test("optionJavaPrinter")) {
421: new JavaPrinter(runtime.console()).dispatch(root);
422: runtime.console().flush();
423: } else if (runtime.test("optionCPrinter")) {
424: new CPrinter(runtime.console()).dispatch(root);
425: runtime.console().flush();
426: } else if (null != runtime.getValue("optionPrinter")) {
427: String pName = (String) runtime.getValue("optionPrinter");
428: try {
429: Class<?> klass = Class.forName(pName);
430: Constructor<?> klassConst = klass
431: .getConstructor(new Class[] { Printer.class });
432: Object printer = klassConst
433: .newInstance(new Object[] { runtime.console() });
434: Method meth = klass.getMethod("dispatch",
435: new Class[] { GNode.class });
436:
437: meth.invoke(printer, new Object[] { root });
438: runtime.console().flush();
439: } catch (ClassNotFoundException e) {
440: runtime.error("Unable to find " + pName);
441: } catch (ExceptionInInitializerError e) {
442: runtime.error("Unable to initialise " + pName);
443: } catch (NoSuchMethodException e) {
444: runtime.error("Unable to locate method 'dispatch' in "
445: + pName);
446: } catch (InstantiationException e) {
447: runtime.error("Unable to instantiate " + pName);
448: } catch (IllegalAccessException e) {
449: runtime.error("Unable to access method 'dispatch' in"
450: + pName);
451: } catch (InvocationTargetException e) {
452: runtime.error("Invocation failure on method 'dispatch");
453: }
454: }
455: }
456:
457: /*
458: * Returns the name of this driver
459: * @return The driver name
460: */
461: public String getName() {
462: return "Xform AST Query and Transformation Language";
463: }
464:
465: /**
466: * Print the results of a query.
467: * @param l The List/Sequence of query results
468: */
469: private void printSequence(List<?> l) {
470: runtime.console().p("(");
471: for (Iterator<?> i = l.iterator(); i.hasNext();) {
472: Object item = i.next();
473: if (item instanceof GNode) {
474: runtime.console().p(((GNode) item).getName());
475: } else if (item instanceof String) {
476: runtime.console().p((String) item);
477: } else if (item instanceof List) {
478: printSequence((List<?>) item);
479: } else if (null == item) {
480: runtime.console().p("null");
481: } else {
482: String msg = "Error: Unidentified object in sequence.";
483: throw new RuntimeException(msg);
484: }
485: if (i.hasNext()) {
486: runtime.console().p(",");
487: }
488: }
489: runtime.console().p(")");
490: }
491:
492: /*
493: * Main: create a new Xform driver and execute with args
494: * @param args The command line arguments
495: */
496: public static void main(String[] args) {
497: new Driver().run(args);
498: }
499: }
|