/* * [The "BSD license"] * Copyright (c) 2010 Terence Parr * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.antlr; import org.antlr.analysis.*; import org.antlr.codegen.CodeGenerator; import org.antlr.misc.Graph; import org.antlr.runtime.misc.Stats; import org.antlr.tool.*; import org.stringtemplate.v4.STGroup; import java.io.*; import java.util.*; /** The main ANTLR entry point. Read a grammar and generate a parser. */ public class Tool { public final Properties antlrSettings = new Properties(); public String VERSION = "3.4"; //public static final String VERSION = "${project.version}"; public static final String UNINITIALIZED_DIR = ""; private List grammarFileNames = new ArrayList(); private boolean generate_NFA_dot = false; private boolean generate_DFA_dot = false; private String outputDirectory = "."; private boolean haveOutputDir = false; private String inputDirectory = null; private String parentGrammarDirectory; private String grammarOutputDirectory; private boolean haveInputDir = false; private String libDirectory = "."; private boolean debug = false; private boolean trace = false; private boolean profile = false; private boolean report = false; private boolean printGrammar = false; private boolean depend = false; private boolean forceAllFilesToOutputDir = false; private boolean forceRelativeOutput = false; protected boolean deleteTempLexer = true; private boolean verbose = false; /** Don't process grammar file if generated files are newer than grammar */ private boolean make = false; private boolean showBanner = true; private static boolean exitNow = false; private static boolean return_dont_exit = false; public String forcedLanguageOption; // -language L on command line // The internal options are for my use on the command line during dev // public static boolean internalOption_PrintGrammarTree = false; public static boolean internalOption_PrintDFA = false; public static boolean internalOption_ShowNFAConfigsInDFA = false; public static boolean internalOption_watchNFAConversion = false; /** * A list of dependency generators that are accumulated aaaas (and if) the * tool is required to sort the provided grammars into build dependency order. protected Map buildDependencyGenerators; */ public static void main(String[] args) { Tool antlr = new Tool(args); if (!exitNow) { antlr.process(); if ( return_dont_exit ) return; if (ErrorManager.getNumErrors() > 0) { System.exit(1); } System.exit(0); } } /** * Load the properties file org/antlr/antlr.properties and populate any * variables that must be initialized from it, such as the version of ANTLR. */ private void loadResources() { InputStream in = null; in = this.getClass().getResourceAsStream("antlr.properties"); // If we found the resource, then load it, otherwise revert to the // defaults. // if (in != null) { try { // Load the resources into the map // antlrSettings.load(in); // Set any variables that we need to populate from the resources // // VERSION = antlrSettings.getProperty("antlr.version"); } catch (Exception e) { // Do nothing, just leave the defaults in place } } } public Tool() { loadResources(); } public Tool(String[] args) { loadResources(); // Set all the options and pick up all the named grammar files processArgs(args); } public void processArgs(String[] args) { if (isVerbose()) { ErrorManager.info("ANTLR Parser Generator Version " + VERSION); showBanner = false; } if (args == null || args.length == 0) { help(); return; } for (int i = 0; i < args.length; i++) { if (args[i].equals("-o") || args[i].equals("-fo")) { if (i + 1 >= args.length) { System.err.println("missing output directory with -fo/-o option; ignoring"); } else { if (args[i].equals("-fo")) { // force output into dir setForceAllFilesToOutputDir(true); } i++; outputDirectory = args[i]; if (outputDirectory.endsWith("/") || outputDirectory.endsWith("\\")) { outputDirectory = outputDirectory.substring(0, getOutputDirectory().length() - 1); } File outDir = new File(outputDirectory); haveOutputDir = true; if (outDir.exists() && !outDir.isDirectory()) { ErrorManager.error(ErrorManager.MSG_OUTPUT_DIR_IS_FILE, outputDirectory); setLibDirectory("."); } } } else if (args[i].equals("-lib")) { if (i + 1 >= args.length) { System.err.println("missing library directory with -lib option; ignoring"); } else { i++; setLibDirectory(args[i]); if (getLibraryDirectory().endsWith("/") || getLibraryDirectory().endsWith("\\")) { setLibDirectory(getLibraryDirectory().substring(0, getLibraryDirectory().length() - 1)); } File outDir = new File(getLibraryDirectory()); if (!outDir.exists()) { ErrorManager.error(ErrorManager.MSG_DIR_NOT_FOUND, getLibraryDirectory()); setLibDirectory("."); } } } else if (args[i].equals("-language")) { if (i + 1 >= args.length) { System.err.println("missing language name; ignoring"); } else { i++; forcedLanguageOption = args[i]; } } else if (args[i].equals("-nfa")) { setGenerate_NFA_dot(true); } else if (args[i].equals("-dfa")) { setGenerate_DFA_dot(true); } else if (args[i].equals("-debug")) { setDebug(true); } else if (args[i].equals("-trace")) { setTrace(true); } else if (args[i].equals("-report")) { setReport(true); } else if (args[i].equals("-profile")) { setProfile(true); } else if (args[i].equals("-print")) { setPrintGrammar(true); } else if (args[i].equals("-depend")) { setDepend(true); } else if (args[i].equals("-verbose")) { setVerbose(true); } else if (args[i].equals("-version")) { version(); exitNow = true; } else if (args[i].equals("-make")) { setMake(true); } else if (args[i].equals("-message-format")) { if (i + 1 >= args.length) { System.err.println("missing output format with -message-format option; using default"); } else { i++; ErrorManager.setFormat(args[i]); } } else if (args[i].equals("-Xgrtree")) { internalOption_PrintGrammarTree = true; // print grammar tree } else if (args[i].equals("-Xdfa")) { internalOption_PrintDFA = true; } else if (args[i].equals("-Xnoprune")) { DFAOptimizer.PRUNE_EBNF_EXIT_BRANCHES = false; } else if (args[i].equals("-Xnocollapse")) { DFAOptimizer.COLLAPSE_ALL_PARALLEL_EDGES = false; } else if (args[i].equals("-Xdbgconversion")) { NFAToDFAConverter.debug = true; } else if (args[i].equals("-Xmultithreaded")) { NFAToDFAConverter.SINGLE_THREADED_NFA_CONVERSION = false; } else if (args[i].equals("-Xnomergestopstates")) { DFAOptimizer.MERGE_STOP_STATES = false; } else if (args[i].equals("-Xdfaverbose")) { internalOption_ShowNFAConfigsInDFA = true; } else if (args[i].equals("-Xwatchconversion")) { internalOption_watchNFAConversion = true; } else if (args[i].equals("-XdbgST")) { CodeGenerator.LAUNCH_ST_INSPECTOR = true; STGroup.trackCreationEvents = true; return_dont_exit = true; } else if (args[i].equals("-Xmaxinlinedfastates")) { if (i + 1 >= args.length) { System.err.println("missing max inline dfa states -Xmaxinlinedfastates option; ignoring"); } else { i++; CodeGenerator.MAX_ACYCLIC_DFA_STATES_INLINE = Integer.parseInt(args[i]); } } else if (args[i].equals("-Xmaxswitchcaselabels")) { if (i + 1 >= args.length) { System.err.println("missing max switch case labels -Xmaxswitchcaselabels option; ignoring"); } else { i++; CodeGenerator.MAX_SWITCH_CASE_LABELS = Integer.parseInt(args[i]); } } else if (args[i].equals("-Xminswitchalts")) { if (i + 1 >= args.length) { System.err.println("missing min switch alternatives -Xminswitchalts option; ignoring"); } else { i++; CodeGenerator.MIN_SWITCH_ALTS = Integer.parseInt(args[i]); } } else if (args[i].equals("-Xm")) { if (i + 1 >= args.length) { System.err.println("missing max recursion with -Xm option; ignoring"); } else { i++; NFAContext.MAX_SAME_RULE_INVOCATIONS_PER_NFA_CONFIG_STACK = Integer.parseInt(args[i]); } } else if (args[i].equals("-Xmaxdfaedges")) { if (i + 1 >= args.length) { System.err.println("missing max number of edges with -Xmaxdfaedges option; ignoring"); } else { i++; DFA.MAX_STATE_TRANSITIONS_FOR_TABLE = Integer.parseInt(args[i]); } } else if (args[i].equals("-Xconversiontimeout")) { if (i + 1 >= args.length) { System.err.println("missing max time in ms -Xconversiontimeout option; ignoring"); } else { i++; DFA.MAX_TIME_PER_DFA_CREATION = Integer.parseInt(args[i]); } } else if (args[i].equals("-Xnfastates")) { DecisionProbe.verbose = true; } else if (args[i].equals("-Xsavelexer")) { deleteTempLexer = false; } else if (args[i].equals("-X")) { Xhelp(); } else { if (args[i].charAt(0) != '-') { // Must be the grammar file addGrammarFile(args[i]); } } } } /* protected void checkForInvalidArguments(String[] args, BitSet cmdLineArgValid) { // check for invalid command line args for (int a = 0; a < args.length; a++) { if (!cmdLineArgValid.member(a)) { System.err.println("invalid command-line argument: " + args[a] + "; ignored"); } } } */ /** * Checks to see if the list of outputFiles all exist, and have * last-modified timestamps which are later than the last-modified * timestamp of all the grammar files involved in build the output * (imports must be checked). If these conditions hold, the method * returns false, otherwise, it returns true. * * @param grammarFileName The grammar file we are checking */ public boolean buildRequired(String grammarFileName) throws IOException { BuildDependencyGenerator bd = new BuildDependencyGenerator(this, grammarFileName); List outputFiles = bd.getGeneratedFileList(); List inputFiles = bd.getDependenciesFileList(); // Note that input directory must be set to use buildRequired File grammarFile; if (haveInputDir) { grammarFile = new File(inputDirectory, grammarFileName); } else { grammarFile = new File(grammarFileName); } long grammarLastModified = grammarFile.lastModified(); for (File outputFile : outputFiles) { if (!outputFile.exists() || grammarLastModified > outputFile.lastModified()) { // One of the output files does not exist or is out of date, so we must build it return true; } // Check all of the imported grammars and see if any of these are younger // than any of the output files. if (inputFiles != null) { for (File inputFile : inputFiles) { if (inputFile.lastModified() > outputFile.lastModified()) { // One of the imported grammar files has been updated so we must build return true; } } } } if (isVerbose()) { System.out.println("Grammar " + grammarFile + " is up to date - build skipped"); } return false; } public void process() { boolean exceptionWhenWritingLexerFile = false; String lexerGrammarFileName = null; // necessary at this scope to have access in the catch below // Have to be tricky here when Maven or build tools call in and must new Tool() // before setting options. The banner won't display that way! if (isVerbose() && showBanner) { ErrorManager.info("ANTLR Parser Generator Version " + VERSION); showBanner = false; } try { sortGrammarFiles(); // update grammarFileNames } catch (Exception e) { ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR,e); } catch (Error e) { ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, e); } for (String grammarFileName : grammarFileNames) { // If we are in make mode (to support build tools like Maven) and the // file is already up to date, then we do not build it (and in verbose mode // we will say so). if (make) { try { if ( !buildRequired(grammarFileName) ) continue; } catch (Exception e) { ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR,e); } } if (isVerbose() && !isDepend()) { System.out.println(grammarFileName); } try { if (isDepend()) { BuildDependencyGenerator dep = new BuildDependencyGenerator(this, grammarFileName); /* List outputFiles = dep.getGeneratedFileList(); List dependents = dep.getDependenciesFileList(); System.out.println("output: "+outputFiles); System.out.println("dependents: "+dependents); */ System.out.println(dep.getDependencies().render()); continue; } Grammar rootGrammar = getRootGrammar(grammarFileName); // we now have all grammars read in as ASTs // (i.e., root and all delegates) rootGrammar.composite.assignTokenTypes(); //rootGrammar.composite.translateLeftRecursiveRules(); rootGrammar.addRulesForSyntacticPredicates(); rootGrammar.composite.defineGrammarSymbols(); rootGrammar.composite.createNFAs(); generateRecognizer(rootGrammar); if (isPrintGrammar()) { rootGrammar.printGrammar(System.out); } if (isReport()) { GrammarReport2 greport = new GrammarReport2(rootGrammar); System.out.print(greport.toString()); // GrammarReport greport = new GrammarReport(rootGrammar); // System.out.println(greport.toString()); // // print out a backtracking report too (that is not encoded into log) // System.out.println(greport.getBacktrackingReport()); } if (isProfile()) { GrammarReport greport = new GrammarReport(rootGrammar); Stats.writeReport(GrammarReport.GRAMMAR_STATS_FILENAME, greport.toNotifyString()); } // now handle the lexer if one was created for a merged spec String lexerGrammarStr = rootGrammar.getLexerGrammar(); //System.out.println("lexer rootGrammar:\n"+lexerGrammarStr); if (rootGrammar.type == Grammar.COMBINED && lexerGrammarStr != null) { lexerGrammarFileName = rootGrammar.getImplicitlyGeneratedLexerFileName(); try { Writer w = getOutputFile(rootGrammar, lexerGrammarFileName); w.write(lexerGrammarStr); w.close(); } catch (IOException e) { // emit different error message when creating the implicit lexer fails // due to write permission error exceptionWhenWritingLexerFile = true; throw e; } try { StringReader sr = new StringReader(lexerGrammarStr); Grammar lexerGrammar = new Grammar(this); lexerGrammar.composite.watchNFAConversion = internalOption_watchNFAConversion; lexerGrammar.implicitLexer = true; //lexerGrammar.setTool(this); File lexerGrammarFullFile = new File(getFileDirectory(lexerGrammarFileName), lexerGrammarFileName); lexerGrammar.setFileName(lexerGrammarFullFile.toString()); lexerGrammar.importTokenVocabulary(rootGrammar); lexerGrammar.parseAndBuildAST(sr); sr.close(); lexerGrammar.composite.assignTokenTypes(); lexerGrammar.addRulesForSyntacticPredicates(); lexerGrammar.composite.defineGrammarSymbols(); lexerGrammar.composite.createNFAs(); generateRecognizer(lexerGrammar); } finally { // make sure we clean up if (deleteTempLexer) { File outputDir = getOutputDirectory(lexerGrammarFileName); File outputFile = new File(outputDir, lexerGrammarFileName); outputFile.delete(); } } } } catch (IOException e) { if (exceptionWhenWritingLexerFile) { ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, e); } else { ErrorManager.error(ErrorManager.MSG_CANNOT_OPEN_FILE, grammarFileName); } } catch (Exception e) { ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, grammarFileName, e); } /* finally { System.out.println("creates="+ Interval.creates); System.out.println("hits="+ Interval.hits); System.out.println("misses="+ Interval.misses); System.out.println("outOfRange="+ Interval.outOfRange); } */ } } public void sortGrammarFiles() throws IOException { //System.out.println("Grammar names "+getGrammarFileNames()); Graph g = new Graph(); List missingFiles = new ArrayList(); for (String gfile : grammarFileNames) { try { GrammarSpelunker grammar = new GrammarSpelunker(inputDirectory, gfile); grammar.parse(); String vocabName = grammar.getTokenVocab(); String grammarName = grammar.getGrammarName(); // Make all grammars depend on any tokenVocab options if ( vocabName!=null ) g.addEdge(gfile, vocabName+CodeGenerator.VOCAB_FILE_EXTENSION); // Make all generated tokens files depend on their grammars g.addEdge(grammarName+CodeGenerator.VOCAB_FILE_EXTENSION, gfile); } catch (FileNotFoundException fnfe) { ErrorManager.error(ErrorManager.MSG_CANNOT_OPEN_FILE, gfile); missingFiles.add(gfile); } } List sorted = g.sort(); //System.out.println("sorted="+sorted); grammarFileNames.clear(); // wipe so we can give new ordered list for (int i = 0; i < sorted.size(); i++) { String f = (String)sorted.get(i); if ( missingFiles.contains(f) ) continue; if ( !(f.endsWith(".g") || f.endsWith(".g3")) ) continue; grammarFileNames.add(f); } //System.out.println("new grammars="+grammarFileNames); } /** Get a grammar mentioned on the command-line and any delegates */ public Grammar getRootGrammar(String grammarFileName) throws IOException { //ST.setLintMode(true); // grammars mentioned on command line are either roots or single grammars. // create the necessary composite in case it's got delegates; even // single grammar needs it to get token types. CompositeGrammar composite = new CompositeGrammar(); Grammar grammar = new Grammar(this, grammarFileName, composite); composite.setDelegationRoot(grammar); FileReader fr = null; File f = null; if (haveInputDir) { f = new File(inputDirectory, grammarFileName); } else { f = new File(grammarFileName); } // Store the location of this grammar as if we import files, we can then // search for imports in the same location as the original grammar as well as in // the lib directory. // parentGrammarDirectory = f.getParent(); if (grammarFileName.lastIndexOf(File.separatorChar) == -1) { grammarOutputDirectory = "."; } else { grammarOutputDirectory = grammarFileName.substring(0, grammarFileName.lastIndexOf(File.separatorChar)); } fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); grammar.parseAndBuildAST(br); composite.watchNFAConversion = internalOption_watchNFAConversion; br.close(); fr.close(); return grammar; } /** Create NFA, DFA and generate code for grammar. * Create NFA for any delegates first. Once all NFA are created, * it's ok to create DFA, which must check for left-recursion. That check * is done by walking the full NFA, which therefore must be complete. * After all NFA, comes DFA conversion for root grammar then code gen for * root grammar. DFA and code gen for delegates comes next. */ protected void generateRecognizer(Grammar grammar) { String language = (String) grammar.getOption("language"); if (language != null) { CodeGenerator generator = new CodeGenerator(this, grammar, language); grammar.setCodeGenerator(generator); generator.setDebug(isDebug()); generator.setProfile(isProfile()); generator.setTrace(isTrace()); // generate NFA early in case of crash later (for debugging) if (isGenerate_NFA_dot()) { generateNFAs(grammar); } // GENERATE CODE generator.genRecognizer(); if (isGenerate_DFA_dot()) { generateDFAs(grammar); } List delegates = grammar.getDirectDelegates(); for (int i = 0; delegates != null && i < delegates.size(); i++) { Grammar delegate = (Grammar) delegates.get(i); if (delegate != grammar) { // already processing this one generateRecognizer(delegate); } } } } public void generateDFAs(Grammar g) { for (int d = 1; d <= g.getNumberOfDecisions(); d++) { DFA dfa = g.getLookaheadDFA(d); if (dfa == null) { continue; // not there for some reason, ignore } DOTGenerator dotGenerator = new DOTGenerator(g); String dot = dotGenerator.getDOT(dfa.startState); String dotFileName = g.name + "." + "dec-" + d; if (g.implicitLexer) { dotFileName = g.name + Grammar.grammarTypeToFileNameSuffix[g.type] + "." + "dec-" + d; } try { writeDOTFile(g, dotFileName, dot); } catch (IOException ioe) { ErrorManager.error(ErrorManager.MSG_CANNOT_GEN_DOT_FILE, dotFileName, ioe); } } } protected void generateNFAs(Grammar g) { DOTGenerator dotGenerator = new DOTGenerator(g); Collection rules = g.getAllImportedRules(); rules.addAll(g.getRules()); for (Iterator itr = rules.iterator(); itr.hasNext();) { Rule r = (Rule) itr.next(); try { String dot = dotGenerator.getDOT(r.startState); if (dot != null) { writeDOTFile(g, r, dot); } } catch (IOException ioe) { ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, ioe); } } } protected void writeDOTFile(Grammar g, Rule r, String dot) throws IOException { writeDOTFile(g, r.grammar.name + "." + r.name, dot); } protected void writeDOTFile(Grammar g, String name, String dot) throws IOException { Writer fw = getOutputFile(g, name + ".dot"); fw.write(dot); fw.close(); } private static void version() { ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION); } private static void help() { ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION); System.err.println("usage: java org.antlr.Tool [args] file.g [file2.g file3.g ...]"); System.err.println(" -o outputDir specify output directory where all output is generated"); System.err.println(" -fo outputDir same as -o but force even files with relative paths to dir"); System.err.println(" -lib dir specify location of token files"); System.err.println(" -depend generate file dependencies"); System.err.println(" -report print out a report about the grammar(s) processed"); System.err.println(" -print print out the grammar without actions"); System.err.println(" -debug generate a parser that emits debugging events"); System.err.println(" -profile generate a parser that computes profiling information"); System.err.println(" -trace generate a recognizer that traces rule entry/exit"); System.err.println(" -nfa generate an NFA for each rule"); System.err.println(" -dfa generate a DFA for each decision point"); System.err.println(" -message-format name specify output style for messages"); System.err.println(" -verbose generate ANTLR version and other information"); System.err.println(" -make only build if generated files older than grammar"); System.err.println(" -version print the version of ANTLR and exit."); System.err.println(" -language L override language grammar option; generate L"); System.err.println(" -X display extended argument list"); } private static void Xhelp() { ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION); System.err.println(" -Xgrtree print the grammar AST"); System.err.println(" -Xdfa print DFA as text "); System.err.println(" -Xnoprune test lookahead against EBNF block exit branches"); System.err.println(" -Xnocollapse collapse incident edges into DFA states"); System.err.println(" -Xdbgconversion dump lots of info during NFA conversion"); System.err.println(" -Xconversiontimeout use to restrict NFA conversion exponentiality"); System.err.println(" -Xmultithreaded run the analysis in 2 threads"); System.err.println(" -Xnomergestopstates do not merge stop states"); System.err.println(" -Xdfaverbose generate DFA states in DOT with NFA configs"); System.err.println(" -Xwatchconversion print a message for each NFA before converting"); System.err.println(" -XdbgST put tags at start/stop of all templates in output"); System.err.println(" -Xnfastates for nondeterminisms, list NFA states for each path"); System.err.println(" -Xm m max number of rule invocations during conversion [" + NFAContext.MAX_SAME_RULE_INVOCATIONS_PER_NFA_CONFIG_STACK + "]"); System.err.println(" -Xmaxdfaedges m max \"comfortable\" number of edges for single DFA state [" + DFA.MAX_STATE_TRANSITIONS_FOR_TABLE + "]"); System.err.println(" -Xmaxinlinedfastates m max DFA states before table used rather than inlining [" + CodeGenerator.MADSI_DEFAULT +"]"); System.err.println(" -Xmaxswitchcaselabels m don't generate switch() statements for dfas bigger than m [" + CodeGenerator.MSCL_DEFAULT +"]"); System.err.println(" -Xminswitchalts m don't generate switch() statements for dfas smaller than m [" + CodeGenerator.MSA_DEFAULT + "]"); System.err.println(" -Xsavelexer don't delete temporary lexers generated from combined grammars"); } /** * Set the threshold of case labels beyond which ANTLR will not instruct the target template * to generate switch() { case xxx: ... * * @param maxSwitchCaseLabels Maximum number of case lables that ANTLR should allow the target code */ public void setMaxSwitchCaseLabels(int maxSwitchCaseLabels) { CodeGenerator.MAX_SWITCH_CASE_LABELS = maxSwitchCaseLabels; } /** * Set the threshold of the number alts, below which ANTLR will not instruct the target * template to use a switch statement. * * @param minSwitchAlts the minimum number of alts required to use a switch staement */ public void setMinSwitchAlts(int minSwitchAlts) { CodeGenerator.MIN_SWITCH_ALTS = minSwitchAlts; } /** * Set the location (base directory) where output files should be produced * by the ANTLR tool. * @param outputDirectory */ public void setOutputDirectory(String outputDirectory) { haveOutputDir = true; this.outputDirectory = outputDirectory; } /** * Used by build tools to force the output files to always be * relative to the base output directory, even though the tool * had to set the output directory to an absolute path as it * cannot rely on the workign directory like command line invocation * can. * * @param forceRelativeOutput true if output files hould always be relative to base output directory */ public void setForceRelativeOutput(boolean forceRelativeOutput) { this.forceRelativeOutput = forceRelativeOutput; } /** * Set the base location of input files. Normally (when the tool is * invoked from the command line), the inputDirectory is not set, but * for build tools such as Maven, we need to be able to locate the input * files relative to the base, as the working directory could be anywhere and * changing workig directories is not a valid concept for JVMs because of threading and * so on. Setting the directory just means that the getFileDirectory() method will * try to open files relative to this input directory. * * @param inputDirectory Input source base directory */ public void setInputDirectory(String inputDirectory) { this.inputDirectory = inputDirectory; haveInputDir = true; } /** This method is used by all code generators to create new output * files. If the outputDir set by -o is not present it will be created. * The final filename is sensitive to the output directory and * the directory where the grammar file was found. If -o is /tmp * and the original grammar file was foo/t.g then output files * go in /tmp/foo. * * The output dir -o spec takes precedence if it's absolute. * E.g., if the grammar file dir is absolute the output dir is given * precendence. "-o /tmp /usr/lib/t.g" results in "/tmp/T.java" as * output (assuming t.g holds T.java). * * If no -o is specified, then just write to the directory where the * grammar file was found. * * If outputDirectory==null then write a String. */ public Writer getOutputFile(Grammar g, String fileName) throws IOException { if (getOutputDirectory() == null) { return new StringWriter(); } // output directory is a function of where the grammar file lives // for subdir/T.g, you get subdir here. Well, depends on -o etc... // But, if this is a .tokens file, then we force the output to // be the base output directory (or current directory if there is not a -o) // File outputDir; if (fileName.endsWith(CodeGenerator.VOCAB_FILE_EXTENSION)) { if (haveOutputDir) { outputDir = new File(getOutputDirectory()); } else { outputDir = new File("."); } } else { outputDir = getOutputDirectory(g.getFileName()); } File outputFile = new File(outputDir, fileName); if (!outputDir.exists()) { outputDir.mkdirs(); } FileWriter fw = new FileWriter(outputFile); return new BufferedWriter(fw); } /** * Return the location where ANTLR will generate output files for a given file. This is a * base directory and output files will be relative to here in some cases * such as when -o option is used and input files are given relative * to the input directory. * * @param fileNameWithPath path to input source * @return */ public File getOutputDirectory(String fileNameWithPath) { File outputDir = new File(getOutputDirectory()); String fileDirectory; // Some files are given to us without a PATH but should should // still be written to the output directory in the relative path of // the output directory. The file directory is either the set of sub directories // or just or the relative path recorded for the parent grammar. This means // that when we write the tokens files, or the .java files for imported grammars // taht we will write them in the correct place. // if (fileNameWithPath.lastIndexOf(File.separatorChar) == -1) { // No path is included in the file name, so make the file // directory the same as the parent grammar (which might sitll be just "" // but when it is not, we will write the file in the correct place. // fileDirectory = grammarOutputDirectory; } else { fileDirectory = fileNameWithPath.substring(0, fileNameWithPath.lastIndexOf(File.separatorChar)); } if (haveOutputDir) { // -o /tmp /var/lib/t.g => /tmp/T.java // -o subdir/output /usr/lib/t.g => subdir/output/T.java // -o . /usr/lib/t.g => ./T.java if ((fileDirectory != null && !forceRelativeOutput) && (new File(fileDirectory).isAbsolute() || fileDirectory.startsWith("~")) || // isAbsolute doesn't count this :( isForceAllFilesToOutputDir()) { // somebody set the dir, it takes precendence; write new file there outputDir = new File(getOutputDirectory()); } else { // -o /tmp subdir/t.g => /tmp/subdir/t.g if (fileDirectory != null) { outputDir = new File(getOutputDirectory(), fileDirectory); } else { outputDir = new File(getOutputDirectory()); } } } else { // they didn't specify a -o dir so just write to location // where grammar is, absolute or relative, this will only happen // with command line invocation as build tools will always // supply an output directory. // outputDir = new File(fileDirectory); } return outputDir; } /** * Name a file from the -lib dir. Imported grammars and .tokens files * * If we do not locate the file in the library directory, then we try * the location of the originating grammar. * * @param fileName input name we are looking for * @return Path to file that we think shuold be the import file * * @throws java.io.IOException */ public String getLibraryFile(String fileName) throws IOException { // First, see if we can find the file in the library directory // File f = new File(getLibraryDirectory() + File.separator + fileName); if (f.exists()) { // Found in the library directory // return f.getAbsolutePath(); } // Need to assume it is in the same location as the input file. Note that // this is only relevant for external build tools and when the input grammar // was specified relative to the source directory (working directory if using // the command line. // return parentGrammarDirectory + File.separator + fileName; } /** Return the directory containing the grammar file for this grammar. * normally this is a relative path from current directory. People will * often do "java org.antlr.Tool grammars/*.g3" So the file will be * "grammars/foo.g3" etc... This method returns "grammars". * * If we have been given a specific input directory as a base, then * we must find the directory relative to this directory, unless the * file name is given to us in absolute terms. */ public String getFileDirectory(String fileName) { File f; if (haveInputDir && !fileName.startsWith(File.separator)) { f = new File(inputDirectory, fileName); } else { f = new File(fileName); } // And ask Java what the base directory of this location is // return f.getParent(); } /** Return a File descriptor for vocab file. Look in library or * in -o output path. antlr -o foo T.g U.g where U needs T.tokens * won't work unless we look in foo too. If we do not find the * file in the lib directory then must assume that the .tokens file * is going to be generated as part of this build and we have defined * .tokens files so that they ALWAYS are generated in the base output * directory, which means the current directory for the command line tool if there * was no output directory specified. */ public File getImportedVocabFile(String vocabName) { File f = new File(getLibraryDirectory(), File.separator + vocabName + CodeGenerator.VOCAB_FILE_EXTENSION); if (f.exists()) { return f; } // We did not find the vocab file in the lib directory, so we need // to look for it in the output directory which is where .tokens // files are generated (in the base, not relative to the input // location.) // if (haveOutputDir) { f = new File(getOutputDirectory(), vocabName + CodeGenerator.VOCAB_FILE_EXTENSION); } else { f = new File(vocabName + CodeGenerator.VOCAB_FILE_EXTENSION); } return f; } /** If the tool needs to panic/exit, how do we do that? */ public void panic() { throw new Error("ANTLR panic"); } /** Return a time stamp string accurate to sec: yyyy-mm-dd hh:mm:ss */ public static String getCurrentTimeStamp() { GregorianCalendar calendar = new java.util.GregorianCalendar(); int y = calendar.get(Calendar.YEAR); int m = calendar.get(Calendar.MONTH) + 1; // zero-based for months int d = calendar.get(Calendar.DAY_OF_MONTH); int h = calendar.get(Calendar.HOUR_OF_DAY); int min = calendar.get(Calendar.MINUTE); int sec = calendar.get(Calendar.SECOND); String sy = String.valueOf(y); String sm = m < 10 ? "0" + m : String.valueOf(m); String sd = d < 10 ? "0" + d : String.valueOf(d); String sh = h < 10 ? "0" + h : String.valueOf(h); String smin = min < 10 ? "0" + min : String.valueOf(min); String ssec = sec < 10 ? "0" + sec : String.valueOf(sec); return new StringBuffer().append(sy).append("-").append(sm).append("-").append(sd).append(" ").append(sh).append(":").append(smin).append(":").append(ssec).toString(); } /** * Provide the List of all grammar file names that the ANTLR tool will * process or has processed. * * @return the grammarFileNames */ public List getGrammarFileNames() { return grammarFileNames; } /** * Indicates whether ANTLR has gnerated or will generate a description of * all the NFAs in Dot format * * @return the generate_NFA_dot */ public boolean isGenerate_NFA_dot() { return generate_NFA_dot; } /** * Indicates whether ANTLR has generated or will generate a description of * all the NFAs in Dot format * * @return the generate_DFA_dot */ public boolean isGenerate_DFA_dot() { return generate_DFA_dot; } /** * Return the Path to the base output directory, where ANTLR * will generate all the output files for the current language target as * well as any ancillary files such as .tokens vocab files. * * @return the output Directory */ public String getOutputDirectory() { return outputDirectory; } /** * Return the Path to the directory in which ANTLR will search for ancillary * files such as .tokens vocab files and imported grammar files. * * @return the lib Directory */ public String getLibraryDirectory() { return libDirectory; } /** * Indicate if ANTLR has generated, or will generate a debug version of the * recognizer. Debug versions of a parser communicate with a debugger such * as that contained in ANTLRWorks and at start up will 'hang' waiting for * a connection on an IP port (49100 by default). * * @return the debug flag */ public boolean isDebug() { return debug; } /** * Indicate whether ANTLR has generated, or will generate a version of the * recognizer that prints trace messages on entry and exit of each rule. * * @return the trace flag */ public boolean isTrace() { return trace; } /** * Indicates whether ANTLR has generated or will generate a version of the * recognizer that gathers statistics about its execution, which it prints when * it terminates. * * @return the profile */ public boolean isProfile() { return profile; } /** * Indicates whether ANTLR has generated or will generate a report of various * elements of the grammar analysis, once it it has finished analyzing a grammar * file. * * @return the report flag */ public boolean isReport() { return report; } /** * Indicates whether ANTLR has printed, or will print, a version of the input grammar * file(s) that is stripped of any action code embedded within. * * @return the printGrammar flag */ public boolean isPrintGrammar() { return printGrammar; } /** * Indicates whether ANTLR has supplied, or will supply, a list of all the things * that the input grammar depends upon and all the things that will be generated * when that grammar is successfully analyzed. * * @return the depend flag */ public boolean isDepend() { return depend; } /** * Indicates whether ANTLR will force all files to the output directory, even * if the input files have relative paths from the input directory. * * @return the forceAllFilesToOutputDir flag */ public boolean isForceAllFilesToOutputDir() { return forceAllFilesToOutputDir; } /** * Indicates whether ANTLR will be verbose when analyzing grammar files, such as * displaying the names of the files it is generating and similar information. * * @return the verbose flag */ public boolean isVerbose() { return verbose; } /** * Provide the current setting of the conversion timeout on DFA creation. * * @return DFA creation timeout value in milliseconds */ public int getConversionTimeout() { return DFA.MAX_TIME_PER_DFA_CREATION; } /** * Returns the current setting of the message format descriptor * @return Current message format */ public String getMessageFormat() { return ErrorManager.getMessageFormat().toString(); } /** * Returns the number of errors that the analysis/processing threw up. * @return Error count */ public int getNumErrors() { return ErrorManager.getNumErrors(); } /** * Indicate whether the tool will analyze the dependencies of the provided grammar * file list and ensure that grammars with dependencies are built * after any of the other gramamrs in the list that they are dependent on. Setting * this option also has the side effect that any grammars that are includes for other * grammars in the list are excluded from individual analysis, which allows the caller * to invoke the tool via org.antlr.tool -make *.g and not worry about the inclusion * of grammars that are just includes for other grammars or what order the grammars * appear on the command line. * * This option was coded to make life easier for tool integration (such as Maven) but * may also be useful at the command line. * * @return true if the tool is currently configured to analyze and sort grammar files. */ public boolean getMake() { return make; } /** * Set the message format to one of ANTLR, gnu, vs2005 * * @param format */ public void setMessageFormat(String format) { ErrorManager.setFormat(format); } /** Provide the List of all grammar file names that the ANTLR tool should process. * * @param grammarFileNames The list of grammar files to process */ public void setGrammarFileNames(List grammarFileNames) { this.grammarFileNames = grammarFileNames; } public void addGrammarFile(String grammarFileName) { if (!grammarFileNames.contains(grammarFileName)) { grammarFileNames.add(grammarFileName); } } /** * Indicate whether ANTLR should generate a description of * all the NFAs in Dot format * * @param generate_NFA_dot True to generate dot descriptions */ public void setGenerate_NFA_dot(boolean generate_NFA_dot) { this.generate_NFA_dot = generate_NFA_dot; } /** * Indicates whether ANTLR should generate a description of * all the NFAs in Dot format * * @param generate_DFA_dot True to generate dot descriptions */ public void setGenerate_DFA_dot(boolean generate_DFA_dot) { this.generate_DFA_dot = generate_DFA_dot; } /** * Set the Path to the directory in which ANTLR will search for ancillary * files such as .tokens vocab files and imported grammar files. * * @param libDirectory the libDirectory to set */ public void setLibDirectory(String libDirectory) { this.libDirectory = libDirectory; } /** * Indicate whether ANTLR should generate a debug version of the * recognizer. Debug versions of a parser communicate with a debugger such * as that contained in ANTLRWorks and at start up will 'hang' waiting for * a connection on an IP port (49100 by default). * * @param debug true to generate a debug mode parser */ public void setDebug(boolean debug) { this.debug = debug; } /** * Indicate whether ANTLR should generate a version of the * recognizer that prints trace messages on entry and exit of each rule * * @param trace true to generate a tracing parser */ public void setTrace(boolean trace) { this.trace = trace; } /** * Indicate whether ANTLR should generate a version of the * recognizer that gathers statistics about its execution, which it prints when * it terminates. * * @param profile true to generate a profiling parser */ public void setProfile(boolean profile) { this.profile = profile; } /** * Indicate whether ANTLR should generate a report of various * elements of the grammar analysis, once it it has finished analyzing a grammar * file. * * @param report true to generate the analysis report */ public void setReport(boolean report) { this.report = report; } /** * Indicate whether ANTLR should print a version of the input grammar * file(s) that is stripped of any action code embedded within. * * @param printGrammar true to generate a stripped file */ public void setPrintGrammar(boolean printGrammar) { this.printGrammar = printGrammar; } /** * Indicate whether ANTLR should supply a list of all the things * that the input grammar depends upon and all the things that will be generated * when that gramamr is successfully analyzed. * * @param depend true to get depends set rather than process the grammar */ public void setDepend(boolean depend) { this.depend = depend; } /** * Indicates whether ANTLR will force all files to the output directory, even * if the input files have relative paths from the input directory. * * @param forceAllFilesToOutputDir true to force files to output directory */ public void setForceAllFilesToOutputDir(boolean forceAllFilesToOutputDir) { this.forceAllFilesToOutputDir = forceAllFilesToOutputDir; } /** * Indicate whether ANTLR should be verbose when analyzing grammar files, such as * displaying the names of the files it is generating and similar information. * * @param verbose true to be verbose */ public void setVerbose(boolean verbose) { this.verbose = verbose; } /** * Indicate whether the tool should analyze the dependencies of the provided grammar * file list and ensure that the grammars with dependencies are built * after any of the other gramamrs in the list that they are dependent on. Setting * this option also has the side effect that any grammars that are includes for other * grammars in the list are excluded from individual analysis, which allows the caller * to invoke the tool via org.antlr.tool -make *.g and not worry about the inclusion * of grammars that are just includes for other grammars or what order the grammars * appear on the command line. * * This option was coded to make life easier for tool integration (such as Maven) but * may also be useful at the command line. * * @param make */ public void setMake(boolean make) { this.make = make; } }