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: //depot/code/org.antlr/release/antlr-2.7.7/antlr/MakeGrammar.java#2 $
 */

import antlr.collections.Stack;
import antlr.collections.impl.LList;
import antlr.collections.impl.Vector;

public class MakeGrammar extends DefineGrammarSymbols {

    protected Stack blocks = new LList(); // track subrules--Stack<BlockContext>
    protected RuleRefElement lastRuleRef;

    protected RuleEndElement ruleEnd;   // used if not nested
    protected RuleBlock ruleBlock;		// points to block of current rule.
    protected int nested = 0;			// nesting inside a subrule
    protected boolean grammarError = false;

    ExceptionSpec currentExceptionSpec = null;

    public MakeGrammar(Tool tool_, String[] args_, LLkAnalyzer analyzer_) {
        super(tool_, args_, analyzer_);
    }

    /** Abort the processing of a grammar (due to syntax errors) */
    public void abortGrammar() {
        String s = "unknown grammar";
        if (grammar != null) {
            s = grammar.getClassName();
        }
        tool.error("aborting grammar '" + s + "' due to errors");
        super.abortGrammar();
    }

    protected void addElementToCurrentAlt(AlternativeElement e) {
        e.enclosingRuleName = ruleBlock.ruleName;
        context().addAlternativeElement(e);
    }

    public void beginAlt(boolean doAutoGen_) {
        super.beginAlt(doAutoGen_);
        Alternative alt = new Alternative();
        alt.setAutoGen(doAutoGen_);
        context().block.addAlternative(alt);
    }

    public void beginChildList() {
        super.beginChildList();
        context().block.addAlternative(new Alternative());
    }

    /** Add an exception group to a rule (currently a no-op) */
    public void beginExceptionGroup() {
        super.beginExceptionGroup();
        if (!(context().block instanceof RuleBlock)) {
            tool.panic("beginExceptionGroup called outside of rule block");
        }
    }

    /** Add an exception spec to an exception group or rule block */
    public void beginExceptionSpec(Token label) {
        // Hack the label string a bit to remove leading/trailing space.
        if (label != null) {
            label.setText(StringUtils.stripFront(StringUtils.stripBack(label.getText(), " \n\r\t"), " \n\r\t"));
        }
        super.beginExceptionSpec(label);
        // Don't check for currentExceptionSpec!=null because syntax errors
        // may leave it set to something.
        currentExceptionSpec = new ExceptionSpec(label);
    }

    public void beginSubRule(Token label, Token start, boolean not) {
        super.beginSubRule(label, start, not);
        // we don't know what kind of subrule it is yet.
        // push a dummy one that will allow us to collect the
        // alternatives.  Later, we'll switch to real object.
        blocks.push(new BlockContext());
        context().block = new AlternativeBlock(grammar, start, not);
        context().altNum = 0; // reset alternative number
        nested++;
        // create a final node to which the last elememt of each
        // alternative will point.
        context().blockEnd = new BlockEndElement(grammar);
        // make sure end node points to start of block
        context().blockEnd.block = context().block;
        labelElement(context().block, label);
    }

    public void beginTree(Token tok) throws SemanticException {
        if (!(grammar instanceof TreeWalkerGrammar)) {
            tool.error("Trees only allowed in TreeParser", grammar.getFilename(), tok.getLine(), tok.getColumn());
            throw new SemanticException("Trees only allowed in TreeParser");
        }
        super.beginTree(tok);
        blocks.push(new TreeBlockContext());
        context().block = new TreeElement(grammar, tok);
        context().altNum = 0; // reset alternative number
    }

    public BlockContext context() {
        if (blocks.height() == 0) {
            return null;
        }
        else {
            return (BlockContext)blocks.top();
        }
    }

    /**Used to build nextToken() for the lexer.
     * This builds a rule which has every "public" rule in the given Vector of
     * rules as it's alternate.  Each rule ref generates a Token object.
     * @param g  The Grammar that is being processed
     * @param lexRules A vector of lexer rules that will be used to create an alternate block.
     * @param rname The name of the resulting rule.
     */
    public static RuleBlock createNextTokenRule(Grammar g, Vector lexRules, String rname) {
        // create actual rule data structure
        RuleBlock rb = new RuleBlock(g, rname);
        rb.setDefaultErrorHandler(g.getDefaultErrorHandler());
        RuleEndElement ruleEnd = new RuleEndElement(g);
        rb.setEndElement(ruleEnd);
        ruleEnd.block = rb;
        // Add an alternative for each element of the rules vector.
        for (int i = 0; i < lexRules.size(); i++) {
            RuleSymbol r = (RuleSymbol)lexRules.elementAt(i);
            if (!r.isDefined()) {
                g.antlrTool.error("Lexer rule " + r.id.substring(1) + " is not defined");
            }
            else {
                if (r.access.equals("public")) {
					Alternative alt = new Alternative(); // create alt we'll add to ref rule
					RuleBlock targetRuleBlock = r.getBlock();
					Vector targetRuleAlts = targetRuleBlock.getAlternatives();
					// collect a sem pred if only one alt and it's at the start;
					// simple, but faster to implement until real hoisting
					if ( targetRuleAlts!=null && targetRuleAlts.size()==1 ) {
						Alternative onlyAlt = (Alternative)targetRuleAlts.elementAt(0);
						if ( onlyAlt.semPred!=null ) {
							// ok, has sem pred, make this rule ref alt have a pred
							alt.semPred = onlyAlt.semPred;
							// REMOVE predicate from target rule???  NOPE, another
							// rule other than nextToken() might invoke it.
						}
					}

                    // create a rule ref to lexer rule
                    // the Token is a RULE_REF not a TOKEN_REF since the
                    // conversion to mRulename has already taken place
                    RuleRefElement rr =
                        new RuleRefElement(g,
                                           new CommonToken(ANTLRTokenTypes.RULE_REF, r.getId()),
                                           GrammarElement.AUTO_GEN_NONE);
                    rr.setLabel("theRetToken");
                    rr.enclosingRuleName = "nextToken";
                    rr.next = ruleEnd;
					alt.addElement(rr);  		// add rule ref to alt
                    alt.setAutoGen(true);		// keep text of elements
                    rb.addAlternative(alt);		// add alt to rule block
                    r.addReference(rr);			// track ref to this rule in rule blk
                }
            }
        }

        rb.setAutoGen(true);		// keep text of elements
        rb.prepareForAnalysis();
        //System.out.println(rb);
        return rb;
    }

    /** Return block as if they had typed: "( rule )?" */
    private AlternativeBlock createOptionalRuleRef(String rule, Token start) {
        // Make the subrule
        AlternativeBlock blk = new AlternativeBlock(grammar, start, false);

        // Make sure rule is defined
        String mrule = CodeGenerator.encodeLexerRuleName(rule); // can only be a lexer rule!
        if (!grammar.isDefined(mrule)) {
            grammar.define(new RuleSymbol(mrule));
        }

        // Make the rule ref element
        // RK: fixme probably easier to abuse start token..
        Token t = new CommonToken(ANTLRTokenTypes.TOKEN_REF, rule);
        t.setLine(start.getLine());
        t.setLine(start.getColumn());
        RuleRefElement rref =
            new RuleRefElement(grammar, t, GrammarElement.AUTO_GEN_NONE);

        rref.enclosingRuleName = ruleBlock.ruleName;

        // Make the end of block element
        BlockEndElement end = new BlockEndElement(grammar);
        end.block = blk;		// end block points back to start of blk

        // Make an alternative, putting the rule ref into it
        Alternative alt = new Alternative(rref);
        alt.addElement(end); // last element in alt points to end of block

        // Add the alternative to this block
        blk.addAlternative(alt);

        // create an empty (optional) alt and add to blk
        Alternative optAlt = new Alternative();
        optAlt.addElement(end);	// points immediately to end of block

        blk.addAlternative(optAlt);

        blk.prepareForAnalysis();
        return blk;
    }

    public void defineRuleName(Token r,
                               String access,
                               boolean ruleAutoGen,
                               String docComment)
        throws SemanticException {
        //		if ( Character.isUpperCase(r.getText().charAt(0)) ) {
        if (r.type == ANTLRTokenTypes.TOKEN_REF) {
            if (!(grammar instanceof LexerGrammar)) {
                tool.error("Lexical rule " + r.getText() +
                           " defined outside of lexer",
                           grammar.getFilename(), r.getLine(), r.getColumn());
                r.setText(r.getText().toLowerCase());
            }
        }
        else {
            if (grammar instanceof LexerGrammar) {
                tool.error("Lexical rule names must be upper case, '" + r.getText() +
                           "' is not",
                           grammar.getFilename(), r.getLine(), r.getColumn());
                r.setText(r.getText().toUpperCase());
            }
        }

        super.defineRuleName(r, access, ruleAutoGen, docComment);
        String id = r.getText();
        //		if ( Character.isUpperCase(id.charAt(0)) ) { // lexer rule?
        if (r.type == ANTLRTokenTypes.TOKEN_REF) { // lexer rule?
            id = CodeGenerator.encodeLexerRuleName(id);
        }
        RuleSymbol rs = (RuleSymbol)grammar.getSymbol(id);
        RuleBlock rb = new RuleBlock(grammar, r.getText(), r.getLine(), ruleAutoGen);

        // Lexer rules do not generate default error handling
        rb.setDefaultErrorHandler(grammar.getDefaultErrorHandler());

        ruleBlock = rb;
        blocks.push(new BlockContext()); // enter new context
        context().block = rb;
        rs.setBlock(rb);
        ruleEnd = new RuleEndElement(grammar);
        rb.setEndElement(ruleEnd);
        nested = 0;
    }

    public void endAlt() {
        super.endAlt();
        if (nested == 0) {	// all rule-level alts link to ruleEnd node
            addElementToCurrentAlt(ruleEnd);
        }
        else {
            addElementToCurrentAlt(context().blockEnd);
        }
        context().altNum++;
    }

    public void endChildList() {
        super.endChildList();
        // create a final node to which the last elememt of the single
        // alternative will point.  Done for compatibility with analyzer.
        // Does NOT point to any block like alternative blocks because the
        // TreeElement is not a block.  This is used only as a placeholder.
        BlockEndElement be = new BlockEndElement(grammar);
        be.block = context().block;
        addElementToCurrentAlt(be);
    }

    public void endExceptionGroup() {
        super.endExceptionGroup();
    }

    public void endExceptionSpec() {
        super.endExceptionSpec();
        if (currentExceptionSpec == null) {
            tool.panic("exception processing internal error -- no active exception spec");
        }
        if (context().block instanceof RuleBlock) {
            // Named rule
            ((RuleBlock)context().block).addExceptionSpec(currentExceptionSpec);
        }
        else {
            // It must be a plain-old alternative block
            if (context().currentAlt().exceptionSpec != null) {
                tool.error("Alternative already has an exception specification", grammar.getFilename(), context().block.getLine(), context().block.getColumn());
            }
            else {
                context().currentAlt().exceptionSpec = currentExceptionSpec;
            }
        }
        currentExceptionSpec = null;
    }

    /** Called at the end of processing a grammar */
    public void endGrammar() {
        if (grammarError) {
            abortGrammar();
        }
        else {
            super.endGrammar();
        }
    }

    public void endRule(String rule) {
        super.endRule(rule);
        BlockContext ctx = (BlockContext)blocks.pop();	// remove scope
        // record the start of this block in the ending node
        ruleEnd.block = ctx.block;
        ruleEnd.block.prepareForAnalysis();
        //System.out.println(ctx.block);
    }

    public void endSubRule() {
        super.endSubRule();
        nested--;
        // remove subrule context from scope stack
        BlockContext ctx = (BlockContext)blocks.pop();
        AlternativeBlock block = ctx.block;

        // If the subrule is marked with ~, check that it is
        // a valid candidate for analysis
        if (
            block.not &&
            !(block instanceof SynPredBlock) &&
            !(block instanceof ZeroOrMoreBlock) &&
            !(block instanceof OneOrMoreBlock)
        ) {
            if (!analyzer.subruleCanBeInverted(block, grammar instanceof LexerGrammar)) {
                String newline = System.getProperty("line.separator");
                tool.error(
                    "This subrule cannot be inverted.  Only subrules of the form:" + newline +
                    "    (T1|T2|T3...) or" + newline +
                    "    ('c1'|'c2'|'c3'...)" + newline +
                    "may be inverted (ranges are also allowed).",
                    grammar.getFilename(),
                    block.getLine(), block.getColumn()
                );
            }
        }

        // add the subrule as element if not a syn pred
        if (block instanceof SynPredBlock) {
            // record a reference to the recently-recognized syn pred in the
            // enclosing block.
            SynPredBlock synpred = (SynPredBlock)block;
            context().block.hasASynPred = true;
            context().currentAlt().synPred = synpred;
            grammar.hasSyntacticPredicate = true;
            synpred.removeTrackingOfRuleRefs(grammar);
        }
        else {
            addElementToCurrentAlt(block);
        }
        ctx.blockEnd.block.prepareForAnalysis();
    }

    public void endTree() {
        super.endTree();
        BlockContext ctx = (BlockContext)blocks.pop();
        addElementToCurrentAlt(ctx.block);		// add new TreeElement to enclosing alt.
    }

    /** Remember that a major error occured in the grammar */
    public void hasError() {
        grammarError = true;
    }

    private void labelElement(AlternativeElement el, Token label) {
        if (label != null) {
            // Does this label already exist?
            for (int i = 0; i < ruleBlock.labeledElements.size(); i++) {
                AlternativeElement altEl = (AlternativeElement)ruleBlock.labeledElements.elementAt(i);
                String l = altEl.getLabel();
                if (l != null && l.equals(label.getText())) {
                    tool.error("Label '" + label.getText() + "' has already been defined", grammar.getFilename(), label.getLine(), label.getColumn());
                    return;
                }
            }
            // add this node to the list of labeled elements
            el.setLabel(label.getText());
            ruleBlock.labeledElements.appendElement(el);
        }
    }

    public void noAutoGenSubRule() {
        context().block.setAutoGen(false);
    }

    public void oneOrMoreSubRule() {
        if (context().block.not) {
            tool.error("'~' cannot be applied to (...)* subrule", grammar.getFilename(), context().block.getLine(), context().block.getColumn());
        }
        // create the right kind of object now that we know what that is
        // and switch the list of alternatives.  Adjust the stack of blocks.
        // copy any init action also.
        OneOrMoreBlock b = new OneOrMoreBlock(grammar);
        setBlock(b, context().block);
        BlockContext old = (BlockContext)blocks.pop(); // remove old scope; we want new type of subrule
        blocks.push(new BlockContext());
        context().block = b;
        context().blockEnd = old.blockEnd;
        context().blockEnd.block = b;
    }

    public void optionalSubRule() {
        if (context().block.not) {
            tool.error("'~' cannot be applied to (...)? subrule", grammar.getFilename(), context().block.getLine(), context().block.getColumn());
        }
        // convert (X)? -> (X|) so that we can ignore optional blocks altogether!
        // It already thinks that we have a simple subrule, just add option block.
        beginAlt(false);
        endAlt();
    }

    public void refAction(Token action) {
        super.refAction(action);
        context().block.hasAnAction = true;
        addElementToCurrentAlt(new ActionElement(grammar, action));
    }

    public void setUserExceptions(String thr) {
        ((RuleBlock)context().block).throwsSpec = thr;
    }

    // Only called for rule blocks
    public void refArgAction(Token action) {
        ((RuleBlock)context().block).argAction = action.getText();
    }

    public void refCharLiteral(Token lit, Token label, boolean inverted, int autoGenType, boolean lastInRule) {
        if (!(grammar instanceof LexerGrammar)) {
            tool.error("Character literal only valid in lexer", grammar.getFilename(), lit.getLine(), lit.getColumn());
            return;
        }
        super.refCharLiteral(lit, label, inverted, autoGenType, lastInRule);
        CharLiteralElement cl = new CharLiteralElement((LexerGrammar)grammar, lit, inverted, autoGenType);

        // Generate a warning for non-lowercase ASCII when case-insensitive
        if (
            !((LexerGrammar)grammar).caseSensitive && cl.getType() < 128 &&
            Character.toLowerCase((char)cl.getType()) != (char)cl.getType()
        ) {
            tool.warning("Character literal must be lowercase when caseSensitive=false", grammar.getFilename(), lit.getLine(), lit.getColumn());
        }

        addElementToCurrentAlt(cl);
        labelElement(cl, label);

        // if ignore option is set, must add an optional call to the specified rule.
        String ignore = ruleBlock.getIgnoreRule();
        if (!lastInRule && ignore != null) {
            addElementToCurrentAlt(createOptionalRuleRef(ignore, lit));
        }
    }

    public void refCharRange(Token t1, Token t2, Token label, int autoGenType, boolean lastInRule) {
        if (!(grammar instanceof LexerGrammar)) {
            tool.error("Character range only valid in lexer", grammar.getFilename(), t1.getLine(), t1.getColumn());
            return;
        }
        int rangeMin = ANTLRLexer.tokenTypeForCharLiteral(t1.getText());
        int rangeMax = ANTLRLexer.tokenTypeForCharLiteral(t2.getText());
        if (rangeMax < rangeMin) {
            tool.error("Malformed range.", grammar.getFilename(), t1.getLine(), t1.getColumn());
            return;
        }

        // Generate a warning for non-lowercase ASCII when case-insensitive
        if (!((LexerGrammar)grammar).caseSensitive) {
            if (rangeMin < 128 && Character.toLowerCase((char)rangeMin) != (char)rangeMin) {
                tool.warning("Character literal must be lowercase when caseSensitive=false", grammar.getFilename(), t1.getLine(), t1.getColumn());
            }
            if (rangeMax < 128 && Character.toLowerCase((char)rangeMax) != (char)rangeMax) {
                tool.warning("Character literal must be lowercase when caseSensitive=false", grammar.getFilename(), t2.getLine(), t2.getColumn());
            }
        }

        super.refCharRange(t1, t2, label, autoGenType, lastInRule);
        CharRangeElement cr = new CharRangeElement((LexerGrammar)grammar, t1, t2, autoGenType);
        addElementToCurrentAlt(cr);
        labelElement(cr, label);

        // if ignore option is set, must add an optional call to the specified rule.
        String ignore = ruleBlock.getIgnoreRule();
        if (!lastInRule && ignore != null) {
            addElementToCurrentAlt(createOptionalRuleRef(ignore, t1));
        }
    }

    public void refTokensSpecElementOption(Token tok,
                                           Token option,
                                           Token value) {
        /*
		System.out.println("setting tokens spec option for "+tok.getText());
		System.out.println(option.getText()+","+value.getText());
		*/
        TokenSymbol ts = (TokenSymbol)
            grammar.tokenManager.getTokenSymbol(tok.getText());
        if (ts == null) {
            tool.panic("cannot find " + tok.getText() + "in tokens {...}");
        }
        if (option.getText().equals("AST")) {
            ts.setASTNodeType(value.getText());
        }
        else {
            grammar.antlrTool.error("invalid tokens {...} element option:" +
                               option.getText(),
                               grammar.getFilename(),
                               option.getLine(), option.getColumn());
        }
    }

    public void refElementOption(Token option, Token value) {
        /*
		System.out.println("setting option for "+context().currentElement());
		System.out.println(option.getText()+","+value.getText());
		*/
        AlternativeElement e = context().currentElement();
        if (e instanceof StringLiteralElement ||
            e instanceof TokenRefElement ||
            e instanceof WildcardElement) {
            ((GrammarAtom)e).setOption(option, value);
        }
        else {
            tool.error("cannot use element option (" + option.getText() +
                       ") for this kind of element",
                       grammar.getFilename(), option.getLine(), option.getColumn());
        }
    }

    /** Add an exception handler to an exception spec */
    public void refExceptionHandler(Token exTypeAndName, Token action) {
        super.refExceptionHandler(exTypeAndName, action);
        if (currentExceptionSpec == null) {
            tool.panic("exception handler processing internal error");
        }
        currentExceptionSpec.addHandler(new ExceptionHandler(exTypeAndName, action));
    }

    public void refInitAction(Token action) {
        super.refAction(action);
        context().block.setInitAction(action.getText());
    }

    public void refMemberAction(Token act) {
        grammar.classMemberAction = act;
    }

    public void refPreambleAction(Token act) {
        super.refPreambleAction(act);
    }

    // Only called for rule blocks
    public void refReturnAction(Token returnAction) {
        if (grammar instanceof LexerGrammar) {
            String name = CodeGenerator.encodeLexerRuleName(((RuleBlock)context().block).getRuleName());
            RuleSymbol rs = (RuleSymbol)grammar.getSymbol(name);
            if (rs.access.equals("public")) {
                tool.warning("public Lexical rules cannot specify return type", grammar.getFilename(), returnAction.getLine(), returnAction.getColumn());
                return;
            }
        }
        ((RuleBlock)context().block).returnAction = returnAction.getText();
    }

    public void refRule(Token idAssign,
                        Token r,
                        Token label,
                        Token args,
                        int autoGenType) {
        // Disallow parser rule references in the lexer
        if (grammar instanceof LexerGrammar) {
            //			if (!Character.isUpperCase(r.getText().charAt(0))) {
            if (r.type != ANTLRTokenTypes.TOKEN_REF) {
                tool.error("Parser rule " + r.getText() + " referenced in lexer");
                return;
            }
            if (autoGenType == GrammarElement.AUTO_GEN_CARET) {
                tool.error("AST specification ^ not allowed in lexer", grammar.getFilename(), r.getLine(), r.getColumn());
            }
        }

        super.refRule(idAssign, r, label, args, autoGenType);
        lastRuleRef = new RuleRefElement(grammar, r, autoGenType);
        if (args != null) {
            lastRuleRef.setArgs(args.getText());
        }
        if (idAssign != null) {
            lastRuleRef.setIdAssign(idAssign.getText());
        }
        addElementToCurrentAlt(lastRuleRef);

        String id = r.getText();
        //		if ( Character.isUpperCase(id.charAt(0)) ) { // lexer rule?
        if (r.type == ANTLRTokenTypes.TOKEN_REF) { // lexer rule?
            id = CodeGenerator.encodeLexerRuleName(id);
        }
        // update symbol table so it knows what nodes reference the rule.
        RuleSymbol rs = (RuleSymbol)grammar.getSymbol(id);
        rs.addReference(lastRuleRef);
        labelElement(lastRuleRef, label);
    }

    public void refSemPred(Token pred) {
        //System.out.println("refSemPred "+pred.getText());
        super.refSemPred(pred);
        //System.out.println("context().block: "+context().block);
        if (context().currentAlt().atStart()) {
            context().currentAlt().semPred = pred.getText();
        }
        else {
            ActionElement a = new ActionElement(grammar, pred);
            a.isSemPred = true;
            addElementToCurrentAlt(a);
        }
        //System.out.println("DONE refSemPred "+pred.getText());
    }

    public void refStringLiteral(Token lit, Token label, int autoGenType, boolean lastInRule) {
        super.refStringLiteral(lit, label, autoGenType, lastInRule);
        if (grammar instanceof TreeWalkerGrammar && autoGenType == GrammarElement.AUTO_GEN_CARET) {
            tool.error("^ not allowed in here for tree-walker", grammar.getFilename(), lit.getLine(), lit.getColumn());
        }
        StringLiteralElement sl = new StringLiteralElement(grammar, lit, autoGenType);

        // If case-insensitive, then check each char of the stirng literal
        if (grammar instanceof LexerGrammar && !((LexerGrammar)grammar).caseSensitive) {
            for (int i = 1; i < lit.getText().length() - 1; i++) {
                char c = lit.getText().charAt(i);
                if (c < 128 && Character.toLowerCase(c) != c) {
                    tool.warning("Characters of string literal must be lowercase when caseSensitive=false", grammar.getFilename(), lit.getLine(), lit.getColumn());
                    break;
                }
            }
        }

        addElementToCurrentAlt(sl);
        labelElement(sl, label);

        // if ignore option is set, must add an optional call to the specified rule.
        String ignore = ruleBlock.getIgnoreRule();
        if (!lastInRule && ignore != null) {
            addElementToCurrentAlt(createOptionalRuleRef(ignore, lit));
        }
    }

    public void refToken(Token idAssign, Token t, Token label, Token args,
                         boolean inverted, int autoGenType, boolean lastInRule) {
        if (grammar instanceof LexerGrammar) {
            // In lexer, token references are really rule references
            if (autoGenType == GrammarElement.AUTO_GEN_CARET) {
                tool.error("AST specification ^ not allowed in lexer", grammar.getFilename(), t.getLine(), t.getColumn());
            }
            if (inverted) {
                tool.error("~TOKEN is not allowed in lexer", grammar.getFilename(), t.getLine(), t.getColumn());
            }
            refRule(idAssign, t, label, args, autoGenType);

            // if ignore option is set, must add an optional call to the specified token rule.
            String ignore = ruleBlock.getIgnoreRule();
            if (!lastInRule && ignore != null) {
                addElementToCurrentAlt(createOptionalRuleRef(ignore, t));
            }
        }
        else {
            // Cannot have token ref args or assignment outside of lexer
            if (idAssign != null) {
                tool.error("Assignment from token reference only allowed in lexer", grammar.getFilename(), idAssign.getLine(), idAssign.getColumn());
            }
            if (args != null) {
                tool.error("Token reference arguments only allowed in lexer", grammar.getFilename(), args.getLine(), args.getColumn());
            }
            super.refToken(idAssign, t, label, args, inverted, autoGenType, lastInRule);
            TokenRefElement te = new TokenRefElement(grammar, t, inverted, autoGenType);
            addElementToCurrentAlt(te);
            labelElement(te, label);
        }
    }

    public void refTokenRange(Token t1, Token t2, Token label, int autoGenType, boolean lastInRule) {
        if (grammar instanceof LexerGrammar) {
            tool.error("Token range not allowed in lexer", grammar.getFilename(), t1.getLine(), t1.getColumn());
            return;
        }
        super.refTokenRange(t1, t2, label, autoGenType, lastInRule);
        TokenRangeElement tr = new TokenRangeElement(grammar, t1, t2, autoGenType);
        if (tr.end < tr.begin) {
            tool.error("Malformed range.", grammar.getFilename(), t1.getLine(), t1.getColumn());
            return;
        }
        addElementToCurrentAlt(tr);
        labelElement(tr, label);
    }

    public void refTreeSpecifier(Token treeSpec) {
        context().currentAlt().treeSpecifier = treeSpec;
    }

    public void refWildcard(Token t, Token label, int autoGenType) {
        super.refWildcard(t, label, autoGenType);
        WildcardElement wc = new WildcardElement(grammar, t, autoGenType);
        addElementToCurrentAlt(wc);
        labelElement(wc, label);
    }

    /** Get ready to process a new grammar */
    public void reset() {
        super.reset();
        blocks = new LList();
        lastRuleRef = null;
        ruleEnd = null;
        ruleBlock = null;
        nested = 0;
        currentExceptionSpec = null;
        grammarError = false;
    }

    public void setArgOfRuleRef(Token argAction) {
        super.setArgOfRuleRef(argAction);
        lastRuleRef.setArgs(argAction.getText());
    }

    public static void setBlock(AlternativeBlock b, AlternativeBlock src) {
        b.setAlternatives(src.getAlternatives());
        b.initAction = src.initAction;
        //b.lookaheadDepth = src.lookaheadDepth;
        b.label = src.label;
        b.hasASynPred = src.hasASynPred;
        b.hasAnAction = src.hasAnAction;
        b.warnWhenFollowAmbig = src.warnWhenFollowAmbig;
        b.generateAmbigWarnings = src.generateAmbigWarnings;
        b.line = src.line;
        b.greedy = src.greedy;
        b.greedySet = src.greedySet;
    }

    public void setRuleOption(Token key, Token value) {
        //((RuleBlock)context().block).setOption(key, value);
        ruleBlock.setOption(key, value);
    }

    public void setSubruleOption(Token key, Token value) {
        ((AlternativeBlock)context().block).setOption(key, value);
    }

    public void synPred() {
        if (context().block.not) {
            tool.error("'~' cannot be applied to syntactic predicate", grammar.getFilename(), context().block.getLine(), context().block.getColumn());
        }
        // create the right kind of object now that we know what that is
        // and switch the list of alternatives.  Adjust the stack of blocks.
        // copy any init action also.
        SynPredBlock b = new SynPredBlock(grammar);
        setBlock(b, context().block);
        BlockContext old = (BlockContext)blocks.pop(); // remove old scope; we want new type of subrule
        blocks.push(new BlockContext());
        context().block = b;
        context().blockEnd = old.blockEnd;
        context().blockEnd.block = b;
    }

    public void zeroOrMoreSubRule() {
        if (context().block.not) {
            tool.error("'~' cannot be applied to (...)+ subrule", grammar.getFilename(), context().block.getLine(), context().block.getColumn());
        }
        // create the right kind of object now that we know what that is
        // and switch the list of alternatives.  Adjust the stack of blocks.
        // copy any init action also.
        ZeroOrMoreBlock b = new ZeroOrMoreBlock(grammar);
        setBlock(b, context().block);
        BlockContext old = (BlockContext)blocks.pop(); // remove old scope; we want new type of subrule
        blocks.push(new BlockContext());
        context().block = b;
        context().blockEnd = old.blockEnd;
        context().blockEnd.block = b;
    }
}
