package antlr;

/* ANTLR Translator Generator
 * Project led by Terence Parr at http://www.cs.usfca.edu
 * Software rights: http://www.antlr.org/license.html
 *
 * $Id:$
 */

//
// ANTLR C# Code Generator by Micheal Jordan
//                            Kunle Odutola       : kunle UNDERSCORE odutola AT hotmail DOT com
//                            Anthony Oguntimehin
//
// With many thanks to Eric V. Smith from the ANTLR list.
//

// HISTORY:
//
// 17-May-2002 kunle    Fixed bug in OctalToUnicode() - was processing non-Octal escape sequences
//                      Also added namespace support based on Cpp version.
// 07-Jun-2002 kunle    Added Scott Ellis's _saveIndex creation optimizations
// 09-Sep-2002 richardN Richard Ney's bug-fix for literals table construction.
//                      [ Hashtable ctor needed instance of hash code provider not it's class name. ]
// 17-Sep-2002 kunle &  Added all Token ID definitions as data member of every Lexer/Parser/TreeParser
//             AOg      [ A by-product of problem-solving phase of the hetero-AST changes below
//                        but, it breaks nothing and restores "normal" ANTLR codegen behaviour. ]
// 19-Oct-2002 kunle &  Completed the work required to support heterogenous ASTs (many changes)
//             AOg   &
//             michealj
// 14-Nov-2002 michealj Added "initializeASTFactory()" to support flexible ASTFactory initialization.
//						[ Thanks to Ric Klaren - for suggesting it and implementing it for Cpp. ]
// 18-Nov-2002 kunle    Added fix to make xx_tokenSet_xx names CLS compliant.
// 01-Dec-2002 richardN Patch to reduce "unreachable code" warnings
// 01-Dec-2002 richardN Fix to generate correct TreeParser token-type classnames.
// 12-Jan-2003 kunle  & Generated Lexers, Parsers and TreeParsers now support ANTLR's tracing option.
//             michealj
// 12-Jan-2003 kunle    Fixed issue where initializeASTFactory() was generated when "buildAST=false"
// 14-Jan-2003 AOg      initializeASTFactory(AST factory) method was modifying the Parser's "astFactory"
//                      member rather than it's own "factory" parameter. Fixed.
// 18-Jan-2003 kunle  & Fixed reported issues with ASTFactory create() calls for hetero ASTs
//             michealj - code generated for LEXER token with hetero-AST option specified does not compile
//                      - code generated for imaginary tokens with hetero-AST option specified uses
//                        default AST type
//                      - code generated for per-TokenRef hetero-AST option specified does not compile
// 18-Jan-2003 kunle    initializeASTFactory(AST) method is now a static public member
// 18-May-2003 kunle    Changes to address outstanding reported issues::
//                      - Fixed reported issues with support for case-sensitive literals
//                      - antlr.SemanticException now imported for all Lexers.
//                        [ This exception is thrown on predicate failure. ]
// 12-Jan-2004 kunle    Added fix for reported issue with un-compileable generated lexers
//
//

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import antlr.collections.impl.BitSet;
import antlr.collections.impl.Vector;
import java.io.PrintWriter; //SAS: changed for proper text file io
import java.io.IOException;
import java.io.FileWriter;

/** Generates MyParser.cs, MyLexer.cs and MyParserTokenTypes.cs */
public class CSharpCodeGenerator extends CodeGenerator {
    // non-zero if inside syntactic predicate generation
    protected int syntacticPredLevel = 0;

	// Are we generating ASTs (for parsers and tree parsers) right now?
	protected boolean genAST = false;

    // Are we saving the text consumed (for lexers) right now?
    protected boolean saveText = false;

    // Grammar parameters set up to handle different grammar classes.
    // These are used to get instanceof tests out of code generation
	boolean usingCustomAST = false;
	String labeledElementType;
    String labeledElementASTType;
	String labeledElementInit;
	String commonExtraArgs;
	String commonExtraParams;
	String commonLocalVars;
	String lt1Value;
	String exceptionThrown;
	String throwNoViable;

	// Tracks the rule being generated.  Used for mapTreeId
    RuleBlock currentRule;
	// Tracks the rule or labeled subrule being generated.  Used for AST generation.
    String currentASTResult;

    /** Mapping between the ids used in the current alt, and the
     * names of variables used to represent their AST values.
     */
    Hashtable treeVariableMap = new Hashtable();

    /** Used to keep track of which AST variables have been defined in a rule
     * (except for the #rule_name and #rule_name_in var's
     */
    Hashtable declaredASTVariables = new Hashtable();

    /* Count of unnamed generated variables */
    int astVarNumber = 1;

    /** Special value used to mark duplicate in treeVariableMap */
    protected static final String NONUNIQUE = new String();

    public static final int caseSizeThreshold = 127; // ascii is max

    private Vector semPreds;
	// Used to keep track of which (heterogeneous AST types are used)
	// which need to be set in the ASTFactory of the generated parser
	private java.util.Vector astTypes;

	private static CSharpNameSpace nameSpace = null;

	// _saveIndex creation optimization -- don't create it unless we need to use it
    int saveIndexCreateLevel;
    int blockNestingLevel;


    /** Create a CSharp code-generator using the given Grammar.
     * The caller must still call setTool, setBehavior, and setAnalyzer
     * before generating code.
     */
	public CSharpCodeGenerator() {
		super();
		charFormatter = new CSharpCharFormatter();
	}

	/** Adds a semantic predicate string to the sem pred vector
	    These strings will be used to build an array of sem pred names
	    when building a debugging parser.  This method should only be
	    called when the debug option is specified
	 */
	protected int addSemPred(String predicate) {
		semPreds.appendElement(predicate);
		return semPreds.size()-1;
	}

	public void exitIfError()
	{
		if (antlrTool.hasError())
		{
			antlrTool.fatalError("Exiting due to errors.");
		}
	}

	/**Generate the parser, lexer, treeparser, and token types in CSharp */
	public void gen() {
		// Do the code generation
		try {
			// Loop over all grammars
			Enumeration grammarIter = behavior.grammars.elements();
			while (grammarIter.hasMoreElements()) {
				Grammar g = (Grammar)grammarIter.nextElement();
				// Connect all the components to each other
				g.setGrammarAnalyzer(analyzer);
				g.setCodeGenerator(this);
				analyzer.setGrammar(g);
				// To get right overloading behavior across heterogeneous grammars
				setupGrammarParameters(g);
				g.generate();
				exitIfError();
			}

			// Loop over all token managers (some of which are lexers)
			Enumeration tmIter = behavior.tokenManagers.elements();
			while (tmIter.hasMoreElements()) {
				TokenManager tm = (TokenManager)tmIter.nextElement();
				if (!tm.isReadOnly()) {
					// Write the token manager tokens as CSharp
					// this must appear before genTokenInterchange so that
					// labels are set on string literals
					genTokenTypes(tm);
					// Write the token manager tokens as plain text
					genTokenInterchange(tm);
				}
				exitIfError();
			}
		}
		catch (IOException e) {
			antlrTool.reportException(e, null);
		}
	}

	/** Generate code for the given grammar element.
	 * @param blk The {...} action to generate
	 */
	public void gen(ActionElement action) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genAction("+action+")");
		if ( action.isSemPred ) {
			genSemPred(action.actionText, action.line);
		}
		else {
			if ( grammar.hasSyntacticPredicate ) {
				println("if (0==inputState.guessing)");
				println("{");
				tabs++;
			}

			ActionTransInfo tInfo = new ActionTransInfo();
			String actionStr = processActionForSpecialSymbols(action.actionText,
																			 action.getLine(),
																			 currentRule, tInfo);

			if ( tInfo.refRuleRoot!=null ) {
				// Somebody referenced "#rule", make sure translated var is valid
				// assignment to #rule is left as a ref also, meaning that assignments
				// with no other refs like "#rule = foo();" still forces this code to be
				// generated (unnecessarily).
				println(tInfo.refRuleRoot + " = ("+labeledElementASTType+")currentAST.root;");
			}

			// dump the translated action
			printAction(actionStr);

			if ( tInfo.assignToRoot ) {
				// Somebody did a "#rule=", reset internal currentAST.root
				println("currentAST.root = "+tInfo.refRuleRoot+";");
				// reset the child pointer too to be last sibling in sibling list
				println("if ( (null != "+tInfo.refRuleRoot+") && (null != "+tInfo.refRuleRoot+".getFirstChild()) )");
				tabs++;
				println("currentAST.child = "+tInfo.refRuleRoot+".getFirstChild();");
				tabs--;
				println("else");
				tabs++;
				println("currentAST.child = "+tInfo.refRuleRoot+";");
				tabs--;
				println("currentAST.advanceChildToEnd();");
			}

			if ( grammar.hasSyntacticPredicate ) {
				tabs--;
				println("}");
			}
		}
	}

	/** Generate code for the given grammar element.
	 * @param blk The "x|y|z|..." block to generate
	 */
	public void gen(AlternativeBlock blk) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("gen("+blk+")");
		println("{");
		tabs++;

		genBlockPreamble(blk);
		genBlockInitAction(blk);

		// Tell AST generation to build subrule result
		String saveCurrentASTResult = currentASTResult;
		if (blk.getLabel() != null) {
			currentASTResult = blk.getLabel();
		}

		boolean ok = grammar.theLLkAnalyzer.deterministic(blk);

		CSharpBlockFinishingInfo howToFinish = genCommonBlock(blk, true);
		genBlockFinish(howToFinish, throwNoViable);

		tabs--;
		println("}");

		// Restore previous AST generation
		currentASTResult = saveCurrentASTResult;
	}
	/** Generate code for the given grammar element.
	 * @param blk The block-end element to generate.  Block-end
	 * elements are synthesized by the grammar parser to represent
	 * the end of a block.
	 */
	public void gen(BlockEndElement end) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genRuleEnd("+end+")");
	}
	/** Generate code for the given grammar element.
	 * @param blk The character literal reference to generate
	 */
	public void gen(CharLiteralElement atom) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genChar("+atom+")");

		if ( atom.getLabel()!=null ) {
			println(atom.getLabel() + " = " + lt1Value + ";");
		}

		boolean oldsaveText = saveText;
		saveText = saveText && atom.getAutoGenType()==GrammarElement.AUTO_GEN_NONE;
		genMatch(atom);
		saveText = oldsaveText;
	}
	/** Generate code for the given grammar element.
	 * @param blk The character-range reference to generate
	 */
	public void gen(CharRangeElement r) {
		if ( r.getLabel()!=null  && syntacticPredLevel == 0) {
			println(r.getLabel() + " = " + lt1Value + ";");
		}
      boolean flag = ( grammar instanceof LexerGrammar &&
            (!saveText || (r.getAutoGenType() == GrammarElement.AUTO_GEN_BANG)) );
      if (flag)
          println("_saveIndex = text.Length;");

      println("matchRange("+OctalToUnicode(r.beginText)+","+OctalToUnicode(r.endText)+");");

      if (flag)
          println("text.Length = _saveIndex;");
	}
	/** Generate the lexer CSharp file */
	public  void gen(LexerGrammar g) throws IOException {
		// If debugging, create a new sempred vector for this grammar
		if (g.debuggingOutput)
			semPreds = new Vector();

		setGrammar(g);
		if (!(grammar instanceof LexerGrammar)) {
			antlrTool.panic("Internal error generating lexer");
		}
		genBody(g);
	}
	/** Generate code for the given grammar element.
	 * @param blk The (...)+ block to generate
	 */
	public void gen(OneOrMoreBlock blk) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("gen+("+blk+")");
		String label;
		String cnt;
		println("{ // ( ... )+");
        tabs++;
        blockNestingLevel++;
		genBlockPreamble(blk);
		if ( blk.getLabel() != null ) {
			cnt = "_cnt_"+blk.getLabel();
		}
		else {
			cnt = "_cnt" + blk.ID;
		}
		println("int "+cnt+"=0;");
		if ( blk.getLabel() != null ) {
			label = blk.getLabel();
		}
		else {
			label = "_loop" + blk.ID;
		}

		println("for (;;)");
		println("{");
		tabs++;
        blockNestingLevel++;
      // generate the init action for ()+ ()* inside the loop
      // this allows us to do usefull EOF checking...
      genBlockInitAction(blk);

		// Tell AST generation to build subrule result
		String saveCurrentASTResult = currentASTResult;
		if (blk.getLabel() != null) {
			currentASTResult = blk.getLabel();
		}

		boolean ok = grammar.theLLkAnalyzer.deterministic(blk);

		// generate exit test if greedy set to false
		// and an alt is ambiguous with exit branch
		// or when lookahead derived purely from end-of-file
		// Lookahead analysis stops when end-of-file is hit,
		// returning set {epsilon}.  Since {epsilon} is not
		// ambig with any real tokens, no error is reported
		// by deterministic() routines and we have to check
		// for the case where the lookahead depth didn't get
		// set to NONDETERMINISTIC (this only happens when the
		// FOLLOW contains real atoms + epsilon).
		boolean generateNonGreedyExitPath = false;
		int nonGreedyExitDepth = grammar.maxk;

		if ( !blk.greedy &&
			blk.exitLookaheadDepth<=grammar.maxk &&
			blk.exitCache[blk.exitLookaheadDepth].containsEpsilon() )
		{
			generateNonGreedyExitPath = true;
			nonGreedyExitDepth = blk.exitLookaheadDepth;
		}
		else if ( !blk.greedy &&
			blk.exitLookaheadDepth==LLkGrammarAnalyzer.NONDETERMINISTIC )
		{
			generateNonGreedyExitPath = true;
		}

		// generate exit test if greedy set to false
		// and an alt is ambiguous with exit branch
		if ( generateNonGreedyExitPath ) {
			if ( DEBUG_CODE_GENERATOR ) {
				System.out.println("nongreedy (...)+ loop; exit depth is "+
					blk.exitLookaheadDepth);
			}
			String predictExit =
				getLookaheadTestExpression(blk.exitCache,
				nonGreedyExitDepth);
			println("// nongreedy exit test");
			println("if (("+cnt+" >= 1) && "+predictExit+") goto "+label+"_breakloop;");
		}

		CSharpBlockFinishingInfo howToFinish = genCommonBlock(blk, false);
		genBlockFinish(
			howToFinish,
			"if ("+cnt+" >= 1) { goto "+label+"_breakloop; } else { " + throwNoViable + "; }"
			);

		println(cnt+"++;");
		tabs--;
        if (blockNestingLevel-- == saveIndexCreateLevel)
            saveIndexCreateLevel = 0;
		println("}");
		_print(label + "_breakloop:");
		println(";");
        tabs--;
        if (blockNestingLevel-- == saveIndexCreateLevel)
            saveIndexCreateLevel = 0;
		println("}    // ( ... )+");

		// Restore previous AST generation
		currentASTResult = saveCurrentASTResult;
	}
	/** Generate the parser CSharp file */
	public void gen(ParserGrammar g) throws IOException {

		// if debugging, set up a new vector to keep track of sempred
		//   strings for this grammar
		if (g.debuggingOutput)
			semPreds = new Vector();

		setGrammar(g);
		if (!(grammar instanceof ParserGrammar)) {
			antlrTool.panic("Internal error generating parser");
		}
		genBody(g);
	}
	/** Generate code for the given grammar element.
	 * @param blk The rule-reference to generate
	 */
	public void gen(RuleRefElement rr)
	{
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genRR("+rr+")");
		RuleSymbol rs = (RuleSymbol)grammar.getSymbol(rr.targetRule);
		if (rs == null || !rs.isDefined())
		{
			// Is this redundant???
			antlrTool.error("Rule '" + rr.targetRule + "' is not defined", grammar.getFilename(), rr.getLine(), rr.getColumn());
			return;
		}
		if (!(rs instanceof RuleSymbol))
		{
			// Is this redundant???
			antlrTool.error("'" + rr.targetRule + "' does not name a grammar rule", grammar.getFilename(), rr.getLine(), rr.getColumn());
			return;
		}

		genErrorTryForElement(rr);

		// AST value for labeled rule refs in tree walker.
		// This is not AST construction;  it is just the input tree node value.
		if ( grammar instanceof TreeWalkerGrammar &&
			rr.getLabel() != null &&
			syntacticPredLevel == 0 )
		{
			println(rr.getLabel() + " = _t==ASTNULL ? null : "+lt1Value+";");
		}

		// if in lexer and ! on rule ref or alt or rule, save buffer index to kill later
        if (grammar instanceof LexerGrammar && (!saveText || rr.getAutoGenType() == GrammarElement.AUTO_GEN_BANG))
		{
			declareSaveIndexVariableIfNeeded();
			println("_saveIndex = text.Length;");
		}

		// Process return value assignment if any
		printTabs();
		if (rr.idAssign != null)
		{
			// Warn if the rule has no return type
			if (rs.block.returnAction == null)
			{
				antlrTool.warning("Rule '" + rr.targetRule + "' has no return type", grammar.getFilename(), rr.getLine(), rr.getColumn());
			}
			_print(rr.idAssign + "=");
		} else {
			// Warn about return value if any, but not inside syntactic predicate
			if ( !(grammar instanceof LexerGrammar) && syntacticPredLevel == 0 && rs.block.returnAction != null)
			{
				antlrTool.warning("Rule '" + rr.targetRule + "' returns a value", grammar.getFilename(), rr.getLine(), rr.getColumn());
			}
		}

		// Call the rule
		GenRuleInvocation(rr);

		// if in lexer and ! on element or alt or rule, save buffer index to kill later
		if ( grammar instanceof LexerGrammar && (!saveText||rr.getAutoGenType()==GrammarElement.AUTO_GEN_BANG) ) {
			declareSaveIndexVariableIfNeeded();
			println("text.Length = _saveIndex;");
		}

		// if not in a syntactic predicate
		if (syntacticPredLevel == 0)
		{
			boolean doNoGuessTest = (
				grammar.hasSyntacticPredicate &&
				(
				grammar.buildAST && rr.getLabel() != null ||
				(genAST && rr.getAutoGenType() == GrammarElement.AUTO_GEN_NONE)
				)
				);
			if (doNoGuessTest) 	{
				println("if (0 == inputState.guessing)");
				println("{");
				tabs++;
			}

			if (grammar.buildAST && rr.getLabel() != null)
			{
				// always gen variable for rule return on labeled rules
				println(rr.getLabel() + "_AST = ("+labeledElementASTType+")returnAST;");
			}
			if (genAST)
			{
				switch (rr.getAutoGenType())
				{
				case GrammarElement.AUTO_GEN_NONE:
					if( usingCustomAST )
						println("astFactory.addASTChild(ref currentAST, (AST)returnAST);");
					else
						println("astFactory.addASTChild(ref currentAST, returnAST);");
					break;
				case GrammarElement.AUTO_GEN_CARET:
					antlrTool.error("Internal: encountered ^ after rule reference");
					break;
				default:
					break;
				}
			}

			// if a lexer and labeled, Token label defined at rule level, just set it here
			if ( grammar instanceof LexerGrammar && rr.getLabel() != null )
			{
				println(rr.getLabel()+" = returnToken_;");
			}

			if (doNoGuessTest)
			{
				tabs--;
				println("}");
			}
		}
		genErrorCatchForElement(rr);
	}
	/** Generate code for the given grammar element.
	 * @param blk The string-literal reference to generate
	 */
	public void gen(StringLiteralElement atom) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genString("+atom+")");

		// Variable declarations for labeled elements
		if (atom.getLabel()!=null && syntacticPredLevel == 0) {
			println(atom.getLabel() + " = " + lt1Value + ";");
		}

		// AST
		genElementAST(atom);

		// is there a bang on the literal?
		boolean oldsaveText = saveText;
		saveText = saveText && atom.getAutoGenType()==GrammarElement.AUTO_GEN_NONE;

		// matching
		genMatch(atom);

			saveText = oldsaveText;

		// tack on tree cursor motion if doing a tree walker
		if (grammar instanceof TreeWalkerGrammar) {
			println("_t = _t.getNextSibling();");
		}
	}

	/** Generate code for the given grammar element.
	 * @param blk The token-range reference to generate
	 */
	public void gen(TokenRangeElement r) {
		genErrorTryForElement(r);
		if ( r.getLabel()!=null  && syntacticPredLevel == 0) {
			println(r.getLabel() + " = " + lt1Value + ";");
		}

		// AST
		genElementAST(r);

		// match
		println("matchRange("+OctalToUnicode(r.beginText)+","+OctalToUnicode(r.endText)+");");
		genErrorCatchForElement(r);
	}

	/** Generate code for the given grammar element.
	 * @param blk The token-reference to generate
	 */
	public void gen(TokenRefElement atom) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genTokenRef("+atom+")");
		if ( grammar instanceof LexerGrammar ) {
			antlrTool.panic("Token reference found in lexer");
		}
		genErrorTryForElement(atom);
		// Assign Token value to token label variable
		if ( atom.getLabel()!=null && syntacticPredLevel == 0) {
			println(atom.getLabel() + " = " + lt1Value + ";");
		}

		// AST
		genElementAST(atom);
		// matching
		genMatch(atom);
		genErrorCatchForElement(atom);

		// tack on tree cursor motion if doing a tree walker
		if (grammar instanceof TreeWalkerGrammar) {
			println("_t = _t.getNextSibling();");
		}
	}

	public void gen(TreeElement t) {
		// save AST cursor
		println("AST __t" + t.ID + " = _t;");

		// If there is a label on the root, then assign that to the variable
		if (t.root.getLabel() != null) {
			println(t.root.getLabel() + " = (ASTNULL == _t) ? null : ("+labeledElementASTType +")_t;");
		}

      // check for invalid modifiers ! and ^ on tree element roots
      if ( t.root.getAutoGenType() == GrammarElement.AUTO_GEN_BANG ) {
          antlrTool.error("Suffixing a root node with '!' is not implemented",
                       grammar.getFilename(), t.getLine(), t.getColumn());
          t.root.setAutoGenType(GrammarElement.AUTO_GEN_NONE);
      }
      if ( t.root.getAutoGenType() == GrammarElement.AUTO_GEN_CARET ) {
          antlrTool.warning("Suffixing a root node with '^' is redundant; already a root",
                       grammar.getFilename(), t.getLine(), t.getColumn());
          t.root.setAutoGenType(GrammarElement.AUTO_GEN_NONE);
      }

		// Generate AST variables
		genElementAST(t.root);
		if (grammar.buildAST) {
			// Save the AST construction state
			println("ASTPair __currentAST" + t.ID + " = currentAST.copy();");
			// Make the next item added a child of the TreeElement root
			println("currentAST.root = currentAST.child;");
			println("currentAST.child = null;");
		}

		// match root
        if ( t.root instanceof WildcardElement ) {
            println("if (null == _t) throw new MismatchedTokenException();");
        }
        else {
				genMatch(t.root);
		}
		// move to list of children
		println("_t = _t.getFirstChild();");

		// walk list of children, generating code for each
		for (int i=0; i<t.getAlternatives().size(); i++) {
			Alternative a = t.getAlternativeAt(i);
			AlternativeElement e = a.head;
			while ( e != null ) {
				e.generate();
				e = e.next;
			}
		}

		if (grammar.buildAST) {
			// restore the AST construction state to that just after the
			// tree root was added
			println("currentAST = __currentAST" + t.ID + ";");
		}
		// restore AST cursor
		println("_t = __t" + t.ID + ";");
		// move cursor to sibling of tree just parsed
		println("_t = _t.getNextSibling();");
	}
	/** Generate the tree-parser CSharp file */
	public void gen(TreeWalkerGrammar g) throws IOException {
		// SAS: debugging stuff removed for now...
		setGrammar(g);
		if (!(grammar instanceof TreeWalkerGrammar)) {
			antlrTool.panic("Internal error generating tree-walker");
		}
		genBody(g);
	}

	/** Generate code for the given grammar element.
	 * @param wc The wildcard element to generate
	 */
	public void gen(WildcardElement wc) {
		// Variable assignment for labeled elements
		if (wc.getLabel()!=null && syntacticPredLevel == 0) {
			println(wc.getLabel() + " = " + lt1Value + ";");
		}

		// AST
		genElementAST(wc);
		// Match anything but EOF
		if (grammar instanceof TreeWalkerGrammar) {
			println("if (null == _t) throw new MismatchedTokenException();");
		}
		else if (grammar instanceof LexerGrammar) {
			if ( grammar instanceof LexerGrammar &&
				(!saveText||wc.getAutoGenType()==GrammarElement.AUTO_GEN_BANG) ) {
				declareSaveIndexVariableIfNeeded();
				println("_saveIndex = text.Length;");
			}
			println("matchNot(EOF/*_CHAR*/);");
			if ( grammar instanceof LexerGrammar &&
				(!saveText||wc.getAutoGenType()==GrammarElement.AUTO_GEN_BANG) ) {
				declareSaveIndexVariableIfNeeded();
				println("text.Length = _saveIndex;"); // kill text atom put in buffer
			}
		}
		else {
			println("matchNot(" + getValueString(Token.EOF_TYPE) + ");");
		}

		// tack on tree cursor motion if doing a tree walker
		if (grammar instanceof TreeWalkerGrammar) {
			println("_t = _t.getNextSibling();");
		}
	}

	/** Generate code for the given grammar element.
	 * @param blk The (...)* block to generate
	 */
	public void gen(ZeroOrMoreBlock blk) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("gen*("+blk+")");
		println("{    // ( ... )*");
		tabs++;
        blockNestingLevel++;
		genBlockPreamble(blk);
		String label;
		if ( blk.getLabel() != null ) {
			label = blk.getLabel();
		}
		else {
			label = "_loop" + blk.ID;
		}
		println("for (;;)");
		println("{");
		tabs++;
        blockNestingLevel++;
		// generate the init action for ()+ ()* inside the loop
        // this allows us to do usefull EOF checking...
        genBlockInitAction(blk);

		// Tell AST generation to build subrule result
		String saveCurrentASTResult = currentASTResult;
		if (blk.getLabel() != null) {
			currentASTResult = blk.getLabel();
		}

		boolean ok = grammar.theLLkAnalyzer.deterministic(blk);

		// generate exit test if greedy set to false
		// and an alt is ambiguous with exit branch
		// or when lookahead derived purely from end-of-file
		// Lookahead analysis stops when end-of-file is hit,
		// returning set {epsilon}.  Since {epsilon} is not
		// ambig with any real tokens, no error is reported
		// by deterministic() routines and we have to check
		// for the case where the lookahead depth didn't get
		// set to NONDETERMINISTIC (this only happens when the
		// FOLLOW contains real atoms + epsilon).
		boolean generateNonGreedyExitPath = false;
		int nonGreedyExitDepth = grammar.maxk;

		if ( !blk.greedy &&
			blk.exitLookaheadDepth<=grammar.maxk &&
			blk.exitCache[blk.exitLookaheadDepth].containsEpsilon() )
		{
			generateNonGreedyExitPath = true;
			nonGreedyExitDepth = blk.exitLookaheadDepth;
		}
		else if ( !blk.greedy &&
			blk.exitLookaheadDepth==LLkGrammarAnalyzer.NONDETERMINISTIC )
		{
			generateNonGreedyExitPath = true;
		}
		if ( generateNonGreedyExitPath ) {
			if ( DEBUG_CODE_GENERATOR ) {
				System.out.println("nongreedy (...)* loop; exit depth is "+
					blk.exitLookaheadDepth);
			}
			String predictExit =
				getLookaheadTestExpression(blk.exitCache,
				nonGreedyExitDepth);
			println("// nongreedy exit test");
			println("if ("+predictExit+") goto "+label+"_breakloop;");
		}

		CSharpBlockFinishingInfo howToFinish = genCommonBlock(blk, false);
		genBlockFinish(howToFinish, "goto " + label + "_breakloop;");

			tabs--;
        if (blockNestingLevel-- == saveIndexCreateLevel)
            saveIndexCreateLevel = 0;
		println("}");
		_print(label+"_breakloop:");
		println(";");
		tabs--;
        if (blockNestingLevel-- == saveIndexCreateLevel)
            saveIndexCreateLevel = 0;
		println("}    // ( ... )*");

		// Restore previous AST generation
		currentASTResult = saveCurrentASTResult;
	}

	/** Generate an alternative.
	  * @param alt  The alternative to generate
	  * @param blk The block to which the alternative belongs
	  */
	protected void genAlt(Alternative alt, AlternativeBlock blk)
	{
		// Save the AST generation state, and set it to that of the alt
		boolean savegenAST = genAST;
		genAST = genAST && alt.getAutoGen();

		boolean oldsaveTest = saveText;
		saveText = saveText && alt.getAutoGen();

		// Reset the variable name map for the alternative
		Hashtable saveMap = treeVariableMap;
		treeVariableMap = new Hashtable();

		// Generate try block around the alt for  error handling
		if (alt.exceptionSpec != null) {
			println("try        // for error handling");
			println("{");
			tabs++;
		}

		AlternativeElement elem = alt.head;
		while ( !(elem instanceof BlockEndElement) ) {
			elem.generate(); // alt can begin with anything. Ask target to gen.
			elem = elem.next;
		}

		if ( genAST)
		{
			if (blk instanceof RuleBlock)
			{
				// Set the AST return value for the rule
				RuleBlock rblk = (RuleBlock)blk;
				if( usingCustomAST )
				{
					println(rblk.getRuleName() + "_AST = ("+labeledElementASTType+")currentAST.root;");
				}
				else
				{
					println(rblk.getRuleName() + "_AST = currentAST.root;");
				}
			}
			else if (blk.getLabel() != null) {
				// ### future: also set AST value for labeled subrules.
				// println(blk.getLabel() + "_AST = ("+labeledElementASTType+")currentAST.root;");
            	antlrTool.warning("Labeled subrules not yet supported", grammar.getFilename(), blk.getLine(), blk.getColumn());
			}
		}

		if (alt.exceptionSpec != null)
		{
			// close try block
			tabs--;
			println("}");
			genErrorHandler(alt.exceptionSpec);
		}

		genAST = savegenAST;
		saveText = oldsaveTest;

		treeVariableMap = saveMap;
	}

	/** Generate all the bitsets to be used in the parser or lexer
	 * Generate the raw bitset data like "long _tokenSet1_data[] = {...};"
	 * and the BitSet object declarations like "BitSet _tokenSet1 = new BitSet(_tokenSet1_data);"
	 * Note that most languages do not support object initialization inside a
	 * class definition, so other code-generators may have to separate the
	 * bitset declarations from the initializations (e.g., put the initializations
	 * in the generated constructor instead).
	 * @param bitsetList The list of bitsets to generate.
	 * @param maxVocabulary Ensure that each generated bitset can contain at least this value.
	 */
	protected void genBitsets( Vector bitsetList, int maxVocabulary ) {
		println("");
		for (int i = 0; i < bitsetList.size(); i++)
		{
			BitSet p = (BitSet)bitsetList.elementAt(i);
			// Ensure that generated BitSet is large enough for vocabulary
			p.growToInclude(maxVocabulary);
            genBitSet(p, i);
        }
    }

    /** Do something simple like:
     *  private static final long[] mk_tokenSet_0() {
     *    long[] data = { -2305839160922996736L, 63L, 16777216L, 0L, 0L, 0L };
     *    return data;
     *  }
     *  public static final BitSet _tokenSet_0 = new BitSet(mk_tokenSet_0());
     *
     *  Or, for large bitsets, optimize init so ranges are collapsed into loops.
     *  This is most useful for lexers using unicode.
     */
    private void genBitSet(BitSet p, int id) {
        // initialization data
        println("private static long[] mk_" + getBitsetName(id) + "()");
        println("{");
        tabs++;
        int n = p.lengthInLongWords();
        if ( n<BITSET_OPTIMIZE_INIT_THRESHOLD ) {
            println("long[] data = { " + p.toStringOfWords() + "};");
        }
        else {
            // will init manually, allocate space then set values
            println("long[] data = new long["+n+"];");
            long[] elems = p.toPackedArray();
            for (int i = 0; i < elems.length;) {
                if ( (i+1)==elems.length || elems[i]!=elems[i+1] ) {
                    // last number or no run of numbers, just dump assignment
                    println("data["+i+"]="+elems[i]+"L;");
                    i++;
                }
                else
				{
                    // scan to find end of run
                    int j;
                    for (j = i + 1; j < elems.length && elems[j]==elems[i]; j++)
                    {
						;
                    }
                    // j-1 is last member of run
                    println("for (int i = "+i+"; i<="+(j-1)+"; i++) { data[i]="+
                            elems[i]+"L; }");
                    i = j;
                }
            }
        }

        println("return data;");
        tabs--;
        println("}");
		// BitSet object
        println("public static readonly BitSet " + getBitsetName(id) + " = new BitSet(" +
            "mk_" + getBitsetName(id) + "()" + ");");
	}

    /** Given the index of a bitset in the bitset list, generate a unique name.
     * Specific code-generators may want to override this
     * if the language does not allow '_' or numerals in identifiers.
     * @param index  The index of the bitset in the bitset list.
     */
    protected String getBitsetName(int index) {
        return "tokenSet_" + index + "_";
    }

	/** Generate the finish of a block, using a combination of the info
	* returned from genCommonBlock() and the action to perform when
	* no alts were taken
	* @param howToFinish The return of genCommonBlock()
	* @param noViableAction What to generate when no alt is taken
	*/
	private void genBlockFinish(CSharpBlockFinishingInfo howToFinish, String noViableAction)
	{

		if (howToFinish.needAnErrorClause &&
			(howToFinish.generatedAnIf || howToFinish.generatedSwitch))
		{
			if ( howToFinish.generatedAnIf ) {
				println("else");
				println("{");
			}
			else {
				println("{");
			}
			tabs++;
			println(noViableAction);
			tabs--;
			println("}");
		}

		if ( howToFinish.postscript!=null ) {
			if (howToFinish.needAnErrorClause && howToFinish.generatedSwitch &&
				!howToFinish.generatedAnIf && noViableAction != null)
			{
				// Check to make sure that noViableAction is only a throw statement
				if (noViableAction.indexOf("throw") == 0 || noViableAction.indexOf("goto") == 0) {
					// Remove the break statement since it isn't reachable with a throw exception
					int endOfBreak = howToFinish.postscript.indexOf("break;") + 6;
					String newPostScript = howToFinish.postscript.substring(endOfBreak);
					println(newPostScript);
				}
				else {
					println(howToFinish.postscript);
				}
			}
			else {
				println(howToFinish.postscript);
			}
		}
	}

    /** Generate the init action for a block, which may be a RuleBlock or a
     * plain AlternativeBLock.
     * @blk The block for which the preamble is to be generated.
     */
    protected void genBlockInitAction(AlternativeBlock blk)
	{
        // dump out init action
        if (blk.initAction != null) {
            printAction(processActionForSpecialSymbols(blk.initAction, blk.getLine(), currentRule, null));
        }
    }

	/** Generate the header for a block, which may be a RuleBlock or a
     * plain AlternativeBLock.  This generates any variable declarations
     * and syntactic-predicate-testing variables.
	 * @blk The block for which the preamble is to be generated.
	 */
	protected void genBlockPreamble(AlternativeBlock blk) {
		// define labels for rule blocks.
		if ( blk instanceof RuleBlock ) {
			RuleBlock rblk = (RuleBlock)blk;
			if ( rblk.labeledElements!=null ) {
				for (int i=0; i<rblk.labeledElements.size(); i++) {

					AlternativeElement a = (AlternativeElement)rblk.labeledElements.elementAt(i);
					//System.out.println("looking at labeled element: "+a);
					//Variables for labeled rule refs and
					//subrules are different than variables for
					//grammar atoms.  This test is a little tricky
					//because we want to get all rule refs and ebnf,
					//but not rule blocks or syntactic predicates
					if (
						a instanceof RuleRefElement ||
						a instanceof AlternativeBlock &&
						!(a instanceof RuleBlock) &&
						!(a instanceof SynPredBlock)
						) {

						if (
							!(a instanceof RuleRefElement) &&
							((AlternativeBlock)a).not &&
							analyzer.subruleCanBeInverted(((AlternativeBlock)a), grammar instanceof LexerGrammar)
							) {
							// Special case for inverted subrules that
							// will be inlined.  Treat these like
							// token or char literal references
							println(labeledElementType + " " + a.getLabel() + " = " + labeledElementInit + ";");
							if (grammar.buildAST) {
								genASTDeclaration(a);
							}
						}
						else {
							if (grammar.buildAST) {
								// Always gen AST variables for
								// labeled elements, even if the
								// element itself is marked with !
								genASTDeclaration(a);
							}
							if ( grammar instanceof LexerGrammar ) {
								println("IToken "+a.getLabel()+" = null;");
							}
							if (grammar instanceof TreeWalkerGrammar) {
								// always generate rule-ref variables
								// for tree walker
								println(labeledElementType + " " + a.getLabel() + " = " + labeledElementInit + ";");
							}
						}
					}
					else {
						// It is a token or literal reference.  Generate the
						// correct variable type for this grammar
						println(labeledElementType + " " + a.getLabel() + " = " + labeledElementInit + ";");
						// In addition, generate *_AST variables if building ASTs
						if (grammar.buildAST) {
							//println(labeledElementASTType+" " + a.getLabel() + "_AST = null;");
							if (a instanceof GrammarAtom &&
								((GrammarAtom)a).getASTNodeType()!=null ) {
								GrammarAtom ga = (GrammarAtom)a;
								genASTDeclaration(a, ga.getASTNodeType());
							}
							else {
								genASTDeclaration(a);
							}
						}
					}
				}
			}
		}
	}

	public void genBody(LexerGrammar g) throws IOException
	{
		// SAS: moved output creation to method so a subclass can change
		//      how the output is generated (for VAJ interface)
		setupOutput(grammar.getClassName());

		genAST = false;	// no way to gen trees.
		saveText = true;	// save consumed characters.

		tabs=0;

		// Generate header common to all CSharp output files
		genHeader();
		// Do not use printAction because we assume tabs==0
		println(behavior.getHeaderAction(""));

      		// Generate the CSharp namespace declaration (if specified)
		if (nameSpace != null)
			nameSpace.emitDeclarations(currentOutput);
		tabs++;

		// Generate header specific to lexer CSharp file
		println("// Generate header specific to lexer CSharp file");
		println("using System;");
		println("using Stream                          = System.IO.Stream;");
		println("using TextReader                      = System.IO.TextReader;");
		println("using Hashtable                       = System.Collections.Hashtable;");
		println("using Comparer                        = System.Collections.Comparer;");
		if ( !(g.caseSensitiveLiterals) )
		{
			println("using CaseInsensitiveHashCodeProvider = System.Collections.CaseInsensitiveHashCodeProvider;");
			println("using CaseInsensitiveComparer         = System.Collections.CaseInsensitiveComparer;");
		}
		println("");
		println("using TokenStreamException            = antlr.TokenStreamException;");
		println("using TokenStreamIOException          = antlr.TokenStreamIOException;");
		println("using TokenStreamRecognitionException = antlr.TokenStreamRecognitionException;");
		println("using CharStreamException             = antlr.CharStreamException;");
		println("using CharStreamIOException           = antlr.CharStreamIOException;");
		println("using ANTLRException                  = antlr.ANTLRException;");
		println("using CharScanner                     = antlr.CharScanner;");
		println("using InputBuffer                     = antlr.InputBuffer;");
		println("using ByteBuffer                      = antlr.ByteBuffer;");
		println("using CharBuffer                      = antlr.CharBuffer;");
		println("using Token                           = antlr.Token;");
        println("using IToken                          = antlr.IToken;");
		println("using CommonToken                     = antlr.CommonToken;");
		println("using SemanticException               = antlr.SemanticException;");
		println("using RecognitionException            = antlr.RecognitionException;");
		println("using NoViableAltForCharException     = antlr.NoViableAltForCharException;");
		println("using MismatchedCharException         = antlr.MismatchedCharException;");
		println("using TokenStream                     = antlr.TokenStream;");
		println("using LexerSharedInputState           = antlr.LexerSharedInputState;");
		println("using BitSet                          = antlr.collections.impl.BitSet;");

		// Generate user-defined lexer file preamble
		println(grammar.preambleAction.getText());

		// Generate lexer class definition
		String sup=null;
		if ( grammar.superClass!=null ) {
			sup = grammar.superClass;
		}
		else {
			sup = "antlr." + grammar.getSuperClass();
		}

		// print javadoc comment if any
		if ( grammar.comment!=null )
		{
			_println(grammar.comment);
		}

        Token tprefix = (Token)grammar.options.get("classHeaderPrefix");
		if (tprefix == null) {
			print("public ");
		}
        else {
            String p = StringUtils.stripFrontBack(tprefix.getText(), "\"", "\"");
            if (p == null) {
				print("public ");
			}
			else {
                print(p+" ");
            }
        }

		print("class " + grammar.getClassName() + " : "+sup);
		println(", TokenStream");
		Token tsuffix = (Token)grammar.options.get("classHeaderSuffix");
		if ( tsuffix != null )
		{
			String suffix = StringUtils.stripFrontBack(tsuffix.getText(),"\"","\"");
			if ( suffix != null )
			{
				print(", "+suffix);	// must be an interface name for CSharp
			}
		}
		println(" {");
		tabs++;

		// Generate 'const' definitions for Token IDs
		genTokenDefinitions(grammar.tokenManager);

		// Generate user-defined lexer class members
		print(
			processActionForSpecialSymbols(grammar.classMemberAction.getText(), grammar.classMemberAction.getLine(), currentRule, null)
			);

		//
		// Generate the constructor from InputStream, which in turn
		// calls the ByteBuffer constructor
		//
		println("public " + grammar.getClassName() + "(Stream ins) : this(new ByteBuffer(ins))");
		println("{");
		println("}");
		println("");

		//
		// Generate the constructor from Reader, which in turn
		// calls the CharBuffer constructor
		//
		println("public " + grammar.getClassName() + "(TextReader r) : this(new CharBuffer(r))");
		println("{");
		println("}");
		println("");

		print("public " + grammar.getClassName() + "(InputBuffer ib)");
		// if debugging, wrap the input buffer in a debugger
		if (grammar.debuggingOutput)
			println(" : this(new LexerSharedInputState(new antlr.debug.DebuggingInputBuffer(ib)))");
		else
			println(" : this(new LexerSharedInputState(ib))");
		println("{");
		println("}");
		println("");

		//
		// Generate the constructor from InputBuffer (char or byte)
		//
		println("public " + grammar.getClassName() + "(LexerSharedInputState state) : base(state)");
		println("{");
		tabs++;
		println("initialize();");
		tabs--;
		println("}");

		// Generate the initialize function
		println("private void initialize()");
		println("{");
		tabs++;

		// if debugging, set up array variables and call user-overridable
		//   debugging setup method
		if ( grammar.debuggingOutput ) {
			println("ruleNames  = _ruleNames;");
			println("semPredNames = _semPredNames;");
			println("setupDebugging();");
		}

	      // Generate the setting of various generated options.
	      // These need to be before the literals since ANTLRHashString depends on
	      // the casesensitive stuff.
	      println("caseSensitiveLiterals = " + g.caseSensitiveLiterals + ";");
	      println("setCaseSensitive(" + g.caseSensitive + ");");

		// Generate the initialization of a hashtable
		// containing the string literals used in the lexer
		// The literals variable itself is in CharScanner
		if (g.caseSensitiveLiterals)
			println("literals = new Hashtable(100, (float) 0.4, null, Comparer.Default);");
		else
			println("literals = new Hashtable(100, (float) 0.4, CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);");
		Enumeration keys = grammar.tokenManager.getTokenSymbolKeys();
		while ( keys.hasMoreElements() ) {
			String key = (String)keys.nextElement();
			if ( key.charAt(0) != '"' ) {
				continue;
			}
			TokenSymbol sym = grammar.tokenManager.getTokenSymbol(key);
			if ( sym instanceof StringLiteralSymbol ) {
				StringLiteralSymbol s = (StringLiteralSymbol)sym;
				println("literals.Add(" + s.getId() + ", " + s.getTokenType() + ");");
			}
		}

		Enumeration ids;
		tabs--;
		println("}");

		// generate the rule name array for debugging
		if (grammar.debuggingOutput) {
			println("private static readonly string[] _ruleNames = new string[] {");

			ids = grammar.rules.elements();
			int ruleNum=0;
			while ( ids.hasMoreElements() ) {
				GrammarSymbol sym = (GrammarSymbol) ids.nextElement();
				if ( sym instanceof RuleSymbol)
					println("  \""+((RuleSymbol)sym).getId()+"\",");
			}
			println("};");
		}

		// Generate nextToken() rule.
		// nextToken() is a synthetic lexer rule that is the implicit OR of all
		// user-defined lexer rules.
		genNextToken();

		// Generate code for each rule in the lexer
		ids = grammar.rules.elements();
		int ruleNum=0;
		while ( ids.hasMoreElements() ) {
			RuleSymbol sym = (RuleSymbol) ids.nextElement();
			// Don't generate the synthetic rules
			if (!sym.getId().equals("mnextToken")) {
				genRule(sym, false, ruleNum++, grammar.tokenManager);
			}
			exitIfError();
		}

		// Generate the semantic predicate map for debugging
		if (grammar.debuggingOutput)
			genSemPredMap();

		// Generate the bitsets used throughout the lexer
		genBitsets(bitsetsUsed, ((LexerGrammar)grammar).charVocabulary.size());

		println("");
		tabs--;
		println("}");

		tabs--;
		// Generate the CSharp namespace closures (if required)
		if (nameSpace != null)
			nameSpace.emitClosures(currentOutput);

		// Close the lexer output stream
		currentOutput.close();
		currentOutput = null;
	}

	public void genInitFactory( Grammar g ) {
		if( g.buildAST )
		{
			// Generate the method to initialize an ASTFactory when we're
			// building AST's
			println("static public void initializeASTFactory( ASTFactory factory )");
			println("{");
			tabs++;

			println("factory.setMaxNodeType("+g.tokenManager.maxTokenType()+");");

	        // Walk the token vocabulary and generate code to register every TokenID->ASTNodeType
	        // mapping specified in the  tokens {...} section with the ASTFactory.
			Vector v = g.tokenManager.getVocabulary();
			for (int i = 0; i < v.size(); i++) {
				String s = (String)v.elementAt(i);
				if (s != null) {
					TokenSymbol ts = g.tokenManager.getTokenSymbol(s);
					if (ts != null && ts.getASTNodeType() != null) {
						println("factory.setTokenTypeASTNodeType(" + s + ", \"" + ts.getASTNodeType() + "\");");
					}
				}
			}

			tabs--;
			println("}");
		}
	}

	public void genBody(ParserGrammar g) throws IOException
	{
		// Open the output stream for the parser and set the currentOutput
		// SAS: moved file setup so subclass could do it (for VAJ interface)
		setupOutput(grammar.getClassName());

		genAST = grammar.buildAST;

		tabs = 0;

		// Generate the header common to all output files.
		genHeader();
		// Do not use printAction because we assume tabs==0
		println(behavior.getHeaderAction(""));

      		// Generate the CSharp namespace declaration (if specified)
		if (nameSpace != null)
			nameSpace.emitDeclarations(currentOutput);
		tabs++;

		// Generate header for the parser
		println("// Generate the header common to all output files.");
		println("using System;");
		println("");
		println("using TokenBuffer              = antlr.TokenBuffer;");
		println("using TokenStreamException     = antlr.TokenStreamException;");
		println("using TokenStreamIOException   = antlr.TokenStreamIOException;");
		println("using ANTLRException           = antlr.ANTLRException;");

		String qualifiedClassName = grammar.getSuperClass();
		String[] unqualifiedClassName = split(qualifiedClassName, ".");
		println("using " + unqualifiedClassName[unqualifiedClassName.length-1] + " = antlr." + qualifiedClassName + ";");

		println("using Token                    = antlr.Token;");
        println("using IToken                   = antlr.IToken;");
		println("using TokenStream              = antlr.TokenStream;");
		println("using RecognitionException     = antlr.RecognitionException;");
		println("using NoViableAltException     = antlr.NoViableAltException;");
		println("using MismatchedTokenException = antlr.MismatchedTokenException;");
		println("using SemanticException        = antlr.SemanticException;");
		println("using ParserSharedInputState   = antlr.ParserSharedInputState;");
		println("using BitSet                   = antlr.collections.impl.BitSet;");
		if ( genAST ) {
			println("using AST                      = antlr.collections.AST;");
			println("using ASTPair                  = antlr.ASTPair;");
			println("using ASTFactory               = antlr.ASTFactory;");
			println("using ASTArray                 = antlr.collections.impl.ASTArray;");
		}

		// Output the user-defined parser preamble
		println(grammar.preambleAction.getText());

		// Generate parser class definition
		String sup=null;
		if ( grammar.superClass != null )
			sup = grammar.superClass;
		else
			sup = "antlr." + grammar.getSuperClass();

		// print javadoc comment if any
		if ( grammar.comment!=null ) {
			_println(grammar.comment);
		}

        Token tprefix = (Token)grammar.options.get("classHeaderPrefix");
		if (tprefix == null) {
			print("public ");
		}
        else {
            String p = StringUtils.stripFrontBack(tprefix.getText(), "\"", "\"");
            if (p == null) {
				print("public ");
			}
			else {
                print(p+" ");
            }
        }

		println("class " + grammar.getClassName() + " : "+sup);

		Token tsuffix = (Token)grammar.options.get("classHeaderSuffix");
		if ( tsuffix != null ) {
			String suffix = StringUtils.stripFrontBack(tsuffix.getText(),"\"","\"");
			if ( suffix != null )
				print("              , "+suffix);	// must be an interface name for CSharp
		}
		println("{");
		tabs++;

		// Generate 'const' definitions for Token IDs
		genTokenDefinitions(grammar.tokenManager);

		// set up an array of all the rule names so the debugger can
		// keep track of them only by number -- less to store in tree...
		if (grammar.debuggingOutput) {
			println("private static readonly string[] _ruleNames = new string[] {");
			tabs++;

			Enumeration ids = grammar.rules.elements();
			int ruleNum=0;
			while ( ids.hasMoreElements() ) {
				GrammarSymbol sym = (GrammarSymbol) ids.nextElement();
				if ( sym instanceof RuleSymbol)
					println("  \""+((RuleSymbol)sym).getId()+"\",");
			}
			tabs--;
			println("};");
		}

		// Generate user-defined parser class members
		print(
			processActionForSpecialSymbols(grammar.classMemberAction.getText(), grammar.classMemberAction.getLine(), currentRule, null)
			);

		// Generate parser class constructor from TokenBuffer
		println("");
		println("protected void initialize()");
		println("{");
		tabs++;
		println("tokenNames = tokenNames_;");

		if( grammar.buildAST )
			println("initializeFactory();");

		// if debugging, set up arrays and call the user-overridable
		//   debugging setup method
		if ( grammar.debuggingOutput ) {
			println("ruleNames  = _ruleNames;");
			println("semPredNames = _semPredNames;");
			println("setupDebugging(tokenBuf);");
		}
		tabs--;
		println("}");
		println("");

		println("");
		println("protected " + grammar.getClassName() + "(TokenBuffer tokenBuf, int k) : base(tokenBuf, k)");
		println("{");
		tabs++;
		println("initialize();");
		tabs--;
		println("}");
		println("");

		println("public " + grammar.getClassName() + "(TokenBuffer tokenBuf) : this(tokenBuf," + grammar.maxk + ")");
		println("{");
		println("}");
		println("");

		// Generate parser class constructor from TokenStream
		println("protected " + grammar.getClassName()+"(TokenStream lexer, int k) : base(lexer,k)");
		println("{");
		tabs++;
		println("initialize();");
		tabs--;
		println("}");
		println("");

		println("public " + grammar.getClassName()+"(TokenStream lexer) : this(lexer," + grammar.maxk + ")");
		println("{");
		println("}");
		println("");

		println("public " + grammar.getClassName()+"(ParserSharedInputState state) : base(state," + grammar.maxk + ")");
		println("{");
		tabs++;
		println("initialize();");
		tabs--;
		println("}");
		println("");

		astTypes = new java.util.Vector(100);

		// Generate code for each rule in the grammar
		Enumeration ids = grammar.rules.elements();
		int ruleNum=0;
		while ( ids.hasMoreElements() ) {
			GrammarSymbol sym = (GrammarSymbol) ids.nextElement();
			if ( sym instanceof RuleSymbol) {
				RuleSymbol rs = (RuleSymbol)sym;
				genRule(rs, rs.references.size()==0, ruleNum++, grammar.tokenManager);
			}
			exitIfError();
		}
		if ( usingCustomAST )
		{
			// when we are using a custom AST, overload Parser.getAST() to return the
			// custom AST type
			println("public new " + labeledElementASTType + " getAST()");
			println("{");
			tabs++;
			println("return (" + labeledElementASTType + ") returnAST;");
			tabs--;
			println("}");
			println("");
		}

		// Generate the method that initializes the ASTFactory when we're
		// building AST's
		println("private void initializeFactory()");
		println("{");
		tabs++;
		if( grammar.buildAST ) {
			println("if (astFactory == null)");
			println("{");
			tabs++;
			if( usingCustomAST )
			{
				println("astFactory = new ASTFactory(\"" + labeledElementASTType + "\");");
			}
			else
				println("astFactory = new ASTFactory();");
			tabs--;
			println("}");
			println("initializeASTFactory( astFactory );");
		}
		tabs--;
		println("}");
		genInitFactory( g );

		// Generate the token names
		genTokenStrings();

		// Generate the bitsets used throughout the grammar
		genBitsets(bitsetsUsed, grammar.tokenManager.maxTokenType());

		// Generate the semantic predicate map for debugging
		if (grammar.debuggingOutput)
			genSemPredMap();

		// Close class definition
		println("");
		tabs--;
		println("}");

		tabs--;
		// Generate the CSharp namespace closures (if required)
		if (nameSpace != null)
			nameSpace.emitClosures(currentOutput);

		// Close the parser output stream
		currentOutput.close();
		currentOutput = null;
	}
	public void genBody(TreeWalkerGrammar g) throws IOException
	{
		// Open the output stream for the parser and set the currentOutput
		// SAS: move file open to method so subclass can override it
		//      (mainly for VAJ interface)
		setupOutput(grammar.getClassName());

		genAST = grammar.buildAST;
		tabs = 0;

		// Generate the header common to all output files.
		genHeader();
		// Do not use printAction because we assume tabs==0
		println(behavior.getHeaderAction(""));

      // Generate the CSharp namespace declaration (if specified)
		if (nameSpace != null)
			nameSpace.emitDeclarations(currentOutput);
		tabs++;

		// Generate header specific to the tree-parser CSharp file
		println("// Generate header specific to the tree-parser CSharp file");
		println("using System;");
		println("");
		println("using " + grammar.getSuperClass() + " = antlr." + grammar.getSuperClass() + ";");
		println("using Token                    = antlr.Token;");
        println("using IToken                   = antlr.IToken;");
		println("using AST                      = antlr.collections.AST;");
		println("using RecognitionException     = antlr.RecognitionException;");
		println("using ANTLRException           = antlr.ANTLRException;");
		println("using NoViableAltException     = antlr.NoViableAltException;");
		println("using MismatchedTokenException = antlr.MismatchedTokenException;");
		println("using SemanticException        = antlr.SemanticException;");
		println("using BitSet                   = antlr.collections.impl.BitSet;");
		println("using ASTPair                  = antlr.ASTPair;");
		println("using ASTFactory               = antlr.ASTFactory;");
		println("using ASTArray                 = antlr.collections.impl.ASTArray;");

		// Output the user-defined parser premamble
		println(grammar.preambleAction.getText());

		// Generate parser class definition
		String sup=null;
		if ( grammar.superClass!=null ) {
			sup = grammar.superClass;
		}
		else {
			sup = "antlr." + grammar.getSuperClass();
		}
		println("");

		// print javadoc comment if any
		if ( grammar.comment!=null ) {
			_println(grammar.comment);
		}

        Token tprefix = (Token)grammar.options.get("classHeaderPrefix");
		if (tprefix == null) {
			print("public ");
		}
        else {
            String p = StringUtils.stripFrontBack(tprefix.getText(), "\"", "\"");
            if (p == null) {
				print("public ");
			}
			else {
                print(p+" ");
            }
        }

		println("class " + grammar.getClassName() + " : "+sup);
		Token tsuffix = (Token)grammar.options.get("classHeaderSuffix");
		if ( tsuffix != null ) {
			String suffix = StringUtils.stripFrontBack(tsuffix.getText(),"\"","\"");
			if ( suffix != null ) {
				print("              , "+suffix);	// must be an interface name for CSharp
			}
		}
		println("{");
		tabs++;

		// Generate 'const' definitions for Token IDs
		genTokenDefinitions(grammar.tokenManager);

		// Generate user-defined parser class members
		print(
			processActionForSpecialSymbols(grammar.classMemberAction.getText(), grammar.classMemberAction.getLine(), currentRule, null)
			);

		// Generate default parser class constructor
		println("public " + grammar.getClassName() + "()");
		println("{");
		tabs++;
		println("tokenNames = tokenNames_;");
		tabs--;
		println("}");
		println("");

		astTypes = new java.util.Vector();
		// Generate code for each rule in the grammar
		Enumeration ids = grammar.rules.elements();
		int ruleNum=0;
		String ruleNameInits = "";
		while ( ids.hasMoreElements() ) {
			GrammarSymbol sym = (GrammarSymbol) ids.nextElement();
			if ( sym instanceof RuleSymbol) {
				RuleSymbol rs = (RuleSymbol)sym;
				genRule(rs, rs.references.size()==0, ruleNum++, grammar.tokenManager);
			}
			exitIfError();
		}

		if ( usingCustomAST )
		{
			// when we are using a custom ast override Parser.getAST to return the
			// custom AST type
			println("public new " + labeledElementASTType + " getAST()");
			println("{");
			tabs++;
			println("return (" + labeledElementASTType + ") returnAST;");
			tabs--;
			println("}");
			println("");
		}

		// Generate the ASTFactory initialization function
		genInitFactory( grammar );

		// Generate the token names
		genTokenStrings();

		// Generate the bitsets used throughout the grammar
		genBitsets(bitsetsUsed, grammar.tokenManager.maxTokenType());

		// Close class definition
		tabs--;
		println("}");
		println("");

		tabs--;
		// Generate the CSharp namespace closures (if required)
		if (nameSpace != null)
			nameSpace.emitClosures(currentOutput);

		// Close the parser output stream
		currentOutput.close();
		currentOutput = null;
	}

	/** Generate a series of case statements that implement a BitSet test.
	 * @param p The Bitset for which cases are to be generated
	 */
	protected void genCases(BitSet p) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genCases("+p+")");
		int[] elems;

		elems = p.toArray();
		// Wrap cases four-per-line for lexer, one-per-line for parser
		int wrap = (grammar instanceof LexerGrammar) ? 4 : 1;
		int j=1;
		boolean startOfLine = true;
		for (int i = 0; i < elems.length; i++) {
			if (j==1) {
				print("");
			} else {
				_print("  ");
			}
			_print("case " + getValueString(elems[i]) + ":");
			if (j==wrap) {
				_println("");
				startOfLine = true;
				j=1;
			}
			else {
				j++;
				startOfLine = false;
			}
		}
		if (!startOfLine) {
			_println("");
		}
	}

	/**Generate common code for a block of alternatives; return a
	* postscript that needs to be generated at the end of the
	* block.  Other routines may append else-clauses and such for
	* error checking before the postfix is generated.  If the
	* grammar is a lexer, then generate alternatives in an order
	* where alternatives requiring deeper lookahead are generated
	* first, and EOF in the lookahead set reduces the depth of
	* the lookahead.  @param blk The block to generate @param
	* noTestForSingle If true, then it does not generate a test
	* for a single alternative.
	*/
	public CSharpBlockFinishingInfo genCommonBlock(AlternativeBlock blk,
		boolean noTestForSingle)
	{
		int nIF=0;
		boolean createdLL1Switch = false;
		int closingBracesOfIFSequence = 0;
		CSharpBlockFinishingInfo finishingInfo = new CSharpBlockFinishingInfo();
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genCommonBlock("+blk+")");

		// Save the AST generation state, and set it to that of the block
		boolean savegenAST = genAST;
		genAST = genAST && blk.getAutoGen();

			boolean oldsaveTest = saveText;
		saveText = saveText && blk.getAutoGen();

		// Is this block inverted?  If so, generate special-case code
		if ( blk.not &&
			analyzer.subruleCanBeInverted(blk, grammar instanceof LexerGrammar) )
		{
			if ( DEBUG_CODE_GENERATOR ) System.out.println("special case: ~(subrule)");
			Lookahead p = analyzer.look(1, blk);
			// Variable assignment for labeled elements
			if (blk.getLabel() != null && syntacticPredLevel == 0) {
				println(blk.getLabel() + " = " + lt1Value + ";");
			}

			// AST
			genElementAST(blk);

			String astArgs="";
			if (grammar instanceof TreeWalkerGrammar) {
				if ( usingCustomAST )
					astArgs = "(AST)_t,";
				else
					astArgs = "_t,";
			}

			// match the bitset for the alternative
			println("match(" + astArgs + getBitsetName(markBitsetForGen(p.fset)) + ");");

			// tack on tree cursor motion if doing a tree walker
			if (grammar instanceof TreeWalkerGrammar)
			{
				println("_t = _t.getNextSibling();");
			}
			return finishingInfo;
		}

		// Special handling for single alt
		if (blk.getAlternatives().size() == 1)
		{
			Alternative alt = blk.getAlternativeAt(0);
			// Generate a warning if there is a synPred for single alt.
			if (alt.synPred != null)
			{
				antlrTool.warning(
					"Syntactic predicate superfluous for single alternative",
					grammar.getFilename(),
               blk.getAlternativeAt(0).synPred.getLine(),
               blk.getAlternativeAt(0).synPred.getColumn()
					);
			}
			if (noTestForSingle)
			{
				if (alt.semPred != null)
				{
					// Generate validating predicate
					genSemPred(alt.semPred, blk.line);
				}
				genAlt(alt, blk);
				return finishingInfo;
			}
		}

		// count number of simple LL(1) cases; only do switch for
		// many LL(1) cases (no preds, no end of token refs)
		// We don't care about exit paths for (...)*, (...)+
		// because we don't explicitly have a test for them
		// as an alt in the loop.
		//
		// Also, we now count how many unicode lookahead sets
		// there are--they must be moved to DEFAULT or ELSE
		// clause.
		int nLL1 = 0;
		for (int i=0; i<blk.getAlternatives().size(); i++)
		{
			Alternative a = blk.getAlternativeAt(i);
			if ( suitableForCaseExpression(a) ) {
				nLL1++;
			}
		}

		// do LL(1) cases
		if ( nLL1 >= makeSwitchThreshold)
		{
			// Determine the name of the item to be compared
			String testExpr = lookaheadString(1);
			createdLL1Switch = true;
			// when parsing trees, convert null to valid tree node with NULL lookahead
			if ( grammar instanceof TreeWalkerGrammar )
			{
				println("if (null == _t)");
				tabs++;
				println("_t = ASTNULL;");
				tabs--;
			}
			println("switch ( " + testExpr+" )");
			println("{");
			//tabs++;
            // update block nesting level as it affects creation of _saveIndex OK?
            blockNestingLevel++;

			for (int i=0; i<blk.alternatives.size(); i++)
			{
				Alternative alt = blk.getAlternativeAt(i);
				// ignore any non-LL(1) alts, predicated alts,
				// or end-of-token alts for case expressions
				if ( !suitableForCaseExpression(alt) )
				{
					continue;
				}
				Lookahead p = alt.cache[1];
				if (p.fset.degree() == 0 && !p.containsEpsilon())
				{
					antlrTool.warning("Alternate omitted due to empty prediction set",
						grammar.getFilename(),
						alt.head.getLine(), alt.head.getColumn());
				}
				else
				{
					genCases(p.fset);
					println("{");
					tabs++;
                    blockNestingLevel++;
					genAlt(alt, blk);
					println("break;");
                    if (blockNestingLevel-- == saveIndexCreateLevel)
                        saveIndexCreateLevel = 0;
					tabs--;
					println("}");
				}
			}
			println("default:");
			tabs++;
		}

		// do non-LL(1) and nondeterministic cases This is tricky in
		// the lexer, because of cases like: STAR : '*' ; ASSIGN_STAR
		// : "*="; Since nextToken is generated without a loop, then
		// the STAR will have end-of-token as it's lookahead set for
		// LA(2).  So, we must generate the alternatives containing
		// trailing end-of-token in their lookahead sets *after* the
		// alternatives without end-of-token.  This implements the
		// usual lexer convention that longer matches come before
		// shorter ones, e.g.  "*=" matches ASSIGN_STAR not STAR
		//
		// For non-lexer grammars, this does not sort the alternates
		// by depth Note that alts whose lookahead is purely
		// end-of-token at k=1 end up as default or else clauses.
		int startDepth = (grammar instanceof LexerGrammar) ? grammar.maxk : 0;
		for (int altDepth = startDepth; altDepth >= 0; altDepth--) {
			if ( DEBUG_CODE_GENERATOR ) System.out.println("checking depth "+altDepth);
			for (int i=0; i<blk.alternatives.size(); i++) {
				Alternative alt = blk.getAlternativeAt(i);
				if ( DEBUG_CODE_GENERATOR ) System.out.println("genAlt: "+i);
				// if we made a switch above, ignore what we already took care
				// of.  Specifically, LL(1) alts with no preds
				// that do not have end-of-token in their prediction set
				// and that are not giant unicode sets.
				if ( createdLL1Switch && suitableForCaseExpression(alt) )
				{
					if ( DEBUG_CODE_GENERATOR ) System.out.println("ignoring alt because it was in the switch");
					continue;
				}
				String e;

				boolean unpredicted = false;

				if (grammar instanceof LexerGrammar) {
					// Calculate the "effective depth" of the alt,
					// which is the max depth at which
					// cache[depth]!=end-of-token
					int effectiveDepth = alt.lookaheadDepth;
					if (effectiveDepth == GrammarAnalyzer.NONDETERMINISTIC)
					{
						// use maximum lookahead
						effectiveDepth = grammar.maxk;
					}
					while ( effectiveDepth >= 1 &&
						alt.cache[effectiveDepth].containsEpsilon() )
					{
						effectiveDepth--;
					}
					// Ignore alts whose effective depth is other than
					// the ones we are generating for this iteration.
					if (effectiveDepth != altDepth)
					{
						if ( DEBUG_CODE_GENERATOR )
							System.out.println("ignoring alt because effectiveDepth!=altDepth;"+effectiveDepth+"!="+altDepth);
						continue;
					}
					unpredicted = lookaheadIsEmpty(alt, effectiveDepth);
					e = getLookaheadTestExpression(alt, effectiveDepth);
				}
				else
				{
					unpredicted = lookaheadIsEmpty(alt, grammar.maxk);
					e = getLookaheadTestExpression(alt, grammar.maxk);
				}

				// Was it a big unicode range that forced unsuitability
				// for a case expression?
            if (alt.cache[1].fset.degree() > caseSizeThreshold &&
                suitableForCaseExpression(alt))
				{
					if ( nIF==0 )
					{
						println("if " + e);
						println("{");
					}
					else {
						println("else if " + e);
						println("{");
					}
				}
				else if (unpredicted &&
					alt.semPred==null &&
					alt.synPred==null)
				{
					// The alt has empty prediction set and no
					// predicate to help out.  if we have not
					// generated a previous if, just put {...} around
					// the end-of-token clause
					if ( nIF==0 ) {
						println("{");
					}
					else {
						println("else {");
					}
					finishingInfo.needAnErrorClause = false;
				}
				else
				{
					// check for sem and syn preds
					// Add any semantic predicate expression to the lookahead test
					if ( alt.semPred != null ) {
						// if debugging, wrap the evaluation of the predicate in a method
						//
						// translate $ and # references
						ActionTransInfo tInfo = new ActionTransInfo();
						String actionStr = processActionForSpecialSymbols(alt.semPred,
							blk.line,
							currentRule,
							tInfo);
						// ignore translation info...we don't need to
						// do anything with it.  call that will inform
						// SemanticPredicateListeners of the result
						if (((grammar instanceof ParserGrammar) || (grammar instanceof LexerGrammar)) &&
								grammar.debuggingOutput) {
							e = "("+e+"&& fireSemanticPredicateEvaluated(antlr.debug.SemanticPredicateEventArgs.PREDICTING,"+ //FIXME
								addSemPred(charFormatter.escapeString(actionStr))+","+actionStr+"))";
						}
						else {
							e = "("+e+"&&("+actionStr +"))";
						}
					}

					// Generate any syntactic predicates
					if ( nIF>0 ) {
						if ( alt.synPred != null ) {
							println("else {");
							tabs++;
                            blockNestingLevel++;
							genSynPred( alt.synPred, e );
							closingBracesOfIFSequence++;
						}
						else {
							println("else if " + e + " {");
						}
					}
					else {
						if ( alt.synPred != null ) {
							genSynPred( alt.synPred, e );
						}
						else {
							// when parsing trees, convert null to valid tree node
							// with NULL lookahead.
							if ( grammar instanceof TreeWalkerGrammar ) {
								println("if (_t == null)");
								tabs++;
								println("_t = ASTNULL;");
								tabs--;
							}
							println("if " + e);
							println("{");
						}
					}

				}
                blockNestingLevel++;

				nIF++;
				tabs++;
				genAlt(alt, blk);
				tabs--;

                if (blockNestingLevel-- == saveIndexCreateLevel)
                    saveIndexCreateLevel = 0;
				println("}");
			}
		}

		String ps = "";
		for (int i=1; i<=closingBracesOfIFSequence; i++) {
			ps+="}";
            if (blockNestingLevel-- == saveIndexCreateLevel)
                saveIndexCreateLevel = 0;
		}

		// Restore the AST generation state
		genAST = savegenAST;

		// restore save text state
		saveText=oldsaveTest;

		// Return the finishing info.
		if ( createdLL1Switch ) {
			tabs--;
			finishingInfo.postscript = ps+"break; }";
            if (blockNestingLevel-- == saveIndexCreateLevel)
                saveIndexCreateLevel = 0;
			finishingInfo.generatedSwitch = true;
			finishingInfo.generatedAnIf = nIF>0;
			//return new CSharpBlockFinishingInfo(ps+"}",true,nIF>0); // close up switch statement

		}
		else {
			finishingInfo.postscript = ps;
			finishingInfo.generatedSwitch = false;
			finishingInfo.generatedAnIf = nIF>0;
			// return new CSharpBlockFinishingInfo(ps, false,nIF>0);
		}
		return finishingInfo;
	}

	private static boolean suitableForCaseExpression(Alternative a) {
		return a.lookaheadDepth == 1 &&
			a.semPred == null &&
			!a.cache[1].containsEpsilon() &&
			a.cache[1].fset.degree()<=caseSizeThreshold;
	}

	/** Generate code to link an element reference into the AST */
	private void genElementAST(AlternativeElement el) {
		// handle case where you're not building trees, but are in tree walker.
		// Just need to get labels set up.
		if ( grammar instanceof TreeWalkerGrammar && !grammar.buildAST )
		{
			String elementRef;
			String astName;

			// Generate names and declarations of the AST variable(s)
			if (el.getLabel() == null)
			{
				elementRef = lt1Value;
				// Generate AST variables for unlabeled stuff
				astName = "tmp" + astVarNumber + "_AST";
				astVarNumber++;
				// Map the generated AST variable in the alternate
				mapTreeVariable(el, astName);
				// Generate an "input" AST variable also
				println(labeledElementASTType+" "+astName+"_in = "+elementRef+";");
			}
			return;
		}

		if (grammar.buildAST && syntacticPredLevel == 0)
		{
      		boolean needASTDecl =
      			(genAST &&
      			(el.getLabel() != null || (el.getAutoGenType() != GrammarElement.AUTO_GEN_BANG)));

      		// RK: if we have a grammar element always generate the decl
      		// since some guy can access it from an action and we can't
      		// peek ahead (well not without making a mess).
      		// I'd prefer taking this out.
      		if (el.getAutoGenType() != GrammarElement.AUTO_GEN_BANG &&
				(el instanceof TokenRefElement))
      			needASTDecl = true;

      		boolean doNoGuessTest = (grammar.hasSyntacticPredicate && needASTDecl);

			String elementRef;
			String astNameBase;

			// Generate names and declarations of the AST variable(s)
			if (el.getLabel() != null)
			{
				// if the element is labeled use that name...
				elementRef = el.getLabel();
				astNameBase = el.getLabel();
			}
			else
			{
				// else generate a temporary name...
				elementRef = lt1Value;
				// Generate AST variables for unlabeled stuff
				astNameBase = "tmp" + astVarNumber;
				astVarNumber++;
			}

      		// Generate the declaration if required.
      		if (needASTDecl)
      		{
				// Generate the declaration
				if ( el instanceof GrammarAtom )
				{
					GrammarAtom ga = (GrammarAtom)el;
					if ( ga.getASTNodeType()!=null )
					{
						genASTDeclaration(el, astNameBase, ga.getASTNodeType());
						//println(ga.getASTNodeType()+" " + astName+" = null;");
					}
					else
					{
						genASTDeclaration(el, astNameBase, labeledElementASTType);
						//println(labeledElementASTType+" " + astName + " = null;");
					}
				}
				else
				{
					genASTDeclaration(el, astNameBase, labeledElementASTType);
					//println(labeledElementASTType+" " + astName + " = null;");
				}
			}

	      	// for convenience..
    		String astName = astNameBase + "_AST";

			// Map the generated AST variable in the alternate
			mapTreeVariable(el, astName);
			if (grammar instanceof TreeWalkerGrammar)
			{
				// Generate an "input" AST variable also
				println(labeledElementASTType+" " + astName + "_in = null;");
			}


			// Enclose actions with !guessing
			if (doNoGuessTest) {
				//println("if (0 == inputState.guessing)");
				//println("{");
				//tabs++;
			}

			// if something has a label assume it will be used
        	// so we must initialize the RefAST
			if (el.getLabel() != null)
			{
				if ( el instanceof GrammarAtom )
				{
					println(astName + " = "+ getASTCreateString((GrammarAtom)el, elementRef) + ";");
				}
				else
				{
					println(astName + " = "+ getASTCreateString(elementRef) + ";");
				}
			}

			// if it has no label but a declaration exists initialize it.
        	if (el.getLabel() == null && needASTDecl)
			{
				elementRef = lt1Value;
				if ( el instanceof GrammarAtom )
				{
					println(astName + " = "+ getASTCreateString((GrammarAtom)el, elementRef) + ";");
				}
				else
				{
					println(astName + " = "+ getASTCreateString(elementRef) + ";");
				}
				// Map the generated AST variable in the alternate
				if (grammar instanceof TreeWalkerGrammar)
				{
					// set "input" AST variable also
					println(astName + "_in = " + elementRef + ";");
				}
			}

			if (genAST)
			{
				switch (el.getAutoGenType())
				{
				case GrammarElement.AUTO_GEN_NONE:
					if ( usingCustomAST ||
						 ( (el instanceof GrammarAtom) &&
                           (((GrammarAtom)el).getASTNodeType() != null) ) )
						println("astFactory.addASTChild(ref currentAST, (AST)" + astName + ");");
					else
						println("astFactory.addASTChild(ref currentAST, " + astName + ");");
					break;
				case GrammarElement.AUTO_GEN_CARET:
					if ( usingCustomAST ||
						 ( (el instanceof GrammarAtom) &&
                           (((GrammarAtom)el).getASTNodeType() != null) ) )
						println("astFactory.makeASTRoot(ref currentAST, (AST)" + astName + ");");
					else
						println("astFactory.makeASTRoot(ref currentAST, " + astName + ");");
					break;
				default:
					break;
				}
			}
			if (doNoGuessTest)
			{
				//tabs--;
				//println("}");
			}
		}
	}


	/** Close the try block and generate catch phrases
	 * if the element has a labeled handler in the rule
	 */
	private void genErrorCatchForElement(AlternativeElement el) {
		if (el.getLabel() == null) return;
		String r = el.enclosingRuleName;
		if ( grammar instanceof LexerGrammar ) {
			r = CodeGenerator.encodeLexerRuleName(el.enclosingRuleName);
		}
		RuleSymbol rs = (RuleSymbol)grammar.getSymbol(r);
		if (rs == null) {
			antlrTool.panic("Enclosing rule not found!");
		}
		ExceptionSpec ex = rs.block.findExceptionSpec(el.getLabel());
		if (ex != null) {
			tabs--;
			println("}");
			genErrorHandler(ex);
		}
	}

	/** Generate the catch phrases for a user-specified error handler */
	private void genErrorHandler(ExceptionSpec ex)
	{
		// Each ExceptionHandler in the ExceptionSpec is a separate catch
		for (int i = 0; i < ex.handlers.size(); i++)
		{
			ExceptionHandler handler = (ExceptionHandler)ex.handlers.elementAt(i);
			// Generate catch phrase
			println("catch (" + handler.exceptionTypeAndName.getText() + ")");
			println("{");
			tabs++;
			if (grammar.hasSyntacticPredicate) {
				println("if (0 == inputState.guessing)");
				println("{");
				tabs++;
			}

		// When not guessing, execute user handler action
		ActionTransInfo tInfo = new ActionTransInfo();
        printAction(processActionForSpecialSymbols(handler.action.getText(),
         					handler.action.getLine(), currentRule, tInfo));

			if (grammar.hasSyntacticPredicate)
			{
				tabs--;
				println("}");
				println("else");
				println("{");
				tabs++;
				// When guessing, rethrow exception
				//println("throw " + extractIdOfAction(handler.exceptionTypeAndName) + ";");
				println("throw;");
				tabs--;
				println("}");
			}
			// Close catch phrase
			tabs--;
			println("}");
		}
	}
	/** Generate a try { opening if the element has a labeled handler in the rule */
	private void genErrorTryForElement(AlternativeElement el) {
		if (el.getLabel() == null) return;
		String r = el.enclosingRuleName;
		if ( grammar instanceof LexerGrammar ) {
			r = CodeGenerator.encodeLexerRuleName(el.enclosingRuleName);
		}
		RuleSymbol rs = (RuleSymbol)grammar.getSymbol(r);
		if (rs == null) {
			antlrTool.panic("Enclosing rule not found!");
		}
		ExceptionSpec ex = rs.block.findExceptionSpec(el.getLabel());
		if (ex != null) {
			println("try   // for error handling");
			println("{");
			tabs++;
		}
	}

    protected void genASTDeclaration(AlternativeElement el)
    {
        genASTDeclaration(el, labeledElementASTType);
    }

    protected void genASTDeclaration(AlternativeElement el, String node_type)
    {
        genASTDeclaration(el, el.getLabel(), node_type);
    }

    protected void genASTDeclaration(AlternativeElement el, String var_name, String node_type)
    {
        // already declared?
        if (declaredASTVariables.contains(el))
            return;

        // emit code
        //String s = StringUtils.stripFrontBack(node_type, "\"", "\"");
        //println(s + " " + var_name + "_AST = null;");
        println(node_type + " " + var_name + "_AST = null;");

        // mark as declared
        declaredASTVariables.put(el,el);
    }

	/** Generate a header that is common to all CSharp files */
	protected void genHeader()
	{
		println("// $ANTLR "+Tool.version+": "+
			"\"" + antlrTool.fileMinusPath(antlrTool.grammarFile) + "\"" +
			" -> "+
			"\""+grammar.getClassName()+".cs\"$");
	}

	private void genLiteralsTest() {
		println("_ttype = testLiteralsTable(_ttype);");
	}

	private void genLiteralsTestForPartialToken() {
		println("_ttype = testLiteralsTable(text.ToString(_begin, text.Length-_begin), _ttype);");
	}

	protected void genMatch(BitSet b) {
	}

	protected void genMatch(GrammarAtom atom) {
		if ( atom instanceof StringLiteralElement ) {
			if ( grammar instanceof LexerGrammar ) {
				genMatchUsingAtomText(atom);
			}
			else {
				genMatchUsingAtomTokenType(atom);
			}
		}
		else if ( atom instanceof CharLiteralElement ) {
			if ( grammar instanceof LexerGrammar ) {
				genMatchUsingAtomText(atom);
			}
			else {
				antlrTool.error("cannot ref character literals in grammar: "+atom);
			}
		}
		else if ( atom instanceof TokenRefElement ) {
			genMatchUsingAtomText(atom);
		} else if (atom instanceof WildcardElement) {
          gen((WildcardElement)atom);
      }
	}
	protected void genMatchUsingAtomText(GrammarAtom atom) {
		// match() for trees needs the _t cursor
		String astArgs="";
		if (grammar instanceof TreeWalkerGrammar) {
			if ( usingCustomAST )
				astArgs="(AST)_t,";
			else
				astArgs="_t,";
		}

		// if in lexer and ! on element, save buffer index to kill later
		if ( grammar instanceof LexerGrammar && (!saveText||atom.getAutoGenType()==GrammarElement.AUTO_GEN_BANG) ) {
			declareSaveIndexVariableIfNeeded();
			println("_saveIndex = text.Length;");
		}

		print(atom.not ? "matchNot(" : "match(");
		_print(astArgs);

		// print out what to match
		if (atom.atomText.equals("EOF")) {
			// horrible hack to handle EOF case
			_print("Token.EOF_TYPE");
		}
		else {
				_print(atom.atomText);
		}
		_println(");");

		if ( grammar instanceof LexerGrammar && (!saveText||atom.getAutoGenType()==GrammarElement.AUTO_GEN_BANG) ) {
			declareSaveIndexVariableIfNeeded();
			println("text.Length = _saveIndex;");		// kill text atom put in buffer
		}
	}

	protected void genMatchUsingAtomTokenType(GrammarAtom atom) {
		// match() for trees needs the _t cursor
		String astArgs="";
		if (grammar instanceof TreeWalkerGrammar) {
			if( usingCustomAST )
				astArgs="(AST)_t,";
			else
				astArgs="_t,";
		}

		// If the literal can be mangled, generate the symbolic constant instead
		String mangledName = null;
		String s = astArgs + getValueString(atom.getType());

		// matching
		println( (atom.not ? "matchNot(" : "match(") + s + ");");
	}

	/** Generate the nextToken() rule.  nextToken() is a synthetic
	* lexer rule that is the implicit OR of all user-defined
	* lexer rules.
	*/
	public void genNextToken() {
		// Are there any public rules?  If not, then just generate a
		// fake nextToken().
		boolean hasPublicRules = false;
		for (int i = 0; i < grammar.rules.size(); i++) {
			RuleSymbol rs = (RuleSymbol)grammar.rules.elementAt(i);
			if ( rs.isDefined() && rs.access.equals("public") ) {
				hasPublicRules = true;
				break;
			}
		}
		if (!hasPublicRules) {
			println("");
			println("override public IToken nextToken()\t\t\t//throws TokenStreamException");
			println("{");
			tabs++;
			println("try");
			println("{");
			tabs++;
			println("uponEOF();");
			tabs--;
			println("}");
			println("catch(CharStreamIOException csioe)");
			println("{");
			tabs++;
			println("throw new TokenStreamIOException(csioe.io);");
			tabs--;
			println("}");
			println("catch(CharStreamException cse)");
			println("{");
			tabs++;
			println("throw new TokenStreamException(cse.Message);");
			tabs--;
			println("}");
			println("return new CommonToken(Token.EOF_TYPE, \"\");");
			tabs--;
			println("}");
			println("");
			return;
		}

		// Create the synthesized nextToken() rule
		RuleBlock nextTokenBlk = MakeGrammar.createNextTokenRule(grammar, grammar.rules, "nextToken");
		// Define the nextToken rule symbol
		RuleSymbol nextTokenRs = new RuleSymbol("mnextToken");
		nextTokenRs.setDefined();
		nextTokenRs.setBlock(nextTokenBlk);
		nextTokenRs.access = "private";
		grammar.define(nextTokenRs);
		// Analyze the nextToken rule
		boolean ok = grammar.theLLkAnalyzer.deterministic(nextTokenBlk);

		// Generate the next token rule
		String filterRule=null;
		if ( ((LexerGrammar)grammar).filterMode ) {
			filterRule = ((LexerGrammar)grammar).filterRule;
		}

		println("");
		println("override public IToken nextToken()\t\t\t//throws TokenStreamException");
		println("{");
		tabs++;
        // delay creation of _saveIndex until we need it OK?
        blockNestingLevel    = 1;
        saveIndexCreateLevel = 0;
		println("IToken theRetToken = null;");
		_println("tryAgain:");
		println("for (;;)");
		println("{");
		tabs++;
		println("IToken _token = null;");
		println("int _ttype = Token.INVALID_TYPE;");
		if ( ((LexerGrammar)grammar).filterMode ) {
			println("setCommitToPath(false);");
			if ( filterRule!=null ) {
				// Here's a good place to ensure that the filter rule actually exists
            if (!grammar.isDefined(CodeGenerator.encodeLexerRuleName(filterRule))) {
            	grammar.antlrTool.error("Filter rule " + filterRule + " does not exist in this lexer");
				}
				else {
					RuleSymbol rs = (RuleSymbol)grammar.getSymbol(CodeGenerator.encodeLexerRuleName(filterRule));
					if ( !rs.isDefined() ) {
						grammar.antlrTool.error("Filter rule " + filterRule + " does not exist in this lexer");
					}
					else if ( rs.access.equals("public") ) {
						grammar.antlrTool.error("Filter rule " + filterRule + " must be protected");
					}
				}
				println("int _m;");
				println("_m = mark();");
			}
		}
		println("resetText();");

		println("try     // for char stream error handling");
		println("{");
		tabs++;

		// Generate try around whole thing to trap scanner errors
		println("try     // for lexical error handling");
		println("{");
		tabs++;

		// Test for public lexical rules with empty paths
		for (int i=0; i<nextTokenBlk.getAlternatives().size(); i++) {
			Alternative a = nextTokenBlk.getAlternativeAt(i);
			if ( a.cache[1].containsEpsilon() ) {
				//String r = a.head.toString();
            RuleRefElement rr = (RuleRefElement)a.head;
            String r = CodeGenerator.decodeLexerRuleName(rr.targetRule);
            antlrTool.warning("public lexical rule "+r+" is optional (can match \"nothing\")");
			}
		}

		// Generate the block
		String newline = System.getProperty("line.separator");
		CSharpBlockFinishingInfo howToFinish = genCommonBlock(nextTokenBlk, false);
		String errFinish = "if (cached_LA1==EOF_CHAR) { uponEOF(); returnToken_ = makeToken(Token.EOF_TYPE); }";

		errFinish += newline+"\t\t\t\t";
		if ( ((LexerGrammar)grammar).filterMode ) {
			if ( filterRule==null ) {
			//kunle: errFinish += "else { consume(); continue tryAgain; }";
			errFinish += "\t\t\t\telse";
			errFinish += "\t\t\t\t{";
			errFinish += "\t\t\t\t\tconsume();";
			errFinish += "\t\t\t\t\tgoto tryAgain;";
			errFinish += "\t\t\t\t}";
			}
			else {
				errFinish += "\t\t\t\t\telse"+newline+
					"\t\t\t\t\t{"+newline+
					"\t\t\t\t\tcommit();"+newline+
					"\t\t\t\t\ttry {m"+filterRule+"(false);}"+newline+
					"\t\t\t\t\tcatch(RecognitionException e)"+newline+
					"\t\t\t\t\t{"+newline+
					"\t\t\t\t\t	// catastrophic failure"+newline+
					"\t\t\t\t\t	reportError(e);"+newline+
					"\t\t\t\t\t	consume();"+newline+
					"\t\t\t\t\t}"+newline+
					"\t\t\t\t\tgoto tryAgain;"+newline+
					"\t\t\t\t}";
			}
		}
		else {
			errFinish += "else {"+throwNoViable+"}";
		}
		genBlockFinish(howToFinish, errFinish);

		// at this point a valid token has been matched, undo "mark" that was done
		if ( ((LexerGrammar)grammar).filterMode && filterRule!=null ) {
			println("commit();");
		}

		// Generate literals test if desired
		// make sure _ttype is set first; note returnToken_ must be
		// non-null as the rule was required to create it.
		println("if ( null==returnToken_ ) goto tryAgain; // found SKIP token");
		println("_ttype = returnToken_.Type;");
		if ( ((LexerGrammar)grammar).getTestLiterals()) {
			genLiteralsTest();
		}

		// return token created by rule reference in switch
		println("returnToken_.Type = _ttype;");
		println("return returnToken_;");

		// Close try block
		tabs--;
		println("}");
		println("catch (RecognitionException e) {");
		tabs++;
		if ( ((LexerGrammar)grammar).filterMode ) {
			if ( filterRule==null ) {
				println("if (!getCommitToPath())");
				println("{");
				tabs++;
				println("consume();");
				println("goto tryAgain;");
				tabs--;
				println("}");
			}
			else {
				println("if (!getCommitToPath())");
				println("{");
				tabs++;
				println("rewind(_m);");
				println("resetText();");
				println("try {m"+filterRule+"(false);}");
				println("catch(RecognitionException ee) {");
				println("	// horrendous failure: error in filter rule");
				println("	reportError(ee);");
				println("	consume();");
				println("}");
				//println("goto tryAgain;");
				tabs--;
				println("}");
				println("else");
			}
		}
		if ( nextTokenBlk.getDefaultErrorHandler() ) {
			println("{");
			tabs++;
			println("reportError(e);");
			println("consume();");
			tabs--;
			println("}");
		}
		else {
			// pass on to invoking routine
			tabs++;
			println("throw new TokenStreamRecognitionException(e);");
			tabs--;
		}
		tabs--;
		println("}");

		// close CharStreamException try
		tabs--;
		println("}");
		println("catch (CharStreamException cse) {");
		println("	if ( cse is CharStreamIOException ) {");
		println("		throw new TokenStreamIOException(((CharStreamIOException)cse).io);");
		println("	}");
		println("	else {");
		println("		throw new TokenStreamException(cse.Message);");
		println("	}");
		println("}");

		// close for-loop
		tabs--;
		println("}");

		// close method nextToken
		tabs--;
		println("}");
		println("");
	}
	/** Gen a named rule block.
	 * ASTs are generated for each element of an alternative unless
	 * the rule or the alternative have a '!' modifier.
	 *
	 * If an alternative defeats the default tree construction, it
	 * must set <rule>_AST to the root of the returned AST.
	 *
	 * Each alternative that does automatic tree construction, builds
	 * up root and child list pointers in an ASTPair structure.
	 *
	 * A rule finishes by setting the returnAST variable from the
	 * ASTPair.
	 *
	 * @param rule The name of the rule to generate
	 * @param startSymbol true if the rule is a start symbol (i.e., not referenced elsewhere)
	*/
	public void genRule(RuleSymbol s, boolean startSymbol, int ruleNum, TokenManager tm) {
		tabs=1;
		if ( DEBUG_CODE_GENERATOR ) System.out.println("genRule("+ s.getId() +")");
		if ( !s.isDefined() ) {
			antlrTool.error("undefined rule: "+ s.getId());
			return;
		}

		// Generate rule return type, name, arguments
		RuleBlock rblk = s.getBlock();
		currentRule = rblk;
		currentASTResult = s.getId();

      // clear list of declared ast variables..
      declaredASTVariables.clear();

		// Save the AST generation state, and set it to that of the rule
		boolean savegenAST = genAST;
		genAST = genAST && rblk.getAutoGen();

		// boolean oldsaveTest = saveText;
		saveText = rblk.getAutoGen();

		// print javadoc comment if any
		if ( s.comment!=null ) {
			_println(s.comment);
		}

		// Gen method access and final qualifier
		//print(s.access + " final ");
		print(s.access + " ");

		// Gen method return type (note lexer return action set at rule creation)
		if (rblk.returnAction != null)
		{
			// Has specified return value
			_print(extractTypeOfAction(rblk.returnAction, rblk.getLine(), rblk.getColumn()) + " ");
		} else {
			// No specified return value
			_print("void ");
		}

		// Gen method name
		_print(s.getId() + "(");

		// Additional rule parameters common to all rules for this grammar
		_print(commonExtraParams);
		if (commonExtraParams.length() != 0 && rblk.argAction != null ) {
			_print(",");
		}

		// Gen arguments
		if (rblk.argAction != null)
		{
			// Has specified arguments
			_println("");
			tabs++;
			println(rblk.argAction);
			tabs--;
			print(")");
		}
		else {
			// No specified arguments
			_print(")");
		}

		// Gen throws clause and open curly
		_print(" //throws " + exceptionThrown);
		if ( grammar instanceof ParserGrammar ) {
			_print(", TokenStreamException");
		}
		else if ( grammar instanceof LexerGrammar ) {
			_print(", CharStreamException, TokenStreamException");
		}
		// Add user-defined exceptions unless lexer (for now)
		if ( rblk.throwsSpec!=null ) {
			if ( grammar instanceof LexerGrammar ) {
				antlrTool.error("user-defined throws spec not allowed (yet) for lexer rule "+rblk.ruleName);
			}
			else {
				_print(", "+rblk.throwsSpec);
			}
		}

		_println("");
		_println("{");
		tabs++;

		// Convert return action to variable declaration
		if (rblk.returnAction != null)
			println(rblk.returnAction + ";");

		// print out definitions needed by rules for various grammar types
		println(commonLocalVars);

		if (grammar.traceRules) {
			if ( grammar instanceof TreeWalkerGrammar ) {
				if ( usingCustomAST )
					println("traceIn(\""+ s.getId() +"\",(AST)_t);");
				else
					println("traceIn(\""+ s.getId() +"\",_t);");
			}
			else {
				println("traceIn(\""+ s.getId() +"\");");
			}
		}

		if ( grammar instanceof LexerGrammar ) {
			// lexer rule default return value is the rule's token name
			// This is a horrible hack to support the built-in EOF lexer rule.
			if (s.getId().equals("mEOF"))
				println("_ttype = Token.EOF_TYPE;");
			else
				println("_ttype = " + s.getId().substring(1)+";");

			// delay creation of _saveIndex until we need it OK?
            blockNestingLevel    = 1;
            saveIndexCreateLevel = 0;

			/*
			      println("boolean old_saveConsumedInput=saveConsumedInput;");
			      if ( !rblk.getAutoGen() ) {		// turn off "save input" if ! on rule
			      println("saveConsumedInput=false;");
			      }
			    */
		}

		// if debugging, write code to mark entry to the rule
		if ( grammar.debuggingOutput)
			if (grammar instanceof ParserGrammar)
				println("fireEnterRule(" + ruleNum + ",0);");
			else if (grammar instanceof LexerGrammar)
			println("fireEnterRule(" + ruleNum + ",_ttype);");


		// Generate trace code if desired
		if ( grammar.debuggingOutput || grammar.traceRules) {
			println("try { // debugging");
			tabs++;
		}

		// Initialize AST variables
		if (grammar instanceof TreeWalkerGrammar) {
			// "Input" value for rule
			println(labeledElementASTType+" " + s.getId() + "_AST_in = ("+labeledElementASTType+")_t;");
		}
		if (grammar.buildAST) {
			// Parser member used to pass AST returns from rule invocations
			println("returnAST = null;");
			// Tracks AST construction
			// println("ASTPair currentAST = (inputState.guessing==0) ? new ASTPair() : null;");
			println("ASTPair currentAST = new ASTPair();");
			// User-settable return value for rule.
			println(labeledElementASTType+" " + s.getId() + "_AST = null;");
		}

		genBlockPreamble(rblk);
		genBlockInitAction(rblk);
		println("");

		// Search for an unlabeled exception specification attached to the rule
		ExceptionSpec unlabeledUserSpec = rblk.findExceptionSpec("");

		// Generate try block around the entire rule for  error handling
		if (unlabeledUserSpec != null || rblk.getDefaultErrorHandler() ) {
			println("try {      // for error handling");
			tabs++;
		}

		// Generate the alternatives
		if ( rblk.alternatives.size()==1 )
		{
			// One alternative -- use simple form
			Alternative alt = rblk.getAlternativeAt(0);
			String pred = alt.semPred;
			if ( pred!=null )
				genSemPred(pred, currentRule.line);
			if (alt.synPred != null) {
				antlrTool.warning(
					"Syntactic predicate ignored for single alternative",
					grammar.getFilename(), alt.synPred.getLine(), alt.synPred.getColumn()
					);
			}
			genAlt(alt, rblk);
		}
		else
		{
			// Multiple alternatives -- generate complex form
			boolean ok = grammar.theLLkAnalyzer.deterministic(rblk);

			CSharpBlockFinishingInfo howToFinish = genCommonBlock(rblk, false);
			genBlockFinish(howToFinish, throwNoViable);
		}

		// Generate catch phrase for error handling
		if (unlabeledUserSpec != null || rblk.getDefaultErrorHandler() ) {
			// Close the try block
			tabs--;
			println("}");
		}

		// Generate user-defined or default catch phrases
		if (unlabeledUserSpec != null)
		{
			genErrorHandler(unlabeledUserSpec);
		}
		else if (rblk.getDefaultErrorHandler())
		{
			// Generate default catch phrase
			println("catch (" + exceptionThrown + " ex)");
			println("{");
			tabs++;
			// Generate code to handle error if not guessing
			if (grammar.hasSyntacticPredicate) {
				println("if (0 == inputState.guessing)");
				println("{");
				tabs++;
			}
			println("reportError(ex);");
			if ( !(grammar instanceof TreeWalkerGrammar) )
			{
				// Generate code to consume until token in k==1 follow set
				Lookahead follow = grammar.theLLkAnalyzer.FOLLOW(1, rblk.endNode);
				String followSetName = getBitsetName(markBitsetForGen(follow.fset));
				println("recover(ex," + followSetName + ");");
			}
			else
			{
				// Just consume one token
			println("if (null != _t)");
			println("{");
			tabs++;
			println("_t = _t.getNextSibling();");
			tabs--;
			println("}");
			}
			if (grammar.hasSyntacticPredicate)
			{
				tabs--;
				// When guessing, rethrow exception
				println("}");
				println("else");
				println("{");
				tabs++;
				println("throw ex;");
				tabs--;
				println("}");
			}
			// Close catch phrase
			tabs--;
			println("}");
		}

		// Squirrel away the AST "return" value
		if (grammar.buildAST) {
			println("returnAST = " + s.getId() + "_AST;");
		}

		// Set return tree value for tree walkers
		if ( grammar instanceof TreeWalkerGrammar ) {
			println("retTree_ = _t;");
		}

		// Generate literals test for lexer rules so marked
		if (rblk.getTestLiterals()) {
			if ( s.access.equals("protected") ) {
				genLiteralsTestForPartialToken();
			}
			else {
				genLiteralsTest();
			}
		}

		// if doing a lexer rule, dump code to create token if necessary
		if ( grammar instanceof LexerGrammar ) {
			println("if (_createToken && (null == _token) && (_ttype != Token.SKIP))");
			println("{");
			tabs++;
			println("_token = makeToken(_ttype);");
			println("_token.setText(text.ToString(_begin, text.Length-_begin));");
			tabs--;
			println("}");
			println("returnToken_ = _token;");
		}

		// Gen the return statement if there is one (lexer has hard-wired return action)
		if (rblk.returnAction != null) {
			println("return " + extractIdOfAction(rblk.returnAction, rblk.getLine(), rblk.getColumn()) + ";");
		}

		if ( grammar.debuggingOutput || grammar.traceRules) {
			tabs--;
			println("}");
			println("finally");
			println("{ // debugging");
			tabs++;

			// If debugging, generate calls to mark exit of rule
			if ( grammar.debuggingOutput)
				if (grammar instanceof ParserGrammar)
					println("fireExitRule(" + ruleNum + ",0);");
				else if (grammar instanceof LexerGrammar)
				println("fireExitRule(" + ruleNum + ",_ttype);");

			if (grammar.traceRules) {
				if ( grammar instanceof TreeWalkerGrammar ) {
					println("traceOut(\""+ s.getId() +"\",_t);");
				}
				else {
					println("traceOut(\""+ s.getId() +"\");");
				}
			}

			tabs--;
			println("}");
		}

		tabs--;
		println("}");
		println("");

		// Restore the AST generation state
		genAST = savegenAST;

		// restore char save state
		// saveText = oldsaveTest;
	}
	private void GenRuleInvocation(RuleRefElement rr) {
		// dump rule name
		_print(rr.targetRule + "(");

		// lexers must tell rule if it should set returnToken_
		if ( grammar instanceof LexerGrammar ) {
			// if labeled, could access Token, so tell rule to create
			if ( rr.getLabel() != null ) {
				_print("true");
			}
			else {
				_print("false");
			}
			if (commonExtraArgs.length() != 0 || rr.args!=null ) {
				_print(",");
			}
		}

		// Extra arguments common to all rules for this grammar
		_print(commonExtraArgs);
		if (commonExtraArgs.length() != 0 && rr.args!=null ) {
			_print(",");
		}

		// Process arguments to method, if any
		RuleSymbol rs = (RuleSymbol)grammar.getSymbol(rr.targetRule);
		if (rr.args != null)
		{
			// When not guessing, execute user arg action
			ActionTransInfo tInfo = new ActionTransInfo();
			String args = processActionForSpecialSymbols(rr.args, 0, currentRule, tInfo);
			if ( tInfo.assignToRoot || tInfo.refRuleRoot!=null )
			{
            antlrTool.error("Arguments of rule reference '" + rr.targetRule + "' cannot set or ref #" +
                 currentRule.getRuleName(), grammar.getFilename(), rr.getLine(), rr.getColumn());
			}
			_print(args);

			// Warn if the rule accepts no arguments
			if (rs.block.argAction == null)
			{
				antlrTool.warning("Rule '" + rr.targetRule + "' accepts no arguments", grammar.getFilename(), rr.getLine(), rr.getColumn());
			}
		}
		else
		{
			// For C++, no warning if rule has parameters, because there may be default
			// values for all of the parameters
			if (rs.block.argAction != null)
			{
				antlrTool.warning("Missing parameters on reference to rule " + rr.targetRule, grammar.getFilename(), rr.getLine(), rr.getColumn());
			}
		}
		_println(");");

		// move down to the first child while parsing
		if ( grammar instanceof TreeWalkerGrammar ) {
			println("_t = retTree_;");
		}
	}
	protected void genSemPred(String pred, int line) {
		// translate $ and # references
		ActionTransInfo tInfo = new ActionTransInfo();
		pred = processActionForSpecialSymbols(pred, line, currentRule, tInfo);
		// ignore translation info...we don't need to do anything with it.
		String escapedPred = charFormatter.escapeString(pred);

		// if debugging, wrap the semantic predicate evaluation in a method
		// that can tell SemanticPredicateListeners the result
		if (grammar.debuggingOutput && ((grammar instanceof ParserGrammar) || (grammar instanceof LexerGrammar)))
			pred = "fireSemanticPredicateEvaluated(antlr.debug.SemanticPredicateEvent.VALIDATING,"
			+ addSemPred(escapedPred) + "," + pred + ")";
		println("if (!(" + pred + "))");
		println("  throw new SemanticException(\"" + escapedPred + "\");");
	}
	/** Write an array of Strings which are the semantic predicate
	 *  expressions.  The debugger will reference them by number only
	 */
	protected void genSemPredMap() {
		Enumeration e = semPreds.elements();
		println("private string[] _semPredNames = {");
		tabs++;
		while(e.hasMoreElements())
			println("\""+e.nextElement()+"\",");
		tabs--;
		println("};");
	}
	protected void genSynPred(SynPredBlock blk, String lookaheadExpr) {
		if ( DEBUG_CODE_GENERATOR ) System.out.println("gen=>("+blk+")");

		// Dump synpred result variable
		println("bool synPredMatched" + blk.ID + " = false;");
		// inserted by Ole Kniemeyer, December 9, 2005
		if (grammar instanceof TreeWalkerGrammar) {
			println("if (_t==null) _t=ASTNULL;");
		}
		// Gen normal lookahead test
		println("if (" + lookaheadExpr + ")");
		println("{");
		tabs++;

		// Save input state
		if ( grammar instanceof TreeWalkerGrammar ) {
			println("AST __t" + blk.ID + " = _t;");
		}
		else {
			println("int _m" + blk.ID + " = mark();");
		}

		// Once inside the try, assume synpred works unless exception caught
		println("synPredMatched" + blk.ID + " = true;");
		println("inputState.guessing++;");

		// if debugging, tell listeners that a synpred has started
		if (grammar.debuggingOutput && ((grammar instanceof ParserGrammar) ||
			(grammar instanceof LexerGrammar))) {
			println("fireSyntacticPredicateStarted();");
		}

		syntacticPredLevel++;
		println("try {");
		tabs++;
		gen((AlternativeBlock)blk);		// gen code to test predicate
		tabs--;
		//println("System.out.println(\"pred "+blk+" succeeded\");");
		println("}");
		//kunle: lose a few warnings cheaply
		//  println("catch (" + exceptionThrown + " pe)");
		println("catch (" + exceptionThrown + ")");
		println("{");
		tabs++;
		println("synPredMatched"+blk.ID+" = false;");
		//println("System.out.println(\"pred "+blk+" failed\");");
		tabs--;
		println("}");

		// Restore input state
		if ( grammar instanceof TreeWalkerGrammar ) {
			println("_t = __t"+blk.ID+";");
		}
		else {
			println("rewind(_m"+blk.ID+");");
		}

		println("inputState.guessing--;");

		// if debugging, tell listeners how the synpred turned out
		if (grammar.debuggingOutput && ((grammar instanceof ParserGrammar) ||
			(grammar instanceof LexerGrammar))) {
			println("if (synPredMatched" + blk.ID +")");
			println("  fireSyntacticPredicateSucceeded();");
			println("else");
			println("  fireSyntacticPredicateFailed();");
		}

		syntacticPredLevel--;
		tabs--;

		// Close lookahead test
		println("}");

		// Test synred result
		println("if ( synPredMatched"+blk.ID+" )");
		println("{");
	}
	/** Generate a static array containing the names of the tokens,
	 * indexed by the token type values.  This static array is used
	 * to format error messages so that the token identifers or literal
	 * strings are displayed instead of the token numbers.
	 *
	 * If a lexical rule has a paraphrase, use it rather than the
	 * token label.
	 */
	public void genTokenStrings() {
		// Generate a string for each token.  This creates a static
		// array of Strings indexed by token type.
		println("");
		println("public static readonly string[] tokenNames_ = new string[] {");
		tabs++;

		// Walk the token vocabulary and generate a Vector of strings
		// from the tokens.
		Vector v = grammar.tokenManager.getVocabulary();
		for (int i = 0; i < v.size(); i++)
		{
			String s = (String)v.elementAt(i);
			if (s == null)
			{
				s = "<"+String.valueOf(i)+">";
			}
			if ( !s.startsWith("\"") && !s.startsWith("<") ) {
				TokenSymbol ts = (TokenSymbol)grammar.tokenManager.getTokenSymbol(s);
				if ( ts!=null && ts.getParaphrase()!=null ) {
					s = StringUtils.stripFrontBack(ts.getParaphrase(), "\"", "\"");
				}
			}
			else if (s.startsWith("\"")) {
				s = StringUtils.stripFrontBack(s, "\"", "\"");
			}
			print(charFormatter.literalString(s));
			if (i != v.size()-1) {
				_print(",");
			}
			_println("");
		}

		// Close the string array initailizer
		tabs--;
		println("};");
	}
	/** Generate the token types CSharp file */
	protected void genTokenTypes(TokenManager tm) throws IOException {
		// Open the token output CSharp file and set the currentOutput stream
		// SAS: file open was moved to a method so a subclass can override
		//      This was mainly for the VAJ interface
		setupOutput(tm.getName() + TokenTypesFileSuffix);

		tabs = 0;

		// Generate the header common to all CSharp files
		genHeader();
		// Do not use printAction because we assume tabs==0
		println(behavior.getHeaderAction(""));

	      // Generate the CSharp namespace declaration (if specified)
		if (nameSpace != null)
			nameSpace.emitDeclarations(currentOutput);
		tabs++;

		// Encapsulate the definitions in a class.  This has to be done as a class because
		// they are all constants and CSharp inteface  types cannot contain constants.
		println("public class " + tm.getName() + TokenTypesFileSuffix);
		//println("public class " + getTokenTypesClassName());
		println("{");
		tabs++;

		genTokenDefinitions(tm);

		// Close the interface
		tabs--;
		println("}");

		tabs--;
		// Generate the CSharp namespace closures (if required)
		if (nameSpace != null)
			nameSpace.emitClosures(currentOutput);

		// Close the tokens output file
		currentOutput.close();
		currentOutput = null;
		exitIfError();
	}
	protected void genTokenDefinitions(TokenManager tm) throws IOException {
		// Generate a definition for each token type
		Vector v = tm.getVocabulary();

		// Do special tokens manually
		println("public const int EOF = " + Token.EOF_TYPE + ";");
		println("public const int NULL_TREE_LOOKAHEAD = " + Token.NULL_TREE_LOOKAHEAD + ";");

		for (int i = Token.MIN_USER_TYPE; i < v.size(); i++) {
			String s = (String)v.elementAt(i);
			if (s != null) {
				if ( s.startsWith("\"") ) {
					// a string literal
					StringLiteralSymbol sl = (StringLiteralSymbol)tm.getTokenSymbol(s);
					if ( sl==null ) {
						antlrTool.panic("String literal " + s + " not in symbol table");
					}
					else if ( sl.label != null ) {
						println("public const int " + sl.label + " = " + i + ";");
					}
					else {
						String mangledName = mangleLiteral(s);
						if (mangledName != null) {
							// We were able to create a meaningful mangled token name
							println("public const int " + mangledName + " = " + i + ";");
							// if no label specified, make the label equal to the mangled name
							sl.label = mangledName;
						}
						else {
							println("// " + s + " = " + i);
						}
					}
				}
				else if ( !s.startsWith("<") ) {
					println("public const int " + s + " = " + i + ";");
				}
			}
		}
		println("");
	}
	/** Process a string for an simple expression for use in xx/action.g
	 * it is used to cast simple tokens/references to the right type for
	 * the generated language. Basically called for every element in
	 * the vector to getASTCreateString(vector V)
	 * @param str A String.
	 */
	public String processStringForASTConstructor( String str )
	{
		/*
		System.out.println("processStringForASTConstructor: str = "+str+
		                   ", custom = "+(new Boolean(usingCustomAST)).toString()+
		                   ", tree = "+(new Boolean((grammar instanceof TreeWalkerGrammar))).toString()+
		                   ", parser = "+(new Boolean((grammar instanceof ParserGrammar))).toString()+
		                   ", notDefined = "+(new Boolean((!(grammar.tokenManager.tokenDefined(str))))).toString()
		                   );
		*/
		if( usingCustomAST &&
			( (grammar instanceof TreeWalkerGrammar)	||
			  (grammar instanceof ParserGrammar) )		&&
			!(grammar.tokenManager.tokenDefined(str)) )
		{
			//System.out.println("processStringForASTConstructor: "+str+" with cast");
			return "(AST)"+str;
		}
		else
		{
			//System.out.println("processStringForASTConstructor: "+str);
			return str;
		}
	}
	/** Get a string for an expression to generate creation of an AST subtree.
	  * @param v A Vector of String, where each element is an expression
	  *          in the target language yielding an AST node.
	  */
	public String getASTCreateString(Vector v) {
		if (v.size() == 0) {
			return "";
		}
		StringBuffer buf = new StringBuffer();
		buf.append("("+labeledElementASTType+
			") astFactory.make(");
		buf.append(v.elementAt(0));
		for (int i = 1; i < v.size(); i++) {
			buf.append(", " + v.elementAt(i));
		}
		buf.append(")");
		return buf.toString();
	}

	/** Get a string for an expression to generate creating of an AST node
	 * @param atom The grammar node for which you are creating the node
	 * @param str The arguments to the AST constructor
	 */
	public String getASTCreateString(GrammarAtom atom, String astCtorArgs) {
		String astCreateString = "astFactory.create(" + astCtorArgs + ")";

		if (atom == null)
			return getASTCreateString(astCtorArgs);
		else {
			if ( atom.getASTNodeType() != null ) {
				// this Atom was instantiated from a Token that had an "AST" option - associating
				// it with a specific heterogeneous AST type - applied to either:
				// 1) it's underlying TokenSymbol (in the "tokens {} section" or,
                // 2) a particular token reference in the grammar
                //
				// For option (1), we simply generate a cast to hetero-AST type
				// For option (2), we generate a call to factory.create(Token, ASTNodeType) and cast it too
                TokenSymbol ts = grammar.tokenManager.getTokenSymbol(atom.getText());
                if ( (ts == null) || (ts.getASTNodeType() != atom.getASTNodeType()) )
				    astCreateString = "(" + atom.getASTNodeType() + ") astFactory.create(" + astCtorArgs + ", \"" + atom.getASTNodeType() + "\")";
                else if ( (ts != null) && (ts.getASTNodeType() != null) )
                    astCreateString = "(" + ts.getASTNodeType() + ") " + astCreateString;
			}
			else if ( usingCustomAST )
				astCreateString = "(" + labeledElementASTType + ") " + astCreateString;
		}
		return astCreateString;
	}

    /** Returns a string expression that creates an AST node using the specified
     *  AST constructor argument string.
	 *  Parses the first (possibly only) argument in the supplied AST ctor argument
	 *	string to obtain the token type -- ctorID.
	 *
	 *  IF the token type is a valid token symbol AND
	 *	   it has an associated AST node type     AND
	 *	   this is not a #[ID, "T", "ASTType"] constructor
	 *	THEN
	 *	   generate a call to factory.create(ID, Text, token.ASTNodeType())
	 *
	 *  #[ID, "T", "ASTType"] constructors are mapped to astFactory.create(ID, "T", "ASTType")
	 *
	 *  The supported AST constructor forms are:
	 *		#[ID]
	 *		#[ID, "text"]
	 *  	#[ID, "text", ASTclassname]	-- introduced in 2.7.2
	 *
     * @param astCtorArgs The arguments to the AST constructor
     */
	public String getASTCreateString(String astCtorArgs) {
		// kunle: 19-Aug-2002
		// This AST creation string is almost certainly[*1] a manual tree construction request.
		// From the manual [I couldn't read ALL of the code ;-)], this can only be one of:
		// 1) #[ID]                     -- 'astCtorArgs' contains: 'ID'                     (without quotes)    or,
		// 2) #[ID, "T"]                -- 'astCtorArgs' contains: 'ID, "Text"'             (without single quotes) or,
		// kunle: 08-Dec-2002 - 2.7.2a6
		// 3) #[ID, "T", "ASTTypeName"] -- 'astCtorArgs' contains: 'ID, "T", "ASTTypeName"' (without single quotes)
		//
		// [*1]  In my tests, 'atom' was '== null' only for manual tree construction requests

		if ( astCtorArgs==null ) {
			astCtorArgs = "";
		}
		String astCreateString 	= "astFactory.create(" + astCtorArgs + ")";
		String  ctorID   	 	= astCtorArgs;
		String	ctorText 	 	= null;
		int		commaIndex;
		boolean	ctorIncludesCustomType = false;		// Is this a #[ID, "t", "ASTType"] constructor?

		commaIndex = astCtorArgs.indexOf(',');
		if ( commaIndex != -1 ) {
			ctorID   = astCtorArgs.substring(0, commaIndex);					// the 'ID'   portion of #[ID, "Text"]
			ctorText = astCtorArgs.substring(commaIndex+1, astCtorArgs.length());	// the 'Text' portion of #[ID, "Text"]
			commaIndex = ctorText.indexOf(',');
			if (commaIndex != -1 ) {
				// This is an AST creation of the form: #[ID, "Text", "ASTTypename"]
				// Support for this was introduced with 2.7.2a6
				// create default type or (since 2.7.2) 3rd arg is classname
				ctorIncludesCustomType = true;
			}
		}
		TokenSymbol ts = grammar.tokenManager.getTokenSymbol(ctorID);
		if ( (null != ts) && (null != ts.getASTNodeType()) )
			astCreateString = "(" + ts.getASTNodeType() + ") " + astCreateString;
		else if ( usingCustomAST )
			astCreateString = "(" + labeledElementASTType + ") " + astCreateString;

		return astCreateString;
	}

	protected String getLookaheadTestExpression(Lookahead[] look, int k) {
		StringBuffer e = new StringBuffer(100);
		boolean first = true;

		e.append("(");
		for (int i = 1; i <= k; i++) {
			BitSet p = look[i].fset;
			if (!first) {
				e.append(") && (");
			}
			first = false;

			// Syn preds can yield <end-of-syn-pred> (epsilon) lookahead.
			// There is no way to predict what that token would be.  Just
			// allow anything instead.
			if (look[i].containsEpsilon()) {
				e.append("true");
			} else {
				e.append(getLookaheadTestTerm(i, p));
			}
		}
		e.append(")");

		return e.toString();
	}

	/**Generate a lookahead test expression for an alternate.  This
	 * will be a series of tests joined by '&&' and enclosed by '()',
	 * the number of such tests being determined by the depth of the lookahead.
	 */
	protected String getLookaheadTestExpression(Alternative alt, int maxDepth) {
		int depth = alt.lookaheadDepth;
		if ( depth == GrammarAnalyzer.NONDETERMINISTIC ) {
			// if the decision is nondeterministic, do the best we can: LL(k)
			// any predicates that are around will be generated later.
			depth = grammar.maxk;
		}

		if ( maxDepth==0 ) {
			// empty lookahead can result from alt with sem pred
			// that can see end of token.  E.g., A : {pred}? ('a')? ;
			return "( true )";
		}
		return "(" + getLookaheadTestExpression(alt.cache,depth) + ")";
	}

	/**Generate a depth==1 lookahead test expression given the BitSet.
	 * This may be one of:
	 * 1) a series of 'x==X||' tests
	 * 2) a range test using >= && <= where possible,
	 * 3) a bitset membership test for complex comparisons
	 * @param k The lookahead level
	 * @param p The lookahead set for level k
	 */
	protected String getLookaheadTestTerm(int k, BitSet p) {
		// Determine the name of the item to be compared
		String ts = lookaheadString(k);

		// Generate a range expression if possible
		int[] elems = p.toArray();
		if (elementsAreRange(elems)) {
			return getRangeExpression(k, elems);
		}

		// Generate a bitset membership test if possible
		StringBuffer e;
		int degree = p.degree();
		if ( degree == 0 ) {
			return "true";
		}

		if (degree >= bitsetTestThreshold) {
			int bitsetIdx = markBitsetForGen(p);
			return getBitsetName(bitsetIdx) + ".member(" + ts + ")";
		}

		// Otherwise, generate the long-winded series of "x==X||" tests
		e = new StringBuffer();
		for (int i = 0; i < elems.length; i++) {
			// Get the compared-to item (token or character value)
			String cs = getValueString(elems[i]);

			// Generate the element comparison
			if ( i>0 ) e.append("||");
			e.append(ts);
			e.append("==");
			e.append(cs);
		}
		return e.toString();
	}

	/** Return an expression for testing a contiguous renage of elements
	 * @param k The lookahead level
	 * @param elems The elements representing the set, usually from BitSet.toArray().
	 * @return String containing test expression.
	 */
	public String getRangeExpression(int k, int[] elems) {
		if (!elementsAreRange(elems)) {
			antlrTool.panic("getRangeExpression called with non-range");
		}
		int begin = elems[0];
		int end = elems[elems.length-1];

		return
			"(" + lookaheadString(k) + " >= " + getValueString(begin) + " && " +
			lookaheadString(k) + " <= " + getValueString(end) + ")";
	}

	/** getValueString: get a string representation of a token or char value
	 * @param value The token or char value
	 */
	private String getValueString(int value) {
		String cs;
		if ( grammar instanceof LexerGrammar ) {
			cs = charFormatter.literalChar(value);
		}
		else
		{
			TokenSymbol ts = grammar.tokenManager.getTokenSymbolAt(value);
			if ( ts == null ) {
				return ""+value; // return token type as string
				// antlrTool.panic("vocabulary for token type " + value + " is null");
			}
			String tId = ts.getId();
			if ( ts instanceof StringLiteralSymbol ) {
				// if string literal, use predefined label if any
				// if no predefined, try to mangle into LITERAL_xxx.
				// if can't mangle, use int value as last resort
				StringLiteralSymbol sl = (StringLiteralSymbol)ts;
				String label = sl.getLabel();
				if ( label!=null ) {
					cs = label;
				}
				else {
					cs = mangleLiteral(tId);
					if (cs == null) {
						cs = String.valueOf(value);
					}
				}
			}
			else {
				cs = tId;
			}
		}
		return cs;
	}

	/**Is the lookahead for this alt empty? */
	protected boolean lookaheadIsEmpty(Alternative alt, int maxDepth) {
		int depth = alt.lookaheadDepth;
		if ( depth == GrammarAnalyzer.NONDETERMINISTIC ) {
			depth = grammar.maxk;
		}
		for (int i=1; i<=depth && i<=maxDepth; i++) {
			BitSet p = alt.cache[i].fset;
			if (p.degree() != 0) {
				return false;
			}
		}
		return true;
	}

	private String lookaheadString(int k) {
		if (grammar instanceof TreeWalkerGrammar) {
			return "_t.Type";
		}
		if (grammar instanceof LexerGrammar) {
			if (k == 1) {
				return "cached_LA1";
			}
			if (k == 2) {
				return "cached_LA2";
			}
		}
		return "LA(" + k + ")";
	}

	/** Mangle a string literal into a meaningful token name.  This is
	  * only possible for literals that are all characters.  The resulting
	  * mangled literal name is literalsPrefix with the text of the literal
	  * appended.
	  * @return A string representing the mangled literal, or null if not possible.
	  */
	private String mangleLiteral(String s) {
		String mangled = antlrTool.literalsPrefix;
		for (int i = 1; i < s.length()-1; i++) {
			if (!Character.isLetter(s.charAt(i)) &&
				s.charAt(i) != '_') {
				return null;
			}
			mangled += s.charAt(i);
		}
		if ( antlrTool.upperCaseMangledLiterals ) {
			mangled = mangled.toUpperCase();
		}
		return mangled;
	}

	/** Map an identifier to it's corresponding tree-node variable.
	  * This is context-sensitive, depending on the rule and alternative
	  * being generated
	  * @param idParam The identifier name to map
	  * @return The mapped id (which may be the same as the input), or null if the mapping is invalid due to duplicates
	  */
	public String mapTreeId(String idParam, ActionTransInfo transInfo) {
		// if not in an action of a rule, nothing to map.
		if ( currentRule==null ) return idParam;

		boolean in_var = false;
		String id = idParam;
		if (grammar instanceof TreeWalkerGrammar)
		{
			if ( !grammar.buildAST )
			{
				in_var = true;
			}
			// If the id ends with "_in", then map it to the input variable
			else if (id.length() > 3 && id.lastIndexOf("_in") == id.length()-3)
			{
				// Strip off the "_in"
				id = id.substring(0, id.length()-3);
				in_var = true;
			}
		}

		// Check the rule labels.  If id is a label, then the output
		// variable is label_AST, and the input variable is plain label.
		for (int i = 0; i < currentRule.labeledElements.size(); i++)
		{
			AlternativeElement elt = (AlternativeElement)currentRule.labeledElements.elementAt(i);
			if (elt.getLabel().equals(id))
			{
				return in_var ? id : id + "_AST";
			}
		}

		// Failing that, check the id-to-variable map for the alternative.
		// If the id is in the map, then output variable is the name in the
		// map, and input variable is name_in
		String s = (String)treeVariableMap.get(id);
		if (s != null)
		{
			if (s == NONUNIQUE)
			{
				// There is more than one element with this id
				antlrTool.error("Ambiguous reference to AST element "+id+
								" in rule "+currentRule.getRuleName());
				return null;
			}
			else if (s.equals(currentRule.getRuleName()))
			{
				// a recursive call to the enclosing rule is
				// ambiguous with the rule itself.
//				if( in_var )
//					System.out.println("returning null (rulename)");
				antlrTool.error("Ambiguous reference to AST element "+id+
								" in rule "+currentRule.getRuleName());
				return null;
			}
			else
			{
				return in_var ? s + "_in" : s;
			}
		}

		// Failing that, check the rule name itself.  Output variable
		// is rule_AST; input variable is rule_AST_in (treeparsers).
		if( id.equals(currentRule.getRuleName()) )
		{
			String r = in_var ? id + "_AST_in" : id + "_AST";
			if ( transInfo!=null ) {
				if ( !in_var ) {
					transInfo.refRuleRoot = r;
				}
			}
			return r;
		}
		else
		{
			// id does not map to anything -- return itself.
			return id;
		}
	}

	/** Given an element and the name of an associated AST variable,
	  * create a mapping between the element "name" and the variable name.
	  */
	private void mapTreeVariable(AlternativeElement e, String name)
	{
		// For tree elements, defer to the root
		if (e instanceof TreeElement) {
			mapTreeVariable( ((TreeElement)e).root, name);
			return;
		}

		// Determine the name of the element, if any, for mapping purposes
		String elName = null;

		// Don't map labeled items
		if (e.getLabel() == null) {
			if (e instanceof TokenRefElement) {
				// use the token id
				elName = ((TokenRefElement)e).atomText;
			}
			else if (e instanceof RuleRefElement) {
				// use the rule name
				elName = ((RuleRefElement)e).targetRule;
			}
		}
		// Add the element to the tree variable map if it has a name
		if (elName != null) {
			if (treeVariableMap.get(elName) != null) {
				// Name is already in the map -- mark it as duplicate
				treeVariableMap.remove(elName);
				treeVariableMap.put(elName, NONUNIQUE);
			}
			else {
				treeVariableMap.put(elName, name);
			}
		}
	}

    /** Lexically process tree-specifiers in the action.
     *  This will replace #id and #(...) with the appropriate
     *  function calls and/or variables.
     */
    protected String processActionForSpecialSymbols(String actionStr,
                                                    int line,
                                                    RuleBlock currentRule,
                                                    ActionTransInfo tInfo)
	{
		if ( actionStr==null || actionStr.length()==0 )
			return null;

        // The action trans info tells us (at the moment) whether an
        // assignment was done to the rule's tree root.
        if (grammar==null)
            return actionStr;

        // see if we have anything to do...
        if ((grammar.buildAST && actionStr.indexOf('#') != -1) ||
            grammar instanceof TreeWalkerGrammar ||
            ((grammar instanceof LexerGrammar ||
            grammar instanceof ParserGrammar)
			  	&& actionStr.indexOf('$') != -1) )
		{
            // Create a lexer to read an action and return the translated version
            antlr.actions.csharp.ActionLexer lexer = new antlr.actions.csharp.ActionLexer(actionStr, currentRule, this, tInfo);

            lexer.setLineOffset(line);
            lexer.setFilename(grammar.getFilename());
            lexer.setTool(antlrTool);

            try {
                lexer.mACTION(true);
                actionStr = lexer.getTokenObject().getText();
                // System.out.println("action translated: "+actionStr);
                // System.out.println("trans info is "+tInfo);
            }
            catch (RecognitionException ex) {
                lexer.reportError(ex);
                return actionStr;
            }
            catch (TokenStreamException tex) {
                antlrTool.panic("Error reading action:"+actionStr);
                return actionStr;
            }
            catch (CharStreamException io) {
                antlrTool.panic("Error reading action:"+actionStr);
                return actionStr;
            }
        }
        return actionStr;
    }

	private void setupGrammarParameters(Grammar g) {
		if (g instanceof ParserGrammar ||
			 g instanceof LexerGrammar  ||
			 g instanceof TreeWalkerGrammar
			)
		{
			/* RK: options also have to be added to Grammar.java and for options
			 * on the file level entries have to be defined in
			 * DefineGrammarSymbols.java and passed around via 'globals' in antlrTool.java
			 */
			if( antlrTool.nameSpace != null )
				nameSpace = new CSharpNameSpace( antlrTool.nameSpace.getName() );
			//genHashLines = antlrTool.genHashLines;

			/* let grammar level options override filelevel ones...
			 */
			if( g.hasOption("namespace") ) {
				Token t = g.getOption("namespace");
				if( t != null ) {
					nameSpace = new CSharpNameSpace(t.getText());
				}
			}
			/*
			if( g.hasOption("genHashLines") ) {
				Token t = g.getOption("genHashLines");
				if( t != null )  {
					String val = StringUtils.stripFrontBack(t.getText(),"\"","\"");
					genHashLines = val.equals("true");
				}
			}
			*/
		}

		if (g instanceof ParserGrammar) {
			labeledElementASTType = "AST";
			if ( g.hasOption("ASTLabelType") ) {
				Token tsuffix = g.getOption("ASTLabelType");
				if ( tsuffix != null ) {
					String suffix = StringUtils.stripFrontBack(tsuffix.getText(), "\"", "\"");
					if ( suffix != null ) {
						usingCustomAST = true;
						labeledElementASTType = suffix;
					}
				}
			}
			labeledElementType = "IToken ";
			labeledElementInit = "null";
			commonExtraArgs = "";
			commonExtraParams = "";
			commonLocalVars = "";
			lt1Value = "LT(1)";
			exceptionThrown = "RecognitionException";
			throwNoViable = "throw new NoViableAltException(LT(1), getFilename());";
		}
		else if (g instanceof LexerGrammar) {
			labeledElementType = "char ";
			labeledElementInit = "'\\0'";
			commonExtraArgs = "";
			commonExtraParams = "bool _createToken";
			commonLocalVars = "int _ttype; IToken _token=null; int _begin=text.Length;";
			lt1Value = "cached_LA1";
			exceptionThrown = "RecognitionException";
			throwNoViable = "throw new NoViableAltForCharException(cached_LA1, getFilename(), getLine(), getColumn());";
		}
		else if (g instanceof TreeWalkerGrammar) {
			labeledElementASTType = "AST";
			labeledElementType = "AST";
			if ( g.hasOption("ASTLabelType") ) {
				Token tsuffix = g.getOption("ASTLabelType");
				if ( tsuffix != null ) {
					String suffix = StringUtils.stripFrontBack(tsuffix.getText(), "\"", "\"");
					if ( suffix != null ) {
						usingCustomAST = true;
						labeledElementASTType = suffix;
						labeledElementType = suffix;
					}
				}
			}
			if ( !g.hasOption("ASTLabelType") ) {
				g.setOption("ASTLabelType", new Token(ANTLRTokenTypes.STRING_LITERAL,"AST"));
			}
			labeledElementInit = "null";
			commonExtraArgs = "_t";
			commonExtraParams = "AST _t";
			commonLocalVars = "";
            if (usingCustomAST)
            	lt1Value = "(_t==ASTNULL) ? null : (" + labeledElementASTType + ")_t";
            else
            	lt1Value = "_t";
			exceptionThrown = "RecognitionException";
			throwNoViable = "throw new NoViableAltException(_t);";
		}
		else {
			antlrTool.panic("Unknown grammar type");
		}
	}

	/** This method exists so a subclass, namely VAJCodeGenerator,
	 *  can open the file in its own evil way.  JavaCodeGenerator
	 *  simply opens a text file...
	 */
	public void setupOutput(String className) throws IOException
	{
		currentOutput = antlrTool.openOutputFile(className + ".cs");
	}

	/** Helper method from Eric Smith's version of CSharpCodeGenerator.*/
	private static String OctalToUnicode(String str)
	{
		// only do any conversion if the string looks like "'\003'"
		if ( (4 <= str.length()) &&
 	        ('\'' == str.charAt(0)) &&
 	        ('\\' == str.charAt(1)) &&
 	        (('0' <= str.charAt(2)) && ('7' >= str.charAt(2))) &&
 	        ('\'' == str.charAt(str.length()-1)) )
		{
			// convert octal representation to decimal, then to hex
			Integer x = Integer.valueOf(str.substring(2, str.length()-1), 8);

			return "'\\x" + Integer.toHexString(x.intValue()) + "'";
		}
		else {
			return str;
		}
	}

	/** Helper method that returns the name of the interface/class/enum type for
	    token type constants.
	 */
	public String getTokenTypesClassName()
	{
		TokenManager tm = grammar.tokenManager;
		return new String(tm.getName() + TokenTypesFileSuffix);
	}

	private void declareSaveIndexVariableIfNeeded()
	{
        if (saveIndexCreateLevel == 0)
        {
            println("int _saveIndex = 0;");
            saveIndexCreateLevel = blockNestingLevel;
        }
	}

	public String[] split(String str, String sep)
	{
		StringTokenizer st = new StringTokenizer(str, sep);
		int count = st.countTokens();
		String[] values = new String[count];

		int i = 0;
		while(st.hasMoreTokens())
		{
    		values[i] = st.nextToken();
    		i++;
		}
		return values;
	}

}
