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

 Grammar conversion to ANTLR v3 and C#:
 Copyright (c) 2008 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.
*/

/** [Warning: TJP says that this is probably out of date as of 11/19/2005,
 *   but since it's probably still useful, I'll leave in.  Don't have energy
 *   to update at the moment.]
 *
 *  Compute the token types for all literals and rules etc..  There are
 *  a few different cases to consider for grammar types and a few situations
 *  within.
 *
 *  CASE 1 : pure parser grammar
 *	a) Any reference to a token gets a token type.
 *  b) The tokens section may alias a token name to a string or char
 *
 *  CASE 2 : pure lexer grammar
 *  a) Import token vocabulary if available. Set token types for any new tokens
 *     to values above last imported token type
 *  b) token rule definitions get token types if not already defined
 *  c) literals do NOT get token types
 *
 *  CASE 3 : merged parser / lexer grammar
 *	a) Any char or string literal gets a token type in a parser rule
 *  b) Any reference to a token gets a token type if not referencing
 *     a fragment lexer rule
 *  c) The tokens section may alias a token name to a string or char
 *     which must add a rule to the lexer
 *  d) token rule definitions get token types if not already defined
 *  e) token rule definitions may also alias a token name to a literal.
 *     E.g., Rule 'FOR : "for";' will alias FOR to "for" in the sense that
 *     references to either in the parser grammar will yield the token type
 *
 *  What this pass does:
 *
 *  0. Collects basic info about the grammar like grammar name and type;
 *     Oh, I have go get the options in case they affect the token types.
 *     E.g., tokenVocab option.
 *     Imports any token vocab name/type pairs into a local hashtable.
 *  1. Finds a list of all literals and token names.
 *  2. Finds a list of all token name rule definitions;
 *     no token rules implies pure parser.
 *  3. Finds a list of all simple token rule defs of form "&lt;NAME&gt; : &lt;literal&gt;;"
 *     and aliases them.
 *  4. Walks token names table and assign types to any unassigned
 *  5. Walks aliases and assign types to referenced literals
 *  6. Walks literals, assigning types if untyped
 *  4. Informs the Grammar object of the type definitions such as:
 *     g.defineToken(&lt;charliteral&gt;, ttype);
 *     g.defineToken(&lt;stringliteral&gt;, ttype);
 *     g.defineToken(&lt;tokenID&gt;, ttype);
 *     where some of the ttype values will be the same for aliases tokens.
 */
tree grammar AssignTokenTypesWalker;

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

@namespace{Antlr3.Grammars}

@header
{
using Grammar = Antlr3.Tool.Grammar;
using GrammarAST = Antlr3.Tool.GrammarAST;
}

public
grammar_[Grammar g]
@init
{
	if ( state.backtracking == 0 )
		Init($g);
}
	:	(	^(LEXER_GRAMMAR    grammarSpec)
		|	^(PARSER_GRAMMAR   grammarSpec)
		|	^(TREE_GRAMMAR     grammarSpec)
		|	^(COMBINED_GRAMMAR grammarSpec)
		)
	;

grammarSpec
	:	ID
		DOC_COMMENT?
		optionsSpec?
		delegateGrammars?
		tokensSpec?
		attrScope*
		( ^(AMPERSAND .*) )* // skip actions
		rules
	;

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

optionsSpec returns [IDictionary<object, object> opts = new Dictionary<object, object>()]
	:	^(OPTIONS (option[$opts])*)
	;

option[IDictionary<object, object> opts]
	:	^(ASSIGN ID optionValue)
		{
			string key = $ID.text;
			$opts[key] = $optionValue.value;
			// check for grammar-level option to import vocabulary
			if ( currentRuleName==null && key.Equals("tokenVocab") )
			{
				grammar.ImportTokenVocabulary($ID,(string)$optionValue.value);
			}
		}
	;

optionValue returns [object value=null]
@init
{
	if (state.backtracking == 0)
		$value = $start.Text;
}
	:	ID
	|	STRING_LITERAL
	|	CHAR_LITERAL
	|	INT				{$value = int.Parse($INT.text);}
//	|	cs=charSet		{$value = $cs;} // return set AST in this case
	;

charSet
	:	^(CHARSET charSetElement)
	;

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

delegateGrammars
	:	^(	'import'
			(	^(ASSIGN ID ID)
			|	ID
			)+
		)
	;

tokensSpec
	:	^(TOKENS tokenSpec*)
	;

tokenSpec
	:	t=TOKEN_REF					{TrackToken($t);}
	|	^(	ASSIGN
			t=TOKEN_REF				{TrackToken($t);}
			(	s=STRING_LITERAL	{TrackString($s); Alias($t, $s);}
			|	s=CHAR_LITERAL		{TrackString($s); Alias($t, $s);}
			)
		)
	;

rules
	:	rule+
	;

rule
	:	^(RULE ruleBody)
	|	^(PREC_RULE ruleBody)
	;

ruleBody
	:	id=ID {currentRuleName=$id.text;}
		modifier?
		^(ARG ARG_ACTION?)
		^(RET ARG_ACTION?)
		throwsSpec?
		optionsSpec?
		ruleScopeSpec?
		( ^(AMPERSAND .*) )*
		block
		exceptionGroup?
		EOR
		{TrackTokenRule($id,$modifier.start,$block.start);}
	;

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

throwsSpec
	:	^('throws' ID+)
	;

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

block
	:	^(	BLOCK
			optionsSpec?
			(alternative rewrite)+
			EOB
		)
	;

alternative
	:	^(ALT element+ EOA)
	;

exceptionGroup
	:	exceptionHandler+ finallyClause?
	|	finallyClause
	;

exceptionHandler
	:	^('catch' ARG_ACTION ACTION)
	;

finallyClause
	:	^('finally' ACTION)
	;

rewrite
	:	^(REWRITES ( ^(REWRITE .*) )* )
	|
	;

element
	:	^(ROOT element)
	|	^(BANG element)
	|	atom
	|	^(NOT element)
	|	^(RANGE atom atom)
	|	^(CHAR_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?)
	|	^(t=TOKEN_REF ARG_ACTION?)	{TrackToken($t);}
	|	t=CHAR_LITERAL				{TrackString($t);}
	|	t=STRING_LITERAL			{TrackString($t);}
	|	WILDCARD
	|	^(DOT ID atom) // scope override on rule
	;

ast_suffix
	:	ROOT
	|	BANG
	;
