/*
 * [The "BSD license"]
 * Copyright (c) 2011 Terence Parr
 * All rights reserved.
 *
 * Grammar conversion to ANTLR v3:
 * Copyright (c) 2011 Sam Harwell
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** Find left-recursive rules */
tree grammar LeftRecursiveRuleWalker;

options {
	language=Java;
	tokenVocab=ANTLR;
    ASTLabelType=GrammarAST;
}

@header {
package org.antlr.grammar.v3;

import org.antlr.analysis.*;
import org.antlr.misc.*;
import org.antlr.tool.*;

import org.antlr.runtime.BitSet;
import org.antlr.runtime.DFA;
}

@members {
protected Grammar grammar;
private String ruleName;
private int outerAlt; // which outer alt of rule?
public int numAlts;  // how many alts for this rule total?

@Override
public void reportError(RecognitionException ex)
{
    Token token = null;
    if (ex instanceof MismatchedTokenException)
    {
        token = ((MismatchedTokenException)ex).token;
    }
    else if (ex instanceof NoViableAltException)
    {
        token = ((NoViableAltException)ex).token;
    }

    ErrorManager.syntaxError(
        ErrorManager.MSG_SYNTAX_ERROR,
        grammar,
        token,
        "assign.types: " + ex.toString(),
        ex);
}

public void setTokenPrec(GrammarAST t, int alt) {}
public void binaryAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) {}
public void ternaryAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) {}
public void prefixAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) {}
public void suffixAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) {}
public void otherAlt(GrammarAST altTree, GrammarAST rewriteTree, int alt) {}
public void setReturnValues(GrammarAST t) {}
}

optionsSpec
	:	^(OPTIONS option+)
	;

option
	:	^(ASSIGN ID optionValue)
	;

optionValue
	:	ID
	|	STRING_LITERAL
	|	CHAR_LITERAL
	|	INT
	;

charSetElement
	:	CHAR_LITERAL
	|	^(OR CHAR_LITERAL CHAR_LITERAL)
	|	^(RANGE CHAR_LITERAL CHAR_LITERAL)
	;

public
rec_rule[Grammar g] returns [boolean isLeftRec]
@init
{
	grammar = g;
	outerAlt = 1;
}
	:	^(	r=RULE id=ID {ruleName=$id.getText();}
			modifier?
			^(ARG ARG_ACTION?)
			^(RET ARG_ACTION?)
			optionsSpec?
			ruleScopeSpec?
			(^(AMPERSAND .*))*
			ruleBlock {$isLeftRec = $ruleBlock.isLeftRec;}
			exceptionGroup?
			EOR
		)
		{if ($ruleBlock.isLeftRec) $r.setType(PREC_RULE);}
	;

modifier
	:	'protected'
	|	'public'
	|	'private'
	|	'fragment'
	;

ruleScopeSpec
 	:	^('scope' ACTION? ID*)
 	;

ruleBlock returns [boolean isLeftRec]
@init{boolean lr=false; this.numAlts = $start.getChildCount();}
	:	^(	BLOCK
			optionsSpec?
			(	outerAlternative
				{if ($outerAlternative.isLeftRec) $isLeftRec = true;}
				rewrite?
				{outerAlt++;}
			)+
			EOB
		)
	;

block
    :   ^(  BLOCK
            optionsSpec?
            ( ^(ALT element+ EOA) rewrite? )+
            EOB   
         )
    ;

/** An alt is either prefix, suffix, binary, or ternary operation or "other" */
outerAlternative returns [boolean isLeftRec]
@init
{
GrammarAST rew = $start.getNextSibling();
if (rew.getType() != REWRITES)
	rew = null;
}
    :   (binaryMultipleOp)=> binaryMultipleOp
                             {binaryAlt($start, rew, outerAlt); $isLeftRec=true;}
    |   (binary)=>           binary       
                             {binaryAlt($start, rew, outerAlt); $isLeftRec=true;}
    |   (ternary)=>          ternary
                             {ternaryAlt($start, rew, outerAlt); $isLeftRec=true;}
    |   (prefix)=>           prefix
                             {prefixAlt($start, rew, outerAlt);}
    |   (suffix)=>           suffix
                             {suffixAlt($start, rew, outerAlt); $isLeftRec=true;}
    |   ^(ALT element+ EOA) // "other" case
                             {otherAlt($start, rew, outerAlt);}
    ;

binary
	:	^( ALT (^(BACKTRACK_SEMPRED .*))? recurseNoLabel op=token recurse EOA ) {setTokenPrec($op.t, outerAlt);}
	;

binaryMultipleOp
	:	^( ALT (^(BACKTRACK_SEMPRED .*))? recurseNoLabel ^( BLOCK ( ^( ALT op=token EOA {setTokenPrec($op.t, outerAlt);} ) )+ EOB ) recurse EOA )
	;

ternary
	:	^( ALT (^(BACKTRACK_SEMPRED .*))? recurseNoLabel op=token recurse token recurse EOA ) {setTokenPrec($op.t, outerAlt);}
	;

prefix : ^( ALT (^(BACKTRACK_SEMPRED .*))? {setTokenPrec((GrammarAST)input.LT(1), outerAlt);} ({!((CommonTree)input.LT(1)).getText().equals(ruleName)}? element)+ recurse EOA ) ;

suffix : ^( ALT (^(BACKTRACK_SEMPRED .*))? recurseNoLabel {setTokenPrec((GrammarAST)input.LT(1), outerAlt);} element+  EOA ) ;

recurse
	:	^(ASSIGN ID recurseNoLabel)
	|	^(PLUS_ASSIGN ID recurseNoLabel)
	|	recurseNoLabel
	;

recurseNoLabel : {((CommonTree)input.LT(1)).getText().equals(ruleName)}? RULE_REF;

/*
elementNotRecursiveRule
    :   {_t.findFirstType(RULE_REF)!=null && _t.findFirstType(RULE_REF).getText().equals(ruleName)}?
        e:element
    ;
*/

token returns [GrammarAST t=null]
	:	^(ASSIGN ID s=token {$t = $s.t;})
	|	^(PLUS_ASSIGN ID s=token {$t = $s.t;})
	|	^(ROOT s=token {$t = $s.t;})
	|	^(BANG s=token {$t = $s.t;})
	|	a=CHAR_LITERAL      {$t = $a;}
	|	b=STRING_LITERAL    {$t = $b;}
	|	c=TOKEN_REF         {$t = $c;}
	;

exceptionGroup
	:	exceptionHandler+ finallyClause?
	|	finallyClause
    ;

exceptionHandler
	:	^('catch' ARG_ACTION ACTION)
	;

finallyClause
	:	^('finally' ACTION)
	;

rewrite
	:	^(REWRITES ( ^( REWRITE SEMPRED? (^(ALT .*)|^(TEMPLATE .*)|ACTION|ETC) ) )* )
	;

element
	:	^(ROOT element)
	|	^(BANG element)
	|	atom
	|	^(NOT element)
	|	^(RANGE atom atom)
	|	^(ASSIGN ID element)
	|	^(PLUS_ASSIGN ID element)
	|	ebnf
	|	tree_
	|	^(SYNPRED block) 
	|	FORCED_ACTION
	|	ACTION
	|	SEMPRED
	|	SYN_SEMPRED
	|	BACKTRACK_SEMPRED
	|	GATED_SEMPRED
	|	EPSILON 
	;

ebnf:   block
    |   ^( OPTIONAL block ) 
    |   ^( CLOSURE block )  
    |   ^( POSITIVE_CLOSURE block ) 
    ;

tree_
	:	^(TREE_BEGIN element+)
	;

atom
	:	^(RULE_REF ARG_ACTION?)
	|	^(TOKEN_REF ARG_ACTION?)
	|	CHAR_LITERAL
	|	STRING_LITERAL
	|	WILDCARD
	|	^(DOT ID atom) // scope override on rule
	;

ast_suffix
	:	ROOT
	|	BANG
	;
