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