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