001: package antlr;
002:
003: /* ANTLR Translator Generator
004: * Project led by Terence Parr at http://www.jGuru.com
005: * Software rights: http://www.antlr.org/RIGHTS.html
006: *
007: * $Id: Tool.java,v 1.1 2004/01/21 19:18:33 rgrimm Exp $
008: */
009:
010: import java.io.*;
011:
012: import antlr.collections.impl.BitSet;
013: import antlr.collections.impl.Vector;
014: import antlr.PreservingFileWriter;
015: import antlr.Version;
016:
017: public class Tool {
018: public static String version = "";
019:
020: /** Object that handles analysis errors */
021: ToolErrorHandler errorHandler;
022:
023: /** Was there an error during parsing or analysis? */
024: protected boolean hasError = false;
025:
026: /** Generate diagnostics? (vs code) */
027: boolean genDiagnostics = false;
028:
029: /** Generate DocBook vs code? */
030: boolean genDocBook = false;
031:
032: /** Generate HTML vs code? */
033: boolean genHTML = false;
034:
035: /** Current output directory for generated files */
036: protected static String outputDir = ".";
037:
038: // Grammar input
039: protected String grammarFile;
040: transient Reader f = new InputStreamReader(System.in);
041: // SAS: changed for proper text io
042: // transient DataInputStream in = null;
043:
044: protected static String literalsPrefix = "LITERAL_";
045: protected static boolean upperCaseMangledLiterals = false;
046:
047: /** C++ file level options */
048: protected NameSpace nameSpace = null;
049: protected String namespaceAntlr = null;
050: protected String namespaceStd = null;
051: protected boolean genHashLines = true;
052: protected boolean noConstructors = false;
053:
054: private BitSet cmdLineArgValid = new BitSet();
055:
056: /** Construct a new Tool. */
057: public Tool() {
058: errorHandler = new DefaultToolErrorHandler(this );
059: }
060:
061: public String getGrammarFile() {
062: return grammarFile;
063: }
064:
065: public boolean hasError() {
066: return hasError;
067: }
068:
069: public NameSpace getNameSpace() {
070: return nameSpace;
071: }
072:
073: public String getNamespaceStd() {
074: return namespaceStd;
075: }
076:
077: public String getNamespaceAntlr() {
078: return namespaceAntlr;
079: }
080:
081: public boolean getGenHashLines() {
082: return genHashLines;
083: }
084:
085: public String getLiteralsPrefix() {
086: return literalsPrefix;
087: }
088:
089: public boolean getUpperCaseMangledLiterals() {
090: return upperCaseMangledLiterals;
091: }
092:
093: public void setFileLineFormatter(FileLineFormatter formatter) {
094: FileLineFormatter.setFormatter(formatter);
095: }
096:
097: protected void checkForInvalidArguments(String[] args,
098: BitSet cmdLineArgValid) {
099: // check for invalid command line args
100: for (int a = 0; a < args.length; a++) {
101: if (!cmdLineArgValid.member(a)) {
102: warning("invalid command-line argument: " + args[a]
103: + "; ignored");
104: }
105: }
106: }
107:
108: /** This example is from the book _Java in a Nutshell_ by David
109: * Flanagan. Written by David Flanagan. Copyright (c) 1996
110: * O'Reilly & Associates. You may study, use, modify, and
111: * distribute this example for any purpose. This example is
112: * provided WITHOUT WARRANTY either expressed or implied. */
113: public void copyFile(String source_name, String dest_name)
114: throws IOException {
115: File source_file = new File(source_name);
116: File destination_file = new File(dest_name);
117: Reader source = null;
118: Writer destination = null;
119: char[] buffer;
120: int bytes_read;
121:
122: try {
123: // First make sure the specified source file
124: // exists, is a file, and is readable.
125: if (!source_file.exists() || !source_file.isFile())
126: throw new FileCopyException(
127: "FileCopy: no such source file: " + source_name);
128: if (!source_file.canRead())
129: throw new FileCopyException("FileCopy: source file "
130: + "is unreadable: " + source_name);
131:
132: // If the destination exists, make sure it is a writeable file
133: // and ask before overwriting it. If the destination doesn't
134: // exist, make sure the directory exists and is writeable.
135: if (destination_file.exists()) {
136: if (destination_file.isFile()) {
137: DataInputStream in = new DataInputStream(System.in);
138: String response;
139:
140: if (!destination_file.canWrite())
141: throw new FileCopyException(
142: "FileCopy: destination "
143: + "file is unwriteable: "
144: + dest_name);
145: /*
146: System.out.print("File " + dest_name +
147: " already exists. Overwrite? (Y/N): ");
148: System.out.flush();
149: response = in.readLine();
150: if (!response.equals("Y") && !response.equals("y"))
151: throw new FileCopyException("FileCopy: copy cancelled.");
152: */
153: } else {
154: throw new FileCopyException(
155: "FileCopy: destination "
156: + "is not a file: " + dest_name);
157: }
158: } else {
159: File parentdir = parent(destination_file);
160: if (!parentdir.exists())
161: throw new FileCopyException(
162: "FileCopy: destination "
163: + "directory doesn't exist: "
164: + dest_name);
165: if (!parentdir.canWrite())
166: throw new FileCopyException(
167: "FileCopy: destination "
168: + "directory is unwriteable: "
169: + dest_name);
170: }
171:
172: // If we've gotten this far, then everything is okay; we can
173: // copy the file.
174: source = new BufferedReader(new FileReader(source_file));
175: destination = new BufferedWriter(new FileWriter(
176: destination_file));
177:
178: buffer = new char[1024];
179: while (true) {
180: bytes_read = source.read(buffer, 0, 1024);
181: if (bytes_read == -1)
182: break;
183: destination.write(buffer, 0, bytes_read);
184: }
185: }
186: // No matter what happens, always close any streams we've opened.
187: finally {
188: if (source != null) {
189: try {
190: source.close();
191: } catch (IOException e) {
192: ;
193: }
194: }
195: if (destination != null) {
196: try {
197: destination.close();
198: } catch (IOException e) {
199: ;
200: }
201: }
202: }
203: }
204:
205: /** Perform processing on the grammar file. Can only be called
206: * from main() @param args The command-line arguments passed to
207: * main(). This wrapper does the System.exit for use with command-line.
208: */
209: public void doEverythingWrapper(String[] args) {
210: int exitCode = doEverything(args);
211: System.exit(exitCode);
212: }
213:
214: /** Process args and have ANTLR do it's stuff without calling System.exit.
215: * Just return the result code. Makes it easy for ANT build tool.
216: */
217: public int doEverything(String[] args) {
218: // run the preprocessor to handle inheritance first.
219:
220: // Start preprocessor. This strips generates an argument list
221: // without -glib options (inside preTool)
222: antlr.preprocessor.Tool preTool = new antlr.preprocessor.Tool(
223: this , args);
224:
225: boolean preprocess_ok = preTool.preprocess();
226: String[] modifiedArgs = preTool.preprocessedArgList();
227:
228: // process arguments for the Tool
229: processArguments(modifiedArgs);
230: if (!preprocess_ok) {
231: return 1;
232: }
233:
234: f = getGrammarReader();
235:
236: ANTLRLexer lexer = new ANTLRLexer(f);
237: TokenBuffer tokenBuf = new TokenBuffer(lexer);
238: LLkAnalyzer analyzer = new LLkAnalyzer(this );
239: MakeGrammar behavior = new MakeGrammar(this , args, analyzer);
240:
241: try {
242: ANTLRParser p = new ANTLRParser(tokenBuf, behavior, this );
243: p.setFilename(grammarFile);
244: p.grammar();
245: if (hasError()) {
246: fatalError("Exiting due to errors.");
247: }
248: checkForInvalidArguments(modifiedArgs, cmdLineArgValid);
249:
250: // Create the right code generator according to the "language" option
251: CodeGenerator codeGen;
252:
253: // SAS: created getLanguage() method so subclass can override
254: // (necessary for VAJ interface)
255: String codeGenClassName = "antlr." + getLanguage(behavior)
256: + "CodeGenerator";
257: try {
258: Class codeGenClass = Class.forName(codeGenClassName);
259: codeGen = (CodeGenerator) codeGenClass.newInstance();
260: codeGen.setBehavior(behavior);
261: codeGen.setAnalyzer(analyzer);
262: codeGen.setTool(this );
263: codeGen.gen();
264: } catch (ClassNotFoundException cnfe) {
265: panic("Cannot instantiate code-generator: "
266: + codeGenClassName);
267: } catch (InstantiationException ie) {
268: panic("Cannot instantiate code-generator: "
269: + codeGenClassName);
270: } catch (IllegalArgumentException ie) {
271: panic("Cannot instantiate code-generator: "
272: + codeGenClassName);
273: } catch (IllegalAccessException iae) {
274: panic("code-generator class '" + codeGenClassName
275: + "' is not accessible");
276: }
277: } catch (RecognitionException pe) {
278: fatalError("Unhandled parser error: " + pe.getMessage());
279: } catch (TokenStreamException io) {
280: fatalError("TokenStreamException: " + io.getMessage());
281: }
282: return 0;
283: }
284:
285: /** Issue an error
286: * @param s The message
287: */
288: public void error(String s) {
289: hasError = true;
290: System.err.println("error: " + s);
291: }
292:
293: /** Issue an error with line number information
294: * @param s The message
295: * @param file The file that has the error (or null)
296: * @param line The grammar file line number on which the error occured (or -1)
297: * @param column The grammar file column number on which the error occured (or -1)
298: */
299: public void error(String s, String file, int line, int column) {
300: hasError = true;
301: System.err.println(FileLineFormatter.getFormatter()
302: .getFormatString(file, line, column)
303: + s);
304: }
305:
306: /** When we are 1.1 compatible...
307: public static Object factory2 (String p, Object[] initargs) {
308: Class c;
309: Object o = null;
310: try {
311: int argslen = initargs.length;
312: Class cl[] = new Class[argslen];
313: for (int i=0;i<argslen;i++) {
314: cl[i] = Class.forName(initargs[i].getClass().getName());
315: }
316: c = Class.forName (p);
317: Constructor con = c.getConstructor (cl);
318: o = con.newInstance (initargs);
319: } catch (Exception e) {
320: System.err.println ("Can't make a " + p);
321: }
322: return o;
323: }
324: */
325: public Object factory(String p) {
326: Class c;
327: Object o = null;
328: try {
329: c = Class.forName(p);// get class def
330: o = c.newInstance(); // make a new one
331: } catch (Exception e) {
332: // either class not found,
333: // class is interface/abstract, or
334: // class or initializer is not accessible.
335: warning("Can't create an object of type " + p);
336: return null;
337: }
338: return o;
339: }
340:
341: public String fileMinusPath(String f) {
342: String separator = System.getProperty("file.separator");
343: int endOfPath = f.lastIndexOf(separator);
344: if (endOfPath == -1) {
345: return f; // no path found
346: }
347: return f.substring(endOfPath + 1);
348: }
349:
350: /** Determine the language used for this run of ANTLR
351: * This was made a method so the subclass can override it
352: */
353: public String getLanguage(MakeGrammar behavior) {
354: if (genDiagnostics) {
355: return "Diagnostic";
356: }
357: if (genHTML) {
358: return "HTML";
359: }
360: if (genDocBook) {
361: return "DocBook";
362: }
363: return behavior.language;
364: }
365:
366: public String getOutputDirectory() {
367: return outputDir;
368: }
369:
370: private static void help() {
371: System.err.println("usage: java antlr.Tool [args] file.g");
372: System.err
373: .println(" -o outputDir specify output directory where all output generated.");
374: System.err
375: .println(" -glib superGrammar specify location of supergrammar file.");
376: System.err
377: .println(" -debug launch the ParseView debugger upon parser invocation.");
378: System.err
379: .println(" -html generate a html file from your grammar.");
380: System.err
381: .println(" -docbook generate a docbook sgml file from your grammar.");
382: System.err
383: .println(" -diagnostic generate a textfile with diagnostics.");
384: System.err
385: .println(" -trace have all rules call traceIn/traceOut.");
386: System.err
387: .println(" -traceLexer have lexer rules call traceIn/traceOut.");
388: System.err
389: .println(" -traceParser have parser rules call traceIn/traceOut.");
390: System.err
391: .println(" -traceTreeParser have tree parser rules call traceIn/traceOut.");
392: System.err.println(" -h|-help|--help this message");
393: }
394:
395: public static void main(String[] args) {
396: System.err.println("ANTLR Parser Generator Version "
397: + Version.project_version + " 1989-2003 jGuru.com");
398: version = Version.project_version;
399:
400: try {
401: if (args.length == 0) {
402: help();
403: System.exit(1);
404: }
405: for (int i = 0; i < args.length; ++i) {
406: if (args[i].equals("-h") || args[i].equals("-help")
407: || args[i].equals("--help")) {
408: help();
409: System.exit(1);
410: }
411: }
412:
413: Tool theTool = new Tool();
414: theTool.doEverything(args);
415: theTool = null;
416: } catch (Exception e) {
417: System.err.println(System.getProperty("line.separator")
418: + System.getProperty("line.separator"));
419: System.err.println("#$%%*&@# internal error: "
420: + e.toString());
421: System.err
422: .println("[complain to nearest government official");
423: System.err
424: .println(" or send hate-mail to parrt@jguru.com;");
425: System.err.println(" please send stack trace with report.]"
426: + System.getProperty("line.separator"));
427: e.printStackTrace();
428: }
429: System.exit(0);
430: }
431:
432: /** This method is used by all code generators to create new output
433: * files.
434: */
435: public PrintWriter openOutputFile(String f) throws IOException {
436: return new PrintWriter(new PreservingFileWriter(outputDir
437: + System.getProperty("file.separator") + f));
438: }
439:
440: public Reader getGrammarReader() {
441: Reader f = null;
442: try {
443: if (grammarFile != null) {
444: f = new BufferedReader(new FileReader(grammarFile));
445: }
446: } catch (IOException e) {
447: fatalError("cannot open grammar file " + grammarFile);
448: }
449: return f;
450: }
451:
452: /** @since 2.7.2
453: */
454: public void reportException(Exception e, String message) {
455: System.err.println(message == null ? e.getMessage() : message
456: + ": " + e.getMessage());
457: }
458:
459: /** @since 2.7.2
460: */
461: public void reportProgress(String message) {
462: System.out.println(message);
463: }
464:
465: /** An error occured that should stop the Tool from doing any work.
466: * The default implementation currently exits (via
467: * {@link java.lang.System.exit(int)} after printing an error message to
468: * <var>stderr</var>. However, the tools should expect that a subclass
469: * will override this to throw an unchecked exception such as
470: * {@link java.lang.IllegalStateException} or another subclass of
471: * {@link java.lang.RuntimeException}. <em>If this method is overriden,
472: * <strong>it must never return normally</strong>; i.e. it must always
473: * throw an exception or call System.exit</em>.
474: * @since 2.7.2
475: * @param s The message
476: */
477: public void fatalError(String message) {
478: System.err.println(message);
479: System.exit(1);
480: }
481:
482: /** Issue an unknown fatal error. <em>If this method is overriden,
483: * <strong>it must never return normally</strong>; i.e. it must always
484: * throw an exception or call System.exit</em>.
485: * @deprecated as of 2.7.2 use {@link #fatalError(String)}. By default
486: * this method executes <code>fatalError("panic");</code>.
487: */
488: public void panic() {
489: fatalError("panic");
490: }
491:
492: /** Issue a fatal error message. <em>If this method is overriden,
493: * <strong>it must never return normally</strong>; i.e. it must always
494: * throw an exception or call System.exit</em>.
495: * @deprecated as of 2.7.2 use {@link #fatalError(String)}. By defaykt
496: * this method executes <code>fatalError("panic: " + s);</code>.
497: * @param s The message
498: */
499: public void panic(String s) {
500: fatalError("panic: " + s);
501: }
502:
503: // File.getParent() can return null when the file is specified without
504: // a directory or is in the root directory.
505: // This method handles those cases.
506: public File parent(File f) {
507: String dirname = f.getParent();
508: if (dirname == null) {
509: if (f.isAbsolute())
510: return new File(File.separator);
511: else
512: return new File(System.getProperty("user.dir"));
513: }
514: return new File(dirname);
515: }
516:
517: /** Parse a list such as "f1.g;f2.g;..." and return a Vector
518: * of the elements.
519: */
520: public static Vector parseSeparatedList(String list, char separator) {
521: Vector v = new Vector(10);
522: StringBuffer buf = new StringBuffer(100);
523: int i = 0;
524: while (i < list.length()) {
525: while (i < list.length() && list.charAt(i) != separator) {
526: buf.append(list.charAt(i));
527: i++;
528: }
529: // add element to vector
530: v.appendElement(buf.toString());
531: buf.setLength(0);
532: // must be a separator or finished.
533: if (i < list.length()) {
534: // not done?
535: i++; // skip separator
536: }
537: }
538: if (v.size() == 0)
539: return null;
540: return v;
541: }
542:
543: /** given a filename, strip off the directory prefix (if any)
544: * and return it. Return "./" if f has no dir prefix.
545: */
546: public String pathToFile(String f) {
547: String separator = System.getProperty("file.separator");
548: int endOfPath = f.lastIndexOf(separator);
549: if (endOfPath == -1) {
550: // no path, use current directory
551: return "." + System.getProperty("file.separator");
552: }
553: return f.substring(0, endOfPath + 1);
554: }
555:
556: /** <p>Process the command-line arguments. Can only be called by Tool.
557: * A bitset is collected of all correct arguments via setArgOk.</p>
558: * @param args The command-line arguments passed to main()
559: *
560: */
561: protected void processArguments(String[] args) {
562: for (int i = 0; i < args.length; i++) {
563: if (args[i].equals("-diagnostic")) {
564: genDiagnostics = true;
565: genHTML = false;
566: setArgOK(i);
567: } else if (args[i].equals("-o")) {
568: setArgOK(i);
569: if (i + 1 >= args.length) {
570: error("missing output directory with -o option; ignoring");
571: } else {
572: i++;
573: setOutputDirectory(args[i]);
574: setArgOK(i);
575: }
576: } else if (args[i].equals("-html")) {
577: genHTML = true;
578: genDiagnostics = false;
579: setArgOK(i);
580: } else if (args[i].equals("-docbook")) {
581: genDocBook = true;
582: genDiagnostics = false;
583: setArgOK(i);
584: } else {
585: if (args[i].charAt(0) != '-') {
586: // Must be the grammar file
587: grammarFile = args[i];
588: setArgOK(i);
589: }
590: }
591: }
592: }
593:
594: public void setArgOK(int i) {
595: cmdLineArgValid.add(i);
596: }
597:
598: public void setOutputDirectory(String o) {
599: outputDir = o;
600: }
601:
602: /** Issue an error; used for general tool errors not for grammar stuff
603: * @param s The message
604: */
605: public void toolError(String s) {
606: System.err.println("error: " + s);
607: }
608:
609: /** Issue a warning
610: * @param s the message
611: */
612: public void warning(String s) {
613: System.err.println("warning: " + s);
614: }
615:
616: /** Issue a warning with line number information
617: * @param s The message
618: * @param file The file that has the warning (or null)
619: * @param line The grammar file line number on which the warning occured (or -1)
620: * @param column The grammar file line number on which the warning occured (or -1)
621: */
622: public void warning(String s, String file, int line, int column) {
623: System.err.println(FileLineFormatter.getFormatter()
624: .getFormatString(file, line, column)
625: + "warning:" + s);
626: }
627:
628: /** Issue a warning with line number information
629: * @param s The lines of the message
630: * @param file The file that has the warning
631: * @param line The grammar file line number on which the warning occured
632: */
633: public void warning(String[] s, String file, int line, int column) {
634: if (s == null || s.length == 0) {
635: panic("bad multi-line message to Tool.warning");
636: }
637: System.err.println(FileLineFormatter.getFormatter()
638: .getFormatString(file, line, column)
639: + "warning:" + s[0]);
640: for (int i = 1; i < s.length; i++) {
641: System.err.println(FileLineFormatter.getFormatter()
642: .getFormatString(file, line, column)
643: + " " + s[i]);
644: }
645: }
646:
647: /**
648: * Support C++ & C# namespaces (for now).
649: * C++: Add a nested namespace name to the current namespace.
650: * C# : Specify an enclosing namespace for the generated code.
651: * DAW: David Wagner -- C# support by kunle odutola
652: */
653: public void setNameSpace(String name) {
654: if (null == nameSpace)
655: nameSpace = new NameSpace(StringUtils.stripFrontBack(name,
656: "\"", "\""));
657: }
658: }
|