/*
 [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.
*/

/** Read in an ANTLR grammar and build an AST.  Try not to do
 *  any actions, just build the tree.
 *
 *  The phases are:
 *
 *		antlr.g (this file)
 *		assign.types.g
 *		define.g
 *		buildnfa.g
 *		antlr.print.g (optional)
 *		codegen.g
 *
 *  Terence Parr
 *  University of San Francisco
 *  2005
 */

grammar ANTLR;

options
{
	language=CSharp3;
	output=AST;
	ASTLabelType=GrammarAST;
}

tokens
{
	//OPTIONS='options';
	//TOKENS='tokens';
	LEXER='lexer';
	PARSER='parser';
	CATCH='catch';
	FINALLY='finally';
	GRAMMAR='grammar';
	PRIVATE='private';
	PROTECTED='protected';
	PUBLIC='public';
	RETURNS='returns';
	THROWS='throws';
	TREE='tree';

	RULE;
	PREC_RULE;
	RECURSIVE_RULE_REF; // flip recursive RULE_REF to RECURSIVE_RULE_REF in prec rules
	BLOCK;
	OPTIONAL;
	CLOSURE;
	POSITIVE_CLOSURE;
	SYNPRED;
	RANGE;
	CHAR_RANGE;
	EPSILON;
	ALT;
	EOR;
	EOB;
	EOA; // end of alt
	ID;
	ARG;
	ARGLIST;
	RET;
	LEXER_GRAMMAR;
	PARSER_GRAMMAR;
	TREE_GRAMMAR;
	COMBINED_GRAMMAR;
	INITACTION;
	FORCED_ACTION; // {{...}} always exec even during syn preds
	LABEL; // $x used in rewrite rules
	TEMPLATE;
	SCOPE='scope';
	IMPORT='import';
	GATED_SEMPRED; // {p}? =>
	SYN_SEMPRED; // (...) =>   it's a manually-specified synpred converted to sempred
	BACKTRACK_SEMPRED; // auto backtracking mode syn pred converted to sempred
	FRAGMENT='fragment';
	DOT;
	REWRITES;
}

@lexer::header {
using ErrorManager = Antlr3.Tool.ErrorManager;
using Grammar = Antlr3.Tool.Grammar;
using StringBuffer = System.Text.StringBuilder;
}

@parser::header {
using ErrorManager = Antlr3.Tool.ErrorManager;
using Grammar = Antlr3.Tool.Grammar;
using GrammarAST = Antlr3.Tool.GrammarAST;
using GrammarType = Antlr3.Tool.GrammarType;
using IntSet = Antlr3.Misc.IIntSet;
using Rule = Antlr3.Tool.Rule;
using RuleType = Antlr3.Tool.RuleType;
using StringBuffer = System.Text.StringBuilder;
}

@lexer::namespace{Antlr3.Grammars}
@parser::namespace{Antlr3.Grammars}

public
grammar_![Grammar g]
@init
{
	this.Grammar = g;
	IDictionary<string, object> opts;
}
@after
{
	Cleanup( $tree );
}
	:	//hdr:headerSpec
		ACTION?
		DOC_COMMENT?
		grammarType id {Grammar.SetName($id.text);} SEMI
		(	optionsSpec {opts = $optionsSpec.opts; Grammar.SetOptions(opts, $optionsSpec.start);}
		)?
		delegateGrammars?
		tokensSpec?
		attrScopes
		actions?
		rules
		EOF
		-> ^(grammarType id DOC_COMMENT? optionsSpec? delegateGrammars? tokensSpec? attrScopes? actions? rules)
	;

grammarType
	:	'lexer'!  'grammar'<LEXER_GRAMMAR> {GrammarType=GrammarType.Lexer; Grammar.type = GrammarType.Lexer;}       // pure lexer
	|	'parser'! 'grammar'<PARSER_GRAMMAR> {GrammarType=GrammarType.Parser; Grammar.type = GrammarType.Parser;}     // pure parser
	|	'tree'!   'grammar'<TREE_GRAMMAR> {GrammarType=GrammarType.TreeParser; Grammar.type = GrammarType.TreeParser;}  // a tree parser
	|			  'grammar'<COMBINED_GRAMMAR> {GrammarType=GrammarType.Combined; Grammar.type = GrammarType.Combined;} // merged parser/lexer

	;

actions
	:	action+
	;

/** Match stuff like @parser::members {int i;} */
action
	:	AMPERSAND^ (actionScopeName COLON! COLON!)? id ACTION
	;

/** Sometimes the scope names will collide with keywords; allow them as
 *  ids for action scopes.
 */
actionScopeName
	:	id
	|	'lexer'<ID>
	|	'parser'<ID>
	;

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

option[IDictionary<string, object> opts]
	:	id ASSIGN^ optionValue
		{
			$opts[$id.text] = $optionValue.value;
		}
	;

optionValue returns [object value]
	:	id			 {$value = $id.text;}
	|	t=STRING_LITERAL {string vs = $t.text;
						  // remove the quotes:
						  $value=vs.Substring(1,vs.Length-2);}
	|	t=CHAR_LITERAL   {string vs = $t.text;
						  // remove the quotes:
						  $value=vs.Substring(1,vs.Length-2);}
	|	t=INT            {$value = int.Parse($t.text);}
	|	t=STAR<STRING_LITERAL>			 {$value = "*";} // used for k=*
//	|	cs:charSet       {value = #cs;} // return set AST in this case
	;

delegateGrammars
	:	'import'^ delegateGrammar (COMMA! delegateGrammar)* SEMI!
	;

delegateGrammar
	:	(lab=id ASSIGN^)? g=id {HandleDelegateGrammar($g.start, $lab.text);}
	;

tokensSpec
	:	TOKENS^
			tokenSpec*
		RCURLY!
	;

tokenSpec
	:	TOKEN_REF (ASSIGN^ (STRING_LITERAL|CHAR_LITERAL))? SEMI!
	;

attrScopes
	:	attrScope*
	;

attrScope
	:	'scope'^ id ruleActions? ACTION
	;

rules
	:	rule+
	;

public
rule
@init
{
	GrammarAST eob=null;
	CommonToken start = (CommonToken)LT(1);
	int startLine = LT(1).Line;
}
	:
	(	(	d=DOC_COMMENT
		)?
		(	mod='protected'
		|	mod='public'
		|	mod='private'
		|	mod='fragment'
		)?
		ruleName=id
		{
			currentRuleName=$ruleName.text;
			if ( GrammarType==GrammarType.Lexer && $mod.text!="fragment" )
				Grammar.lexerRuleNamesInCombined.Add(currentRuleName);
		}
		BANG?
		( aa=ARG_ACTION )?
		( 'returns' rt=ARG_ACTION  )?
		throwsSpec?
		optionsSpec?
		scopes=ruleScopeSpec
		ruleActions?
		COLON
		ruleAltList[$optionsSpec.opts]
		SEMI
		( ex=exceptionGroup )?
		->	^(	RULE[$ruleName.start, "rule"]
				$ruleName
				// the modifier will be 0 or one of the modifiers:
				$mod?
				^(ARG["ARG"] $aa?)
				^(RET["RET"] $rt?)
				throwsSpec?
				optionsSpec?
				$scopes
				ruleActions?
				ruleAltList
				$ex?
				EOR[$SEMI,"<end-of-rule>"])
	)
	{
		$tree.SetTreeEnclosingRuleNameDeeply(currentRuleName);
		((GrammarAST)$tree.GetChild(0)).BlockOptions = $optionsSpec.opts;
	}
	;

ruleActions
	:	ruleAction+
	;

/** Match stuff like @init {int i;} */
ruleAction
	:	AMPERSAND^ id ACTION
	;

throwsSpec
	:	'throws'^ id (COMMA! id)*
	;

ruleScopeSpec
	:	( 'scope' ruleActions? ACTION )?
		( 'scope' idList SEMI )*
		-> ^(SCOPE[$start,"scope"] ruleActions? ACTION? idList*)
	;

ruleAltList[IDictionary<string, object> opts]
@init
{
	GrammarAST blkRoot = null;
	GrammarAST save = currentBlockAST;
}
	:	( -> BLOCK[input.LT(-1),"BLOCK"] )
		{
			blkRoot = (GrammarAST)$tree.GetChild(0);
			blkRoot.BlockOptions = $opts;
			currentBlockAST = blkRoot;
		}
		(	a1=alternative r1=rewrite
			{if (LA(1)==OR||(LA(2)==QUESTION||LA(2)==PLUS||LA(2)==STAR)) PrefixWithSynPred($a1.tree);}
			-> $a1 $r1?
		)
		(	(	OR a2=alternative r2=rewrite
				{if (LA(1)==OR||(LA(2)==QUESTION||LA(2)==PLUS||LA(2)==STAR)) PrefixWithSynPred($a2.tree);}
				-> $ruleAltList $a2 $r2?
			)+
		|
		)
		-> ^({blkRoot} $ruleAltList EOB["<end-of-block>"])
	;
finally { currentBlockAST = save; }

/** Build #(BLOCK ( #(ALT ...) EOB )+ ) */
block
@init
{
	GrammarAST save = currentBlockAST;
}
	:	(	lp=LPAREN
			-> BLOCK[$lp,"BLOCK"]
		)
		{currentBlockAST = (GrammarAST)$tree.GetChild(0);}
		(
			// 2nd alt and optional branch ambig due to
			// linear approx LL(2) issue.  COLON ACTION
			// matched correctly in 2nd alt.
			(optionsSpec {((GrammarAST)$tree.GetChild(0)).SetOptions(Grammar,$optionsSpec.opts);})?
			( ruleActions )?
			COLON
		|	ACTION COLON
		)?

		a=alternative r=rewrite
		{
			stream_alternative.Add( $r.tree );
			if ( LA(1)==OR || (LA(2)==QUESTION||LA(2)==PLUS||LA(2)==STAR) )
				PrefixWithSynPred($a.tree);
		}
		(	OR a=alternative r=rewrite
			{
				stream_alternative.Add( $r.tree );
				if (LA(1)==OR||(LA(2)==QUESTION||LA(2)==PLUS||LA(2)==STAR))
					PrefixWithSynPred($a.tree);
			}
		)*

		rp=RPAREN
		-> ^($block optionsSpec? ruleActions? ACTION? alternative+ EOB[$rp,"<end-of-block>"])
	;
finally { currentBlockAST = save; }

// ALT and EOA have indexes tracking start/stop of entire alt
alternative
	:	element+
		-> ^(ALT[$start,"ALT"] element+ EOA[input.LT(-1),"<end-of-alt>"])
	|	// epsilon alt
		-> ^(ALT[$start,"ALT"] EPSILON[input.LT(-1),"epsilon"] EOA[input.LT(-1),"<end-of-alt>"])
	;

exceptionGroup
	:	exceptionHandler+ finallyClause?
	|	finallyClause
	;

exceptionHandler
	:	'catch'^ ARG_ACTION ACTION
	;

finallyClause
	:	'finally'^ ACTION
	;

element
	:	elementNoOptionSpec
	;

elementNoOptionSpec
@init
{
	IntSet elements=null;
}
	:	(	id (ASSIGN^|PLUS_ASSIGN^)
			(	atom (ebnfSuffix[root_0,false]! {root_0 = $ebnfSuffix.tree;})?
			|	ebnf
			)
		)
	|	atom
		(	ebnfSuffix[$atom.tree,false]! {root_0 = $ebnfSuffix.tree;}
		)?
	|	ebnf
	|	FORCED_ACTION
	|	ACTION
	|	p=SEMPRED ( IMPLIES! {$p.Type = GATED_SEMPRED;} )?
		{
		Grammar.blocksWithSemPreds.Add(currentBlockAST);
		}
	|	t3=tree_
	;

atom
	:	range (ROOT^|BANG^)?
	|	(
			// grammar.rule but ensure no spaces. "A . B" is not a qualified ref
			// We do here rather than lexer so we can build a tree
			({LT(1).CharPositionInLine+LT(1).Text.Length==LT(2).CharPositionInLine&&
			 LT(2).CharPositionInLine+1==LT(3).CharPositionInLine}? id WILDCARD (terminal|ruleref)) =>
			id WILDCARD^ (terminal|ruleref) {$WILDCARD.Type = DOT;}
		|	terminal
		|	ruleref
		)
	|	notSet (ROOT^|BANG^)?
	;

ruleref
	:	RULE_REF^ ARG_ACTION? (ROOT^|BANG^)?
	;

notSet
	:	NOT^
		(	notTerminal
		|	block
		)
	;

treeRoot
@init{atTreeRoot=true;}
@after{atTreeRoot=false;}
	:	id (ASSIGN^|PLUS_ASSIGN^) (atom|block)
	|	atom
	|	block
	;

tree_
	:	TREE_BEGIN^
		treeRoot element+
		RPAREN!
	;

/** matches ENBF blocks (and sets via block rule) */
ebnf
	:	block
		(	QUESTION
			-> ^(OPTIONAL[$start,"?"] block)
		|	STAR
			-> ^(CLOSURE[$start,"*"] block)
		|	PLUS
			-> ^(POSITIVE_CLOSURE[$start,"+"] block)
		|	IMPLIES // syntactic predicate
			// ignore for lexer rules in combined
			-> {GrammarType == GrammarType.Combined && Rule.GetRuleType(currentRuleName) == RuleType.Lexer}? ^(SYNPRED[$start,"=>"] block)
			// create manually specified (...)=> predicate; convert to sempred
			-> {CreateSynSemPredFromBlock($block.tree, SYN_SEMPRED)}
		|	ROOT
			-> ^(ROOT block)
		|	BANG
			-> ^(BANG block)
		|
			-> block
		)
	;

range!
	:	{Rule.GetRuleType(currentRuleName) == RuleType.Lexer}?=>
	 	CHAR_LITERAL RANGE<type=CHAR_RANGE;text="..">^ CHAR_LITERAL
	|	// range elsewhere is an error
		(	// have to generate something for surrounding code, just return first token
			TOKEN_REF r=RANGE! TOKEN_REF!
		|	STRING_LITERAL r=RANGE! STRING_LITERAL!
		|	CHAR_LITERAL r=RANGE! CHAR_LITERAL!
		)
		{
		ErrorManager.SyntaxError(
			ErrorManager.MSG_RANGE_OP_ILLEGAL,Grammar,$r,null,null);
		}
	;

terminal
	:	CHAR_LITERAL^ ( elementOptions[$CHAR_LITERAL.tree]! )? (ROOT^|BANG^)?

	|	TOKEN_REF^
		( elementOptions[$TOKEN_REF.tree]! )?
		( ARG_ACTION )? // Args are only valid for lexer rules
		(ROOT^|BANG^)?

	|	STRING_LITERAL^ ( elementOptions[$STRING_LITERAL.tree]! )? (ROOT^|BANG^)?

	|	WILDCARD (ROOT^|BANG^)?
		{
			if ( atTreeRoot )
			{
				ErrorManager.SyntaxError(
					ErrorManager.MSG_WILDCARD_AS_ROOT,Grammar,$WILDCARD,null,null);
			}
		}
	;

elementOptions[GrammarAST terminalAST]
	:	OPEN_ELEMENT_OPTION^ defaultNodeOption[terminalAST] CLOSE_ELEMENT_OPTION!
	|	OPEN_ELEMENT_OPTION^ elementOption[terminalAST] (SEMI! elementOption[terminalAST])* CLOSE_ELEMENT_OPTION!
	;

defaultNodeOption[GrammarAST terminalAST]
	:	elementOptionId
		{terminalAST.SetTerminalOption(Grammar,Grammar.defaultTokenOption,$elementOptionId.qid);}
	;

elementOption[GrammarAST terminalAST]
	:	id ASSIGN^
		(	elementOptionId
			{terminalAST.SetTerminalOption(Grammar,$id.text,$elementOptionId.qid);}
		|	(t=STRING_LITERAL|t=DOUBLE_QUOTE_STRING_LITERAL|t=DOUBLE_ANGLE_STRING_LITERAL)
			{terminalAST.SetTerminalOption(Grammar,$id.text,$t.text);}
		)
	;

elementOptionId returns [string qid]
@init{StringBuffer buf = new StringBuffer();}
	:	i=id {buf.Append($i.text);} ('.' i=id {buf.Append("." + $i.text);})*
		{$qid = buf.ToString();}
	;

ebnfSuffix[GrammarAST elemAST, bool inRewrite]
@init
{
GrammarAST blkRoot=null;
GrammarAST alt=null;
GrammarAST save = currentBlockAST;
}
@after
{
currentBlockAST = save;
}
	:	(	-> BLOCK[$elemAST.Token, "BLOCK"]
		)
		{ blkRoot = (GrammarAST)$tree.GetChild(0); currentBlockAST = blkRoot; }
		(	// create alt
			-> ^(ALT[$elemAST.Token, "ALT"] {$elemAST} EOA["<end-of-alt>"])
		)
		{
			alt = (GrammarAST)$tree.GetChild(0);
			if ( !inRewrite )
				PrefixWithSynPred(alt);
		}
		(	QUESTION
			-> OPTIONAL[$elemAST.Token,"?"]
		|	STAR
			-> CLOSURE[$elemAST.Token,"*"]
		|	PLUS
			-> POSITIVE_CLOSURE[$elemAST.Token,"+"]
		)
		-> ^($ebnfSuffix ^({blkRoot} {alt} EOB[$elemAST.Token, "<end-of-block>"]))
	;

notTerminal
	:	CHAR_LITERAL
	|	TOKEN_REF
	|	STRING_LITERAL
	;

idList
	:	id (COMMA! id)*
	;

id
	:	TOKEN_REF<ID>
	|	RULE_REF<ID>
	;

// R E W R I T E  S Y N T A X

rewrite
	:	rewrite_with_sempred*
		REWRITE rewrite_alternative
		-> ^(REWRITES rewrite_with_sempred* ^(REWRITE rewrite_alternative))
	|
	;

rewrite_with_sempred
	:	REWRITE^ SEMPRED rewrite_alternative
	;

rewrite_block
	:	LPAREN<type=BLOCK;text="BLOCK">^
		rewrite_alternative
		RPAREN<type=EOB;text="<end-of-block>">
	;

rewrite_alternative
options{k=1;}
	:	{Grammar.BuildTemplate}? => rewrite_template

	|	{Grammar.BuildAST}? => ( rewrite_element )+
		-> {!stream_rewrite_element.HasNext}? ^(ALT[LT(1),"ALT"] EPSILON["epsilon"] EOA["<end-of-alt>"])
		-> ^(ALT[LT(1),"ALT"] rewrite_element+ EOA["<end-of-alt>"])

	|
		-> ^(ALT[LT(1),"ALT"] EPSILON["epsilon"] EOA["<end-of-alt>"])
	|	{Grammar.BuildAST}? ETC
	;

rewrite_element
	:	(	rewrite_atom
			-> rewrite_atom
		)
		(	ebnfSuffix[$rewrite_atom.tree,true]
			-> ebnfSuffix
		)?
	|	rewrite_ebnf
	|	(	rewrite_tree
			-> rewrite_tree
		)
		(	ebnfSuffix[$rewrite_tree.tree,true]
			-> ebnfSuffix
		)?
	;

rewrite_atom
	:	TOKEN_REF^ elementOptions[$TOKEN_REF.tree]!? ARG_ACTION? // for imaginary nodes
	|	RULE_REF
	|	CHAR_LITERAL elementOptions[$CHAR_LITERAL.tree]!?
	|	STRING_LITERAL elementOptions[$STRING_LITERAL.tree]!?
	|	DOLLAR! label // reference to a label in a rewrite rule
	|	ACTION
	;

label
	:	TOKEN_REF<LABEL>
	|	RULE_REF<LABEL>
	;

rewrite_ebnf
	:	b=rewrite_block
		(	QUESTION
			-> ^(OPTIONAL[$b.start,"?"] $b)
		|	STAR
			-> ^(CLOSURE[$b.start,"*"] $b)
		|	PLUS
			-> ^(POSITIVE_CLOSURE[$b.start,"+"] $b)
		)
	;

rewrite_tree
	:	TREE_BEGIN^
			rewrite_atom rewrite_element*
		RPAREN!
	;

/** Build a tree for a template rewrite:
	  ^(TEMPLATE (ID|ACTION) ^(ARGLIST ^(ARG ID ACTION) ...) )
	where ARGLIST is always there even if no args exist.
	ID can be "template" keyword.  If first child is ACTION then it's
	an indirect template ref

	-> foo(a={...}, b={...})
	-> ({string-e})(a={...}, b={...})  // e evaluates to template name
	-> {%{$ID.text}} // create literal template from string (done in ActionTranslator)
	-> {st-expr} // st-expr evaluates to ST
 */
public
rewrite_template
options{k=1;}
	:	// -> template(a={...},...) "..."
		{LT(1).Text.Equals("template")}? => // inline
		(	rewrite_template_head
			-> rewrite_template_head
		)
		( st=DOUBLE_QUOTE_STRING_LITERAL | st=DOUBLE_ANGLE_STRING_LITERAL )
		{ adaptor.AddChild( $tree.GetChild(0), adaptor.Create($st) ); }

	|	// -> foo(a={...}, ...)
		rewrite_template_head

	|	// -> ({expr})(a={...}, ...)
		rewrite_indirect_template_head

	|	// -> {...}
		ACTION
	;

/** -> foo(a={...}, ...) */
rewrite_template_head
	:	id LPAREN<type=TEMPLATE;text="TEMPLATE">^
		rewrite_template_args
		RPAREN!
	;

/** -> ({expr})(a={...}, ...) */
rewrite_indirect_template_head
	:	LPAREN<type=TEMPLATE;text="TEMPLATE">^
		ACTION
		RPAREN!
		LPAREN! rewrite_template_args RPAREN!
	;

rewrite_template_args
	:	rewrite_template_arg (COMMA rewrite_template_arg)*
		-> ^(ARGLIST["ARGLIST"] rewrite_template_arg+)
	|
		-> ARGLIST["ARGLIST"]
	;

rewrite_template_arg
	:	id ASSIGN<type=ARG;text="ARG">^ ACTION
	;

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// L E X E R

// get rid of warnings:
fragment STRING_LITERAL : ;
fragment FORCED_ACTION : ;
fragment DOC_COMMENT : ;
fragment SEMPRED : ;

WS
	:	(	' '
		|	'\t'
		|	('\r')? '\n'
		)
		{ $channel = Hidden; }
	;

COMMENT
	:	( SL_COMMENT | ML_COMMENT[ref $type] )
		{
			if ( $type != DOC_COMMENT )
				$channel = Hidden;
		}
	;

fragment
SL_COMMENT
	:	'//'
		(	(' $ANTLR') => ' $ANTLR ' SRC (('\r')? '\n')? // src directive
		|	~('\r'|'\n')* (('\r')? '\n')?
		)
	;

fragment
ML_COMMENT[ref int type]
	:	'/*'
		{type = (input.LA(1) == '*' && input.LA(2) != '/') ? DOC_COMMENT : ML_COMMENT;}
		.*
		'*/'
	;

OPEN_ELEMENT_OPTION
	:	'<'
	;

CLOSE_ELEMENT_OPTION
	:	'>'
	;

AMPERSAND : '@';

COMMA : ',';

QUESTION :	'?' ;

TREE_BEGIN : '^(' ;

LPAREN:	'(' ;

RPAREN:	')' ;

COLON :	':' ;

STAR:	'*' ;

PLUS:	'+' ;

ASSIGN : '=' ;

PLUS_ASSIGN : '+=' ;

IMPLIES : '=>' ;

REWRITE : '->' ;

SEMI:	';' ;

ROOT : '^' {hasASTOperator=true;} ;

BANG : '!' {hasASTOperator=true;} ;

OR	:	'|' ;

WILDCARD : '.' ;

ETC : '...' ;

RANGE : '..' ;

NOT :	'~' ;

RCURLY:	'}'	;

DOLLAR : '$' ;

STRAY_BRACKET
	:	']'
	;

CHAR_LITERAL
	:	'\''
		(	ESC
		|	~('\\'|'\'')
		)*
		'\''
		{
			StringBuffer s = Grammar.GetUnescapedStringFromGrammarStringLiteral($text);
			if ( s.Length > 1 )
			{
				$type = STRING_LITERAL;
			}
		}
	;

DOUBLE_QUOTE_STRING_LITERAL
@init
{
	System.Text.StringBuilder builder = new System.Text.StringBuilder();
}
	:	'"'							{builder.Append('"');}
		(	('\\\"') => '\\' '"'	{builder.Append('"');}
		|	'\\' c=~'"'				{builder.Append("\\" + (char)$c);}
		|	c=~('\\'|'"')			{builder.Append((char)$c);}
		)*
		'"'							{builder.Append('"');}
		{
			Text = builder.ToString();
		}
	;

DOUBLE_ANGLE_STRING_LITERAL
	:	'<<' .* '>>'
	;

fragment
ESC
	:	'\\'
		(	// due to the way ESC is used, we don't need to handle the following character in different ways
			/*'n'
		|	'r'
		|	't'
		|	'b'
		|	'f'
		|	'"'
		|	'\''
		|	'\\'
		|	'>'
		|	'u' XDIGIT XDIGIT XDIGIT XDIGIT
		|*/	. // unknown, leave as it is
		)
	;

fragment
DIGIT
	:	'0'..'9'
	;

fragment
XDIGIT
	:	'0' .. '9'
	|	'a' .. 'f'
	|	'A' .. 'F'
	;

INT
	:	('0'..'9')+
	;

ARG_ACTION
@init
{
	string text_ = string.Empty;
}
	:	'['
		NESTED_ARG_ACTION[out text_]
		']'
		{ Text = text_; }
	;

fragment
NESTED_ARG_ACTION[out string text_]
@init
{
	$text_ = string.Empty;
	System.Text.StringBuilder builder = new System.Text.StringBuilder();
}
	:	(	('\\]') => '\\' ']'		{builder.Append("]");}
		|	'\\' c=~(']')			{builder.Append("\\" + (char)$c);}
		|	ACTION_STRING_LITERAL	{builder.Append($ACTION_STRING_LITERAL.text);}
		|	ACTION_CHAR_LITERAL		{builder.Append($ACTION_CHAR_LITERAL.text);}
		|	c=~('\\'|'"'|'\''|']')	{builder.Append((char)$c);}
		)*
		{
			$text_ = builder.ToString();
		}
	;

ACTION
@init
{
	int actionLine = Line;
	int actionColumn = CharPositionInLine;
}
	:	NESTED_ACTION
		('?' {$type = SEMPRED;})?
		{
			string action = $text;
			int n = 1; // num delimiter chars
			if ( action.StartsWith("{{") && action.EndsWith("}}") )
			{
				$type = FORCED_ACTION;
				n = 2;
			}
			action = action.Substring(n,action.Length-n - ($type==SEMPRED ? 1 : 0) - n);
			Text = action;
		}
	;

fragment
NESTED_ACTION
	:	'{'
		(	NESTED_ACTION
		|	ACTION_CHAR_LITERAL
		|	('//' | '/*') => COMMENT
		|	ACTION_STRING_LITERAL
		|	ACTION_ESC
		|	~('{'|'\''|'"'|'\\'|'}')
		)*
		'}'
	;

fragment
ACTION_CHAR_LITERAL
	:	'\''
		(	ACTION_ESC
		|	~('\\'|'\'')
		)*
		'\''
	;

fragment
ACTION_STRING_LITERAL
	:	'"'
		(	ACTION_ESC
		|	~('\\'|'"')
		)*
		'"'
	;

fragment
ACTION_ESC
	:	'\\\''
	|	'\\\"'
	|	'\\' ~('\''|'"')
	;

TOKEN_REF
	:	'A'..'Z'
		(	'a'..'z'|'A'..'Z'|'_'|'0'..'9'
		)*
	;

TOKENS
	:	'tokens' WS_LOOP '{'
	;

OPTIONS
	:	'options' WS_LOOP '{'
	;

// we get a warning here when looking for options '{', but it works right
RULE_REF
	:	'a'..'z' ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
	;

fragment
WS_LOOP
	:	(	WS
		|	COMMENT
		)*
	;

fragment
WS_OPT
	:	WS?
	;

/** Reset the file and line information; useful when the grammar
 *  has been generated so that errors are shown relative to the
 *  original file like the old C preprocessor used to do.
 */
fragment
SRC
	:	'src' ' ' file=ACTION_STRING_LITERAL ' ' line=INT
		{
			Filename = $file.text.Substring(1,$file.text.Length-2);
			Line = int.Parse($line.text) - 1;  // -1 because SL_COMMENT will increment the line no. KR
		}
	;
