PageRenderTime 45ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/tool/src/main/java/org/antlr/codegen/CodeGenerator.java

https://bitbucket.org/jwalton/antlr3
Java | 1310 lines | 987 code | 77 blank | 246 comment | 93 complexity | 27b81c4a2f16716208571827af5284cd MD5 | raw 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.codegen;
  29. import org.antlr.Tool;
  30. import org.antlr.analysis.DFA;
  31. import org.antlr.analysis.*;
  32. import org.antlr.grammar.v3.ANTLRLexer;
  33. import org.antlr.grammar.v3.ANTLRParser;
  34. import org.antlr.grammar.v3.ActionTranslator;
  35. import org.antlr.grammar.v3.CodeGenTreeWalker;
  36. import org.antlr.misc.BitSet;
  37. import org.antlr.misc.*;
  38. import org.antlr.runtime.*;
  39. import org.antlr.runtime.tree.CommonTreeNodeStream;
  40. import org.antlr.tool.*;
  41. import org.stringtemplate.v4.*;
  42. import java.io.IOException;
  43. import java.io.Writer;
  44. import java.util.*;
  45. /** ANTLR's code generator.
  46. *
  47. * Generate recognizers derived from grammars. Language independence
  48. * achieved through the use of STGroup objects. All output
  49. * strings are completely encapsulated in the group files such as Java.stg.
  50. * Some computations are done that are unused by a particular language.
  51. * This generator just computes and sets the values into the templates;
  52. * the templates are free to use or not use the information.
  53. *
  54. * To make a new code generation target, define X.stg for language X
  55. * by copying from existing Y.stg most closely releated to your language;
  56. * e.g., to do CSharp.stg copy Java.stg. The template group file has a
  57. * bunch of templates that are needed by the code generator. You can add
  58. * a new target w/o even recompiling ANTLR itself. The language=X option
  59. * in a grammar file dictates which templates get loaded/used.
  60. *
  61. * Some language like C need both parser files and header files. Java needs
  62. * to have a separate file for the cyclic DFA as ANTLR generates bytecodes
  63. * directly (which cannot be in the generated parser Java file). To facilitate
  64. * this,
  65. *
  66. * cyclic can be in same file, but header, output must be searpate. recognizer
  67. * is in outptufile.
  68. */
  69. public class CodeGenerator {
  70. /** When generating SWITCH statements, some targets might need to limit
  71. * the size (based upon the number of case labels). Generally, this
  72. * limit will be hit only for lexers where wildcard in a UNICODE
  73. * vocabulary environment would generate a SWITCH with 65000 labels.
  74. */
  75. public final static int MSCL_DEFAULT = 300;
  76. public static int MAX_SWITCH_CASE_LABELS = MSCL_DEFAULT;
  77. public final static int MSA_DEFAULT = 3;
  78. public static int MIN_SWITCH_ALTS = MSA_DEFAULT;
  79. public boolean GENERATE_SWITCHES_WHEN_POSSIBLE = true;
  80. public static boolean LAUNCH_ST_INSPECTOR = false;
  81. public final static int MADSI_DEFAULT = 60; // do lots of states inline (needed for expression rules)
  82. public static int MAX_ACYCLIC_DFA_STATES_INLINE = MADSI_DEFAULT;
  83. public static String classpathTemplateRootDirectoryName =
  84. "org/antlr/codegen/templates";
  85. /** Which grammar are we generating code for? Each generator
  86. * is attached to a specific grammar.
  87. */
  88. public Grammar grammar;
  89. /** What language are we generating? */
  90. protected String language;
  91. /** The target specifies how to write out files and do other language
  92. * specific actions.
  93. */
  94. public Target target = null;
  95. /** Where are the templates this generator should use to generate code? */
  96. protected STGroup templates;
  97. /** The basic output templates without AST or templates stuff; this will be
  98. * the templates loaded for the language such as Java.stg *and* the Dbg
  99. * stuff if turned on. This is used for generating syntactic predicates.
  100. */
  101. protected STGroup baseTemplates;
  102. protected ST recognizerST;
  103. protected ST outputFileST;
  104. protected ST headerFileST;
  105. /** Used to create unique labels */
  106. protected int uniqueLabelNumber = 1;
  107. /** A reference to the ANTLR tool so we can learn about output directories
  108. * and such.
  109. */
  110. protected Tool tool;
  111. /** Generate debugging event method calls */
  112. protected boolean debug;
  113. /** Create a Tracer object and make the recognizer invoke this. */
  114. protected boolean trace;
  115. /** Track runtime parsing information about decisions etc...
  116. * This requires the debugging event mechanism to work.
  117. */
  118. protected boolean profile;
  119. protected int lineWidth = 72;
  120. /** I have factored out the generation of acyclic DFAs to separate class */
  121. public ACyclicDFACodeGenerator acyclicDFAGenerator =
  122. new ACyclicDFACodeGenerator(this);
  123. /** I have factored out the generation of cyclic DFAs to separate class */
  124. /*
  125. public CyclicDFACodeGenerator cyclicDFAGenerator =
  126. new CyclicDFACodeGenerator(this);
  127. */
  128. public static final String VOCAB_FILE_EXTENSION = ".tokens";
  129. protected final static String vocabFilePattern =
  130. "<tokens:{it|<it.name>=<it.type>\n}>" +
  131. "<literals:{it|<it.name>=<it.type>\n}>";
  132. public CodeGenerator(Tool tool, Grammar grammar, String language) {
  133. this.tool = tool;
  134. this.grammar = grammar;
  135. this.language = language;
  136. target = loadLanguageTarget(language);
  137. }
  138. public static Target loadLanguageTarget(String language) {
  139. Target target = null;
  140. String targetName = "org.antlr.codegen."+language+"Target";
  141. try {
  142. Class<? extends Target> c = Class.forName(targetName).asSubclass(Target.class);
  143. target = (Target)c.newInstance();
  144. }
  145. catch (ClassNotFoundException cnfe) {
  146. target = new Target(); // use default
  147. }
  148. catch (InstantiationException ie) {
  149. ErrorManager.error(ErrorManager.MSG_CANNOT_CREATE_TARGET_GENERATOR,
  150. targetName,
  151. ie);
  152. }
  153. catch (IllegalAccessException cnfe) {
  154. ErrorManager.error(ErrorManager.MSG_CANNOT_CREATE_TARGET_GENERATOR,
  155. targetName,
  156. cnfe);
  157. }
  158. return target;
  159. }
  160. /** load the main language.stg template group file */
  161. public void loadTemplates(String language) {
  162. String langDir = classpathTemplateRootDirectoryName+"/"+language;
  163. STGroup coreTemplates = new STGroupFile(langDir+"/"+language+".stg");
  164. baseTemplates = coreTemplates;
  165. if ( coreTemplates ==null ) {
  166. ErrorManager.error(ErrorManager.MSG_MISSING_CODE_GEN_TEMPLATES,
  167. language);
  168. return;
  169. }
  170. // dynamically add subgroups that act like filters to apply to
  171. // their supergroup. E.g., Java:Dbg:AST:ASTParser::ASTDbg.
  172. String outputOption = (String)grammar.getOption("output");
  173. if ( outputOption!=null && outputOption.equals("AST") ) {
  174. if ( debug && grammar.type!=Grammar.LEXER ) {
  175. STGroup dbgTemplates = new STGroupFile(langDir+"/Dbg.stg");
  176. dbgTemplates.importTemplates(coreTemplates);
  177. baseTemplates = dbgTemplates;
  178. STGroup astTemplates = new STGroupFile(langDir+"/AST.stg");
  179. astTemplates.importTemplates(dbgTemplates);
  180. STGroup astParserTemplates;
  181. if ( grammar.type==Grammar.TREE_PARSER ) {
  182. astParserTemplates = new STGroupFile(langDir+"/ASTTreeParser.stg");
  183. astParserTemplates.importTemplates(astTemplates);
  184. }
  185. else {
  186. astParserTemplates = new STGroupFile(langDir+"/ASTParser.stg");
  187. astParserTemplates.importTemplates(astTemplates);
  188. }
  189. STGroup astDbgTemplates = new STGroupFile(langDir+"/ASTDbg.stg");
  190. astDbgTemplates.importTemplates(astParserTemplates);
  191. templates = astDbgTemplates;
  192. dbgTemplates.iterateAcrossValues = true; // ST v3 compatibility with Maps
  193. astDbgTemplates.iterateAcrossValues = true;
  194. astParserTemplates.iterateAcrossValues = true;
  195. }
  196. else {
  197. STGroup astTemplates = new STGroupFile(langDir+"/AST.stg");
  198. astTemplates.importTemplates(coreTemplates);
  199. STGroup astParserTemplates;
  200. if ( grammar.type==Grammar.TREE_PARSER ) {
  201. astParserTemplates = new STGroupFile(langDir+"/ASTTreeParser.stg");
  202. astParserTemplates.importTemplates(astTemplates);
  203. }
  204. else {
  205. astParserTemplates = new STGroupFile(langDir+"/ASTParser.stg");
  206. astParserTemplates.importTemplates(astTemplates);
  207. }
  208. templates = astParserTemplates;
  209. astTemplates.iterateAcrossValues = true; // ST v3 compatibility with Maps
  210. astParserTemplates.iterateAcrossValues = true;
  211. }
  212. }
  213. else if ( outputOption!=null && outputOption.equals("template") ) {
  214. if ( debug && grammar.type!=Grammar.LEXER ) {
  215. STGroup dbgTemplates = new STGroupFile(langDir+"/Dbg.stg");
  216. dbgTemplates.importTemplates(coreTemplates);
  217. baseTemplates = dbgTemplates;
  218. STGroup stTemplates = new STGroupFile(langDir+"/ST.stg");
  219. stTemplates.importTemplates(dbgTemplates);
  220. templates = stTemplates;
  221. dbgTemplates.iterateAcrossValues = true;
  222. }
  223. else {
  224. STGroup stTemplates = new STGroupFile(langDir+"/ST.stg");
  225. stTemplates.importTemplates(coreTemplates);
  226. templates = stTemplates;
  227. }
  228. templates.iterateAcrossValues = true; // ST v3 compatibility with Maps
  229. }
  230. else if ( debug && grammar.type!=Grammar.LEXER ) {
  231. STGroup dbgTemplates = new STGroupFile(langDir+"/Dbg.stg");
  232. dbgTemplates.importTemplates(coreTemplates);
  233. templates = dbgTemplates;
  234. baseTemplates = templates;
  235. baseTemplates.iterateAcrossValues = true; // ST v3 compatibility with Maps
  236. }
  237. else {
  238. templates = coreTemplates;
  239. coreTemplates.iterateAcrossValues = true; // ST v3 compatibility with Maps
  240. }
  241. }
  242. /** Given the grammar to which we are attached, walk the AST associated
  243. * with that grammar to create NFAs. Then create the DFAs for all
  244. * decision points in the grammar by converting the NFAs to DFAs.
  245. * Finally, walk the AST again to generate code.
  246. *
  247. * Either 1 or 2 files are written:
  248. *
  249. * recognizer: the main parser/lexer/treewalker item
  250. * header file: language like C/C++ need extern definitions
  251. *
  252. * The target, such as JavaTarget, dictates which files get written.
  253. */
  254. public ST genRecognizer() {
  255. //System.out.println("### generate "+grammar.name+" recognizer");
  256. // LOAD OUTPUT TEMPLATES
  257. loadTemplates(language);
  258. if ( templates==null ) {
  259. return null;
  260. }
  261. // CREATE NFA FROM GRAMMAR, CREATE DFA FROM NFA
  262. if ( ErrorManager.doNotAttemptAnalysis() ) {
  263. return null;
  264. }
  265. target.performGrammarAnalysis(this, grammar);
  266. // some grammar analysis errors will not yield reliable DFA
  267. if ( ErrorManager.doNotAttemptCodeGen() ) {
  268. return null;
  269. }
  270. // OPTIMIZE DFA
  271. DFAOptimizer optimizer = new DFAOptimizer(grammar);
  272. optimizer.optimize();
  273. // OUTPUT FILE (contains recognizerST)
  274. outputFileST = templates.getInstanceOf("outputFile");
  275. // HEADER FILE
  276. if ( templates.isDefined("headerFile") ) {
  277. headerFileST = templates.getInstanceOf("headerFile");
  278. }
  279. else {
  280. // create a dummy to avoid null-checks all over code generator
  281. headerFileST = new ST(templates,"xyz");
  282. headerFileST.add("cyclicDFAs", (Object)null); // it normally sees this from outputFile
  283. //headerFileST.impl.name = "dummy-header-file";
  284. }
  285. boolean filterMode = grammar.getOption("filter")!=null &&
  286. grammar.getOption("filter").equals("true");
  287. boolean canBacktrack = grammar.getSyntacticPredicates()!=null ||
  288. grammar.composite.getRootGrammar().atLeastOneBacktrackOption ||
  289. filterMode;
  290. // TODO: move this down further because generating the recognizer
  291. // alters the model with info on who uses predefined properties etc...
  292. // The actions here might refer to something.
  293. // The only two possible output files are available at this point.
  294. // Verify action scopes are ok for target and dump actions into output
  295. // Templates can say <actions.parser.header> for example.
  296. Map<String, Map<String, Object>> actions = grammar.getActions();
  297. verifyActionScopesOkForTarget(actions);
  298. // translate $x::y references
  299. translateActionAttributeReferences(actions);
  300. ST gateST = templates.getInstanceOf("actionGate");
  301. if ( filterMode ) {
  302. // if filtering, we need to set actions to execute at backtracking
  303. // level 1 not 0.
  304. gateST = templates.getInstanceOf("filteringActionGate");
  305. }
  306. grammar.setSynPredGateIfNotAlready(gateST);
  307. headerFileST.add("actions", actions);
  308. outputFileST.add("actions", actions);
  309. headerFileST.add("buildTemplate", grammar.buildTemplate());
  310. outputFileST.add("buildTemplate", grammar.buildTemplate());
  311. headerFileST.add("buildAST", grammar.buildAST());
  312. outputFileST.add("buildAST", grammar.buildAST());
  313. outputFileST.add("rewriteMode", grammar.rewriteMode());
  314. headerFileST.add("rewriteMode", grammar.rewriteMode());
  315. outputFileST.add("backtracking", canBacktrack);
  316. headerFileST.add("backtracking", canBacktrack);
  317. // turn on memoize attribute at grammar level so we can create ruleMemo.
  318. // each rule has memoize attr that hides this one, indicating whether
  319. // it needs to save results
  320. String memoize = (String)grammar.getOption("memoize");
  321. outputFileST.add("memoize",
  322. (grammar.atLeastOneRuleMemoizes ||
  323. memoize != null && memoize.equals("true") &&
  324. canBacktrack));
  325. headerFileST.add("memoize",
  326. (grammar.atLeastOneRuleMemoizes ||
  327. memoize != null && memoize.equals("true") &&
  328. canBacktrack));
  329. outputFileST.add("trace", trace);
  330. headerFileST.add("trace", trace);
  331. outputFileST.add("profile", profile);
  332. headerFileST.add("profile", profile);
  333. // RECOGNIZER
  334. if ( grammar.type==Grammar.LEXER ) {
  335. recognizerST = templates.getInstanceOf("lexer");
  336. outputFileST.add("LEXER", true);
  337. headerFileST.add("LEXER", true);
  338. recognizerST.add("filterMode",
  339. filterMode);
  340. }
  341. else if ( grammar.type==Grammar.PARSER ||
  342. grammar.type==Grammar.COMBINED )
  343. {
  344. recognizerST = templates.getInstanceOf("parser");
  345. outputFileST.add("PARSER", true);
  346. headerFileST.add("PARSER", true);
  347. }
  348. else {
  349. recognizerST = templates.getInstanceOf("treeParser");
  350. outputFileST.add("TREE_PARSER", true);
  351. headerFileST.add("TREE_PARSER", true);
  352. recognizerST.add("filterMode",
  353. filterMode);
  354. }
  355. outputFileST.add("recognizer", recognizerST);
  356. headerFileST.add("recognizer", recognizerST);
  357. outputFileST.add("actionScope",
  358. grammar.getDefaultActionScope(grammar.type));
  359. headerFileST.add("actionScope",
  360. grammar.getDefaultActionScope(grammar.type));
  361. String targetAppropriateFileNameString =
  362. target.getTargetStringLiteralFromString(grammar.getFileName());
  363. outputFileST.add("fileName", targetAppropriateFileNameString);
  364. headerFileST.add("fileName", targetAppropriateFileNameString);
  365. outputFileST.add("ANTLRVersion", tool.VERSION);
  366. headerFileST.add("ANTLRVersion", tool.VERSION);
  367. outputFileST.add("generatedTimestamp", Tool.getCurrentTimeStamp());
  368. headerFileST.add("generatedTimestamp", Tool.getCurrentTimeStamp());
  369. // GENERATE RECOGNIZER
  370. // Walk the AST holding the input grammar, this time generating code
  371. // Decisions are generated by using the precomputed DFAs
  372. // Fill in the various templates with data
  373. CodeGenTreeWalker gen = new CodeGenTreeWalker(new CommonTreeNodeStream(grammar.getGrammarTree()));
  374. try {
  375. gen.grammar_(
  376. grammar,
  377. recognizerST,
  378. outputFileST,
  379. headerFileST);
  380. }
  381. catch (RecognitionException re) {
  382. ErrorManager.error(ErrorManager.MSG_BAD_AST_STRUCTURE,
  383. re);
  384. }
  385. genTokenTypeConstants(recognizerST);
  386. genTokenTypeConstants(outputFileST);
  387. genTokenTypeConstants(headerFileST);
  388. if ( grammar.type!=Grammar.LEXER ) {
  389. genTokenTypeNames(recognizerST);
  390. genTokenTypeNames(outputFileST);
  391. genTokenTypeNames(headerFileST);
  392. }
  393. // Now that we know what synpreds are used, we can set into template
  394. Set<String> synpredNames = null;
  395. if ( grammar.synPredNamesUsedInDFA.size()>0 ) {
  396. synpredNames = grammar.synPredNamesUsedInDFA;
  397. }
  398. outputFileST.add("synpreds", synpredNames);
  399. headerFileST.add("synpreds", synpredNames);
  400. // all recognizers can see Grammar object
  401. recognizerST.add("grammar", grammar);
  402. if (LAUNCH_ST_INSPECTOR) {
  403. outputFileST.inspect();
  404. if ( templates.isDefined("headerFile") ) headerFileST.inspect();
  405. }
  406. // WRITE FILES
  407. try {
  408. target.genRecognizerFile(tool,this,grammar,outputFileST);
  409. if ( templates.isDefined("headerFile") ) {
  410. ST extST = templates.getInstanceOf("headerFileExtension");
  411. target.genRecognizerHeaderFile(tool,this,grammar,headerFileST,extST.render());
  412. }
  413. // write out the vocab interchange file; used by antlr,
  414. // does not change per target
  415. ST tokenVocabSerialization = genTokenVocabOutput();
  416. String vocabFileName = getVocabFileName();
  417. if ( vocabFileName!=null ) {
  418. write(tokenVocabSerialization, vocabFileName);
  419. }
  420. //System.out.println(outputFileST.getDOTForDependencyGraph(false));
  421. }
  422. catch (IOException ioe) {
  423. ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, ioe);
  424. }
  425. /*
  426. System.out.println("num obj.prop refs: "+ ASTExpr.totalObjPropRefs);
  427. System.out.println("num reflection lookups: "+ ASTExpr.totalReflectionLookups);
  428. */
  429. return outputFileST;
  430. }
  431. /** Some targets will have some extra scopes like C++ may have
  432. * '@headerfile:name {action}' or something. Make sure the
  433. * target likes the scopes in action table.
  434. */
  435. protected void verifyActionScopesOkForTarget(Map<String, Map<String, Object>> actions) {
  436. for (Map.Entry<String, Map<String, Object>> entry : actions.entrySet()) {
  437. String scope = entry.getKey();
  438. if ( !target.isValidActionScope(grammar.type, scope) ) {
  439. // get any action from the scope to get error location
  440. Map<String, Object> scopeActions = entry.getValue();
  441. GrammarAST actionAST =
  442. (GrammarAST)scopeActions.values().iterator().next();
  443. ErrorManager.grammarError(
  444. ErrorManager.MSG_INVALID_ACTION_SCOPE,grammar,
  445. actionAST.getToken(),scope,
  446. grammar.getGrammarTypeString());
  447. }
  448. }
  449. }
  450. /** Actions may reference $x::y attributes, call translateAction on
  451. * each action and replace that action in the Map.
  452. */
  453. protected void translateActionAttributeReferences(Map<String, Map<String, Object>> actions) {
  454. for (Map.Entry<String, Map<String, Object>> entry : actions.entrySet()) {
  455. Map<String, Object> scopeActions = entry.getValue();
  456. translateActionAttributeReferencesForSingleScope(null,scopeActions);
  457. }
  458. }
  459. /** Use for translating rule @init{...} actions that have no scope */
  460. public void translateActionAttributeReferencesForSingleScope(
  461. Rule r,
  462. Map<String, Object> scopeActions)
  463. {
  464. String ruleName=null;
  465. if ( r!=null ) {
  466. ruleName = r.name;
  467. }
  468. for (Map.Entry<String, Object> entry : scopeActions.entrySet()) {
  469. String name = entry.getKey();
  470. GrammarAST actionAST = (GrammarAST)entry.getValue();
  471. List<?> chunks = translateAction(ruleName,actionAST);
  472. scopeActions.put(name, chunks); // replace with translation
  473. }
  474. }
  475. /** Error recovery in ANTLR recognizers.
  476. *
  477. * Based upon original ideas:
  478. *
  479. * Algorithms + Data Structures = Programs by Niklaus Wirth
  480. *
  481. * and
  482. *
  483. * A note on error recovery in recursive descent parsers:
  484. * http://portal.acm.org/citation.cfm?id=947902.947905
  485. *
  486. * Later, Josef Grosch had some good ideas:
  487. * Efficient and Comfortable Error Recovery in Recursive Descent Parsers:
  488. * ftp://www.cocolab.com/products/cocktail/doca4.ps/ell.ps.zip
  489. *
  490. * Like Grosch I implemented local FOLLOW sets that are combined at run-time
  491. * upon error to avoid parsing overhead.
  492. */
  493. public void generateLocalFOLLOW(GrammarAST referencedElementNode,
  494. String referencedElementName,
  495. String enclosingRuleName,
  496. int elementIndex)
  497. {
  498. /*
  499. System.out.println("compute FOLLOW "+grammar.name+"."+referencedElementNode.toString()+
  500. " for "+referencedElementName+"#"+elementIndex +" in "+
  501. enclosingRuleName+
  502. " line="+referencedElementNode.getLine());
  503. */
  504. NFAState followingNFAState = referencedElementNode.followingNFAState;
  505. LookaheadSet follow = null;
  506. if ( followingNFAState!=null ) {
  507. // compute follow for this element and, as side-effect, track
  508. // the rule LOOK sensitivity.
  509. follow = grammar.FIRST(followingNFAState);
  510. }
  511. if ( follow==null ) {
  512. ErrorManager.internalError("no follow state or cannot compute follow");
  513. follow = new LookaheadSet();
  514. }
  515. if ( follow.member(Label.EOF) ) {
  516. // TODO: can we just remove? Seems needed here:
  517. // compilation_unit : global_statement* EOF
  518. // Actually i guess we resync to EOF regardless
  519. follow.remove(Label.EOF);
  520. }
  521. //System.out.println(" "+follow);
  522. List<Integer> tokenTypeList;
  523. long[] words;
  524. if ( follow.tokenTypeSet==null ) {
  525. words = new long[1];
  526. tokenTypeList = new ArrayList<Integer>();
  527. }
  528. else {
  529. BitSet bits = BitSet.of(follow.tokenTypeSet);
  530. words = bits.toPackedArray();
  531. tokenTypeList = follow.tokenTypeSet.toList();
  532. }
  533. // use the target to convert to hex strings (typically)
  534. String[] wordStrings = new String[words.length];
  535. for (int j = 0; j < words.length; j++) {
  536. long w = words[j];
  537. wordStrings[j] = target.getTarget64BitStringFromValue(w);
  538. }
  539. recognizerST.addAggr("bitsets.{name,inName,bits,tokenTypes,tokenIndex}",
  540. referencedElementName,
  541. enclosingRuleName,
  542. wordStrings,
  543. tokenTypeList,
  544. Utils.integer(elementIndex));
  545. outputFileST.addAggr("bitsets.{name,inName,bits,tokenTypes,tokenIndex}",
  546. referencedElementName,
  547. enclosingRuleName,
  548. wordStrings,
  549. tokenTypeList,
  550. Utils.integer(elementIndex));
  551. headerFileST.addAggr("bitsets.{name,inName,bits,tokenTypes,tokenIndex}",
  552. referencedElementName,
  553. enclosingRuleName,
  554. wordStrings,
  555. tokenTypeList,
  556. Utils.integer(elementIndex));
  557. }
  558. // L O O K A H E A D D E C I S I O N G E N E R A T I O N
  559. /** Generate code that computes the predicted alt given a DFA. The
  560. * recognizerST can be either the main generated recognizerTemplate
  561. * for storage in the main parser file or a separate file. It's up to
  562. * the code that ultimately invokes the codegen.g grammar rule.
  563. *
  564. * Regardless, the output file and header file get a copy of the DFAs.
  565. */
  566. public ST genLookaheadDecision(ST recognizerST,
  567. DFA dfa)
  568. {
  569. ST decisionST;
  570. // If we are doing inline DFA and this one is acyclic and LL(*)
  571. // I have to check for is-non-LL(*) because if non-LL(*) the cyclic
  572. // check is not done by DFA.verify(); that is, verify() avoids
  573. // doesStateReachAcceptState() if non-LL(*)
  574. if ( dfa.canInlineDecision() ) {
  575. decisionST =
  576. acyclicDFAGenerator.genFixedLookaheadDecision(getTemplates(), dfa);
  577. }
  578. else {
  579. // generate any kind of DFA here (cyclic or acyclic)
  580. dfa.createStateTables(this);
  581. outputFileST.add("cyclicDFAs", dfa);
  582. headerFileST.add("cyclicDFAs", dfa);
  583. decisionST = templates.getInstanceOf("dfaDecision");
  584. String description = dfa.getNFADecisionStartState().getDescription();
  585. description = target.getTargetStringLiteralFromString(description);
  586. if ( description!=null ) {
  587. decisionST.add("description", description);
  588. }
  589. decisionST.add("decisionNumber",
  590. Utils.integer(dfa.getDecisionNumber()));
  591. }
  592. return decisionST;
  593. }
  594. /** A special state is huge (too big for state tables) or has a predicated
  595. * edge. Generate a simple if-then-else. Cannot be an accept state as
  596. * they have no emanating edges. Don't worry about switch vs if-then-else
  597. * because if you get here, the state is super complicated and needs an
  598. * if-then-else. This is used by the new DFA scheme created June 2006.
  599. */
  600. public ST generateSpecialState(DFAState s) {
  601. ST stateST;
  602. stateST = templates.getInstanceOf("cyclicDFAState");
  603. stateST.add("needErrorClause", true);
  604. stateST.add("semPredState",
  605. s.isResolvedWithPredicates());
  606. stateST.add("stateNumber", s.stateNumber);
  607. stateST.add("decisionNumber", s.dfa.decisionNumber);
  608. boolean foundGatedPred = false;
  609. ST eotST = null;
  610. for (int i = 0; i < s.getNumberOfTransitions(); i++) {
  611. Transition edge = s.transition(i);
  612. ST edgeST;
  613. if ( edge.label.getAtom()==Label.EOT ) {
  614. // this is the default clause; has to held until last
  615. edgeST = templates.getInstanceOf("eotDFAEdge");
  616. stateST.remove("needErrorClause");
  617. eotST = edgeST;
  618. }
  619. else {
  620. edgeST = templates.getInstanceOf("cyclicDFAEdge");
  621. ST exprST =
  622. genLabelExpr(templates,edge,1);
  623. edgeST.add("labelExpr", exprST);
  624. }
  625. edgeST.add("edgeNumber", Utils.integer(i + 1));
  626. edgeST.add("targetStateNumber",
  627. Utils.integer(edge.target.stateNumber));
  628. // stick in any gated predicates for any edge if not already a pred
  629. if ( !edge.label.isSemanticPredicate() ) {
  630. DFAState t = (DFAState)edge.target;
  631. SemanticContext preds = t.getGatedPredicatesInNFAConfigurations();
  632. if ( preds!=null ) {
  633. foundGatedPred = true;
  634. ST predST = preds.genExpr(this,
  635. getTemplates(),
  636. t.dfa);
  637. edgeST.add("predicates", predST.render());
  638. }
  639. }
  640. if ( edge.label.getAtom()!=Label.EOT ) {
  641. stateST.add("edges", edgeST);
  642. }
  643. }
  644. if ( foundGatedPred ) {
  645. // state has >= 1 edge with a gated pred (syn or sem)
  646. // must rewind input first, set flag.
  647. stateST.add("semPredState", foundGatedPred);
  648. }
  649. if ( eotST!=null ) {
  650. stateST.add("edges", eotST);
  651. }
  652. return stateST;
  653. }
  654. /** Generate an expression for traversing an edge. */
  655. protected ST genLabelExpr(STGroup templates,
  656. Transition edge,
  657. int k)
  658. {
  659. Label label = edge.label;
  660. if ( label.isSemanticPredicate() ) {
  661. return genSemanticPredicateExpr(templates, edge);
  662. }
  663. if ( label.isSet() ) {
  664. return genSetExpr(templates, label.getSet(), k, true);
  665. }
  666. // must be simple label
  667. ST eST = templates.getInstanceOf("lookaheadTest");
  668. eST.add("atom", getTokenTypeAsTargetLabel(label.getAtom()));
  669. eST.add("atomAsInt", Utils.integer(label.getAtom()));
  670. eST.add("k", Utils.integer(k));
  671. return eST;
  672. }
  673. protected ST genSemanticPredicateExpr(STGroup templates,
  674. Transition edge)
  675. {
  676. DFA dfa = ((DFAState)edge.target).dfa; // which DFA are we in
  677. Label label = edge.label;
  678. SemanticContext semCtx = label.getSemanticContext();
  679. return semCtx.genExpr(this,templates,dfa);
  680. }
  681. /** For intervals such as [3..3, 30..35], generate an expression that
  682. * tests the lookahead similar to LA(1)==3 || (LA(1)>=30&&LA(1)<=35)
  683. */
  684. public ST genSetExpr(STGroup templates,
  685. IntSet set,
  686. int k,
  687. boolean partOfDFA)
  688. {
  689. if ( !(set instanceof IntervalSet) ) {
  690. throw new IllegalArgumentException("unable to generate expressions for non IntervalSet objects");
  691. }
  692. IntervalSet iset = (IntervalSet)set;
  693. if ( iset.getIntervals()==null || iset.getIntervals().isEmpty() ) {
  694. ST emptyST = new ST(templates, "");
  695. emptyST.impl.name = "empty-set-expr";
  696. return emptyST;
  697. }
  698. String testSTName = "lookaheadTest";
  699. String testRangeSTName = "lookaheadRangeTest";
  700. if ( !partOfDFA ) {
  701. testSTName = "isolatedLookaheadTest";
  702. testRangeSTName = "isolatedLookaheadRangeTest";
  703. }
  704. ST setST = templates.getInstanceOf("setTest");
  705. Iterator<Interval> iter = iset.getIntervals().iterator();
  706. int rangeNumber = 1;
  707. while (iter.hasNext()) {
  708. Interval I = iter.next();
  709. int a = I.a;
  710. int b = I.b;
  711. ST eST;
  712. if ( a==b ) {
  713. eST = templates.getInstanceOf(testSTName);
  714. eST.add("atom", getTokenTypeAsTargetLabel(a));
  715. eST.add("atomAsInt", Utils.integer(a));
  716. //eST.add("k",Utils.integer(k));
  717. }
  718. else {
  719. eST = templates.getInstanceOf(testRangeSTName);
  720. eST.add("lower", getTokenTypeAsTargetLabel(a));
  721. eST.add("lowerAsInt", Utils.integer(a));
  722. eST.add("upper", getTokenTypeAsTargetLabel(b));
  723. eST.add("upperAsInt", Utils.integer(b));
  724. eST.add("rangeNumber", Utils.integer(rangeNumber));
  725. }
  726. eST.add("k", Utils.integer(k));
  727. setST.add("ranges", eST);
  728. rangeNumber++;
  729. }
  730. return setST;
  731. }
  732. // T O K E N D E F I N I T I O N G E N E R A T I O N
  733. /** Set attributes tokens and literals attributes in the incoming
  734. * code template. This is not the token vocab interchange file, but
  735. * rather a list of token type ID needed by the recognizer.
  736. */
  737. protected void genTokenTypeConstants(ST code) {
  738. // make constants for the token types
  739. for (String tokenID : grammar.getTokenIDs()) {
  740. int tokenType = grammar.getTokenType(tokenID);
  741. if ( tokenType==Label.EOF ||
  742. tokenType>=Label.MIN_TOKEN_TYPE )
  743. {
  744. // don't do FAUX labels 'cept EOF
  745. code.addAggr("tokens.{name,type}", tokenID, Utils.integer(tokenType));
  746. }
  747. }
  748. }
  749. /** Generate a token names table that maps token type to a printable
  750. * name: either the label like INT or the literal like "begin".
  751. */
  752. protected void genTokenTypeNames(ST code) {
  753. for (int t=Label.MIN_TOKEN_TYPE; t<=grammar.getMaxTokenType(); t++) {
  754. String tokenName = grammar.getTokenDisplayName(t);
  755. if ( tokenName!=null ) {
  756. tokenName=target.getTargetStringLiteralFromString(tokenName, true);
  757. code.add("tokenNames", tokenName);
  758. }
  759. }
  760. }
  761. /** Get a meaningful name for a token type useful during code generation.
  762. * Literals without associated names are converted to the string equivalent
  763. * of their integer values. Used to generate x==ID and x==34 type comparisons
  764. * etc... Essentially we are looking for the most obvious way to refer
  765. * to a token type in the generated code. If in the lexer, return the
  766. * char literal translated to the target language. For example, ttype=10
  767. * will yield '\n' from the getTokenDisplayName method. That must
  768. * be converted to the target languages literals. For most C-derived
  769. * languages no translation is needed.
  770. */
  771. public String getTokenTypeAsTargetLabel(int ttype) {
  772. if ( grammar.type==Grammar.LEXER ) {
  773. String name = grammar.getTokenDisplayName(ttype);
  774. return target.getTargetCharLiteralFromANTLRCharLiteral(this,name);
  775. }
  776. return target.getTokenTypeAsTargetLabel(this,ttype);
  777. }
  778. /** Generate a token vocab file with all the token names/types. For example:
  779. * ID=7
  780. * FOR=8
  781. * 'for'=8
  782. *
  783. * This is independent of the target language; used by antlr internally
  784. */
  785. protected ST genTokenVocabOutput() {
  786. ST vocabFileST = new ST(vocabFilePattern);
  787. vocabFileST.add("literals",(Object)null); // "define" literals arg
  788. vocabFileST.add("tokens",(Object)null);
  789. vocabFileST.impl.name = "vocab-file";
  790. // make constants for the token names
  791. for (String tokenID : grammar.getTokenIDs()) {
  792. int tokenType = grammar.getTokenType(tokenID);
  793. if ( tokenType>=Label.MIN_TOKEN_TYPE ) {
  794. vocabFileST.addAggr("tokens.{name,type}", tokenID, Utils.integer(tokenType));
  795. }
  796. }
  797. // now dump the strings
  798. for (String literal : grammar.getStringLiterals()) {
  799. int tokenType = grammar.getTokenType(literal);
  800. if ( tokenType>=Label.MIN_TOKEN_TYPE ) {
  801. vocabFileST.addAggr("tokens.{name,type}", literal, Utils.integer(tokenType));
  802. }
  803. }
  804. return vocabFileST;
  805. }
  806. public List<? extends Object> translateAction(String ruleName,
  807. GrammarAST actionTree)
  808. {
  809. if ( actionTree.getType()==ANTLRParser.ARG_ACTION ) {
  810. return translateArgAction(ruleName, actionTree);
  811. }
  812. ActionTranslator translator = new ActionTranslator(this,ruleName,actionTree);
  813. List<Object> chunks = translator.translateToChunks();
  814. chunks = target.postProcessAction(chunks, actionTree.token);
  815. return chunks;
  816. }
  817. /** Translate an action like [3,"foo",a[3]] and return a List of the
  818. * translated actions. Because actions are themselves translated to a list
  819. * of chunks, must cat together into a ST>. Don't translate
  820. * to strings early as we need to eval templates in context.
  821. */
  822. public List<ST> translateArgAction(String ruleName,
  823. GrammarAST actionTree)
  824. {
  825. String actionText = actionTree.token.getText();
  826. List<String> args = getListOfArgumentsFromAction(actionText,',');
  827. List<ST> translatedArgs = new ArrayList<ST>();
  828. for (String arg : args) {
  829. if ( arg!=null ) {
  830. Token actionToken =
  831. new CommonToken(ANTLRParser.ACTION,arg);
  832. ActionTranslator translator =
  833. new ActionTranslator(this,ruleName,
  834. actionToken,
  835. actionTree.outerAltNum);
  836. List<Object> chunks = translator.translateToChunks();
  837. chunks = target.postProcessAction(chunks, actionToken);
  838. ST catST = new ST(templates, "<chunks>");
  839. catST.add("chunks", chunks);
  840. translatedArgs.add(catST);
  841. }
  842. }
  843. if ( translatedArgs.isEmpty() ) {
  844. return null;
  845. }
  846. return translatedArgs;
  847. }
  848. public static List<String> getListOfArgumentsFromAction(String actionText,
  849. int separatorChar)
  850. {
  851. List<String> args = new ArrayList<String>();
  852. getListOfArgumentsFromAction(actionText, 0, -1, separatorChar, args);
  853. return args;
  854. }
  855. /** Given an arg action like
  856. *
  857. * [x, (*a).foo(21,33), 3.2+1, '\n',
  858. * "a,oo\nick", {bl, "fdkj"eck}, ["cat\n,", x, 43]]
  859. *
  860. * convert to a list of arguments. Allow nested square brackets etc...
  861. * Set separatorChar to ';' or ',' or whatever you want.
  862. */
  863. public static int getListOfArgumentsFromAction(String actionText,
  864. int start,
  865. int targetChar,
  866. int separatorChar,
  867. List<String> args)
  868. {
  869. if ( actionText==null ) {
  870. return -1;
  871. }
  872. actionText = actionText.replaceAll("//.*\n", "");
  873. int n = actionText.length();
  874. //System.out.println("actionText@"+start+"->"+(char)targetChar+"="+actionText.substring(start,n));
  875. int p = start;
  876. int last = p;
  877. while ( p<n && actionText.charAt(p)!=targetChar ) {
  878. int c = actionText.charAt(p);
  879. switch ( c ) {
  880. case '\'' :
  881. p++;
  882. while ( p<n && actionText.charAt(p)!='\'' ) {
  883. if ( actionText.charAt(p)=='\\' && (p+1)<n &&
  884. actionText.charAt(p+1)=='\'' )
  885. {
  886. p++; // skip escaped quote
  887. }
  888. p++;
  889. }
  890. p++;
  891. break;
  892. case '"' :
  893. p++;
  894. while ( p<n && actionText.charAt(p)!='\"' ) {
  895. if ( actionText.charAt(p)=='\\' && (p+1)<n &&
  896. actionText.charAt(p+1)=='\"' )
  897. {
  898. p++; // skip escaped quote
  899. }
  900. p++;
  901. }
  902. p++;
  903. break;
  904. case '(' :
  905. p = getListOfArgumentsFromAction(actionText,p+1,')',separatorChar,args);
  906. break;
  907. case '{' :
  908. p = getListOfArgumentsFromAction(actionText,p+1,'}',separatorChar,args);
  909. break;
  910. case '<' :
  911. if ( actionText.indexOf('>',p+1)>=p ) {
  912. // do we see a matching '>' ahead? if so, hope it's a generic
  913. // and not less followed by expr with greater than
  914. p = getListOfArgumentsFromAction(actionText,p+1,'>',separatorChar,args);
  915. }
  916. else {
  917. p++; // treat as normal char
  918. }
  919. break;
  920. case '[' :
  921. p = getListOfArgumentsFromAction(actionText,p+1,']',separatorChar,args);
  922. break;
  923. default :
  924. if ( c==separatorChar && targetChar==-1 ) {
  925. String arg = actionText.substring(last, p);
  926. //System.out.println("arg="+arg);
  927. args.add(arg.trim());
  928. last = p+1;
  929. }
  930. p++;
  931. break;
  932. }
  933. }
  934. if ( targetChar==-1 && p<=n ) {
  935. String arg = actionText.substring(last, p).trim();
  936. //System.out.println("arg="+arg);
  937. if ( arg.length()>0 ) {
  938. args.add(arg.trim());
  939. }
  940. }
  941. p++;
  942. return p;
  943. }
  944. /** Given a template constructor action like %foo(a={...}) in
  945. * an action, translate it to the appropriate template constructor
  946. * from the templateLib. This translates a *piece* of the action.
  947. */
  948. public ST translateTemplateConstructor(String ruleName,
  949. int outerAltNum,
  950. Token actionToken,
  951. String templateActionText)
  952. {
  953. // first, parse with antlr.g
  954. //System.out.println("translate template: "+templateActionText);
  955. ANTLRLexer lexer = new ANTLRLexer(new ANTLRStringStream(templateActionText));
  956. lexer.setFileName(grammar.getFileName());
  957. ANTLRParser parser = ANTLRParser.createParser(new CommonTokenStream(lexer));
  958. parser.setFileName(grammar.getFileName());
  959. ANTLRParser.rewrite_template_return parseResult = null;
  960. try {
  961. parseResult = parser.rewrite_template();
  962. }
  963. catch (RecognitionException re) {
  964. ErrorManager.grammarError(ErrorManager.MSG_INVALID_TEMPLATE_ACTION,
  965. grammar,
  966. actionToken,
  967. templateActionText);
  968. }
  969. catch (Exception tse) {
  970. ErrorManager.internalError("can't parse template action",tse);
  971. }
  972. GrammarAST rewriteTree = (GrammarAST)parseResult.getTree();
  973. // then translate via codegen.g
  974. CodeGenTreeWalker gen = new CodeGenTreeWalker(new CommonTreeNodeStream(rewriteTree));
  975. gen.init(grammar);
  976. gen.setCurrentRuleName(ruleName);
  977. gen.setOuterAltNum(outerAltNum);
  978. ST st = null;
  979. try {
  980. st = gen.rewrite_template();
  981. }
  982. catch (RecognitionException re) {
  983. ErrorManager.error(ErrorManager.MSG_BAD_AST_STRUCTURE,
  984. re);
  985. }
  986. return st;
  987. }
  988. public void issueInvalidScopeError(String x,
  989. String y,
  990. Rule enclosingRule,
  991. Token actionToken,
  992. int outerAltNum)
  993. {
  994. //System.out.println("error $"+x+"::"+y);
  995. Rule r = grammar.getRule(x);
  996. AttributeScope scope = grammar.getGlobalScope(x);
  997. if ( scope==null ) {
  998. if ( r!=null ) {
  999. scope = r.ruleScope; // if not global, might be rule scope
  1000. }
  1001. }
  1002. if ( scope==null ) {
  1003. ErrorManager.grammarError(ErrorManager.MSG_UNKNOWN_DYNAMIC_SCOPE,
  1004. grammar,
  1005. actionToken,
  1006. x);
  1007. }
  1008. else if ( scope.getAttribute(y)==null ) {
  1009. ErrorManager.grammarError(ErrorManager.MSG_UNKNOWN_DYNAMIC_SCOPE_ATTRIBUTE,
  1010. grammar,
  1011. actionToken,
  1012. x,
  1013. y);
  1014. }
  1015. }
  1016. public void issueInvalidAttributeError(String x,
  1017. String y,
  1018. Rule enclosingRule,
  1019. Token actionToken,
  1020. int outerAltNum)
  1021. {
  1022. //System.out.println("error $"+x+"."+y);
  1023. if ( enclosingRule==null ) {
  1024. // action not in a rule
  1025. ErrorManager.grammarError(ErrorManager.MSG_ATTRIBUTE_REF_NOT_IN_RULE,
  1026. grammar,
  1027. actionToken,
  1028. x,
  1029. y);
  1030. return;
  1031. }
  1032. // action is in a rule
  1033. Grammar.LabelElementPair label = enclosingRule.getRuleLabel(x);
  1034. if ( label!=null || enclosingRule.getRuleRefsInAlt(x, outerAltNum)!=null ) {
  1035. // $rulelabel.attr or $ruleref.attr; must be unknown attr
  1036. String refdRuleName = x;
  1037. if ( label!=null ) {
  1038. refdRuleName = enclosingRule.getRuleLabel(x).referencedRuleName;
  1039. }
  1040. Rule refdRule = grammar.getRule(refdRuleName);
  1041. AttributeScope scope = refdRule.getAttributeScope(y);
  1042. if ( scope==null ) {
  1043. ErrorManager.grammarError(ErrorManager.MSG_UNKNOWN_RULE_ATTRIBUTE,
  1044. grammar,
  1045. actionToken,
  1046. refdRuleName,
  1047. y);
  1048. }
  1049. else if ( scope.isParameterScope ) {
  1050. ErrorManager.grammarError(ErrorManager.MSG_INVALID_RULE_PARAMETER_REF,
  1051. grammar,
  1052. actionToken,
  1053. refdRuleName,
  1054. y);
  1055. }
  1056. else if ( scope.isDynamicRuleScope ) {
  1057. ErrorManager.grammarError(ErrorManager.MSG_INVALID_RULE_SCOPE_ATTRIBUTE_REF,
  1058. grammar,
  1059. actionToken,
  1060. refdRuleName,
  1061. y);
  1062. }
  1063. }
  1064. }
  1065. public void issueInvalidAttributeError(String x,
  1066. Rule enclosingRule,
  1067. Token actionToken,
  1068. int outerAltNum)
  1069. {
  1070. //System.out.println("error $"+x);
  1071. if ( enclosingRule==null ) {
  1072. // action not in a rule
  1073. ErrorManager.grammarError(ErrorManager.MSG_ATTRIBUTE_REF_NOT_IN_RULE,
  1074. grammar,
  1075. actionToken,
  1076. x);
  1077. return;
  1078. }
  1079. // action is in a rule
  1080. Grammar.LabelElementPair label = enclosingRule.getRuleLabel(x);
  1081. AttributeScope scope = enclosingRule.getAttributeScope(x);
  1082. if ( label!=null ||
  1083. enclosingRule.getRuleRefsInAlt(x, outerAltNum)!=null ||
  1084. enclosingRule.name.equals(x) )
  1085. {
  1086. ErrorManager.grammarError(ErrorManager.MSG_ISOLATED_RULE_SCOPE,
  1087. grammar,
  1088. actionToken,
  1089. x);
  1090. }
  1091. else if ( scope!=null && scope.isDynamicRuleScope ) {
  1092. ErrorManager.grammarError(ErrorManager.MSG_ISOLATED_RULE_ATTRIBUTE,
  1093. grammar,
  1094. actionToken,
  1095. x);
  1096. }
  1097. else {
  1098. ErrorManager.grammarError(ErrorManager.MSG_UNKNOWN_SIMPLE_ATTRIBUTE,
  1099. grammar,
  1100. actionToken,
  1101. x);
  1102. }
  1103. }
  1104. // M I S C
  1105. public STGroup getTemplates() {
  1106. return templates;
  1107. }
  1108. public STGroup getBaseTemplates() {
  1109. return baseTemplates;
  1110. }
  1111. public void setDebug(boolean debug) {
  1112. this.debug = debug;
  1113. }
  1114. public void setTrace(boolean trace) {
  1115. this.trace = trace;
  1116. }
  1117. public void setProfile(boolean profile) {
  1118. this.profile = profile;
  1119. if ( profile ) {
  1120. setDebug(true); // requires debug events
  1121. }
  1122. }
  1123. public ST getRecognizerST() {
  1124. return outputFileST;
  1125. }
  1126. /** Generate TParser.java and TLexer.java from T.g if combined, else
  1127. * just use T.java as output regardless of type.
  1128. */
  1129. public String getRecognizerFileName(String name, int type) {
  1130. ST extST = templates.getInstanceOf("codeFileExtension");
  1131. String recognizerName = grammar.getRecognizerName();
  1132. return recognizerName+extST.render();
  1133. /*
  1134. String suffix = "";
  1135. if ( type==Grammar.COMBINED ||
  1136. (type==Grammar.LEXER && !grammar.implicitLexer) )
  1137. {
  1138. suffix = Grammar.grammarTypeToFileNameSuffix[type];
  1139. }
  1140. return name+suffix+extST.toString();
  1141. */
  1142. }
  1143. /** What is the name of the vocab file generated for this grammar?
  1144. * Returns null if no .tokens file should be generated.
  1145. */
  1146. public String getVocabFileName() {
  1147. if ( grammar.isBuiltFromString() ) {
  1148. return null;
  1149. }
  1150. return grammar.name+VOCAB_FILE_EXTENSION;
  1151. }
  1152. public void write(ST code, String fileName) throws IOException {
  1153. //long start = System.currentTimeMillis();
  1154. Writer w = tool.getOutputFile(grammar, fileName);
  1155. // Write the output to a StringWriter
  1156. STWriter wr = new AutoIndentWriter(w);
  1157. wr.setLineWidth(lineWidth);
  1158. code.write(wr);
  1159. w.close();
  1160. //long stop = System.currentTimeMillis();
  1161. //System.out.println("render time for "+fileName+": "+(int)(stop-start)+"ms");
  1162. }
  1163. /** You can generate a switch rather than if-then-else for a DFA state
  1164. * if there are no semantic predicates and the number of edge label
  1165. * values is small enough; e.g., don't generate a switch for a state
  1166. * containing an edge label such as 20..52330 (the resulting byte codes
  1167. * would overflow the method 65k limit probably).
  1168. */
  1169. protected boolean canGenerateSwitch(DFAState s) {
  1170. if ( !GENERATE_SWITCHES_WHEN_POSSIBLE ) {
  1171. return false;
  1172. }
  1173. int size = 0;
  1174. for (int i = 0; i < s.getNumberOfTransitions(); i++) {
  1175. Transition edge = s.transition(i);
  1176. if ( edge.label.isSemanticPredicate() ) {
  1177. return false;
  1178. }
  1179. // can't do a switch if the edges are going to require predicates
  1180. if ( edge.label.getAtom()==Label.EOT ) {
  1181. int EOTPredicts = ((DFAState)edge.target).getUniquelyPredictedAlt();
  1182. if ( EOTPredicts==NFA.INVALID_ALT_NUMBER ) {
  1183. // EOT target has to be a predicate then; no unique alt
  1184. return false;
  1185. }
  1186. }
  1187. // if target is a state with gated preds, we need to use preds on
  1188. // this edge then to reach it.
  1189. if ( ((DFAState)edge.target).getGatedPredicatesInNFAConfigurations()!=null ) {
  1190. return false;
  1191. }
  1192. size += edge.label.getSet().size();
  1193. }
  1194. if ( s.getNumberOfTransitions()<MIN_SWITCH_ALTS ||
  1195. size>MAX_SWITCH_CASE_LABELS ) {
  1196. return false;
  1197. }
  1198. return true;
  1199. }
  1200. /** Create a label to track a token / rule reference's result.
  1201. * Technically, this is a place where I break model-view separation
  1202. * as I am creating a variable name that could be invalid in a
  1203. * target language, however, label ::= <ID><INT> is probably ok in
  1204. * all languages we care about.
  1205. */
  1206. public String createUniqueLabel(String name) {
  1207. return new StringBuffer()
  1208. .append(name).append(uniqueLabelNumber++).toString();
  1209. }
  1210. }