PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/antlr-3.4/tool/src/main/java/org/antlr/Tool.java

https://bitbucket.org/cyanogenmod/android_external_antlr
Java | 1390 lines | 802 code | 112 blank | 476 comment | 184 complexity | aea9e135b14be527a959cf17425b61ba MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * [The "BSD license"]
  3. * Copyright (c) 2010 Terence Parr
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. The name of the author may not be used to endorse or promote products
  15. * derived from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. package org.antlr;
  29. import org.antlr.analysis.*;
  30. import org.antlr.codegen.CodeGenerator;
  31. import org.antlr.misc.Graph;
  32. import org.antlr.runtime.misc.Stats;
  33. import org.antlr.tool.*;
  34. import org.stringtemplate.v4.STGroup;
  35. import java.io.*;
  36. import java.util.*;
  37. /** The main ANTLR entry point. Read a grammar and generate a parser. */
  38. public class Tool {
  39. public final Properties antlrSettings = new Properties();
  40. public String VERSION = "3.4";
  41. //public static final String VERSION = "${project.version}";
  42. public static final String UNINITIALIZED_DIR = "<unset-dir>";
  43. private List<String> grammarFileNames = new ArrayList<String>();
  44. private boolean generate_NFA_dot = false;
  45. private boolean generate_DFA_dot = false;
  46. private String outputDirectory = ".";
  47. private boolean haveOutputDir = false;
  48. private String inputDirectory = null;
  49. private String parentGrammarDirectory;
  50. private String grammarOutputDirectory;
  51. private boolean haveInputDir = false;
  52. private String libDirectory = ".";
  53. private boolean debug = false;
  54. private boolean trace = false;
  55. private boolean profile = false;
  56. private boolean report = false;
  57. private boolean printGrammar = false;
  58. private boolean depend = false;
  59. private boolean forceAllFilesToOutputDir = false;
  60. private boolean forceRelativeOutput = false;
  61. protected boolean deleteTempLexer = true;
  62. private boolean verbose = false;
  63. /** Don't process grammar file if generated files are newer than grammar */
  64. private boolean make = false;
  65. private boolean showBanner = true;
  66. private static boolean exitNow = false;
  67. private static boolean return_dont_exit = false;
  68. public String forcedLanguageOption; // -language L on command line
  69. // The internal options are for my use on the command line during dev
  70. //
  71. public static boolean internalOption_PrintGrammarTree = false;
  72. public static boolean internalOption_PrintDFA = false;
  73. public static boolean internalOption_ShowNFAConfigsInDFA = false;
  74. public static boolean internalOption_watchNFAConversion = false;
  75. /**
  76. * A list of dependency generators that are accumulated aaaas (and if) the
  77. * tool is required to sort the provided grammars into build dependency order.
  78. protected Map<String, BuildDependencyGenerator> buildDependencyGenerators;
  79. */
  80. public static void main(String[] args) {
  81. Tool antlr = new Tool(args);
  82. if (!exitNow) {
  83. antlr.process();
  84. if ( return_dont_exit ) return;
  85. if (ErrorManager.getNumErrors() > 0) {
  86. System.exit(1);
  87. }
  88. System.exit(0);
  89. }
  90. }
  91. /**
  92. * Load the properties file org/antlr/antlr.properties and populate any
  93. * variables that must be initialized from it, such as the version of ANTLR.
  94. */
  95. private void loadResources() {
  96. InputStream in = null;
  97. in = this.getClass().getResourceAsStream("antlr.properties");
  98. // If we found the resource, then load it, otherwise revert to the
  99. // defaults.
  100. //
  101. if (in != null) {
  102. try {
  103. // Load the resources into the map
  104. //
  105. antlrSettings.load(in);
  106. // Set any variables that we need to populate from the resources
  107. //
  108. // VERSION = antlrSettings.getProperty("antlr.version");
  109. } catch (Exception e) {
  110. // Do nothing, just leave the defaults in place
  111. }
  112. }
  113. }
  114. public Tool() {
  115. loadResources();
  116. }
  117. public Tool(String[] args) {
  118. loadResources();
  119. // Set all the options and pick up all the named grammar files
  120. processArgs(args);
  121. }
  122. public void processArgs(String[] args) {
  123. if (isVerbose()) {
  124. ErrorManager.info("ANTLR Parser Generator Version " + VERSION);
  125. showBanner = false;
  126. }
  127. if (args == null || args.length == 0) {
  128. help();
  129. return;
  130. }
  131. for (int i = 0; i < args.length; i++) {
  132. if (args[i].equals("-o") || args[i].equals("-fo")) {
  133. if (i + 1 >= args.length) {
  134. System.err.println("missing output directory with -fo/-o option; ignoring");
  135. }
  136. else {
  137. if (args[i].equals("-fo")) { // force output into dir
  138. setForceAllFilesToOutputDir(true);
  139. }
  140. i++;
  141. outputDirectory = args[i];
  142. if (outputDirectory.endsWith("/") ||
  143. outputDirectory.endsWith("\\")) {
  144. outputDirectory =
  145. outputDirectory.substring(0, getOutputDirectory().length() - 1);
  146. }
  147. File outDir = new File(outputDirectory);
  148. haveOutputDir = true;
  149. if (outDir.exists() && !outDir.isDirectory()) {
  150. ErrorManager.error(ErrorManager.MSG_OUTPUT_DIR_IS_FILE, outputDirectory);
  151. setLibDirectory(".");
  152. }
  153. }
  154. }
  155. else if (args[i].equals("-lib")) {
  156. if (i + 1 >= args.length) {
  157. System.err.println("missing library directory with -lib option; ignoring");
  158. }
  159. else {
  160. i++;
  161. setLibDirectory(args[i]);
  162. if (getLibraryDirectory().endsWith("/") ||
  163. getLibraryDirectory().endsWith("\\")) {
  164. setLibDirectory(getLibraryDirectory().substring(0, getLibraryDirectory().length() - 1));
  165. }
  166. File outDir = new File(getLibraryDirectory());
  167. if (!outDir.exists()) {
  168. ErrorManager.error(ErrorManager.MSG_DIR_NOT_FOUND, getLibraryDirectory());
  169. setLibDirectory(".");
  170. }
  171. }
  172. }
  173. else if (args[i].equals("-language")) {
  174. if (i + 1 >= args.length) {
  175. System.err.println("missing language name; ignoring");
  176. }
  177. else {
  178. i++;
  179. forcedLanguageOption = args[i];
  180. }
  181. }
  182. else if (args[i].equals("-nfa")) {
  183. setGenerate_NFA_dot(true);
  184. }
  185. else if (args[i].equals("-dfa")) {
  186. setGenerate_DFA_dot(true);
  187. }
  188. else if (args[i].equals("-debug")) {
  189. setDebug(true);
  190. }
  191. else if (args[i].equals("-trace")) {
  192. setTrace(true);
  193. }
  194. else if (args[i].equals("-report")) {
  195. setReport(true);
  196. }
  197. else if (args[i].equals("-profile")) {
  198. setProfile(true);
  199. }
  200. else if (args[i].equals("-print")) {
  201. setPrintGrammar(true);
  202. }
  203. else if (args[i].equals("-depend")) {
  204. setDepend(true);
  205. }
  206. else if (args[i].equals("-verbose")) {
  207. setVerbose(true);
  208. }
  209. else if (args[i].equals("-version")) {
  210. version();
  211. exitNow = true;
  212. }
  213. else if (args[i].equals("-make")) {
  214. setMake(true);
  215. }
  216. else if (args[i].equals("-message-format")) {
  217. if (i + 1 >= args.length) {
  218. System.err.println("missing output format with -message-format option; using default");
  219. }
  220. else {
  221. i++;
  222. ErrorManager.setFormat(args[i]);
  223. }
  224. }
  225. else if (args[i].equals("-Xgrtree")) {
  226. internalOption_PrintGrammarTree = true; // print grammar tree
  227. }
  228. else if (args[i].equals("-Xdfa")) {
  229. internalOption_PrintDFA = true;
  230. }
  231. else if (args[i].equals("-Xnoprune")) {
  232. DFAOptimizer.PRUNE_EBNF_EXIT_BRANCHES = false;
  233. }
  234. else if (args[i].equals("-Xnocollapse")) {
  235. DFAOptimizer.COLLAPSE_ALL_PARALLEL_EDGES = false;
  236. }
  237. else if (args[i].equals("-Xdbgconversion")) {
  238. NFAToDFAConverter.debug = true;
  239. }
  240. else if (args[i].equals("-Xmultithreaded")) {
  241. NFAToDFAConverter.SINGLE_THREADED_NFA_CONVERSION = false;
  242. }
  243. else if (args[i].equals("-Xnomergestopstates")) {
  244. DFAOptimizer.MERGE_STOP_STATES = false;
  245. }
  246. else if (args[i].equals("-Xdfaverbose")) {
  247. internalOption_ShowNFAConfigsInDFA = true;
  248. }
  249. else if (args[i].equals("-Xwatchconversion")) {
  250. internalOption_watchNFAConversion = true;
  251. }
  252. else if (args[i].equals("-XdbgST")) {
  253. CodeGenerator.LAUNCH_ST_INSPECTOR = true;
  254. STGroup.trackCreationEvents = true;
  255. return_dont_exit = true;
  256. }
  257. else if (args[i].equals("-Xmaxinlinedfastates")) {
  258. if (i + 1 >= args.length) {
  259. System.err.println("missing max inline dfa states -Xmaxinlinedfastates option; ignoring");
  260. }
  261. else {
  262. i++;
  263. CodeGenerator.MAX_ACYCLIC_DFA_STATES_INLINE = Integer.parseInt(args[i]);
  264. }
  265. }
  266. else if (args[i].equals("-Xmaxswitchcaselabels")) {
  267. if (i + 1 >= args.length) {
  268. System.err.println("missing max switch case labels -Xmaxswitchcaselabels option; ignoring");
  269. }
  270. else {
  271. i++;
  272. CodeGenerator.MAX_SWITCH_CASE_LABELS = Integer.parseInt(args[i]);
  273. }
  274. }
  275. else if (args[i].equals("-Xminswitchalts")) {
  276. if (i + 1 >= args.length) {
  277. System.err.println("missing min switch alternatives -Xminswitchalts option; ignoring");
  278. }
  279. else {
  280. i++;
  281. CodeGenerator.MIN_SWITCH_ALTS = Integer.parseInt(args[i]);
  282. }
  283. }
  284. else if (args[i].equals("-Xm")) {
  285. if (i + 1 >= args.length) {
  286. System.err.println("missing max recursion with -Xm option; ignoring");
  287. }
  288. else {
  289. i++;
  290. NFAContext.MAX_SAME_RULE_INVOCATIONS_PER_NFA_CONFIG_STACK = Integer.parseInt(args[i]);
  291. }
  292. }
  293. else if (args[i].equals("-Xmaxdfaedges")) {
  294. if (i + 1 >= args.length) {
  295. System.err.println("missing max number of edges with -Xmaxdfaedges option; ignoring");
  296. }
  297. else {
  298. i++;
  299. DFA.MAX_STATE_TRANSITIONS_FOR_TABLE = Integer.parseInt(args[i]);
  300. }
  301. }
  302. else if (args[i].equals("-Xconversiontimeout")) {
  303. if (i + 1 >= args.length) {
  304. System.err.println("missing max time in ms -Xconversiontimeout option; ignoring");
  305. }
  306. else {
  307. i++;
  308. DFA.MAX_TIME_PER_DFA_CREATION = Integer.parseInt(args[i]);
  309. }
  310. }
  311. else if (args[i].equals("-Xnfastates")) {
  312. DecisionProbe.verbose = true;
  313. }
  314. else if (args[i].equals("-Xsavelexer")) {
  315. deleteTempLexer = false;
  316. }
  317. else if (args[i].equals("-X")) {
  318. Xhelp();
  319. }
  320. else {
  321. if (args[i].charAt(0) != '-') {
  322. // Must be the grammar file
  323. addGrammarFile(args[i]);
  324. }
  325. }
  326. }
  327. }
  328. /*
  329. protected void checkForInvalidArguments(String[] args, BitSet cmdLineArgValid) {
  330. // check for invalid command line args
  331. for (int a = 0; a < args.length; a++) {
  332. if (!cmdLineArgValid.member(a)) {
  333. System.err.println("invalid command-line argument: " + args[a] + "; ignored");
  334. }
  335. }
  336. }
  337. */
  338. /**
  339. * Checks to see if the list of outputFiles all exist, and have
  340. * last-modified timestamps which are later than the last-modified
  341. * timestamp of all the grammar files involved in build the output
  342. * (imports must be checked). If these conditions hold, the method
  343. * returns false, otherwise, it returns true.
  344. *
  345. * @param grammarFileName The grammar file we are checking
  346. */
  347. public boolean buildRequired(String grammarFileName)
  348. throws IOException
  349. {
  350. BuildDependencyGenerator bd =
  351. new BuildDependencyGenerator(this, grammarFileName);
  352. List<File> outputFiles = bd.getGeneratedFileList();
  353. List<File> inputFiles = bd.getDependenciesFileList();
  354. // Note that input directory must be set to use buildRequired
  355. File grammarFile;
  356. if (haveInputDir) {
  357. grammarFile = new File(inputDirectory, grammarFileName);
  358. }
  359. else {
  360. grammarFile = new File(grammarFileName);
  361. }
  362. long grammarLastModified = grammarFile.lastModified();
  363. for (File outputFile : outputFiles) {
  364. if (!outputFile.exists() || grammarLastModified > outputFile.lastModified()) {
  365. // One of the output files does not exist or is out of date, so we must build it
  366. return true;
  367. }
  368. // Check all of the imported grammars and see if any of these are younger
  369. // than any of the output files.
  370. if (inputFiles != null) {
  371. for (File inputFile : inputFiles) {
  372. if (inputFile.lastModified() > outputFile.lastModified()) {
  373. // One of the imported grammar files has been updated so we must build
  374. return true;
  375. }
  376. }
  377. }
  378. }
  379. if (isVerbose()) {
  380. System.out.println("Grammar " + grammarFile + " is up to date - build skipped");
  381. }
  382. return false;
  383. }
  384. public void process() {
  385. boolean exceptionWhenWritingLexerFile = false;
  386. String lexerGrammarFileName = null; // necessary at this scope to have access in the catch below
  387. // Have to be tricky here when Maven or build tools call in and must new Tool()
  388. // before setting options. The banner won't display that way!
  389. if (isVerbose() && showBanner) {
  390. ErrorManager.info("ANTLR Parser Generator Version " + VERSION);
  391. showBanner = false;
  392. }
  393. try {
  394. sortGrammarFiles(); // update grammarFileNames
  395. }
  396. catch (Exception e) {
  397. ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR,e);
  398. }
  399. catch (Error e) {
  400. ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, e);
  401. }
  402. for (String grammarFileName : grammarFileNames) {
  403. // If we are in make mode (to support build tools like Maven) and the
  404. // file is already up to date, then we do not build it (and in verbose mode
  405. // we will say so).
  406. if (make) {
  407. try {
  408. if ( !buildRequired(grammarFileName) ) continue;
  409. }
  410. catch (Exception e) {
  411. ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR,e);
  412. }
  413. }
  414. if (isVerbose() && !isDepend()) {
  415. System.out.println(grammarFileName);
  416. }
  417. try {
  418. if (isDepend()) {
  419. BuildDependencyGenerator dep =
  420. new BuildDependencyGenerator(this, grammarFileName);
  421. /*
  422. List outputFiles = dep.getGeneratedFileList();
  423. List dependents = dep.getDependenciesFileList();
  424. System.out.println("output: "+outputFiles);
  425. System.out.println("dependents: "+dependents);
  426. */
  427. System.out.println(dep.getDependencies().render());
  428. continue;
  429. }
  430. Grammar rootGrammar = getRootGrammar(grammarFileName);
  431. // we now have all grammars read in as ASTs
  432. // (i.e., root and all delegates)
  433. rootGrammar.composite.assignTokenTypes();
  434. //rootGrammar.composite.translateLeftRecursiveRules();
  435. rootGrammar.addRulesForSyntacticPredicates();
  436. rootGrammar.composite.defineGrammarSymbols();
  437. rootGrammar.composite.createNFAs();
  438. generateRecognizer(rootGrammar);
  439. if (isPrintGrammar()) {
  440. rootGrammar.printGrammar(System.out);
  441. }
  442. if (isReport()) {
  443. GrammarReport2 greport = new GrammarReport2(rootGrammar);
  444. System.out.print(greport.toString());
  445. // GrammarReport greport = new GrammarReport(rootGrammar);
  446. // System.out.println(greport.toString());
  447. // // print out a backtracking report too (that is not encoded into log)
  448. // System.out.println(greport.getBacktrackingReport());
  449. }
  450. if (isProfile()) {
  451. GrammarReport greport = new GrammarReport(rootGrammar);
  452. Stats.writeReport(GrammarReport.GRAMMAR_STATS_FILENAME,
  453. greport.toNotifyString());
  454. }
  455. // now handle the lexer if one was created for a merged spec
  456. String lexerGrammarStr = rootGrammar.getLexerGrammar();
  457. //System.out.println("lexer rootGrammar:\n"+lexerGrammarStr);
  458. if (rootGrammar.type == Grammar.COMBINED && lexerGrammarStr != null) {
  459. lexerGrammarFileName = rootGrammar.getImplicitlyGeneratedLexerFileName();
  460. try {
  461. Writer w = getOutputFile(rootGrammar, lexerGrammarFileName);
  462. w.write(lexerGrammarStr);
  463. w.close();
  464. }
  465. catch (IOException e) {
  466. // emit different error message when creating the implicit lexer fails
  467. // due to write permission error
  468. exceptionWhenWritingLexerFile = true;
  469. throw e;
  470. }
  471. try {
  472. StringReader sr = new StringReader(lexerGrammarStr);
  473. Grammar lexerGrammar = new Grammar(this);
  474. lexerGrammar.composite.watchNFAConversion = internalOption_watchNFAConversion;
  475. lexerGrammar.implicitLexer = true;
  476. //lexerGrammar.setTool(this);
  477. File lexerGrammarFullFile =
  478. new File(getFileDirectory(lexerGrammarFileName), lexerGrammarFileName);
  479. lexerGrammar.setFileName(lexerGrammarFullFile.toString());
  480. lexerGrammar.importTokenVocabulary(rootGrammar);
  481. lexerGrammar.parseAndBuildAST(sr);
  482. sr.close();
  483. lexerGrammar.composite.assignTokenTypes();
  484. lexerGrammar.addRulesForSyntacticPredicates();
  485. lexerGrammar.composite.defineGrammarSymbols();
  486. lexerGrammar.composite.createNFAs();
  487. generateRecognizer(lexerGrammar);
  488. }
  489. finally {
  490. // make sure we clean up
  491. if (deleteTempLexer) {
  492. File outputDir = getOutputDirectory(lexerGrammarFileName);
  493. File outputFile = new File(outputDir, lexerGrammarFileName);
  494. outputFile.delete();
  495. }
  496. }
  497. }
  498. }
  499. catch (IOException e) {
  500. if (exceptionWhenWritingLexerFile) {
  501. ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, e);
  502. }
  503. else {
  504. ErrorManager.error(ErrorManager.MSG_CANNOT_OPEN_FILE,
  505. grammarFileName);
  506. }
  507. }
  508. catch (Exception e) {
  509. ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, grammarFileName, e);
  510. }
  511. /*
  512. finally {
  513. System.out.println("creates="+ Interval.creates);
  514. System.out.println("hits="+ Interval.hits);
  515. System.out.println("misses="+ Interval.misses);
  516. System.out.println("outOfRange="+ Interval.outOfRange);
  517. }
  518. */
  519. }
  520. }
  521. public void sortGrammarFiles() throws IOException {
  522. //System.out.println("Grammar names "+getGrammarFileNames());
  523. Graph g = new Graph();
  524. List<String> missingFiles = new ArrayList<String>();
  525. for (String gfile : grammarFileNames) {
  526. try {
  527. GrammarSpelunker grammar = new GrammarSpelunker(inputDirectory, gfile);
  528. grammar.parse();
  529. String vocabName = grammar.getTokenVocab();
  530. String grammarName = grammar.getGrammarName();
  531. // Make all grammars depend on any tokenVocab options
  532. if ( vocabName!=null ) g.addEdge(gfile, vocabName+CodeGenerator.VOCAB_FILE_EXTENSION);
  533. // Make all generated tokens files depend on their grammars
  534. g.addEdge(grammarName+CodeGenerator.VOCAB_FILE_EXTENSION, gfile);
  535. }
  536. catch (FileNotFoundException fnfe) {
  537. ErrorManager.error(ErrorManager.MSG_CANNOT_OPEN_FILE, gfile);
  538. missingFiles.add(gfile);
  539. }
  540. }
  541. List<Object> sorted = g.sort();
  542. //System.out.println("sorted="+sorted);
  543. grammarFileNames.clear(); // wipe so we can give new ordered list
  544. for (int i = 0; i < sorted.size(); i++) {
  545. String f = (String)sorted.get(i);
  546. if ( missingFiles.contains(f) ) continue;
  547. if ( !(f.endsWith(".g") || f.endsWith(".g3")) ) continue;
  548. grammarFileNames.add(f);
  549. }
  550. //System.out.println("new grammars="+grammarFileNames);
  551. }
  552. /** Get a grammar mentioned on the command-line and any delegates */
  553. public Grammar getRootGrammar(String grammarFileName)
  554. throws IOException
  555. {
  556. //ST.setLintMode(true);
  557. // grammars mentioned on command line are either roots or single grammars.
  558. // create the necessary composite in case it's got delegates; even
  559. // single grammar needs it to get token types.
  560. CompositeGrammar composite = new CompositeGrammar();
  561. Grammar grammar = new Grammar(this, grammarFileName, composite);
  562. composite.setDelegationRoot(grammar);
  563. FileReader fr = null;
  564. File f = null;
  565. if (haveInputDir) {
  566. f = new File(inputDirectory, grammarFileName);
  567. }
  568. else {
  569. f = new File(grammarFileName);
  570. }
  571. // Store the location of this grammar as if we import files, we can then
  572. // search for imports in the same location as the original grammar as well as in
  573. // the lib directory.
  574. //
  575. parentGrammarDirectory = f.getParent();
  576. if (grammarFileName.lastIndexOf(File.separatorChar) == -1) {
  577. grammarOutputDirectory = ".";
  578. }
  579. else {
  580. grammarOutputDirectory = grammarFileName.substring(0, grammarFileName.lastIndexOf(File.separatorChar));
  581. }
  582. fr = new FileReader(f);
  583. BufferedReader br = new BufferedReader(fr);
  584. grammar.parseAndBuildAST(br);
  585. composite.watchNFAConversion = internalOption_watchNFAConversion;
  586. br.close();
  587. fr.close();
  588. return grammar;
  589. }
  590. /** Create NFA, DFA and generate code for grammar.
  591. * Create NFA for any delegates first. Once all NFA are created,
  592. * it's ok to create DFA, which must check for left-recursion. That check
  593. * is done by walking the full NFA, which therefore must be complete.
  594. * After all NFA, comes DFA conversion for root grammar then code gen for
  595. * root grammar. DFA and code gen for delegates comes next.
  596. */
  597. protected void generateRecognizer(Grammar grammar) {
  598. String language = (String) grammar.getOption("language");
  599. if (language != null) {
  600. CodeGenerator generator = new CodeGenerator(this, grammar, language);
  601. grammar.setCodeGenerator(generator);
  602. generator.setDebug(isDebug());
  603. generator.setProfile(isProfile());
  604. generator.setTrace(isTrace());
  605. // generate NFA early in case of crash later (for debugging)
  606. if (isGenerate_NFA_dot()) {
  607. generateNFAs(grammar);
  608. }
  609. // GENERATE CODE
  610. generator.genRecognizer();
  611. if (isGenerate_DFA_dot()) {
  612. generateDFAs(grammar);
  613. }
  614. List<Grammar> delegates = grammar.getDirectDelegates();
  615. for (int i = 0; delegates != null && i < delegates.size(); i++) {
  616. Grammar delegate = (Grammar) delegates.get(i);
  617. if (delegate != grammar) { // already processing this one
  618. generateRecognizer(delegate);
  619. }
  620. }
  621. }
  622. }
  623. public void generateDFAs(Grammar g) {
  624. for (int d = 1; d <= g.getNumberOfDecisions(); d++) {
  625. DFA dfa = g.getLookaheadDFA(d);
  626. if (dfa == null) {
  627. continue; // not there for some reason, ignore
  628. }
  629. DOTGenerator dotGenerator = new DOTGenerator(g);
  630. String dot = dotGenerator.getDOT(dfa.startState);
  631. String dotFileName = g.name + "." + "dec-" + d;
  632. if (g.implicitLexer) {
  633. dotFileName = g.name + Grammar.grammarTypeToFileNameSuffix[g.type] + "." + "dec-" + d;
  634. }
  635. try {
  636. writeDOTFile(g, dotFileName, dot);
  637. } catch (IOException ioe) {
  638. ErrorManager.error(ErrorManager.MSG_CANNOT_GEN_DOT_FILE,
  639. dotFileName,
  640. ioe);
  641. }
  642. }
  643. }
  644. protected void generateNFAs(Grammar g) {
  645. DOTGenerator dotGenerator = new DOTGenerator(g);
  646. Collection rules = g.getAllImportedRules();
  647. rules.addAll(g.getRules());
  648. for (Iterator itr = rules.iterator(); itr.hasNext();) {
  649. Rule r = (Rule) itr.next();
  650. try {
  651. String dot = dotGenerator.getDOT(r.startState);
  652. if (dot != null) {
  653. writeDOTFile(g, r, dot);
  654. }
  655. } catch (IOException ioe) {
  656. ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, ioe);
  657. }
  658. }
  659. }
  660. protected void writeDOTFile(Grammar g, Rule r, String dot) throws IOException {
  661. writeDOTFile(g, r.grammar.name + "." + r.name, dot);
  662. }
  663. protected void writeDOTFile(Grammar g, String name, String dot) throws IOException {
  664. Writer fw = getOutputFile(g, name + ".dot");
  665. fw.write(dot);
  666. fw.close();
  667. }
  668. private static void version() {
  669. ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION);
  670. }
  671. private static void help() {
  672. ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION);
  673. System.err.println("usage: java org.antlr.Tool [args] file.g [file2.g file3.g ...]");
  674. System.err.println(" -o outputDir specify output directory where all output is generated");
  675. System.err.println(" -fo outputDir same as -o but force even files with relative paths to dir");
  676. System.err.println(" -lib dir specify location of token files");
  677. System.err.println(" -depend generate file dependencies");
  678. System.err.println(" -report print out a report about the grammar(s) processed");
  679. System.err.println(" -print print out the grammar without actions");
  680. System.err.println(" -debug generate a parser that emits debugging events");
  681. System.err.println(" -profile generate a parser that computes profiling information");
  682. System.err.println(" -trace generate a recognizer that traces rule entry/exit");
  683. System.err.println(" -nfa generate an NFA for each rule");
  684. System.err.println(" -dfa generate a DFA for each decision point");
  685. System.err.println(" -message-format name specify output style for messages");
  686. System.err.println(" -verbose generate ANTLR version and other information");
  687. System.err.println(" -make only build if generated files older than grammar");
  688. System.err.println(" -version print the version of ANTLR and exit.");
  689. System.err.println(" -language L override language grammar option; generate L");
  690. System.err.println(" -X display extended argument list");
  691. }
  692. private static void Xhelp() {
  693. ErrorManager.info("ANTLR Parser Generator Version " + new Tool().VERSION);
  694. System.err.println(" -Xgrtree print the grammar AST");
  695. System.err.println(" -Xdfa print DFA as text ");
  696. System.err.println(" -Xnoprune test lookahead against EBNF block exit branches");
  697. System.err.println(" -Xnocollapse collapse incident edges into DFA states");
  698. System.err.println(" -Xdbgconversion dump lots of info during NFA conversion");
  699. System.err.println(" -Xconversiontimeout use to restrict NFA conversion exponentiality");
  700. System.err.println(" -Xmultithreaded run the analysis in 2 threads");
  701. System.err.println(" -Xnomergestopstates do not merge stop states");
  702. System.err.println(" -Xdfaverbose generate DFA states in DOT with NFA configs");
  703. System.err.println(" -Xwatchconversion print a message for each NFA before converting");
  704. System.err.println(" -XdbgST put tags at start/stop of all templates in output");
  705. System.err.println(" -Xnfastates for nondeterminisms, list NFA states for each path");
  706. System.err.println(" -Xm m max number of rule invocations during conversion [" + NFAContext.MAX_SAME_RULE_INVOCATIONS_PER_NFA_CONFIG_STACK + "]");
  707. System.err.println(" -Xmaxdfaedges m max \"comfortable\" number of edges for single DFA state [" + DFA.MAX_STATE_TRANSITIONS_FOR_TABLE + "]");
  708. System.err.println(" -Xmaxinlinedfastates m max DFA states before table used rather than inlining [" + CodeGenerator.MADSI_DEFAULT +"]");
  709. System.err.println(" -Xmaxswitchcaselabels m don't generate switch() statements for dfas bigger than m [" + CodeGenerator.MSCL_DEFAULT +"]");
  710. System.err.println(" -Xminswitchalts m don't generate switch() statements for dfas smaller than m [" + CodeGenerator.MSA_DEFAULT + "]");
  711. System.err.println(" -Xsavelexer don't delete temporary lexers generated from combined grammars");
  712. }
  713. /**
  714. * Set the threshold of case labels beyond which ANTLR will not instruct the target template
  715. * to generate switch() { case xxx: ...
  716. *
  717. * @param maxSwitchCaseLabels Maximum number of case lables that ANTLR should allow the target code
  718. */
  719. public void setMaxSwitchCaseLabels(int maxSwitchCaseLabels) {
  720. CodeGenerator.MAX_SWITCH_CASE_LABELS = maxSwitchCaseLabels;
  721. }
  722. /**
  723. * Set the threshold of the number alts, below which ANTLR will not instruct the target
  724. * template to use a switch statement.
  725. *
  726. * @param minSwitchAlts the minimum number of alts required to use a switch staement
  727. */
  728. public void setMinSwitchAlts(int minSwitchAlts) {
  729. CodeGenerator.MIN_SWITCH_ALTS = minSwitchAlts;
  730. }
  731. /**
  732. * Set the location (base directory) where output files should be produced
  733. * by the ANTLR tool.
  734. * @param outputDirectory
  735. */
  736. public void setOutputDirectory(String outputDirectory) {
  737. haveOutputDir = true;
  738. this.outputDirectory = outputDirectory;
  739. }
  740. /**
  741. * Used by build tools to force the output files to always be
  742. * relative to the base output directory, even though the tool
  743. * had to set the output directory to an absolute path as it
  744. * cannot rely on the workign directory like command line invocation
  745. * can.
  746. *
  747. * @param forceRelativeOutput true if output files hould always be relative to base output directory
  748. */
  749. public void setForceRelativeOutput(boolean forceRelativeOutput) {
  750. this.forceRelativeOutput = forceRelativeOutput;
  751. }
  752. /**
  753. * Set the base location of input files. Normally (when the tool is
  754. * invoked from the command line), the inputDirectory is not set, but
  755. * for build tools such as Maven, we need to be able to locate the input
  756. * files relative to the base, as the working directory could be anywhere and
  757. * changing workig directories is not a valid concept for JVMs because of threading and
  758. * so on. Setting the directory just means that the getFileDirectory() method will
  759. * try to open files relative to this input directory.
  760. *
  761. * @param inputDirectory Input source base directory
  762. */
  763. public void setInputDirectory(String inputDirectory) {
  764. this.inputDirectory = inputDirectory;
  765. haveInputDir = true;
  766. }
  767. /** This method is used by all code generators to create new output
  768. * files. If the outputDir set by -o is not present it will be created.
  769. * The final filename is sensitive to the output directory and
  770. * the directory where the grammar file was found. If -o is /tmp
  771. * and the original grammar file was foo/t.g then output files
  772. * go in /tmp/foo.
  773. *
  774. * The output dir -o spec takes precedence if it's absolute.
  775. * E.g., if the grammar file dir is absolute the output dir is given
  776. * precendence. "-o /tmp /usr/lib/t.g" results in "/tmp/T.java" as
  777. * output (assuming t.g holds T.java).
  778. *
  779. * If no -o is specified, then just write to the directory where the
  780. * grammar file was found.
  781. *
  782. * If outputDirectory==null then write a String.
  783. */
  784. public Writer getOutputFile(Grammar g, String fileName) throws IOException {
  785. if (getOutputDirectory() == null) {
  786. return new StringWriter();
  787. }
  788. // output directory is a function of where the grammar file lives
  789. // for subdir/T.g, you get subdir here. Well, depends on -o etc...
  790. // But, if this is a .tokens file, then we force the output to
  791. // be the base output directory (or current directory if there is not a -o)
  792. //
  793. File outputDir;
  794. if (fileName.endsWith(CodeGenerator.VOCAB_FILE_EXTENSION)) {
  795. if (haveOutputDir) {
  796. outputDir = new File(getOutputDirectory());
  797. }
  798. else {
  799. outputDir = new File(".");
  800. }
  801. }
  802. else {
  803. outputDir = getOutputDirectory(g.getFileName());
  804. }
  805. File outputFile = new File(outputDir, fileName);
  806. if (!outputDir.exists()) {
  807. outputDir.mkdirs();
  808. }
  809. FileWriter fw = new FileWriter(outputFile);
  810. return new BufferedWriter(fw);
  811. }
  812. /**
  813. * Return the location where ANTLR will generate output files for a given file. This is a
  814. * base directory and output files will be relative to here in some cases
  815. * such as when -o option is used and input files are given relative
  816. * to the input directory.
  817. *
  818. * @param fileNameWithPath path to input source
  819. * @return
  820. */
  821. public File getOutputDirectory(String fileNameWithPath) {
  822. File outputDir = new File(getOutputDirectory());
  823. String fileDirectory;
  824. // Some files are given to us without a PATH but should should
  825. // still be written to the output directory in the relative path of
  826. // the output directory. The file directory is either the set of sub directories
  827. // or just or the relative path recorded for the parent grammar. This means
  828. // that when we write the tokens files, or the .java files for imported grammars
  829. // taht we will write them in the correct place.
  830. //
  831. if (fileNameWithPath.lastIndexOf(File.separatorChar) == -1) {
  832. // No path is included in the file name, so make the file
  833. // directory the same as the parent grammar (which might sitll be just ""
  834. // but when it is not, we will write the file in the correct place.
  835. //
  836. fileDirectory = grammarOutputDirectory;
  837. }
  838. else {
  839. fileDirectory = fileNameWithPath.substring(0, fileNameWithPath.lastIndexOf(File.separatorChar));
  840. }
  841. if (haveOutputDir) {
  842. // -o /tmp /var/lib/t.g => /tmp/T.java
  843. // -o subdir/output /usr/lib/t.g => subdir/output/T.java
  844. // -o . /usr/lib/t.g => ./T.java
  845. if ((fileDirectory != null && !forceRelativeOutput) &&
  846. (new File(fileDirectory).isAbsolute() ||
  847. fileDirectory.startsWith("~")) || // isAbsolute doesn't count this :(
  848. isForceAllFilesToOutputDir()) {
  849. // somebody set the dir, it takes precendence; write new file there
  850. outputDir = new File(getOutputDirectory());
  851. }
  852. else {
  853. // -o /tmp subdir/t.g => /tmp/subdir/t.g
  854. if (fileDirectory != null) {
  855. outputDir = new File(getOutputDirectory(), fileDirectory);
  856. }
  857. else {
  858. outputDir = new File(getOutputDirectory());
  859. }
  860. }
  861. }
  862. else {
  863. // they didn't specify a -o dir so just write to location
  864. // where grammar is, absolute or relative, this will only happen
  865. // with command line invocation as build tools will always
  866. // supply an output directory.
  867. //
  868. outputDir = new File(fileDirectory);
  869. }
  870. return outputDir;
  871. }
  872. /**
  873. * Name a file from the -lib dir. Imported grammars and .tokens files
  874. *
  875. * If we do not locate the file in the library directory, then we try
  876. * the location of the originating grammar.
  877. *
  878. * @param fileName input name we are looking for
  879. * @return Path to file that we think shuold be the import file
  880. *
  881. * @throws java.io.IOException
  882. */
  883. public String getLibraryFile(String fileName) throws IOException {
  884. // First, see if we can find the file in the library directory
  885. //
  886. File f = new File(getLibraryDirectory() + File.separator + fileName);
  887. if (f.exists()) {
  888. // Found in the library directory
  889. //
  890. return f.getAbsolutePath();
  891. }
  892. // Need to assume it is in the same location as the input file. Note that
  893. // this is only relevant for external build tools and when the input grammar
  894. // was specified relative to the source directory (working directory if using
  895. // the command line.
  896. //
  897. return parentGrammarDirectory + File.separator + fileName;
  898. }
  899. /** Return the directory containing the grammar file for this grammar.
  900. * normally this is a relative path from current directory. People will
  901. * often do "java org.antlr.Tool grammars/*.g3" So the file will be
  902. * "grammars/foo.g3" etc... This method returns "grammars".
  903. *
  904. * If we have been given a specific input directory as a base, then
  905. * we must find the directory relative to this directory, unless the
  906. * file name is given to us in absolute terms.
  907. */
  908. public String getFileDirectory(String fileName) {
  909. File f;
  910. if (haveInputDir && !fileName.startsWith(File.separator)) {
  911. f = new File(inputDirectory, fileName);
  912. }
  913. else {
  914. f = new File(fileName);
  915. }
  916. // And ask Java what the base directory of this location is
  917. //
  918. return f.getParent();
  919. }
  920. /** Return a File descriptor for vocab file. Look in library or
  921. * in -o output path. antlr -o foo T.g U.g where U needs T.tokens
  922. * won't work unless we look in foo too. If we do not find the
  923. * file in the lib directory then must assume that the .tokens file
  924. * is going to be generated as part of this build and we have defined
  925. * .tokens files so that they ALWAYS are generated in the base output
  926. * directory, which means the current directory for the command line tool if there
  927. * was no output directory specified.
  928. */
  929. public File getImportedVocabFile(String vocabName) {
  930. File f = new File(getLibraryDirectory(),
  931. File.separator +
  932. vocabName +
  933. CodeGenerator.VOCAB_FILE_EXTENSION);
  934. if (f.exists()) {
  935. return f;
  936. }
  937. // We did not find the vocab file in the lib directory, so we need
  938. // to look for it in the output directory which is where .tokens
  939. // files are generated (in the base, not relative to the input
  940. // location.)
  941. //
  942. if (haveOutputDir) {
  943. f = new File(getOutputDirectory(), vocabName + CodeGenerator.VOCAB_FILE_EXTENSION);
  944. }
  945. else {
  946. f = new File(vocabName + CodeGenerator.VOCAB_FILE_EXTENSION);
  947. }
  948. return f;
  949. }
  950. /** If the tool needs to panic/exit, how do we do that?
  951. */
  952. public void panic() {
  953. throw new Error("ANTLR panic");
  954. }
  955. /** Return a time stamp string accurate to sec: yyyy-mm-dd hh:mm:ss
  956. */
  957. public static String getCurrentTimeStamp() {
  958. GregorianCalendar calendar = new java.util.GregorianCalendar();
  959. int y = calendar.get(Calendar.YEAR);
  960. int m = calendar.get(Calendar.MONTH) + 1; // zero-based for months
  961. int d = calendar.get(Calendar.DAY_OF_MONTH);
  962. int h = calendar.get(Calendar.HOUR_OF_DAY);
  963. int min = calendar.get(Calendar.MINUTE);
  964. int sec = calendar.get(Calendar.SECOND);
  965. String sy = String.valueOf(y);
  966. String sm = m < 10 ? "0" + m : String.valueOf(m);
  967. String sd = d < 10 ? "0" + d : String.valueOf(d);
  968. String sh = h < 10 ? "0" + h : String.valueOf(h);
  969. String smin = min < 10 ? "0" + min : String.valueOf(min);
  970. String ssec = sec < 10 ? "0" + sec : String.valueOf(sec);
  971. return new StringBuffer().append(sy).append("-").append(sm).append("-").append(sd).append(" ").append(sh).append(":").append(smin).append(":").append(ssec).toString();
  972. }
  973. /**
  974. * Provide the List of all grammar file names that the ANTLR tool will
  975. * process or has processed.
  976. *
  977. * @return the grammarFileNames
  978. */
  979. public List<String> getGrammarFileNames() {
  980. return grammarFileNames;
  981. }
  982. /**
  983. * Indicates whether ANTLR has gnerated or will generate a description of
  984. * all the NFAs in <a href="http://www.graphviz.org">Dot format</a>
  985. *
  986. * @return the generate_NFA_dot
  987. */
  988. public boolean isGenerate_NFA_dot() {
  989. return generate_NFA_dot;
  990. }
  991. /**
  992. * Indicates whether ANTLR has generated or will generate a description of
  993. * all the NFAs in <a href="http://www.graphviz.org">Dot format</a>
  994. *
  995. * @return the generate_DFA_dot
  996. */
  997. public boolean isGenerate_DFA_dot() {
  998. return generate_DFA_dot;
  999. }
  1000. /**
  1001. * Return the Path to the base output directory, where ANTLR
  1002. * will generate all the output files for the current language target as
  1003. * well as any ancillary files such as .tokens vocab files.
  1004. *
  1005. * @return the output Directory
  1006. */
  1007. public String getOutputDirectory() {
  1008. return outputDirectory;
  1009. }
  1010. /**
  1011. * Return the Path to the directory in which ANTLR will search for ancillary
  1012. * files such as .tokens vocab files and imported grammar files.
  1013. *
  1014. * @return the lib Directory
  1015. */
  1016. public String getLibraryDirectory() {
  1017. return libDirectory;
  1018. }
  1019. /**
  1020. * Indicate if ANTLR has generated, or will generate a debug version of the
  1021. * recognizer. Debug versions of a parser communicate with a debugger such
  1022. * as that contained in ANTLRWorks and at start up will 'hang' waiting for
  1023. * a connection on an IP port (49100 by default).
  1024. *
  1025. * @return the debug flag
  1026. */
  1027. public boolean isDebug() {
  1028. return debug;
  1029. }
  1030. /**
  1031. * Indicate whether ANTLR has generated, or will generate a version of the
  1032. * recognizer that prints trace messages on entry and exit of each rule.
  1033. *
  1034. * @return the trace flag
  1035. */
  1036. public boolean isTrace() {
  1037. return trace;
  1038. }
  1039. /**
  1040. * Indicates whether ANTLR has generated or will generate a version of the
  1041. * recognizer that gathers statistics about its execution, which it prints when
  1042. * it terminates.
  1043. *
  1044. * @return the profile
  1045. */
  1046. public boolean isProfile() {
  1047. return profile;
  1048. }
  1049. /**
  1050. * Indicates whether ANTLR has generated or will generate a report of various
  1051. * elements of the grammar analysis, once it it has finished analyzing a grammar
  1052. * file.
  1053. *
  1054. * @return the report flag
  1055. */
  1056. public boolean isReport() {
  1057. return report;
  1058. }
  1059. /**
  1060. * Indicates whether ANTLR has printed, or will print, a version of the input grammar
  1061. * file(s) that is stripped of any action code embedded within.
  1062. *
  1063. * @return the printGrammar flag
  1064. */
  1065. public boolean isPrintGrammar() {
  1066. return printGrammar;
  1067. }
  1068. /**
  1069. * Indicates whether ANTLR has supplied, or will supply, a list of all the things
  1070. * that the input grammar depends upon and all the things that will be generated
  1071. * when that grammar is successfully analyzed.
  1072. *
  1073. * @return the depend flag
  1074. */
  1075. public boolean isDepend() {
  1076. return depend;
  1077. }
  1078. /**
  1079. * Indicates whether ANTLR will force all files to the output directory, even
  1080. * if the input files have relative paths from the input directory.
  1081. *
  1082. * @return the forceAllFilesToOutputDir flag
  1083. */
  1084. public boolean isForceAllFilesToOutputDir() {
  1085. return forceAllFilesToOutputDir;
  1086. }
  1087. /**
  1088. * Indicates whether ANTLR will be verbose when analyzing grammar files, such as
  1089. * displaying the names of the files it is generating and similar information.
  1090. *
  1091. * @return the verbose flag
  1092. */
  1093. public boolean isVerbose() {
  1094. return verbose;
  1095. }
  1096. /**
  1097. * Provide the current setting of the conversion timeout on DFA creation.
  1098. *
  1099. * @return DFA creation timeout value in milliseconds
  1100. */
  1101. public int getConversionTimeout() {
  1102. return DFA.MAX_TIME_PER_DFA_CREATION;
  1103. }
  1104. /**
  1105. * Returns the current setting of the message format descriptor
  1106. * @return Current message format
  1107. */
  1108. public String getMessageFormat() {
  1109. return ErrorManager.getMessageFormat().toString();
  1110. }
  1111. /**
  1112. * Returns the number of errors that the analysis/processing threw up.
  1113. * @return Error count
  1114. */
  1115. public int getNumErrors() {
  1116. return ErrorManager.getNumErrors();
  1117. }
  1118. /**
  1119. * Indicate whether the tool will analyze the dependencies of the provided grammar
  1120. * file list and ensure that grammars with dependencies are built
  1121. * after any of the other gramamrs in the list that they are dependent on. Setting
  1122. * this option also has the side effect that any grammars that are includes for other
  1123. * grammars in the list are excluded from individual analysis, which allows the caller
  1124. * to invoke the tool via org.antlr.tool -make *.g and not worry about the inclusion
  1125. * of grammars that are just include…

Large files files are truncated, but you can click here to view the full file