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 */
28package org.antlr.codegen;
29
30
31import org.antlr.Tool;
32import org.antlr.analysis.DFA;
33import org.antlr.analysis.*;
34import org.antlr.grammar.v3.ANTLRLexer;
35import org.antlr.grammar.v3.ANTLRParser;
36import org.antlr.grammar.v3.ActionTranslator;
37import org.antlr.grammar.v3.CodeGenTreeWalker;
38import org.antlr.misc.BitSet;
39import org.antlr.misc.*;
40import org.antlr.runtime.*;
41import org.antlr.runtime.tree.CommonTreeNodeStream;
42import org.antlr.tool.*;
43import org.stringtemplate.v4.*;
44
45import java.io.IOException;
46import java.io.Writer;
47import 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 */
73public 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
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
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 */
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	 */
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	 */
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	 */
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 */
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	 */
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	 */
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	 */
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. */
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
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	 */
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	 */
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	 */
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	 */
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	 */
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
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	 */
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
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	 */
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	 */
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
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
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
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
1205	public STGroup getTemplates() {
1206		return templates;
1207	}
1208
1209	public STGroup getBaseTemplates() {
1210		return baseTemplates;
1211	}
1212
1213	public void setDebug(boolean debug) {
1214		this.debug = debug;
1215	}
1216
1217	public void setTrace(boolean trace) {
1218		this.trace = trace;
1219	}
1220
1221	public void setProfile(boolean profile) {
1222		this.profile = profile;
1223		if ( profile ) {
1224			setDebug(true); // requires debug events
1225		}
1226	}
1227
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	 */
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	 */
1253	public String getVocabFileName() {
1254		if ( grammar.isBuiltFromString() ) {
1255			return null;
1256		}
1257		return grammar.name+VOCAB_FILE_EXTENSION;
1258	}
1259
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	 */
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	 */
1316	public String createUniqueLabel(String name) {
1317		return new StringBuffer()
1318			.append(name).append(uniqueLabelNumber++).toString();
1319	}
1320}
1321