• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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