PageRenderTime 64ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/nickolas_pohilets/antlr3
Java | 1429 lines | 1084 code | 62 blank | 283 comment | 46 complexity | e7eafe0ada82e44959f0dcddc805e806 MD5 | raw file
Possible License(s): LGPL-2.0

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

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