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