/*
   Copyright 2010,2011 Kevin Glynn (kevin.glynn@twigletsoftware.com)
*/

// JavaMaker.g
//
// Convert C# parse tree to a Java parse tree
//
tree grammar JavaMaker;

options {
    tokenVocab=cs;
    ASTLabelType=CommonTree;
    language=CSharp2;
    superClass='Twiglet.CS2J.Translator.Transform.CommonWalker';
    output=AST;
}

// A scope to keep track of the namespaces available at any point in the program
scope NSContext {
    int filler;
    string currentNS;

    // namespaces in scope
    List<string> namespaces;

    // Alias map: these two lists are actually a map from alias to namespace
    // so aliases[i] -> namespaces[i]
    List<string> aliasKeys;
    List<string> aliasNamespaces;
}

// A scope to keep track of the current type context
scope TypeContext {
    string typeName;
}

@namespace { Twiglet.CS2J.Translator.Transform }

@header
{
    using System;
    using System.Globalization;
}

@members
{

    // Since a CS file may comtain multiple top level types (and so generate multiple Java
    // files) we build a map from type name to AST for each top level type
    // We also build a lit of type names so that we can maintain the order (so comments
    // at the end of the file will get included when we emit the java for the last type)
    public IDictionary<string, CUnit> CUMap { get; set; }
    public IList<string> CUKeys { get; set; }

    protected string ParentNameSpace {
        get {
            return ((NSContext_scope)$NSContext.ToArray()[1]).currentNS;
        }
    }

    protected List<string> CollectSearchPath {
        get {
            List<string> ret = new List<string>();
            Object[] nsCtxtArr = $NSContext.ToArray();
            for (int i = nsCtxtArr.Length - 1; i >= 0; i--) {
                foreach (string v in ((NSContext_scope)nsCtxtArr[i]).namespaces) {
                    ret.Add(v);
                }
            }
            return ret;
        }
    }

    protected List<string> CollectAliasKeys {
        get {
            List<string> ret = new List<string>();
            Object[] nsCtxtArr = $NSContext.ToArray();
            for (int i = nsCtxtArr.Length - 1; i >= 0; i--) {
                foreach (string v in ((NSContext_scope)nsCtxtArr[i]).aliasKeys) {
                    ret.Add(v);
                }
            }
            return ret;
        }
    }

    protected List<string> CollectAliasNamespaces {
        get {
            List<string> ret = new List<string>();
            Object[] nsCtxtArr = $NSContext.ToArray();
            for (int i = nsCtxtArr.Length - 1; i >= 0; i--) {
                foreach (string v in ((NSContext_scope)nsCtxtArr[i]).aliasNamespaces) {
                    ret.Add(v);
                }
            }
            return ret;
        }
    }

    // TREE CONSTRUCTION
    protected CommonTree mkPayloadList(List<string> payloads) {
        CommonTree root = (CommonTree)adaptor.Nil;

        foreach (string p in payloads) {
            adaptor.AddChild(root, (CommonTree)adaptor.Create(PAYLOAD, p));
        }
        return root;
    }

    protected CommonTree mangleModifiersForType(CommonTree modifiers) {
        if (modifiers == null || modifiers.Children == null)
            return modifiers;
        CommonTree stripped = (CommonTree)modifiers.DupNode();
        for (int i = 0; i < modifiers.Children.Count; i++) {
            if (((CommonTree)modifiers.Children[i]).Token.Text != "static") {
                adaptor.AddChild(stripped, modifiers.Children[i]);
            }
        }
        return stripped;
    }

    protected CommonTree addConstModifiers(IToken tok, List<string> filter) {
        CommonTree root = (CommonTree)adaptor.Nil;

        if (filter == null || !filter.Contains("static") )
        {
            adaptor.AddChild(root, (CommonTree)adaptor.Create(STATIC, tok, "static"));
        }
        if (filter == null || !filter.Contains("final")) 
        {
            adaptor.AddChild(root, (CommonTree)adaptor.Create(FINAL, tok, "final"));
        }
        root = (CommonTree)adaptor.RulePostProcessing(root);
        return root;
    }

    // embedded statement is ";", or, "{" ... "}", or a single statement. In the latter case we wrap with braces
    protected CommonTree embeddedStatementToBlock(IToken tok, CommonTree embedStat) {

        if ((!embedStat.IsNil && adaptor.GetType(embedStat) == SEMI) ||
            (embedStat.IsNil && adaptor.GetChildCount(embedStat) >= 1 && adaptor.GetType(embedStat) == SEMI)) {
           // Do Nothing, already a block
           return embedStat;
        }
        CommonTree root = (CommonTree)adaptor.Nil;
        
        adaptor.AddChild(root, (CommonTree)adaptor.Create(OPEN_BRACE, tok, "{"));
        adaptor.AddChild(root, dupTree(embedStat));
        adaptor.AddChild(root, (CommonTree)adaptor.Create(CLOSE_BRACE, tok, "}"));

        root = (CommonTree)adaptor.RulePostProcessing(root);
        return root;
    }

// for ["conn", "conn1", "conn2"] generate:
//
//                   if (conn != null)
//                      ((IDisposable)conn).Dispose();
// 
//                   
//                  if (conn2 != null)
//                      ((IDisposable)conn2).Dispose();
// 
//                   
//                  if (conn3 != null)
//                      ((IDisposable)conn3).Dispose();
// used in the finally block of the using translation
    protected CommonTree addDisposeVars(IToken tok, List<string> vars) {
                	
        CommonTree root = (CommonTree)adaptor.Nil;

        foreach (string var in vars) {
        
            CommonTree root_1 = (CommonTree)adaptor.Nil;
            root_1 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(IF, tok, "if"), root_1);

            CommonTree root_2 = (CommonTree)adaptor.Nil;
            root_2 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(NOT_EQUAL, tok, "!="), root_2);

            adaptor.AddChild(root_2, (CommonTree)adaptor.Create(IDENTIFIER, tok, var));
            adaptor.AddChild(root_2, (CommonTree)adaptor.Create(NULL, tok, "null"));

            adaptor.AddChild(root_1, root_2);

            adaptor.AddChild(root_1, (CommonTree)adaptor.Create(SEP, "SEP"));

            root_2 = (CommonTree)adaptor.Nil;
            root_2 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(APPLY, tok, "APPLY"), root_2);

            CommonTree root_3 = (CommonTree)adaptor.Nil;
            root_3 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(DOT, tok, "."), root_3);

            CommonTree root_4 = (CommonTree)adaptor.Nil;
            root_4 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(CAST_EXPR, tok, "CAST"), root_4);

            CommonTree root_5 = (CommonTree)adaptor.Nil;
            root_5 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(TYPE, tok, "TYPE"), root_5);

            adaptor.AddChild(root_5, (CommonTree)adaptor.Create(IDENTIFIER, tok, "IDisposable"));

            adaptor.AddChild(root_4, root_5);

            adaptor.AddChild(root_4, (CommonTree)adaptor.Create(IDENTIFIER, tok, var));

            adaptor.AddChild(root_3, root_4);

            adaptor.AddChild(root_3, (CommonTree)adaptor.Create(IDENTIFIER, tok, "Dispose"));

            adaptor.AddChild(root_2, root_3);

            adaptor.AddChild(root_1, root_2);

            adaptor.AddChild(root_1, (CommonTree)adaptor.Create(SEMI, tok, ";"));

            adaptor.AddChild(root, root_1);
        }

        return (CommonTree)adaptor.RulePostProcessing(root);
    }

    // TODO:  Read reserved words from a file so that they can be extended by customer
    private readonly static string[] javaReserved = new string[] { "int", "protected", "package" };
    
    protected string fixBrokenId(string id)
    {
        // Console.WriteLine(id);
        foreach (string k in javaReserved)
        {
            if (k == id) 
            {
                return "__" + id;
            }
        }
        return id;
    }

    // Map of C# built in types to Java equivalents
    Dictionary<string, string> predefined_type_map = new Dictionary<string, string>()
    {
        {"bool", "boolean"},
        {"decimal", "double"},
        {"object", "Object"},
        {"string", "String"}
    };

    protected CommonTree mkHole() {
        return mkHole(null);
    }

    protected CommonTree mkHole(IToken tok) {
        return (CommonTree)adaptor.Create(KGHOLE, tok, "KGHOLE");
    }

    // counter to ensure that the catch vars we introduce are unique 
    protected int dummyCatchVarCtr = 0;

    protected int newVarCtr = 0;

    protected CommonTree dupTree(CommonTree t) {
        return (CommonTree)adaptor.DupTree(t);
    }

}

/********************************************************************************************
                          Parser section
*********************************************************************************************/

///////////////////////////////////////////////////////

public compilation_unit
scope NSContext;
@init {
    $NSContext::currentNS = "";
    $NSContext::namespaces = new List<string>();
    $NSContext::aliasKeys = new List<string>();
    $NSContext::aliasNamespaces = new List<string>();
}
:
	namespace_body;

namespace_declaration
scope NSContext;
@init {
    $NSContext::currentNS = "";
    $NSContext::namespaces = new List<string>();
    $NSContext::aliasKeys = new List<string>();
    $NSContext::aliasNamespaces = new List<string>();
}:
	'namespace'   qi=qualified_identifier
    {     
        // extend parent namespace
        $NSContext::currentNS = this.ParentNameSpace + $qi.thetext;
        $NSContext::namespaces.Add($NSContext::currentNS);
    }
    namespace_block   ';'? ;
namespace_block:
	'{'   namespace_body   '}' ;
namespace_body:
	extern_alias_directives?   using_directives?   global_attributes?   namespace_member_declarations? ;
extern_alias_directives:
	extern_alias_directive+ ;
extern_alias_directive:
	e='extern'   'alias'   i=identifier  ';' { Warning($e.line, "[UNSUPPORTED] External Alias " + $i.text); } ;
using_directives:
	using_directive+ ;
using_directive:
	(using_alias_directive
	| using_namespace_directive) ;
using_alias_directive:
	'using'	  identifier   '='   namespace_or_type_name   ';' 
        {$NSContext::aliasKeys.Add($identifier.text);$NSContext::aliasNamespaces.Add($namespace_or_type_name.thetext); } ;
using_namespace_directive:
	'using'   namespace_name   ';' {$NSContext::namespaces.Add($namespace_name.thetext); };
namespace_member_declarations:
	namespace_member_declaration+ ;
namespace_member_declaration
@init { string ns = $NSContext::currentNS; 
        bool isCompUnit = false;
}
@after {
    if (isCompUnit) {
       if (CUKeys.Contains(ns+"."+$ty.name)) {
          { Warning($ty.start.Token.Line, "[UNSUPPORTED] Cannot have a class with multiple generic type overloadings: " + ns+"."+$ty.name); }
       }
       else {
          CUMap.Add(ns+"."+$ty.name, new CUnit($namespace_member_declaration.tree,CollectSearchPath,CollectAliasKeys,CollectAliasNamespaces)); 
          CUKeys.Add(ns+"."+$ty.name);
       }
    }; 
}
:
	namespace_declaration
	| attributes?   modifiers?   ty=type_declaration[$attributes.tree, mangleModifiersForType($modifiers.tree)]  { isCompUnit = true; } ->  ^(PACKAGE[$ty.start.Token, "package"] PAYLOAD[ns] $ty);
// type_declaration is only called at the top level, so each of the types declared
// here will become a Java compilation unit (and go to its own file)
type_declaration[CommonTree atts, CommonTree mods] returns [string name]
:
    ('partial') => p='partial'!  { Warning($p.line, "[UNSUPPORTED] 'partial' definition"); }  
                                (pc=class_declaration[$atts, $mods] { $name=$pc.name; }
								| ps=struct_declaration[$atts, $mods] { $name=$ps.name; }
								| pi=interface_declaration[$atts, $mods] { $name=$pi.name; }) 
	| c=class_declaration[$atts, $mods] { $name=$c.name; }
	| s=struct_declaration[$atts, $mods] { $name=$s.name; }
	| i=interface_declaration[$atts, $mods] { $name=$i.name; }
	| e=enum_declaration[$atts, $mods] { $name=$e.name; }
	| d=delegate_declaration[$atts, $mods] { $name=$d.name; }
    ;
// Identifiers
qualified_identifier returns [string thetext]:
	i1=identifier { $thetext = $i1.text; } ('.' ip=identifier { $thetext += "." + $ip.text; } )*;
namespace_name returns [string thetext]
	: namespace_or_type_name { $thetext = $namespace_or_type_name.thetext; };

modifiers returns [List<string> modList]
@init {
    $modList = new List<string>();
}:
	 (modifier { if ($modifier.tree != null) $modList.Add( $modifier.tree.Text); })+ ;
modifier: 
	'new' -> /* No new in Java*/ | 'public' | 'protected' | 'private' | 'internal' ->  /* translate to package-private */| 'unsafe' ->  | 'abstract' | 'sealed' -> FINAL["final"] | 'static'
	| 'readonly' -> /* no equivalent in C# (this is like a const that can be initialized separately in the constructor) */ | 'volatile' | e='extern'  { Warning($e.line, "[UNSUPPORTED] 'extern' modifier"); }  | 'virtual' -> | 'override' -> /* not in Java, maybe convert to override annotation */;

class_member_declaration:
	a=attributes?
	m=modifiers?
	( c='const'   ct=type   constant_declarators   ';' -> ^(FIELD[$c.token, "FIELD"] $a? $m? { addConstModifiers($c.token, $m.modList) } $ct constant_declarators)
	| ev=event_declaration	-> ^(EVENT[$ev.start.Token, "EVENT"] $a? $m? $ev)
	| p='partial' { Warning($p.line, "[UNSUPPORTED] 'partial' definition"); } (v1=void_type m3=method_declaration[$a.tree, $m.tree, $m.modList, $v1.tree, $v1.text] -> $m3 
			   | pi=interface_declaration[$a.tree, $m.tree] -> $pi
			   | pc=class_declaration[$a.tree, $m.tree] -> $pc
			   | ps=struct_declaration[$a.tree, $m.tree] -> $ps)
	| i=interface_declaration[$a.tree, $m.tree] -> $i
	| v2=void_type   m1=method_declaration[$a.tree, $m.tree, $m.modList, $v2.tree, $v2.text] -> $m1 
	| t=type ( (member_name  type_parameter_list? '(') => m2=method_declaration[$a.tree, $m.tree, $m.modList, $t.tree, $t.text] -> $m2
		   | (member_name   '{') => pd=property_declaration[$a.tree, $m.tree, $t.tree] -> $pd
		   | (member_name   '.'   'this') => type_name '.' ix1=indexer_declaration[$a.tree, $m.tree, $t.tree] -> $ix1
		   | ix2=indexer_declaration[$a.tree, $m.tree, $t.tree]	-> $ix2 //this
	       | field_declaration     -> ^(FIELD[$t.start.Token, "FIELD"] $a? $m? $t field_declaration) // qid
	       | operator_declaration -> ^(OPERATOR[$t.start.Token, "OPERATOR"] $a? $m? $t operator_declaration)
	       )
//	common_modifiers// (method_modifiers | field_modifiers)
	
	| cd=class_declaration[$a.tree, $m.tree] -> $cd
	| sd=struct_declaration[$a.tree, $m.tree] -> $sd
	| ed=enum_declaration[$a.tree, $m.tree] -> $ed
	| dd=delegate_declaration[$a.tree, $m.tree] -> $dd
	| co3=conversion_operator_declaration -> ^(CONVERSION_OPERATOR[$co3.start.Token, "CONVERSION"] $a? $m? $co3)
	| con3=constructor_declaration[$a.tree, $m.tree, $m.modList]	-> $con3
	| de3=destructor_declaration -> $de3
	) 
	;

primary_expression: 
	('this'    brackets[null]) => (t='this' -> $t)  (b1=brackets[$primary_expression.tree] -> $b1) (pp1=primary_expression_part[$primary_expression.tree] -> $pp1) *
	| ('base'   brackets[null]) => (b='base' -> SUPER[$b.token, "super"])  (b2=brackets[$primary_expression.tree] -> $b2) (pp2=primary_expression_part[$primary_expression.tree] -> $pp2) *
	| (primary_expression_start -> primary_expression_start)   (pp3=primary_expression_part[$primary_expression.tree] -> $pp3 )*
// keving:TODO fixup
	| 'new' (   (object_creation_expression   ('.'|'->'|'[')) => 
					(oc1=object_creation_expression -> $oc1)   (pp4=primary_expression_part[ $primary_expression.tree ] -> $pp4 )+ 		// new Foo(arg, arg).Member
				// try the simple one first, this has no argS and no expressions
				// symantically could be object creation
                // keving:  No, try object_creation_expression first, it could be new type ( xx ) {}  
                // can also match delegate_creation, will have to distinguish in NetMaker.g
				| (object_creation_expression) => oc2=object_creation_expression -> $oc2
				| delegate_creation_expression -> delegate_creation_expression // new FooDelegate (MyFunction)
				| anonymous_object_creation_expression -> anonymous_object_creation_expression)							// new {int X, string Y} 
	| sizeof_expression						// sizeof (struct)
	| checked_expression            		// checked (...
	| unchecked_expression          		// unchecked {...}
	| default_value_expression      		// default
	| anonymous_method_expression			// delegate (int foo) {}
	;

primary_expression_start:
	predefined_type            
	| (identifier    generic_argument_list) => identifier   generic_argument_list
	| identifier ((c='::'^   identifier { Warning($c.line, "[UNSUPPORTED] external aliases are not yet supported"); })?)!
	| 'this' 
	| b='base' -> SUPER[$b.token, "super"]
	| paren_expression
	| typeof_expression             // typeof(Foo).Name
	| literal
	;

primary_expression_part [CommonTree lhs]:
	 access_identifier[$lhs]
	| brackets_or_arguments[$lhs] 
	| p='++' -> ^(POSTINC[$p.token, "++"] { dupTree($lhs) } )
	| m='--' -> ^(POSTDEC[$m.token, "--"] { dupTree($lhs) } )
    ;
access_identifier [CommonTree lhs]:
	access_operator   type_or_generic -> ^(access_operator { dupTree($lhs) } type_or_generic);
access_operator:
	'.'  |  '->' ;
brackets_or_arguments [CommonTree lhs]:
	brackets[$lhs] | arguments[$lhs] ;
brackets [CommonTree lhs]:
	'['   expression_list?   ']' -> ^(INDEX { dupTree($lhs) } expression_list?);	
// keving: TODO: drop this.
paren_expression:	
	'('   expression   ')' -> ^(PARENS expression);
arguments [CommonTree lhs]: 
	'('   argument_list?   ')' -> ^(APPLY { dupTree($lhs) } argument_list?);
argument_list: 
	a1=argument (',' an+=argument)* -> ^(ARGS[$a1.start.Token,"ARGS"] $a1 $an*);
// 4.0
argument:
	argument_name   argument_value
	| argument_value;
argument_name:
	identifier   ':';
argument_value: 
	expression 
	| ref_variable_reference 
	| 'out'   variable_reference ;
ref_variable_reference:
	'ref' 
		(('('   type   ')') =>   '('   type   ')'   (ref_variable_reference | variable_reference)   // SomeFunc(ref (int) ref foo)
																									// SomeFunc(ref (int) foo)
		| variable_reference);	// SomeFunc(ref foo)
// lvalue
variable_reference:
	expression;
rank_specifiers: 
	rank_specifier+ ;        
// convert dimension separators into additional dimensions, so [,,] -> [] [] []
rank_specifier: 
	o='['   dim_separators?   c=']' -> $o $c dim_separators?;
dim_separators
@init {
    CommonTree ret = (CommonTree)adaptor.Nil;
}
@after {
    $dim_separators.tree = ret;
}: 
        (c=',' { adaptor.AddChild(ret, adaptor.Create(OPEN_BRACKET, $c.token, "["));adaptor.AddChild(ret, adaptor.Create(CLOSE_BRACKET, $c.token, "]")); })+ -> ;

delegate_creation_expression: 
	// 'new'   
	t1=type_name   '('   t2=type_name   ')' -> ^(NEW_DELEGATE[$t1.start.Token, "new(delegate)"] ^(TYPE[$t1.start.Token, "TYPE"] $t1) ^(ARGS[$t2.start.Token, "ARGS"] $t2));
anonymous_object_creation_expression: 
	// 'new'
	i=anonymous_object_initializer -> ^(NEW_ANON_OBJECT[$i.tree.Token, "new(anonobj)"] anonymous_object_initializer);
anonymous_object_initializer: 
	'{'   (member_declarator_list   ','?)?   '}';
member_declarator_list: 
	member_declarator  (',' member_declarator)* ; 
member_declarator: 
	qid   ('='   expression)? ;
primary_or_array_creation_expression:
	(array_creation_expression) => array_creation_expression
	| primary_expression 
	;
// new Type[2] { }
array_creation_expression
@init {
    bool removeDimensions = false;
    CommonTree ret = null;
}
@after {
    if (ret != null)
       $array_creation_expression.tree = (CommonTree)adaptor.RulePostProcessing(ret);
    if (removeDimensions) {
        if ($array_creation_expression.tree != null && 
            adaptor.GetChildCount($array_creation_expression.tree) > 3 &&
            adaptor.GetType(adaptor.GetChild($array_creation_expression.tree, 1)) == OPEN_BRACKET) {
            // Delete until CLOSE_BRACKET
            while (adaptor.GetType(adaptor.GetChild($array_creation_expression.tree, 2)) != CLOSE_BRACKET) {
                adaptor.DeleteChild($array_creation_expression.tree, 2);
            }
            // push open / close bracket into type.
            CommonTree type = (CommonTree)adaptor.GetChild($array_creation_expression.tree, 0);
            adaptor.AddChild(type, adaptor.DupTree(adaptor.GetChild($array_creation_expression.tree, 1)));
            adaptor.AddChild(type, adaptor.DupTree(adaptor.GetChild($array_creation_expression.tree, 2)));
            adaptor.DeleteChild($array_creation_expression.tree, 2);
            adaptor.DeleteChild($array_creation_expression.tree, 1);

        }
    }
}:
	n=NEW_ARRAY   
		(type   ((o='['   expression_list   c=']' -> ^($n type $o expression_list $c))  { ret = (CommonTree)adaptor.RulePostProcessing($array_creation_expression.tree); }
					(   (rank_specifiers { adaptor.AddChild(ret, $rank_specifiers.tree); })?   
                        (ai1=array_initializer { adaptor.AddChild(ret, $ai1.tree); 
                                             removeDimensions = true; /* If an initializer is provided then drop the dimensions */} )?	// new int[4]
					// | invocation_part*
					| ( ((arguments[null]   ('['|'.'|'->')) => as1=arguments[ ret ]   ip=invocation_part[ $as1.tree ] { ret = $ip.tree; })// new object[2].GetEnumerator()
					  | ip2=invocation_part[ret] { ret = $ip2.tree; })*   as2=arguments[ ret ] { ret = $as2.tree; }
					)							// new int[4]()
				| array_initializer		-> ^($n type array_initializer)
				)
		| rank_specifier   // [,]
			(array_initializer	// var a = new[] { 1, 10, 100, 1000 }; // int[]
		    ) -> ^($n rank_specifier array_initializer)
		) ;
array_initializer:
	'{'   variable_initializer_list?   ','?   '}' ;
variable_initializer_list:
	variable_initializer (',' variable_initializer)* ;
variable_initializer:
	expression	| array_initializer ;
sizeof_expression:
	'sizeof'^   '('!   unmanaged_type   ')'!;
checked_expression: 
	'checked'^   '('!   expression   ')'! ;
unchecked_expression: 
	'unchecked'^   '('!   expression   ')'! ;
default_value_expression: 
	'default'^   '('!   type   ')'! ;
anonymous_method_expression:
	'delegate'^   explicit_anonymous_function_signature?   block;
explicit_anonymous_function_signature:
	'('   explicit_anonymous_function_parameter_list?   ')' ;
explicit_anonymous_function_parameter_list:
	explicit_anonymous_function_parameter   (','   explicit_anonymous_function_parameter)* ;	
explicit_anonymous_function_parameter:
	anonymous_function_parameter_modifier?   type   identifier;
anonymous_function_parameter_modifier:
	'ref' | 'out';


///////////////////////////////////////////////////////
object_creation_expression: 
	// 'new'
	type   
		( '('   argument_list?   ')'   o1=object_or_collection_initializer?  -> ^(NEW[$type.start.Token, "new"] type argument_list? $o1?)
		  | o2=object_or_collection_initializer -> ^(NEW[$type.start.Token, "new"] type $o2)) 
	;
object_or_collection_initializer: 
	'{'  (object_initializer 
		| collection_initializer) ;
collection_initializer: 
	element_initializer_list   ','?   '}' ;
element_initializer_list: 
	element_initializer  (',' element_initializer)* ;
element_initializer: 
	non_assignment_expression 
	| '{'   expression_list   '}' ;
// object-initializer eg's
//	Rectangle r = new Rectangle {
//		P1 = new Point { X = 0, Y = 1 },
//		P2 = new Point { X = 2, Y = 3 }
//	};
// TODO: comma should only follow a member_initializer_list
object_initializer: 
	member_initializer_list?   ','?   '}' ;
member_initializer_list: 
	member_initializer  (',' member_initializer)* ;
member_initializer: 
	identifier   '='   initializer_value ;
initializer_value: 
	expression 
	| object_or_collection_initializer ;

///////////////////////////////////////////////////////

typeof_expression: 
	'typeof'^   '('!   ((unbound_type_name) => unbound_type_name
					  | type 
					  | void_type)   ')'! ;
// unbound type examples
//foo<bar<X<>>>
//bar::foo<>
//foo1::foo2.foo3<,,>
unbound_type_name:		// qualified_identifier v2
//	unbound_type_name_start unbound_type_name_part* ;
	unbound_type_name_start   
		(((generic_dimension_specifier   '.') => generic_dimension_specifier   unbound_type_name_part)
		| unbound_type_name_part)*   
			generic_dimension_specifier
	;

unbound_type_name_start:
	identifier ('::' identifier)?;
unbound_type_name_part:
	'.'   identifier;
generic_dimension_specifier: 
	'<'   commas?   '>' ;
commas: 
	','+ ; 

///////////////////////////////////////////////////////
//	Type Section
///////////////////////////////////////////////////////

type_name returns [string thetext]: 
	namespace_or_type_name { $thetext = $namespace_or_type_name.thetext; };
namespace_or_type_name returns [string thetext]: 
	 t1=type_or_generic  { $thetext=t1.type+formatTyargs($t1.generic_arguments); } ('::'^ tc=type_or_generic { $thetext+="::"+tc.type+formatTyargs($tc.generic_arguments); })? ('.'^   tn=type_or_generic { $thetext+="."+tn.type+formatTyargs($tn.generic_arguments); } )* ;
type_or_generic returns [string type, List<string> generic_arguments]
@init {
    $generic_arguments = new List<string>();
}
@after{
    $type = $t.text;
}:
	(identifier   generic_argument_list) => t=identifier   ga=generic_argument_list { $generic_arguments = $ga.tyargs; }
	| t=identifier ;

// keving: as far as I can see this is (<interfacename>.)?identifier (<tyargs>)? at lease for C# 3.0 and less.
qid returns [string name, List<string> tyargs]:		// qualified_identifier v2
	(qs=qid_start -> $qs)  (qp=qid_part[$qid.tree] -> $qp)* { $name=$qid_start.name; $tyargs = $qid_start.tyargs; }
	;
qid_start returns [string name, List<string> tyargs]:
	predefined_type { $name = $predefined_type.thetext; }
	| (identifier    generic_argument_list)	=> identifier   generic_argument_list { $name = $identifier.text; $tyargs = $generic_argument_list.tyargs; } 
//	| 'this'
//	| 'base'
	| i1=identifier  { $name = $i1.text; } ('::'   inext=identifier { $name+="::" + $inext.text; })?
	| literal { $name = $literal.text; }
	;		// 0.ToString() is legal


qid_part[CommonTree lhs]:
	access_identifier[ $lhs ] ;

generic_argument_list returns [List<string> tyargs]
@after { 
    $tyargs = $ta.tyargs;
}
: 
	'<'   ta=type_arguments   '>' ;
type_arguments returns [List<string> tyargs]
@init {
    $tyargs = new List<string>();
}
: 
	t1=type { $tyargs.Add($t1.thetext); } (',' tn=type { $tyargs.Add($tn.thetext); })* ;

type returns [string thetext]:
         ((predefined_type | type_name)  rank_specifiers) => (p1=predefined_type { $thetext = $p1.thetext; } | tn1=type_name { $thetext = $tn1.thetext; })   rs=rank_specifiers  { $thetext += $rs.text; } ('*' { $thetext += "*"; })* -> ^(TYPE $p1? $tn1? $rs '*'*)
       | ((predefined_type | type_name)  ('*'+ | '?')) => (p2=predefined_type { $thetext = $p2.thetext; } | tn2=type_name { $thetext = $tn2.thetext; })   (('*' { $thetext += "*"; })+ | o2='?' { $thetext += "?"; }) -> ^(TYPE $p2? $tn2? '*'* $o2?)
       | (p3=predefined_type { $thetext = $p3.thetext; } | tn3=type_name { $thetext = $tn3.thetext; }) -> ^(TYPE $p3? $tn3?)
       | v='void' { $thetext = "System.Void"; } ('*' { $thetext += "*"; })+  -> ^(TYPE[$v.token, "TYPE"] $v '*'+)
       ;
non_nullable_type:
	(p=predefined_type | t=type_name) rs=rank_specifiers? '*'* ->  ^(TYPE["TYPE"] $p? $t? $rs? '*'*)
       | v='void' '*'+  -> ^(TYPE[$v.token,"TYPE"] $v '*'+)
       ;
non_array_type:
	type;
array_type:
	type;
unmanaged_type:
	type;
class_type:
	type;
pointer_type:
	type;


///////////////////////////////////////////////////////
//	Statement Section
///////////////////////////////////////////////////////
block:
	';'
	| '{'   statement_list?   '}';
statement_list:
	statement+ ;
	
///////////////////////////////////////////////////////
//	Expression Section
///////////////////////////////////////////////////////	
expression: 
	(unary_expression   assignment_operator) => assignment	
	| non_assignment_expression
	;
expression_list:
	expression  (','   expression)* ;
assignment:
	unary_expression   assignment_operator   expression ;
unary_expression: 
	//('(' arguments ')' ('[' | '.' | '(')) => primary_or_array_creation_expression
    (cast_expression) => cast_expression
	| primary_or_array_creation_expression -> primary_or_array_creation_expression
	| p='+'   unary_expression -> ^(MONOPLUS[$p.token,"+"] unary_expression) 
	| m='-'   unary_expression -> ^(MONOMINUS[$m.token, "-"] unary_expression) 
	| n='!'   unary_expression -> ^(MONONOT[$n.token, "!"] unary_expression) 
	| t='~'   unary_expression -> ^(MONOTWIDDLE[$t.token, "~"] unary_expression) 
	| pre_increment_expression -> pre_increment_expression
	| pre_decrement_expression -> pre_decrement_expression
	| pointer_indirection_expression -> pointer_indirection_expression
	| addressof_expression -> addressof_expression 
	;
cast_expression:
//	//'('   type   ')'   unary_expression ; 
	l='('   type   ')'   unary_expression -> ^(CAST_EXPR[$l.token, "CAST"] type unary_expression);
assignment_operator:
	'=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | r='>' '>=' -> RIGHT_SHIFT_ASSIGN[$r.token, ">>="] ;
pre_increment_expression: 
	s='++'   unary_expression -> ^(PREINC[$s.token, "++"] unary_expression) ;
pre_decrement_expression: 
	s='--'   unary_expression -> ^(PREDEC[$s.token, "--"] unary_expression);
pointer_indirection_expression:
	s='*'   unary_expression -> ^(MONOSTAR[$s.token, "*"] unary_expression);
addressof_expression:
	a='&'   unary_expression -> ^(ADDRESSOF[$a.token, "&"] unary_expression);

non_assignment_expression:
	//'non ASSIGNment'
	(anonymous_function_signature   '=>')	=> lambda_expression
	| (query_expression) => query_expression 
	| conditional_expression
	;

///////////////////////////////////////////////////////
//	Conditional Expression Section
///////////////////////////////////////////////////////

multiplicative_expression:
	(u1=unary_expression -> $u1) ((op='*'|op='/'|op='%')  un=unary_expression -> ^($op $multiplicative_expression $un) )*	;
additive_expression:
	multiplicative_expression (('+'|'-')^   multiplicative_expression)* ;
// >> check needed (no whitespace)
shift_expression:
    (a1=additive_expression -> $a1) ((so='<<' a3=additive_expression -> ^($so $shift_expression $a3))
                            | (r='>' '>' a2=additive_expression -> ^(RIGHT_SHIFT[$r.token, ">>"] $shift_expression $a2)) 
                           )* ;
relational_expression:
	(s1=shift_expression -> $s1) 
		(	((o='<'|o='>'|o='>='|o='<=')	s2=shift_expression -> ^($o $relational_expression $s2))
			| (i='is'  t=non_nullable_type -> ^(INSTANCEOF[$i.Token,"instanceof"] $relational_expression $t) 
                | i1='as' t1=non_nullable_type -> ^(COND_EXPR[$i1.Token, "?:"] 
                                                        ^(INSTANCEOF[$i1.Token,"instanceof"] { dupTree($relational_expression.tree) } { dupTree($t1.tree) } ) 
                                                        ^(CAST_EXPR[$i1.Token, "(cast)"] { dupTree($t1.tree) } { dupTree($relational_expression.tree) }) 
                                                        ^(CAST_EXPR[$i1.Token, "(cast)"] { dupTree($t1.tree) } NULL[$i1.Token, "null"])))
		)* ;
equality_expression:
	relational_expression
	   (('=='|'!=')^   relational_expression)* ;
and_expression:
	equality_expression ('&'^   equality_expression)* ;
exclusive_or_expression:
	and_expression ('^'^   and_expression)* ;
inclusive_or_expression:
	exclusive_or_expression   ('|'^   exclusive_or_expression)* ;
conditional_and_expression:
	inclusive_or_expression   ('&&'^   inclusive_or_expression)* ;
conditional_or_expression:
	conditional_and_expression  ('||'^   conditional_and_expression)* ;

null_coalescing_expression:
	(e1=conditional_or_expression -> $e1)  (qq='??'   e2=conditional_or_expression -> ^(COND_EXPR[$qq.token, "?:"]
                                                                                          ^(NOT_EQUAL[$qq.token, "!="] { dupTree($null_coalescing_expression.tree) } NULL[$qq.Token, "null"])
                                                                                          { dupTree($null_coalescing_expression.tree) }
                                                                                          { dupTree($e2.tree) })
                                           )* ;
conditional_expression:
     (ne=null_coalescing_expression  -> $ne) (q='?'   te=expression   ':'   ee=expression ->  ^(COND_EXPR[$q.token, "?:"] $conditional_expression $te $ee))? ;
//	(null_coalescing_expression   '?'   expression   ':') => e1=null_coalescing_expression   q='?'   e2=expression   ':'   e3=expression -> ^(COND_EXPR[$q.token, "?:"] $e1 $e2 $e3)
//    | null_coalescing_expression   ;
      
///////////////////////////////////////////////////////
//	lambda Section
///////////////////////////////////////////////////////
lambda_expression:
	anonymous_function_signature   '=>'   anonymous_function_body;
anonymous_function_signature:
	'('	(explicit_anonymous_function_parameter_list
		| implicit_anonymous_function_parameter_list)?	')'
	| implicit_anonymous_function_parameter_list
	;
implicit_anonymous_function_parameter_list:
	implicit_anonymous_function_parameter   (','   implicit_anonymous_function_parameter)* ;
implicit_anonymous_function_parameter:
	identifier;
anonymous_function_body:
	expression
	| block ;

///////////////////////////////////////////////////////
//	LINQ Section
///////////////////////////////////////////////////////
query_expression:
	from_clause   query_body ;
query_body:
	// match 'into' to closest query_body
	query_body_clauses?   select_or_group_clause   (('into') => query_continuation)? ;
query_continuation:
	'into'   identifier   query_body;
query_body_clauses:
	query_body_clause+ ;
query_body_clause:
	from_clause
	| let_clause
	| where_clause
	| join_clause
	| orderby_clause;
from_clause:
	'from'   type?   identifier   'in'   expression ;
join_clause:
	'join'   type?   identifier   'in'   expression   'on'   expression   'equals'   expression ('into' identifier)? ;
let_clause:
	'let'   identifier   '='   expression;
orderby_clause:
	'orderby'   ordering_list ;
ordering_list:
	ordering   (','   ordering)* ;
ordering:
	expression    ordering_direction
	;
ordering_direction:
	'ascending'
	| 'descending' ;
select_or_group_clause:
	select_clause
	| group_clause ;
select_clause:
	'select'   expression ;
group_clause:
	'group'   expression   'by'   expression ;
where_clause:
	'where'   boolean_expression ;
boolean_expression:
	expression;

///////////////////////////////////////////////////////
// B.2.13 Attributes
///////////////////////////////////////////////////////
global_attributes: 
	global_attribute+ ;
global_attribute: 
	'['   global_attribute_target_specifier   attribute_list   ','?   ']' ;
global_attribute_target_specifier: 
	global_attribute_target   ':' ;
global_attribute_target: 
	'assembly' | 'module' ;
attributes: 
	attribute_sections ;
attribute_sections: 
	attribute_section+ ;
attribute_section: 
	'['   attribute_target_specifier?   attribute_list   ','?   ']' ;
attribute_target_specifier: 
	attribute_target   ':' ;
attribute_target: 
	'field' | 'event' | 'method' | 'param' | 'property' | 'return' | 'type' ;
attribute_list: 
	attribute (',' attribute)* ; 
attribute: 
	type_name   attribute_arguments? ;
// TODO:  allows a mix of named/positional arguments in any order
attribute_arguments: 
	'('   (')'										// empty
		   | (positional_argument   ((','   identifier   '=') => named_argument
		   							 |','	positional_argument)*
			  )	')'
			) ;
positional_argument_list: 
	a1=positional_argument (',' an+=positional_argument)* -> ^(ARGS[$a1.start.Token,"ARGS"] $a1 $an*);
positional_argument: 
	attribute_argument_expression ;
named_argument_list: 
	a1=named_argument (',' an+=named_argument)* -> ^(ARGS[$a1.start.Token,"ARGS"] $a1 $an*);
named_argument: 
	identifier   '='   attribute_argument_expression ;
attribute_argument_expression: 
	expression ;

///////////////////////////////////////////////////////
//	Class Section
///////////////////////////////////////////////////////

class_declaration[CommonTree atts, CommonTree mods] returns [string name]
scope TypeContext;
:
	c='class' identifier  { $TypeContext::typeName = $identifier.text; } type_parameter_list? { $name = mkGenericTypeAlias($identifier.text, $type_parameter_list.names); }  class_base?   type_parameter_constraints_clauses?   class_body   ';'? 
    -> ^(CLASS[$c.Token] { dupTree($atts) } { dupTree($mods) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base?  class_body );

type_parameter_list returns [List<string> names] 
@init {
    List<string> names = new List<string>();
}:
    '<'! attributes? t1=type_parameter { names.Add($t1.name); } ( ','!  attributes? tn=type_parameter { names.Add($tn.name); })* '>'! ;

type_parameter returns [string name]:
    identifier { $name = $identifier.text; } ;

class_base:
	// just put all types in a single list.  In NetMaker we will extract the base class if necessary
	':'   ts+=type (','   ts+=type)* -> ^(IMPLEMENTS $ts)*;
	
//interface_type_list:
//	ts+=type (','   ts+=type)* -> $ts+;

class_body:
	'{'   class_member_declarations?   '}' ;
class_member_declarations:
	class_member_declaration+ ;

///////////////////////////////////////////////////////
constant_declaration:
	'const'   type   constant_declarators   ';' ;
constant_declarators:
	constant_declarator (',' constant_declarator)* ;
constant_declarator:
	identifier   ('='   constant_expression)? ;
constant_expression:
	expression;

///////////////////////////////////////////////////////
field_declaration:
	variable_declarators   ';'!	;
variable_declarators:
	variable_declarator (','   variable_declarator)* ;
variable_declarator:
	type_name ('='   variable_initializer)? ;		// eg. event EventHandler IInterface.VariableName = Foo;

///////////////////////////////////////////////////////
method_declaration [CommonTree atts, CommonTree mods, List<string> modList, CommonTree type, string typeText]
@init {
    bool isToString = false;
    CommonTree exceptions = null;
    CommonTree optMain = null;
    bool isVoid = $typeText == "void";
    bool isInt = $typeText == "int"  || $typeText == "System.Int32" || $typeText == "Int32";
    bool isMain = isVoid || isInt;
    bool isMainHasArg = false;
}:
        // TODO:  According to the spec the C# Main() method should be static and not public.  We aren't checking for lack of public 
        // we can check the modifiers in modList if we want to enforce that.
		member_name { isToString = $member_name.text == "ToString"; isMain &= $member_name.text == "Main"; } 
        (type_parameter_list { isToString = false; isMain = false; })? 
        '(' 
            // We are looking for ToString(), and Main(string[] args), where arg is optional.  
            (formal_parameter_list 
              { isToString = false; 
                if (isMain) {
                    isMain = false;
                    // since we have an argument, must check its an array of String
                    if ($formal_parameter_list.tree != null && $formal_parameter_list.tree.Children != null && $formal_parameter_list.tree.Children.Count == 2) {
                        // parameter list children size is 2 (type arg)
                        CommonTree argTy = (CommonTree)$formal_parameter_list.tree.Children[0];
                        if (argTy != null && argTy.Children != null && argTy.Children.Count == 3) {
                            // Looking for Children of "String" "[" "]"
                            if (argTy.Children[0].Text.ToLower() == "string" &&
                                argTy.Children[1].Text == "[" &&
                                argTy.Children[2].Text == "]") {
                                // Bingo!
                                isMain = true;
                                isMainHasArg = true;
                            }
                        } 
                    }
                } 
              }
            )?   
        ')'  
        ( type_parameter_constraints_clauses { isToString = false; isMain = false; })?   
        b=method_body[isToString] 

        // build main method if required
        argParam=magicMainArgs[isMain && isMainHasArg, $member_name.tree.Token]
        mainApply=magicMainApply[isMain, $member_name.tree.Token, $TypeContext::typeName, $argParam.tree]
        mainCall=magicMainExit[isMain, isInt, $member_name.tree.Token, $mainApply.tree]
        mainMethod=magicMainWrapper[isMain, $member_name.tree.Token, $mainCall.tree]
    

        { if (isToString) {
              $member_name.tree.Token.Text = "toString";
          }
          exceptions = $b.exceptionList;
       }
       -> $mainMethod?
          ^(METHOD { dupTree($atts) } { dupTree($mods) } { dupTree($type) } 
            member_name type_parameter_constraints_clauses? type_parameter_list? formal_parameter_list? $b { exceptions });

method_body [bool smotherExceptions] returns [CommonTree exceptionList]:
	{smotherExceptions}? b=block nb=magicSmotherExceptions[dupTree($b.tree) ]
       -> $nb 
   | b=block el=magicThrowsException[true,$b.tree.Token] { $exceptionList=$el.tree; }   
       -> $b
         ;
member_name returns [string rawId]:
    (type_or_generic '.')* i=identifier { $rawId = $i.text; }
   // keving [interface_type.identifier] | type_name '.' identifier 
    ;

member_name_orig returns [string name, List<string> tyargs]:
	qid { $name = $qid.name; $tyargs = $qid.tyargs; } ;		// IInterface<int>.Method logic added.

///////////////////////////////////////////////////////
property_declaration [CommonTree atts, CommonTree mods, CommonTree type]
scope { bool emptyGetterSetter; }
@init {
    $property_declaration::emptyGetterSetter = false;
    CommonTree privateVar = null;               
}
:
	i=member_name   '{'   ads=accessor_declarations[atts, mods, type, $i.text, $i.rawId]   '}' 
        v=magicMkPropertyVar[type, "__" + $i.tree.Text] { privateVar = $property_declaration::emptyGetterSetter ? $v.tree : null; }-> { privateVar } $ads ;

accessor_declarations [CommonTree atts, CommonTree mods, CommonTree type, string propName, string rawVarName]:
    accessor_declaration[atts, mods, type, propName, rawVarName]+;

accessor_declaration [CommonTree atts, CommonTree mods, CommonTree type, string propName, string rawVarName]
@init {
     CommonTree propBlock = null; 
     bool mkBody = false;
}:
	la=attributes? lm=accessor_modifier? 
      (g='get' ((';')=> gbe=';'  { $property_declaration::emptyGetterSetter = true; propBlock = $gbe.tree; mkBody = true; rawVarName = "__" + rawVarName; } 
                | gb=block { propBlock = $gb.tree; } ) getm=magicPropGetter[atts, $la.tree, mods, $lm.tree, type, $g.token, propBlock, propName, mkBody, rawVarName] -> $getm
       | s='set' ((';')=> sbe=';'  { $property_declaration::emptyGetterSetter = true; propBlock = $sbe.tree; mkBody = true; rawVarName = "__" + rawVarName; } 
                  | sb=block { propBlock = $sb.tree; } ) setm=magicPropSetter[atts, $la.tree, mods, $lm.tree, type, $s.token, propBlock, propName, mkBody, rawVarName] -> $setm)
    ;
accessor_modifier:
	'protected' 'internal'? | 'private' | 'internal' 'protected'?;

///////////////////////////////////////////////////////
event_declaration:
	'event'   type
		((member_name   '{') => member_name   '{'   event_accessor_declarations   '}'
		| variable_declarators   ';')	// typename=foo;
		;
event_modifiers:
	modifier+ ;
event_accessor_declarations:
	attributes?   ((add_accessor_declaration   attributes?   remove_accessor_declaration)
	              | (remove_accessor_declaration   attributes?   add_accessor_declaration)) ;
add_accessor_declaration:
	'add'   block ;
remove_accessor_declaration:
	'remove'   block ;

///////////////////////////////////////////////////////
//	enum declaration
///////////////////////////////////////////////////////
enum_declaration[CommonTree atts, CommonTree mods] returns [string name]
scope TypeContext;
:
	e='enum'   identifier  { $name = $identifier.text; $TypeContext::typeName = $identifier.text; } enum_base?   enum_body   ';'? 
            -> ^(ENUM[$e.token, "ENUM"] { dupTree($atts) } { dupTree($mods) } identifier enum_base? enum_body);
enum_base:
	':'   integral_type ;
enum_body:
	'{' (enum_member_declarations ','?)?   '}' -> ^(ENUM_BODY enum_member_declarations? ) ;
enum_member_declarations
@init {
    SortedList<int,CommonTree> members = new SortedList<int,CommonTree>();
    int next = 0;
}
@after{
    $enum_member_declarations.tree = (CommonTree)adaptor.Nil;
    if (next > 0 && next < MAX_DUMMY_ENUMS) {
       int dummyCounter = 0;
       for (int i = 0; i < next; i++) {
          if (members.ContainsKey(i)) {
             adaptor.AddChild($enum_member_declarations.tree, members[i]);
          }
          else {
             adaptor.AddChild($enum_member_declarations.tree, adaptor.Create(IDENTIFIER, $e.start.Token, "__dummyEnum__" + dummyCounter++));
          }
       }
    }
    else {
       Warning($e.tree.Line, "[UNSUPPORTED] We do not yet generate dummy enum members for enums that need more than " + MAX_DUMMY_ENUMS + " entries.");
       foreach (CommonTree en in members.Values) {
          adaptor.AddChild($enum_member_declarations.tree, en);
       }
    }
}
:
	e=enum_member_declaration[members,ref next] (',' enum_member_declaration[members, ref next])* 
    -> 
    ;
enum_member_declaration[ SortedList<int,CommonTree> members, ref int next]
@init {
    int calcValue = 0;
}:
        // Fill in members, a map from enum's value to AST
	attributes?   identifier  { $members[$next] = $identifier.tree; $next++; } 
        ((eq='='   ( ((NUMBER | Hex_number) (','|'}')) => 
                        { $members.Remove($next-1); } 
                           (i=NUMBER { calcValue = Int32.Parse($i.text); } 
                            | i=Hex_number { calcValue = Int32.Parse($i.text.Substring(2), NumberStyles.AllowHexSpecifier); } )  
                        { if (calcValue < 0 || calcValue > Int32.MaxValue) {
                             Warning($eq.line, "[UNSUPPORTED] enum member's value initialization ignored, only numeric literals in the range 0..MAXINT supported for enum values"); 
                             calcValue = $next-1;
                          }
                          else if (calcValue < $next-1) {
                             Warning($eq.line, "[UNSUPPORTED] enum member's value initialization ignored, value has already been assigned and enum values must be unique"); 
                             calcValue = $next-1;
                          }
                          $members[calcValue] = $identifier.tree; $next = calcValue + 1; } 
                  | expression  { Warning($eq.line, "[UNSUPPORTED] enum member's value initialization ignored, only numeric literals supported for enum values"); } ))?)! ;
//enum_modifiers:
//	enum_modifier+ ;
//enum_modifier:
//	'new' | 'public' | 'protected' | 'internal' | 'private' ;
integral_type: 
	'sbyte' | 'byte' | 'short' | 'ushort' | 'int' | 'uint' | 'long' | 'ulong' | 'char' ;

// B.2.12 Delegates
delegate_declaration[CommonTree atts, CommonTree mods] returns [string name]
scope TypeContext;
:
	d='delegate'   return_type   identifier { $name = $identifier.text; $TypeContext::typeName = $identifier.text; }  variant_generic_parameter_list?   
		'('   formal_parameter_list?   ')'   type_parameter_constraints_clauses?   ';' -> 
    ^(DELEGATE[$d.token, "DELEGATE"] { dupTree($atts) } { dupTree($mods) }  return_type   identifier type_parameter_constraints_clauses?  variant_generic_parameter_list?   
		'('   formal_parameter_list?   ')' );
delegate_modifiers:
	modifier+ ;
// 4.0
variant_generic_parameter_list returns [List<string> tyargs]
@init {
    $tyargs = new List<string>();
}:
	'<'!   variant_type_parameters[$tyargs]   '>'! ;
variant_type_parameters [List<string> tyargs]:
	v1=variant_type_variable_name { tyargs.Add($v1.text); } (',' vn=variant_type_variable_name  { tyargs.Add($vn.text); })* -> variant_type_variable_name+ ;
variant_type_variable_name:
	attributes?   variance_annotation?   type_variable_name ;
variance_annotation:
	'in' -> IN | 'out' -> OUT;

type_parameter_constraints_clauses:
	type_parameter_constraints_clause+ ;
type_parameter_constraints_clause:
	'where'   type_variable_name   ':'   type_parameter_constraint_list -> ^(TYPE_PARAM_CONSTRAINT type_variable_name type_parameter_constraint_list?) ;
// class, Circle, new()
type_parameter_constraint_list:                                                   
    ('class' | 'struct')   (','   secondary_constraint_list)?   (','   constructor_constraint)? -> secondary_constraint_list?
	| secondary_constraint_list   (','   constructor_constraint)? -> secondary_constraint_list
	| constructor_constraint -> ;
//primary_constraint:
//	class_type
//	| 'class'
//	| 'struct' ;
secondary_constraint_list:
	secondary_constraint (',' secondary_constraint)* -> secondary_constraint+ ;
secondary_constraint:
	type_name ;	// | type_variable_name) ;
type_variable_name: 
	identifier ;
// keving: TOTEST we drop new constraints,  but what will happen in Java for this case? 
constructor_constraint:
	'new'   '('   ')' ;
return_type:
	type
	|  void_type ;
formal_parameter_list:
	formal_parameter (',' formal_parameter)* -> ^(PARAMS formal_parameter+);
formal_parameter:
	attributes?   (fixed_parameter | parameter_array) 
	| '__arglist';	// __arglist is undocumented, see google
fixed_parameters:
	fixed_parameter   (','   fixed_parameter)* ;
// 4.0
fixed_parameter:
	parameter_modifier?   type   identifier   default_argument? ;
// 4.0
default_argument:
	'=' expression;
parameter_modifier:
	'ref' | 'out' | 'this' ;
parameter_array:
	p='params'^   t=type   identifier 
    { 
       // type will be an array, need to strip the final [] for java
       int numComponents = adaptor.GetChildCount($t.tree); 
       // sanity check
       if (numComponents >= 3 &&
            adaptor.GetType(adaptor.GetChild($t.tree, numComponents-2)) == OPEN_BRACKET &&
            adaptor.GetType(adaptor.GetChild($t.tree, numComponents-1)) == CLOSE_BRACKET) 
       {
           adaptor.DeleteChild($t.tree, numComponents-1);
           adaptor.DeleteChild($t.tree, numComponents-2);
       }
       else {
           Error($p.line, "[SOURCE ERROR] params type must be an array");
       }
    }
    ;

///////////////////////////////////////////////////////
interface_declaration[CommonTree atts, CommonTree mods] returns [string name]
scope TypeContext;
:
	c='interface'   identifier { $name = $identifier.text; $TypeContext::typeName = $identifier.text; }  variant_generic_parameter_list? 
    	interface_base?   type_parameter_constraints_clauses?   interface_body   ';'? 
    -> ^(INTERFACE[$c.Token] { dupTree($atts) } { dupTree($mods) } identifier type_parameter_constraints_clauses? variant_generic_parameter_list? interface_base?  interface_body );

interface_base:
	c=':'   ts+=type (','   ts+=type)* -> ^(EXTENDS[$c.token,"extends"] $ts)*;

interface_modifiers: 
	modifier+ ;
interface_body:
	'{'   interface_member_declarations?   '}' ;
interface_member_declarations:
	interface_member_declaration+ ;
interface_member_declaration:
	a=attributes?    m=modifiers?
		(vt=void_type   im1=interface_method_declaration[$a.tree, $m.tree, $vt.tree] -> $im1
		| ie=interface_event_declaration[$a.tree, $m.tree] -> $ie
		| t=type   ( (identifier type_parameter_list?  '(') => im2=interface_method_declaration[$a.tree, $m.tree, $t.tree] -> $im2
                   // property will rewrite to one, or two method headers
		         | (member_name   '{') => ip=interface_property_declaration[$a.tree, $m.tree, $t.tree] -> $ip //^(PROPERTY[$t.start.Token, "PROPERTY"] $a? $m? $t interface_property_declaration)
				 | ii=interface_indexer_declaration[$a.tree, $m.tree, $t.tree] -> $ii)
		) 
		;
interface_property_declaration [CommonTree atts, CommonTree mods, CommonTree type]:
	i=identifier   '{'   iads=interface_accessor_declarations[atts, mods, type, $i.text]   '}' -> $iads ;
interface_method_declaration [CommonTree atts, CommonTree mods, CommonTree type]:
	identifier   type_parameter_list?
	    '('   formal_parameter_list?   ')'   type_parameter_constraints_clauses?    s=';' magicThrowsException[true,$s.token]
       -> ^(METHOD { dupTree($atts) } { dupTree($mods) } { dupTree($type) } 
            identifier type_parameter_constraints_clauses? type_parameter_list? formal_parameter_list? magicThrowsException);
interface_event_declaration [CommonTree atts, CommonTree mods]:
	//attributes?   'new'?   
	e='event'   type   identifier   ';' -> ^(EVENT[$e.token, "EVENT"] { dupTree($atts) } { dupTree($mods) } type identifier)
   ; 
interface_indexer_declaration [CommonTree atts, CommonTree mods, CommonTree type]: 
	// attributes?    'new'?    type   
	'this'   '['   formal_parameter_list   ']'   '{'   indexer_accessor_declarations[atts,mods,type,$formal_parameter_list.tree]   '}' -> indexer_accessor_declarations ;
interface_accessor_declarations [CommonTree atts, CommonTree mods, CommonTree type, string propName]:
    interface_accessor_declaration[atts, mods, type, propName]+
    ;
interface_accessor_declaration [CommonTree atts, CommonTree mods, CommonTree type, string propName]:
	la=attributes? (g='get' semi=';' magicPropGetter[atts, $la.tree, mods, null, type, $g.token, $semi.tree, propName, false, ""] -> magicPropGetter
                    | s='set' semi=';'  magicPropSetter[atts, $la.tree, mods, null, type, $s.token, $semi.tree, propName, false, ""] -> magicPropSetter)
    ;
	
///////////////////////////////////////////////////////
struct_declaration[CommonTree atts, CommonTree mods] returns [string name]
scope TypeContext;
:
	c='struct'  identifier  { $TypeContext::typeName = $identifier.text; } type_parameter_list? { $name = mkGenericTypeAlias($identifier.text, $type_parameter_list.names); }  class_base?   type_parameter_constraints_clauses?   struct_body[$identifier.text]   ';'? 
    -> ^(CLASS[$c.Token, "class"] { dupTree($atts) } { dupTree($mods) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base? struct_body );

struct_body [string structName]:
	o='{'  magicDefaultConstructor[$o.token, structName] class_member_declarations? '}' ;


///////////////////////////////////////////////////////
indexer_declaration [CommonTree atts, CommonTree mods, CommonTree type]: 
	'this'   '['   formal_parameter_list   ']'   '{'   indexer_accessor_declarations[atts, mods, type, $formal_parameter_list.tree]   '}' -> indexer_accessor_declarations ;
//indexer_declarator:
	//(type_name '.')?   
//	'this'   '['   formal_parameter_list   ']' ;
	

indexer_accessor_declarations [CommonTree atts, CommonTree mods, CommonTree type, CommonTree idxparams]:
    indexer_accessor_declaration[atts, mods, type, idxparams]+;

indexer_accessor_declaration [CommonTree atts, CommonTree mods, CommonTree type, CommonTree idxparams]
@init {
     CommonTree idxBlock = null; 
}:
	la=attributes? lm=accessor_modifier? 
      (g='get' ((';')=> gbe=';'  { idxBlock = $gbe.tree; } 
                | gb=block { idxBlock = $gb.tree; } ) geti=magicIdxGetter[atts, $la.tree, mods, $lm.tree, type, $g.token, idxBlock, idxparams] -> $geti
       | s='set' ((';')=> sbe=';'  { idxBlock = $sbe.tree; }
                  | sb=block { idxBlock = $sb.tree; } ) seti=magicIdxSetter[atts, $la.tree, mods, $lm.tree, type, $s.token, idxBlock, idxparams] -> $seti)
    ;

///////////////////////////////////////////////////////
operator_declaration:
	operator_declarator   operator_body ;
operator_declarator:
	'operator' 
	(('+' | '-')   '('   type   identifier   (binary_operator_declarator | unary_operator_declarator)
		| overloadable_unary_operator   '('   type identifier   unary_operator_declarator
		| overloadable_binary_operator   '('   type identifier   binary_operator_declarator) ;
unary_operator_declarator:
	   ')' ;
overloadable_unary_operator:
	/*'+' |  '-' | */ '!' |  '~' |  '++' |  '--' |  'true' |  'false' ;
binary_operator_declarator:
	','   type   identifier   ')' ;
// >> check needed
overloadable_binary_operator:
	/*'+' | '-' | */ '*' | '/' | '%' | '&' | '|' | '^' | '<<' | '>' '>' | '==' | '!=' | '>' | '<' | '>=' | '<=' ; 

conversion_operator_declaration:
	conversion_operator_declarator   operator_body ;
conversion_operator_declarator:
	('implicit' | 'explicit')  'operator'   type   '('   type   identifier   ')' ;
operator_body:
	block ;

///////////////////////////////////////////////////////
constructor_declaration[CommonTree atts, CommonTree mods, List<string> modList]:
		i=identifier   '('   p=formal_parameter_list?   s=')'   init=constructor_initializer? b=constructor_body[$init.tree] magicThrowsException[true,$s.token]
            ->  ^(CONSTRUCTOR[$i.tree.Token, "CONSTRUCTOR"] { dupTree($atts) } { dupTree($mods) } $i $p? $b magicThrowsException);
constructor_initializer: 
	':' tok='this' '('   argument_list?   ')' 
         -> ^(APPLY[$tok.token, "APPLY"] $tok argument_list?) SEMI[$tok.token, ";"]
	| ':' tok='base' '('   argument_list?   ')' 
         -> ^(APPLY[$tok.token, "APPLY"] SUPER[$tok.token, "super"] argument_list?) SEMI[$tok.token, ";"] 
    ;
constructor_body[CommonTree init]:
	{init == null}?=> s=';' -> $s 
	| s1=';' -> OPEN_BRACE[$s1.token, "{"] { dupTree(init) } CLOSE_BRACE[$s1.token, "}"]
    | a='{' ss+=statement* b='}' -> $a { dupTree(init) } $ss* $b
    ;

///////////////////////////////////////////////////////
//static_constructor_declaration:
//	identifier   '('   ')'  static_constructor_body ;
//static_constructor_body:
//	block ;

///////////////////////////////////////////////////////
destructor_declaration:
	t='~'  identifier   '('   ')'    destructor_body f=magicFinalize[$t.token, $destructor_body.tree] -> $f;
destructor_body:
	block ;

///////////////////////////////////////////////////////
// invocation_expression:
// 	invocation_start   (((arguments[null]   ('['|'.'|'->')) => arguments[ (CommonTree)adaptor.Create(KGHOLE, "KGHOLE") ]   invocation_part)
// 						| invocation_part)*   arguments[ (CommonTree)adaptor.Create(KGHOLE, "KGHOLE") ] ;
// invocation_start:
// 	predefined_type 
// 	| (identifier    generic_argument_list)	=> identifier   generic_argument_list
// 	| 'this' 
// 	| b='base' -> SUPER[$b.token, "super"]
// 	| identifier   ('::'   identifier)?
// 	| typeof_expression             // typeof(Foo).Name
// 	;
invocation_part [CommonTree start]:
	 access_identifier[ $start ]
	| brackets[ $start ] ;

///////////////////////////////////////////////////////

statement:
	(declaration_statement) => declaration_statement
	| (identifier   ':') => labeled_statement
	| embedded_statement 
	;
embedded_statement:
	block
	| selection_statement	// if, switch
	| iteration_statement	// while, do, for, foreach
	| jump_statement		// break, continue, goto, return, throw
	| try_statement
	| checked_statement
	| unchecked_statement
	| lock_statement
	| using_statement 
	| yield_statement 
	| unsafe_statement
	| fixed_statement
	| expression_statement	// expression!
	;
fixed_statement:
	'fixed'   '('   pointer_type fixed_pointer_declarators   ')'   embedded_statement ;
fixed_pointer_declarators:
	fixed_pointer_declarator   (','   fixed_pointer_declarator)* ;
fixed_pointer_declarator:
	identifier   '='   fixed_pointer_initializer ;
fixed_pointer_initializer:
	//'&'   variable_reference   // unary_expression covers this
	expression;
unsafe_statement:
	'unsafe'^   block;
labeled_statement:
	identifier   ':'^   statement ;
declaration_statement:
	(local_variable_declaration 
	| local_constant_declaration) ';' ;
local_variable_declaration returns [List<string> variableNames]:
	local_variable_type   local_variable_declarators { $variableNames = $local_variable_declarators.variableNames; };
local_variable_type:
	('var') => v='var' -> TYPE_VAR[$v.token, "var"]
	| ('dynamic') => d='dynamic' -> TYPE_DYNAMIC[$d.token,"dynamic"]
	| type ;
local_variable_declarators returns [List<string> variableNames]
@init {
    $variableNames = new List<string>(); 
}:
	i1=local_variable_declarator { $variableNames.Add($i1.variableName); } (',' ip=local_variable_declarator { $variableNames.Add($ip.variableName); })* ;
local_variable_declarator returns [string variableName]:
	identifier { $variableName = $identifier.text; } ('='   local_variable_initializer)? ; 
local_variable_initializer:
	expression
	| array_initializer 
	| stackalloc_initializer;
stackalloc_initializer:
	'stackalloc'   unmanaged_type   '['   expression   ']' ;
local_constant_declaration:
	'const'   type   constant_declarators ;
expression_statement:
	expression   ';' ;

// TODO: should be assignment, call, increment, decrement, and new object expressions
statement_expression:
	expression
	;
selection_statement:
	if_statement
	| switch_statement ;
if_statement:
	// else goes with closest if
	i='if'   '('   boolean_expression   ')'   embedded_statement (('else') => else_statement)? -> ^(IF[$i.Token] boolean_expression SEP embedded_statement else_statement?)
//	'if'   '('   boolean_expression   ')'   embedded_statement (('else') => else_statement)?
	;
else_statement:
	'else'   embedded_statement	;
switch_statement:
	s='switch'   '('   expression   ')'   switch_block -> ^($s expression switch_block);
switch_block:
	'{'!   switch_section*   '}'! ;
//switch_sections:
//	switch_section+ ;
switch_section:
	switch_label+   statement_list -> ^(SWITCH_SECTION switch_label+ statement_list);
//switch_labels:
//	switch_label+ ;
switch_label:
	('case'^   constant_expression   ':'!)
	| ('default'   ':'!);
iteration_statement:
	while_statement
	| do_statement
	| for_statement
	| foreach_statement ;
while_statement:
	w='while'   '('   boolean_expression   ')'   embedded_statement -> ^($w boolean_expression SEP embedded_statement);
do_statement:
	'do'   embedded_statement   'while'   '('   boolean_expression   ')'   ';' ;
for_statement:
	f='for'   '('   for_initializer?   ';'   for_condition?   ';'   for_iterator?   ')'   embedded_statement 
         -> ^($f for_initializer? SEP for_condition? SEP for_iterator? SEP embedded_statement);
for_initializer:
	(local_variable_declaration) => local_variable_declaration
	| statement_expression_list 
	;
for_condition:
	boolean_expression ;
for_iterator:
	statement_expression_list ;
statement_expression_list:
	statement_expression (',' statement_expression)* ;
foreach_statement:
	f='foreach'   '('   local_variable_type   identifier   'in'   expression   ')'   embedded_statement 
    -> ^($f local_variable_type   identifier  expression SEP  embedded_statement);
jump_statement:
	break_statement
	| continue_statement
	| goto_statement
	| return_statement
	| throw_statement ;
break_statement:
	'break'   ';' ;
continue_statement:
	'continue'   ';' ;
goto_statement:
	'goto'   ( identifier
			 | 'case'   constant_expression
			 | 'default')   ';' ;
return_statement:
	'return'^   expression?   ';'! ;
throw_statement
// If throw exp is missing then it is the var from closest enclosing catch
@init {
    CommonTree var = null;
    bool missingThrowExp = true;
}:
	t='throw'   (e=expression { missingThrowExp = false;})?  { var = missingThrowExp ? dupTree($catch_clause::throwVar) : $e.tree; } ';' -> ^($t { var });
try_statement:
      t='try'   block   ( catch_clauses   finally_clause?
					  | finally_clause) -> ^($t block catch_clauses? finally_clause?);
// We rewrite the catch clauses so that they all have the form "(catch Type Var)" by introducing
// Throwable and dummy vars as necessary
catch_clauses:
    catch_clause+;
catch_clause
scope { CommonTree throwVar; }
@init {
    CommonTree ty = null, var = null;
}:
    c='catch' ('('   given_t=class_type { ty = $given_t.tree; }  (given_v=identifier { var = $given_v.tree; } | magic_v=magicCatchVar { var = $magic_v.tree; } ) ')'
                 | magic_t=magicThrowableType[true,$c.token] magic_v=magicCatchVar { ty = $magic_t.tree; var = $magic_v.tree; })   { $catch_clause::throwVar = var; } block
       -> ^($c { ty } { var } block)
    ;
finally_clause:
	'finally'^   block ;
checked_statement:
	'checked'   block ;
unchecked_statement:
	'unchecked'   block ;
lock_statement:
	'lock'   '('  expression   ')'   embedded_statement ;
// TODO: Can we avoid surrounding this with braces if not needed?
using_statement
@init {
    CommonTree disposers = null;
}:
// see http://msdn.microsoft.com/en-us/library/yh598w02.aspx for translation
	u='using'   '('    resource_acquisition   c=')'    embedded_statement
     { disposers = addDisposeVars($c.token, $resource_acquisition.resourceNames); } 
     f=magicFinally[$c.token, disposers]
     magicTry[$u.token, state.backtracking == 0 ? embeddedStatementToBlock($u.token, $embedded_statement.tree) : null, null, $f.tree] 
     -> OPEN_BRACE[$u.token, "{"] 
            resource_acquisition SEMI[$c.token, ";"] 
            magicTry
        CLOSE_BRACE[$u.token, "}"] 
     ;
resource_acquisition returns [List<string> resourceNames]
@init {
    $resourceNames = new List<string>();
}:
	(local_variable_declaration) => local_variable_declaration { $resourceNames = $local_variable_declaration.variableNames; } 
	| expression { $resourceNames.Add("__newVar"+newVarCtr); } -> ^(TYPE[$expression.tree.Token, "TYPE"] IDENTIFIER[$expression.tree.Token, "IDisposable"]) 
                         IDENTIFIER[$expression.tree.Token, "__newVar"+newVarCtr++] ASSIGN[$expression.tree.Token, "="] expression //SEMI[$expression.tree.Token, ";"]
    ;
yield_statement:
	'yield'   ('return'   expression   ';'
	          | 'break'   ';') ;

///////////////////////////////////////////////////////
//	Lexar Section
///////////////////////////////////////////////////////

predefined_type returns [string thetext]
@after{
    string newText;
    if (predefined_type_map.TryGetValue($predefined_type.tree.Token.Text, out newText)) {
        $predefined_type.tree.Token.Text = newText;
    }
}:
	  'bool'    { $thetext = "System.Boolean"; }
    | 'byte'    { $thetext = "System.Byte"; }    
    | 'char'    { $thetext = "System.Char"; } 
    | 'decimal' { $thetext = "System.Decimal"; } 
    | 'double'  { $thetext = "System.Double"; } 
    | 'float'   { $thetext = "System.Single"; } 
    | 'int'     { $thetext = "System.Int32"; }   
    | 'long'    { $thetext = "System.Int64"; } 
    | 'object'  { $thetext = "System.Object"; } 
    | 'sbyte'   { $thetext = "System.SByte"; } 
	| 'short'   { $thetext = "System.Int16"; } 
    | 'string'  { $thetext = "System.String"; } 
    | 'uint'    { $thetext = "System.UInt32"; } 
    | 'ulong'   { $thetext = "System.UInt64"; } 
    | 'ushort'  { $thetext = "System.UInt16"; } 
    ;

identifier
@after {
    string fixedId = fixBrokenId($identifier.tree.Token.Text); 
    $identifier.tree.Token.Text = fixedId;
}:
 	IDENTIFIER | also_keyword; 

keyword:
	'abstract' | 'as' | 'base' | 'bool' | 'break' | 'byte' | 'case' |  'catch' | 'char' | 'checked' | 'class' | 'const' | 'continue' | 'decimal' | 'default' | 'delegate' | 'do' |	'double' | 'else' |	 'enum'  | 'event' | 'explicit' | 'extern' | 'false' | 'finally' | 'fixed' | 'float' | 'for' | 'foreach' | 'goto' | 'if' | 'implicit' | 'in' | 'int' | 'interface' | 'internal' | 'is' | 'lock' | 'long' | 'namespace' | 'new' | 'null' | 'object' | 'operator' | 'out' | 'override' | 'params' | 'private' | 'protected' | 'public' | 'readonly' | 'ref' | 'return' | 'sbyte' | 'sealed' | 'short' | 'sizeof' | 'stackalloc' | 'static' | 'string' | 'struct' | 'switch' | 'this' | 'throw' | 'true' | 'try' | 'typeof' | 'uint' | 'ulong' | 'unchecked' | 'unsafe' | 'ushort' | 'using' | 'virtual' | 'void' | 'volatile' ;

also_keyword:
	'add' | 'alias' | 'assembly' | 'module' | 'field' | 'method' | 'param' | 'property' | 'type' | 'yield'
	| 'from' | 'into' | 'join' | 'on' | 'where' | 'orderby' | 'group' | 'by' | 'ascending' | 'descending' 
	| 'equals' | 'select' | 'pragma' | 'let' | 'remove' | 'get' | 'set' | 'var' | '__arglist' | 'dynamic' | 'elif' 
	| 'endif' | 'define' | 'undef';

literal
@init {
   string newText = null;
}
@after{
   if (newText != null) {
      $literal.tree.Token.Text = newText;
   }
}:
	Real_literal
	| n=NUMBER 
      { 
         // Remove '[uU]' from any trailing integer suffix
         newText = $n.text.Replace("u","").Replace("U","");
         if (newText.Length < $n.text.Length) {
            { Warning($n.line, "[UNSUPPORTED] Unsigned number literal converted to signed number: " + $n.text + " -> " + newText); } ;
         }
      }
      -> {UInt64.Parse($n.text.TrimEnd(new Char[] {'u','U','l','L'})) > Int32.MaxValue}? LONGNUMBER[$n.token, newText]
      -> $n
	| h=Hex_number
      { 
         // Remove '[uU]' from any trailing integer suffix
         newText = $h.text.Replace("u","").Replace("U","");
         if (newText.Length < $h.text.Length) {
            { Warning($h.line, "[UNSUPPORTED] Unsigned number literal converted to signed number: " + $h.text + " -> " + newText); } ;
         }
      }
	| Character_literal
	| STRINGLITERAL
	| Verbatim_string_literal
	| TRUE
	| FALSE
	| NULL 
	;

void_type:
    v='void' -> ^(TYPE[$v.token, "TYPE"] $v);

magicThrowableType[bool isOn, IToken tok]:
 -> {isOn}? ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, Cfg.TranslatorExceptionIsThrowable ? "Throwable" : "Exception"])
 -> 
;

magicCatchVar:
  -> IDENTIFIER["__dummyCatchVar" + dummyCatchVarCtr++];

magicPropGetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken getTok, CommonTree body, string propName, bool mkBody, string varName]
@init {
    CommonTree realBody = body;
    CommonTree exceptionList = null;
}: 
    b=magicGetterBody[mkBody,getTok,varName] { if (mkBody) realBody = $b.tree; } e=magicThrowsException[!mkBody,getTok] { if (!mkBody) exceptionList = $e.tree; }
    -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } { dupTree(type)} IDENTIFIER[getTok, "get"+propName] { dupTree(realBody) } { exceptionList }) 
    ;
magicPropSetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken setTok, CommonTree body, string propName, bool mkBody, string varName]
@init {
    CommonTree realBody = body;
    CommonTree exceptionList = null;
}: 
    b=magicSetterBody[mkBody,setTok,varName] { if (mkBody) realBody = $b.tree; } e=magicThrowsException[!mkBody,setTok] { if (!mkBody) exceptionList = $e.tree; }
    -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } ^(TYPE[setTok, "TYPE"] IDENTIFIER[setTok, "void"] ) IDENTIFIER[setTok, "set"+propName] ^(PARAMS[setTok, "PARAMS"] { dupTree(type)} IDENTIFIER[setTok, "value"]) { dupTree(realBody) } { exceptionList } ) 
    ;

magicSemi:
   -> SEMI;

magicMkPropertyVar[CommonTree type, string varText] :
 -> ^(FIELD[$type.token, "FIELD"] PRIVATE[$type.token, "private"] { dupTree(type) } IDENTIFIER[$type.token, varText])
    ; 

magicGetterBody[bool isOn, IToken getTok, string varName]:    
 -> { isOn }? OPEN_BRACE[getTok,"{"] ^(RETURN[getTok, "return"] IDENTIFIER[getTok, varName]) CLOSE_BRACE[getTok,"}"]
 ->
   ;
magicSetterBody[bool isOn, IToken setTok, string varName]:    
 -> { isOn }? OPEN_BRACE[setTok,"{"] IDENTIFIER[setTok, varName] ASSIGN[setTok,"="] IDENTIFIER[setTok, "value"] SEMI[setTok, ";"] CLOSE_BRACE[setTok,"}"]
 -> 
   ;

magicIdxGetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken getTok, CommonTree body, CommonTree idxparams]
: 
    magicThrowsException[true,getTok]
    -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } { dupTree(type)} IDENTIFIER[getTok, "get___idx"] { dupTree(idxparams) } { dupTree(body) } magicThrowsException) 
    ;
magicIdxSetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken setTok, CommonTree body, CommonTree idxparams]
@init {
    CommonTree augParams = dupTree(idxparams);
    adaptor.AddChild(augParams, dupTree($type));
    adaptor.AddChild(augParams, (CommonTree)adaptor.Create(IDENTIFIER, setTok, "value"));
}
:
    magicThrowsException[true,setTok] 
    -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } ^(TYPE[setTok, "TYPE"] IDENTIFIER[setTok, "void"] ) IDENTIFIER[setTok, "set___idx"] { augParams } { dupTree(body) } magicThrowsException ) 
    ;

// keving: can't get this to work reasonably
//magicMkConstModifiers[IToken tok, List<string> filter]: 
//    ({ !filter.Contains("static") }?=> -> STATIC[tok, "static"] ) ( { !filter.Contains("public") }?=> -> $magicMkConstModifiers FINAL[tok, "final"] ); 


magicSmotherExceptions[CommonTree body]:
   magicSmotherExceptionsThrow[body, "RuntimeException"] 
;

magicSmotherExceptionsThrow[CommonTree body, string exception]:
  v=magicCatchVar magicThrowableType[true, body.Token]
 -> OPEN_BRACE["{"]
       ^(TRY["try"] 
            { dupTree(body) }
         ^(CATCH["catch"] magicThrowableType { dupTree($v.tree) } 
           OPEN_BRACE["{"] ^(THROW["throw"] ^(NEW["new"] ^(TYPE["TYPE"] IDENTIFIER[exception]) ^(ARGS["ARGS"] { dupTree($v.tree) }))) CLOSE_BRACE["}"]))
    CLOSE_BRACE["}"]
;

// METHOD{ public static TYPE{ void } main PARAMS{ TYPE{ String [ ] } args } { APPLY{ .{ System exit } ARGS{ APPLY{ .{ Program Main } } } } ;

magicMainArgs[bool isOn, IToken tok]:
 -> { isOn }?
      ^(ARGS[tok, "ARGS"] IDENTIFIER[tok, "args"])
 -> 
;

magicMainApply[bool isOn, IToken tok, string klass, CommonTree args]:
 -> { isOn }?
     ^(APPLY[tok, "APPLY"] ^(DOT[tok,"."] IDENTIFIER[tok,klass] IDENTIFIER[tok,"Main"]) { dupTree(args) } ) 
 -> 
; 

magicMainExit[bool isOn, bool retInt, IToken tok, CommonTree body]:
 -> { isOn && retInt }?
     ^(APPLY[tok, "APPLY"] ^(DOT[tok,"."] IDENTIFIER[tok,"System"] IDENTIFIER[tok,"exit"]) ^(ARGS[tok, "ARGS"] { dupTree(body) } ) ) 
 -> { isOn }?
      { dupTree(body) }
 -> 
;
    

magicMainWrapper[bool isOn, IToken tok, CommonTree body]:
 magicThrowsException[isOn,tok]
 -> { isOn }?
    ^(METHOD[tok, "METHOD"]
         PUBLIC[tok, "public"] STATIC[tok,"static"] 
         ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "void"]) 
             IDENTIFIER[tok, "main"] ^(PARAMS[tok, "PARAMS"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok,"String"] OPEN_BRACKET[tok, "["] CLOSE_BRACKET[tok, "]"]) IDENTIFIER[tok, "args"]) 
              OPEN_BRACE[tok, "{"] { dupTree(body) } SEMI[tok, ";"] CLOSE_BRACE[tok, "}"] 
      magicThrowsException)
 -> 
;

magicTry[IToken tok, CommonTree body, CommonTree catches, CommonTree fin]:
->
   ^(TRY[tok, "try"] { dupTree(body) } { dupTree(catches) } { dupTree(fin) })
;

magicDispose[IToken tok, string var]:
-> ^(IF[tok, "if"] ^(NOT_EQUAL[tok, "!="] IDENTIFIER[tok, var] NULL[tok, "null"]) SEP 
         ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] ^(CAST_EXPR[tok, "CAST"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "IDisposable"]) IDENTIFIER[tok, var]) IDENTIFIER[tok, "Dispose"])) SEMI[tok, ";"])
;

magicFinally[IToken tok, CommonTree statement_list]:
->
   ^(FINALLY[tok, "finally"] OPEN_BRACE[tok, "{"] { dupTree(statement_list) } CLOSE_BRACE[tok, "}"])
;

magicFinalize[IToken tok, CommonTree body]:
 magicThrowsException[true,tok]
->
    ^(METHOD[tok, "METHOD"]
         PROTECTED[tok, "protected"] 
         ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "void"]) IDENTIFIER[tok, "finalize"] 
              OPEN_BRACE[tok, "{"] 
                    ^(TRY[tok, "try"] { dupTree(body) } 
                         ^(FINALLY[tok, "finally"] OPEN_BRACE[tok, "{"] ^(APPLY[tok, "APPLY"] ^(DOT[tok,"."] SUPER[tok,"super"] IDENTIFIER[tok,"finalize"])) SEMI[tok, ";"] CLOSE_BRACE[tok, "}"]))
              CLOSE_BRACE[tok, "}"]
     // Always throws Throwable to match Object.finalize()
     EXCEPTION[tok, "Throwable"])
;

magicDefaultConstructor[IToken tok, string name]:
->
    ^(CONSTRUCTOR[tok, "CONSTRUCTOR"]
         PUBLIC[tok, "public"] 
         IDENTIFIER[tok, name]
              OPEN_BRACE[tok, "{"] 
              CLOSE_BRACE[tok, "}"] 
      )
;

magicThrowsException[bool isOn, IToken tok]:
->  {isOn}? EXCEPTION[tok, Cfg.TranslatorExceptionIsThrowable ? "Throwable" : "Exception"]
-> 
;