/*
 [The "BSD licence"]
 Copyright (c) 2011 Terence Parr
 All rights reserved.

 Grammar conversion to ANTLR v3 and C#:
 Copyright (c) 2011 Sam Harwell, Pixel Mine, Inc.
 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.
*/

/** Walk a grammar and generate code by gradually building up
 *  a bigger and bigger Template.
 *
 *  Terence Parr
 *  University of San Francisco
 *  June 15, 2004
 */
tree grammar CodeGenTreeWalker;

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

@header
{
using Antlr3.Analysis;
using Antlr3.Misc;
using Antlr3.Tool;
using Antlr3.Extensions;

using BitSet = Antlr.Runtime.BitSet;
using DFA = Antlr.Runtime.DFA;
using Template = Antlr4.StringTemplate.Template;
using TemplateGroup = Antlr4.StringTemplate.TemplateGroup;
using TokenWithIndex = Antlr.Runtime.CommonToken;
}

@namespace{Antlr3.Grammars}

public
grammar_[Grammar g,
		 Template recognizerST,
		 Template outputFileST,
		 Template headerFileST]
@init
{
	HandleGrammarInit($g, $recognizerST, $outputFileST, $headerFileST);
}
	:	(	^( LEXER_GRAMMAR grammarSpec )
		|	^( PARSER_GRAMMAR grammarSpec )
		|	^( TREE_GRAMMAR grammarSpec )
		|	^( COMBINED_GRAMMAR grammarSpec )
		)
	;

attrScope
	:	^( 'scope' ID ( ^(AMPERSAND .*) )* ACTION )
	;

grammarSpec
	:   ID
		(	comment=DOC_COMMENT
		)?
								{HandleGrammarSpecAfterName($comment);}
		( ^(OPTIONS .*) )?
		( ^(IMPORT .*) )?
		( ^(TOKENS .*) )?
		(attrScope)*
		( ^(AMPERSAND .*) )*
		rules[recognizerST]
	;

rules[Template recognizerST]
@init
{
string ruleName;
bool generated;
HandleRulesInit($start, out ruleName, out generated);
}
	:	(	(	options {k=1;} :
				{generated}? =>
				rST=rule						{HandleRulesRule($rST.code);}
			|	^(RULE .*)
			|	^(PREC_RULE .*) // ignore
			)
												{{HandleRulesForcedAfterRule(ref ruleName, ref generated);}}
		)+
	;

rule returns [Template code=null]
@init
{
string initAction;
GrammarAST block2;
Antlr3.Analysis.DFA dfa;
Rule ruleDescr;
string description;
HandleRuleInit($start, out initAction, out block2, out dfa, out ruleDescr, out description);

// For syn preds, we don't want any AST code etc... in there.
// Save old templates ptr and restore later.  Base templates include Dbg.
TemplateGroup saveGroup = templates;
if (ruleDescr.IsSynPred && generator.Target.UseBaseTemplatesForSynPredFragments)
	templates = generator.BaseTemplates;
}
	:	^(	RULE id=ID						{HandleRuleAfterId($id);}
			(mod=modifier)?
			^(ARG (ARG_ACTION)?)
			^(RET (ARG_ACTION)?)
			(throwsSpec)?
			( ^(OPTIONS .*) )?
			(ruleScopeSpec)?
			( ^(AMPERSAND .*) )*
			b=block["ruleBlock", dfa, null]		{HandleRuleAfterBlock(out $code, $start, block2, ruleDescr, description, $b.code);}

			(exceptionGroup[$code])?
			EOR
		)
											{HandleRuleEnd($code, $start, initAction, description, $mod.start, $b.code);}
	;
finally { templates = saveGroup; }

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

throwsSpec
	:	^('throws' ID+)
	;

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

block[string blockTemplateName, Antlr3.Analysis.DFA dfa, GrammarAST label]
	 returns [Template code]
options { k=1; }
@init
{
	int altNum;
	HandleBlockInit($blockTemplateName, $dfa, ref $code, out altNum);
}
	:	{((GrammarAST)input.LT(1)).SetValue != null}? => setBlock		{HandleBlockSetBlock($code, $setBlock.code);}

	|	^(  BLOCK
			( ^(OPTIONS .*) )? // ignore
			( alt=alternative[$label] rew=rewrite								{HandleBlockAlternative($code, ref altNum, $alt.code, $rew.start, $rew.code);}
			)+
			EOB
		 )
	;
finally {HandleBlockFinally();}

setBlock returns [Template code]
	:	^(s=BLOCK .*)							{HandleSetBlock(out $code, $start, $s);}
	;

setAlternative
	:	^(ALT setElement+ EOA)
	;

exceptionGroup[Template ruleST]
	:	exceptionHandler[$ruleST]+ finallyClause[$ruleST]?
	|	finallyClause[$ruleST]
	;

exceptionHandler[Template ruleST]
	:	^('catch' ARG_ACTION ACTION)			{HandleExceptionHandler($ruleST, $ARG_ACTION, $ACTION);}
	;

finallyClause[Template ruleST]
	:	^('finally' ACTION)						{HandleFinallyClause($ruleST, $ACTION);}
	;

alternative[GrammarAST label] returns [Template code]
@init
{
HandleAlternativeInit(out $code, $start);
}
	:	^(	ALT
			(	e=element[$label,null]			{HandleAlternativeElement($code, $e.start, $e.code);}
			)+
			EOA
		)
	;

element[GrammarAST label, GrammarAST astSuffix] returns [Template code]
options { k=1; }
	:	^(ROOT e=element[$label,$ROOT])							{$code = $e.code;}
	|	^(BANG e=element[$label,$BANG])							{$code = $e.code;}
	|	^(n=NOT notElement[$n, $label, $astSuffix])				{$code = $notElement.code;}
	|	^(ASSIGN alabel=ID e=element[$alabel,$astSuffix])		{$code = $e.code;}
	|	^(PLUS_ASSIGN label2=ID e=element[$label2,$astSuffix])	{$code = $e.code;}
	|	^(CHAR_RANGE a=CHAR_LITERAL b=CHAR_LITERAL)				{HandleElementCharRange($label, out $code, $a, $b);}
	|	{$start.SetValue == null}? ebnf[$label]					{$code = $ebnf.code;}
	|	atom[null, $label, $astSuffix]							{$code = $atom.code;}
	|	tree_													{$code = $tree_.code;}
	|	element_action											{$code = $element_action.code;}
	|	(sp=SEMPRED | sp=GATED_SEMPRED)							{HandleElementSemanticPredicate(out $code, $sp);}
	|	SYN_SEMPRED // used only in lookahead; don't generate validating pred
	|	^(SYNPRED .*)
	|	^(BACKTRACK_SEMPRED .*)
	|	EPSILON
	;

element_action returns [Template code]
	:	action=ACTION			{HandleElementAction(out $code, $action, false);}
	|	action=FORCED_ACTION	{HandleElementAction(out $code, $action, true);}
	;

notElement[GrammarAST n, GrammarAST label, GrammarAST astSuffix] returns [Template code]
@init
{
	IIntSet elements = null;
}
	:	(	t=CHAR_LITERAL		{HandleNotElementCharLiteral(out elements, $t);}
		|	t=STRING_LITERAL	{HandleNotElementStringLiteral(out elements, $t);}
		|	t=TOKEN_REF			{HandleNotElementTokenReference(out elements, $t);}
		|	^(t=BLOCK .*)		{HandleNotElementBlock(out elements, $t);}
		)
								{HandleNotElementEnd($n, $label, $astSuffix, out $code, elements, $start);}
	;

ebnf[GrammarAST label] returns [Template code=null]
@init
{
	GrammarAST b = (GrammarAST)$start.GetChild(0);
	GrammarAST eob = (GrammarAST)b.LastChild; // loops will use EOB DFA
}
	:	(	blk=block["block", $start.LookaheadDFA, $label]										{$code = $blk.code;}
		|	^(OPTIONAL blk=block["optionalBlock", $start.LookaheadDFA, $label])					{$code = $blk.code;}
		|	^(CLOSURE blk=block["closureBlock", eob.LookaheadDFA, $label])						{$code = $blk.code;}
		|	^(POSITIVE_CLOSURE blk=block["positiveClosureBlock", eob.LookaheadDFA, $label])		{$code = $blk.code;}
		)
		{
			string description = grammar.GrammarTreeToString($start, false);
			description = generator.Target.GetTargetStringLiteralFromString(description);
			$code.SetAttribute("description", description);
		}
	;

tree_ returns [Template code]
@init
{
	rewriteTreeNestingLevel++;
	GrammarAST rootSuffix = null;
	if ( state.backtracking == 0 )
	{
		$code = templates.GetInstanceOf("tree");
		NFAState afterDOWN = (NFAState)$start.NFATreeDownState.GetTransition(0).Target;
		LookaheadSet s = grammar.Look(afterDOWN);
		if ( s.Member(Label.UP) ) {
			// nullable child list if we can see the UP as the next token
			// we need an "if ( input.LA(1)==Token.DOWN )" gate around
			// the child list.
			$code.SetAttribute("nullableChildList", "true");
		}
		$code.SetAttribute("enclosingTreeLevel", rewriteTreeNestingLevel-1);
		$code.SetAttribute("treeLevel", rewriteTreeNestingLevel);
		Rule r = grammar.GetRule(currentRuleName);
		if ( grammar.BuildAST && !r.HasRewrite(outerAltNum) ) {
			rootSuffix = new GrammarAST(ROOT,"ROOT");
		}
	}
}
	:	^(	TREE_BEGIN
			el=element[null,rootSuffix]
			{
				$code.SetAttribute("root.{el,line,pos}",
								  $el.code,
								  $el.start.Line,
								  $el.start.CharPositionInLine + 1
								  );
			}
			// push all the immediately-following actions out before children
			// so actions aren't guarded by the "if (input.LA(1)==Token.DOWN)"
			// guard in generated code.
			(	(element_action) =>
				act=element_action
				{
					$code.SetAttribute("actionsAfterRoot.{el,line,pos}",
									  $act.code,
									  $act.start.Line,
									  $act.start.CharPositionInLine + 1
									);
				}
			)*
			(	 el=element[null,null]
				 {
				 $code.SetAttribute("children.{el,line,pos}",
								  $el.code,
								  $el.start.Line,
								  $el.start.CharPositionInLine + 1
								  );
				 }
			)*
		)
	;
finally { rewriteTreeNestingLevel--; }

atom[GrammarAST scope, GrammarAST label, GrammarAST astSuffix] 
	returns [Template code]
@init
{
	string labelText;
	HandleAtomInit($start, $label, ref $astSuffix, out labelText);
}
	:   ^( r=RULE_REF (rarg=ARG_ACTION)? )		{HandleAtomRuleReference($scope, $label, $astSuffix, out $code, labelText, $r, $rarg);}
	|	^( t=TOKEN_REF (targ=ARG_ACTION)? )		{HandleAtomTokenReference($scope, $label, $astSuffix, out $code, labelText, $t, $targ);}
	|	c=CHAR_LITERAL							{HandleAtomCharLiteral($label, $astSuffix, out $code, labelText, $c);}
	|	s=STRING_LITERAL						{HandleAtomStringLiteral($label, $astSuffix, out $code, labelText, $s);}
	|	w=WILDCARD								{HandleAtomWildcard($astSuffix, out $code, labelText, $w);}
	|	^(DOT ID a=atom[$ID, $label, $astSuffix]) // scope override on rule or token
												{ $code = $a.code; }
	|	set[$label, $astSuffix]					{ $code = $set.code; }
	;

ast_suffix
	:	ROOT
	|	BANG
	;

set[GrammarAST label, GrammarAST astSuffix] returns [Template code]
	:	// only care that it's a BLOCK with setValue!=null
		^(s=BLOCK .*)							{HandleSet($label, $astSuffix, out $code, $s);}
	;

setElement
	:	CHAR_LITERAL
	|	TOKEN_REF
	|	STRING_LITERAL
	|	^(CHAR_RANGE CHAR_LITERAL CHAR_LITERAL)
	;

// REWRITE stuff

rewrite returns [Template code]
@init
{
HandleRewriteInit(out $code, $start);
}
	:	^(	REWRITES
			(
																		{HandleRewriteBeforeRewrite();}
				^( r=REWRITE (pred=SEMPRED)? alt=rewrite_alternative)	{HandleRewriteAfterRewrite($code, $r, ref $pred, $alt.code);}
			)*
		)
	|
	;

rewrite_block[string blockTemplateName] returns [Template code]
@init
{
	rewriteBlockNestingLevel++;
	Template save_currentBlockST = currentBlockST;
	if ( state.backtracking == 0 )
	{
		$code = templates.GetInstanceOf(blockTemplateName);
		currentBlockST = $code;
		$code.SetAttribute("rewriteBlockLevel", rewriteBlockNestingLevel);
	}
}
	:	^(	BLOCK
			{
				currentBlockST.SetAttribute("referencedElementsDeep",
					GetTokenTypesAsTargetLabels($BLOCK.rewriteRefsDeep));
				currentBlockST.SetAttribute("referencedElements",
					GetTokenTypesAsTargetLabels($BLOCK.rewriteRefsShallow));
			}
			alt=rewrite_alternative
			EOB
		)
		{
			$code.SetAttribute("alt", $alt.code);
		}
	;
finally { rewriteBlockNestingLevel--; currentBlockST = save_currentBlockST; }

rewrite_alternative returns [Template code]
	:	{generator.Grammar.BuildAST}?
		^(	a=ALT {$code=templates.GetInstanceOf("rewriteElementList");}
			(	(
					el=rewrite_element
					{$code.SetAttribute("elements.{el,line,pos}",
										$el.code,
										$el.start.Line,
										$el.start.CharPositionInLine + 1
										);
					}
				)+
			|	EPSILON
				{$code.SetAttribute("elements.{el,line,pos}",
								   templates.GetInstanceOf("rewriteEmptyAlt"),
								   $a.Line,
								   $a.CharPositionInLine + 1
								   );
				}
			)
			EOA
		 )

	|	{generator.Grammar.BuildTemplate}? rewrite_template
		{ $code = $rewrite_template.code; }

	|	// reproduce same input (only AST at moment)
		ETC
	;

rewrite_element returns [Template code]
@init
{
	IIntSet elements=null;
	GrammarAST ast = null;
}
	:	rewrite_atom[false]		{ $code = $rewrite_atom.code; }
	|	rewrite_ebnf			{ $code = $rewrite_ebnf.code; }
	|	rewrite_tree			{ $code = $rewrite_tree.code; }
	;

rewrite_ebnf returns [Template code]
	:	(	^(OPTIONAL b=rewrite_block["rewriteOptionalBlock"])
		|	^(CLOSURE b=rewrite_block["rewriteClosureBlock"])
		|	^(POSITIVE_CLOSURE b=rewrite_block["rewritePositiveClosureBlock"])
		)
		{HandleRewriteEbnf(out $code, $start, $b.code);}
	;

rewrite_tree returns [Template code]
@init
{
	rewriteTreeNestingLevel++;
	if ( state.backtracking == 0 )
	{
		$code = templates.GetInstanceOf("rewriteTree");
		$code.SetAttribute("treeLevel", rewriteTreeNestingLevel);
		$code.SetAttribute("enclosingTreeLevel", rewriteTreeNestingLevel-1);
	}
}
	:	^(	TREE_BEGIN
			r=rewrite_atom[true]
			{
				$code.SetAttribute("root.{el,line,pos}",
								   $r.code,
								   $r.start.Line,
								   $r.start.CharPositionInLine + 1
								  );
			}
			(
			  el=rewrite_element
			  {
				$code.SetAttribute("children.{el,line,pos}",
									$el.code,
									$el.start.Line,
									$el.start.CharPositionInLine + 1
									);
			  }
			)*
		)
		{
			string description = grammar.GrammarTreeToString($start, false);
			description = generator.Target.GetTargetStringLiteralFromString(description);
			$code.SetAttribute("description", description);
		}
	;
finally { rewriteTreeNestingLevel--; }

rewrite_atom[bool isRoot] returns [Template code]
	:   r=RULE_REF								{HandleRewriteAtomRuleReference(isRoot, out $code, $r);}
	|	(	^(term=TOKEN_REF (arg=ARG_ACTION)?)
		|	term=CHAR_LITERAL
		|	term=STRING_LITERAL
		)										{HandleRewriteAtomTokenReference($isRoot, out $code, $start, $term, $arg);}
	|	LABEL									{HandleRewriteAtomLabel($isRoot, out $code, $LABEL);}
	|	ACTION									{HandleRewriteAtomAction($isRoot, out $code, $ACTION);}
	;

public
rewrite_template returns [Template code]
	:	^(ALT EPSILON EOA)					{HandleRewriteTemplateAlt(out $code);}
	|	^(	TEMPLATE (id=ID|ind=ACTION)		{HandleRewriteTemplateAfterId(out $code, $id, $ind);}
			^(	ARGLIST
				(	^(ARG arg=ID a=ACTION)	{HandleRewriteTemplateArg($code, $arg, $a);}
				)*
			)
			(	DOUBLE_QUOTE_STRING_LITERAL	{HandleRewriteTemplateLiteral($code, $DOUBLE_QUOTE_STRING_LITERAL, 1);}
			|	DOUBLE_ANGLE_STRING_LITERAL	{HandleRewriteTemplateLiteral($code, $DOUBLE_ANGLE_STRING_LITERAL, 2);}
			)?
		)

	|	act=ACTION							{HandleRewriteTemplateAction(out $code, $act);}
	;
