header
{
	using System.IO;
	using System.Text;
	using System.Collections;
	using System.Globalization;
	using ASTFrame						= antlr.debug.misc.ASTFrame;
}

options
{
	language 	= "CSharp";	
	namespace	= "RusticiSoftware.Translator";
}

/*
[The "BSD licence"]
Copyright (c) 2002-2005 Kunle Odutola
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.


/// <summary>
/// An AST Printer (or un-parser) that prints the source code that a tree represents. 
/// </summary>
///
/// <remarks>
/// <para>
///	The default behaviour of this PrettyPrinter is to print out the AST and generate
/// source code that is as close to the original as is possible.
/// </para>
/// <para>
///	This behaviour can be overridden by supplying an <see cref="ICodeStyleScheme"/>
/// object that contains the settings for a custom formatting code style.
/// </para>
/// <para>
/// The TreeParser defined below is designed for the AST created by CSharpParser.
/// See the file "CSharpParser.g" for the details of that Parser.
/// </para>
///
/// <para>
/// History
/// </para>
///
/// <para>
/// 05-Jun-2003 kunle	  Created file.
/// </para>
///
/// </remarks>


*/


/* keving:
** 
** Notes
** 
** --  It is important that we don't share parts of the AST (e.g., when we copy the namespace
**     into each compilation unit). BE CAREFUL TO CREATE A FRESH COPY INSTEAD OF SHARING.
**   
** --  We throw out nearly anything embedded in Pre Processing Directives, can we, should we do better?
**
*/
class CSharpTranslator extends TreeParser("RusticiSoftware.Translator.JavaTreeParser");

options
{
	importVocab						= CSharpJava;
	buildAST						= true;
	ASTLabelType					= "ASTNode";
	defaultErrorHandler				= true;
}

//=============================================================================
// Start of CODE
//=============================================================================

{	
	// counter so that dummy Catchall Vars will be unique 
	private int dummyCatchallCtr = 0;
	
	// throw 'holes' for the exception being caught in the current context
	private ArrayList currExHoles = new ArrayList();
	 
	private Hashtable defines = new Hashtable(); 
	
	private bool NotExcluded(CodeMaskEnums codeMask, CodeMaskEnums construct)
	{
		return ((codeMask & construct) != 0 );
	}
	
	    
        // If this is the Main method then return a wrapper 'main' method, else return null
        private AST WrapMainMethod(AST mods, AST typ, AST id, AST ps)
        {
            if (id.getText() != "Main")
                // Quick exit
                return null;
            else
            {   
                // Check return type is int or void
                // typ == #( TYPE (identifier | predefinedType | VOID) #(ARRAY_RANKS (rankSpecifier)*) )
                AST rettyp = typ.getFirstChild();
                if (typ.getFirstChild().getNextSibling().getFirstChild() == null &&
                    (rettyp.Type == VOID || rettyp.Type == INT))
                   {
                      // return type ok, check params is empty or string[], looking for
                      // #(	FORMAL_PARAMETER_LIST #( PARAMETER_FIXED type[w] identifier[w]) ) 
                      if (ps.getNumberOfChildren() < 2)
                      {
                         AST arg = ps.getFirstChild();
                         if (arg == null ||
                              // Arguments type is string
                             (arg.getFirstChild().getFirstChild().Type == STRING &&
                              // and it is a single array
                              arg.getFirstChild().getFirstChild().getNextSibling().getFirstChild().Type == ARRAY_RANK &&
                              arg.getFirstChild().getFirstChild().getNextSibling().getFirstChild().getFirstChild() == null)) 
                         {
                            // Finally,  is it public and static?
                            // keving:  According to $3.1 the Main method does not have to be public
                            AST m = mods.getFirstChild();
                            bool stat = false;
                            // mbool pub = false;
                            while (m != null) 
                            {
                                if (m.Type == STATIC)
                                  stat = true;
                                //if (m.Type == PUBLIC)
                                  //pub = true;
                                m = m.getNextSibling();  
                            }
                            if (stat)// && pub) 
                            {
                                // Found a Main method, construct wrapper
                                // public static void main(String[] args) 
                                ASTNode ret =  #( [METHOD_DECL],
													#( [MODIFIERS], [PUBLIC, "public"], [STATIC, "static"] ),
													#( [TYPE], [VOID, "void"], #( [ARRAY_RANKS] ) ),
													[IDENTIFIER, "main"],
													#( [FORMAL_PARAMETER_LIST], 
														#( [PARAMETER_FIXED], 
															 #( [TYPE], [STRING, "String"], #( [ARRAY_RANKS] , [ARRAY_RANK] ) ),
															[IDENTIFIER, "args"]
														) 
													), #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ) );
								// The body varies depending on return type of Main and arguments
								AST mainArgs = null;
								string innerMain = this.ClassInProcess + ".Main(${args})";
								if (arg != null)
								   mainArgs = #( [EXPR], [IDENTIFIER, "args"] ); 
								AST mainCall = #( [EXPR],
													#( [JAVAWRAPPER],
													    #([IDENTIFIER, innerMain]),
														#([IDENTIFIER, "${args}"]), #([EXPR_LIST], mainArgs)
													 )
											     );
								AST wrappedMainCall = null; 
								if (rettyp.Type == INT) 
									wrappedMainCall = #( [EXPR],
															#( [JAVAWRAPPER],
															    #( [IDENTIFIER, "System.exit(${call})"] ),
																	#([IDENTIFIER, "${call}"]), #( [EXPR_LIST], mainCall )
																 ) );
								else
								    wrappedMainCall = mainCall;	 												 
								ret.addChild( #([BLOCK], #([EXPR_STMT], wrappedMainCall)) );
								return ret;
                            }
                            else
                              return null;
                         }
                         else 
                           return null;
                       }
                       else
                         return null;
                   }
                 else  
                   return null;
            } 
        }
        
        
        private ASTNode SmotherCheckedExceptions(ASTNode b, String reThrow) 
        {
        
        	
        	ASTNode retAST = #( [TRY, "try"],
								  astFactory.dupTree(b),
							      #( [CATCH, "catch"],
									   #( [FIELD_DECL, "FIELD_DECL"], 
										  #( [MODIFIERS, "MODIFIERS"] ),
										  #( [TYPE, "type"],
										    #( [JAVAWRAPPER], [IDENTIFIER, "Exception"] ),
										    #( [ARRAY_RANKS] ) 
										   ),
										  #( [VAR_DECLARATOR, "VAR_DECLARATOR"],
												#( [IDENTIFIER, "__e"] ) 
										   ) ),
									   #( [BLOCK], 
											#( [THROW, "throw"],
												#([EXPR], #( [OBJ_CREATE_EXPR, "new"], 
													#( [TYPE], #([JAVAWRAPPER], [IDENTIFIER, reThrow]), #([ARRAY_RANKS]) ), 
											        #( [EXPR_LIST], #([EXPR],  #([IDENTIFIER, "__e"]) ) ) ) )
												)) 
								       ) );
		    return retAST;
		    
		}
        
        // Strip one rank from array type t
        // t = #( TYPE baseType #( ARRAY_RANKS #( ARRAY_RANK COMMA* )* )
        // NOTE: Updates t in place
        private ASTNode StripOneRank(ASTNode t)
        {
			ASTNode baseType = (ASTNode) t.getFirstChild();
			ASTNode arrayRanks = (ASTNode) baseType.getNextSibling();
			ASTNode rank = (ASTNode) arrayRanks.getFirstChild();
			
			if (rank == null)
			{
			    Console.Error.WriteLine("ERROR: Expected array type");
			}
			else
			{
			    if (rank.getNextSibling() == null)
			    {
			       // Only one rank
			       arrayRanks.setFirstChild(null);
			    }
			    else
			    {
			       // Remove final rank (we could just remove the first, but ....) 
			       ASTNode prev = rank;  // Init to 1st element 
			       rank = (ASTNode) rank.getNextSibling(); // Init to 2nd element
				   while (rank.getNextSibling() != null)  // Will always succeed first time through
				   {
				      prev = rank;
				      rank = (ASTNode) rank.getNextSibling();
				   }
				   prev.setNextSibling(null);
				 }
			}
			return t;	 
        }
}

//=============================================================================
// Start of RULES
//=============================================================================


// A C# compilation unit is a file containing a mixture of namespaces, classes,
// instances, structs, etc. We do some jiggery pokery here to convert this to a list
// of Java compilation units, each of which contains appropriate package names, imports,
// and a single class / instance declaration.  Each of these Java compilation units will be
// written out to a separate java source file by the driver program.  
compilationUnit! [Object w]
	:	#(	COMPILATION_UNIT
			justPreprocessorDirectives[w]
			uses:usingDirectives[w]
			globalAttributes[w]
			cus:namespaceMemberDeclarations[w, #uses, null /* initial namespace */]
		)
		{ ## = #( [MULTI_COMPILATION_UNITS], cus) ; }
	;
	
usingDirectives [Object w]
	:	#(	USING_DIRECTIVES
			(	preprocessorDirective[w, CodeMaskEnums.UsingDirectives]
			|	usingDirective[w] 
			)*
		) 
	;
	
usingDirective [Object w]
	:	#( USING_NAMESPACE_DIRECTIVE 	
			pn:qualifiedIdentifier[w] 			
		) 
	|	#(	USING_ALIAS_DIRECTIVE     	
			alias:identifier[w] 					
			pna:qualifiedIdentifier[w] 			
		)
	;

namespaceMemberDeclarations [Object w, ASTNode uses, ASTNode pns]
	:	(	namespaceMemberDeclaration[w, uses, pns]
		|	preprocessorDirective[w, CodeMaskEnums.NamespaceMemberDeclarations]
		)* 
	;
	
namespaceMemberDeclaration [Object w, ASTNode uses, ASTNode ns]
	:	namespaceDeclaration[w, uses, ns]
	|	typeDeclaration[w, uses, ns, true]
	;
	
typeDeclaration! [Object w, ASTNode uses, ASTNode ns, bool topLevel]
		{ if (topLevel) {
			// reset java imports for each compilation unit 
			initialize();
		  };
		}
		:	cla:classDeclaration[w]
				{ if (topLevel) {					
				     ## = #( [COMPILATION_UNIT], 
							   #( [PACKAGE_DEF, "package"], astFactory.dupTree(ns)), 
							       astFactory.dupTree(uses), GetImports(), 
							       #cla );
				  }			    
				  else {
				     ## = #cla ;
				  }; }
		|	str:structDeclaration[w]
				{ if (topLevel) {
					  ## = #( [COMPILATION_UNIT], 
								#( [PACKAGE_DEF, "package"], astFactory.dupTree(ns)),
								astFactory.dupTree(uses), GetImports(),
								#str ); 
				  } else {
					  ## = #str;
				  }; }
		|	iface:interfaceDeclaration[w]		
				{ if (topLevel) {
					  ## = #( [COMPILATION_UNIT], 
								#( [PACKAGE_DEF, "package"], astFactory.dupTree(ns)),
								astFactory.dupTree(uses), GetImports(),
								#iface ); 
				  } else {
					  ## = #iface;
				  }; }
		|	enm:enumDeclaration[w]
				{ if (topLevel) {
					  ## = #( [COMPILATION_UNIT], 
								#( [PACKAGE_DEF, "package"], astFactory.dupTree(ns)),
								astFactory.dupTree(uses), GetImports(),
								#enm ); 
				  } else {
					  ## = #enm;
				  }; }		
		|	delegateDeclaration[w]	
	;

namespaceDeclaration! [Object w, ASTNode uses, ASTNode ns]
 { ASTNode add_ns = null; 
   ASTNode new_ns = null, tmp = null;
   }
	:	#(  NAMESPACE  
			m_ns:qualifiedIdentifier[w] { if (ns == null) 
											// easy, replace with new namespace 
											add_ns = #m_ns;
										  else {
										    if (ns.Type == IDENTIFIER) 
												// A single identifier, just prepend it to existing namespace
										        add_ns = #( [DOT], #( [IDENTIFIER, ns.getText()] ), #m_ns);
										    else {
												// A dotted identifier list, build new qualified id 
												tmp = (ASTNode) ns.getFirstChild();
												new_ns = #( [DOT], #( [IDENTIFIER, tmp.getText()] ) );  
												add_ns = new_ns;     // remember head of new namespace  
												//new_ns = (ASTNode) add_ns.getFirstChild();
												tmp = (ASTNode) tmp.getNextSibling();   
												while (tmp.Type == DOT) {
													tmp = (ASTNode) tmp.getFirstChild();
													new_ns.addChildEx( #( [DOT], #( [IDENTIFIER, tmp.getText()] ) ) );
													new_ns = (ASTNode) new_ns.getFirstChild().getNextSibling(); // Newly added child
													tmp = (ASTNode) tmp.getNextSibling();
												}
												new_ns.addChildEx(#( [DOT], #( [IDENTIFIER, tmp.getText()] ), #m_ns));
											} 
										  } ; }
			bdy:namespaceBody[w, uses, add_ns] 
		) { ## = #bdy; }
	;
	
namespaceBody! [Object w, ASTNode uses, ASTNode ns]
 { ASTNode uses_plus = null; }
	:	#( NAMESPACE_BODY  m_uses:usingDirectives[w]  
				{ uses_plus = (ASTNode) astFactory.dupTree(uses); 
				  uses_plus.addChild(#m_uses.getFirstChild()); 
				} 
			mem:namespaceMemberDeclarations[w, uses_plus, ns] 
			CLOSE_CURLY    
		) { ## = #mem; }
	;
	
classModifiers [Object w]
	:	modifiers[w]			// indirection for any special processing
	;
		
modifiers [Object w] // TODO: If there are no access modifiers then make it private (check)
	:	#( MODIFIERS ( modifier[w] )* )
	;

modifier [Object w]
	:	( ABSTRACT				// tick
		|/*nw:*/NEW			
		|/*ov:*/OVERRIDE		
		| PUBLIC				// tick	
		| PROTECTED				// tick	
		|! INTERNAL		{## = null;}    // equate internal to package level. This is default in Java. 		
		| PRIVATE				// tick	
		|! SEALED       { ## = #( [FINAL, "final"] ); }              // tick
		|/*st:*/STATIC		
		|/*vi:*/VIRTUAL		
		|/*ext:*/EXTERN		
		|! READONLY		{ ## = #( [FINAL, "final"] ); }              // tick
		|/*un:*/UNSAFE		
		|/*vo:*/VOLATILE		
		)
	;


typeName [Object w]
	:	predefinedType[w]
	|	qualifiedIdentifier[w]
	;
	
classTypeName [Object w]
	:	qualifiedIdentifier[w]
	|!   OBJECT    { ## = #( [DOT, "DOT"], #( [IDENTIFIER, "System"] ),	#( [IDENTIFIER, "Object"] ) ); }		
	|/*str:*/STRING			
	;
	
identifier [Object w]
	: id:IDENTIFIER	{ fixBrokenIds(##); }	
	;
	
qualifiedIdentifier [Object w]
	:	(	identifier[w]
		|	#(DOT identifier[w]	qualifiedIdentifier[w] )
		) 
	;

	
//
// A.2.2 Types
//

type [Object w]
	:	#(	TYPE 
			( 	qualifiedIdentifier[w]
			| 	predefinedType[w]
			|   VOID 
			)
			pointerSpecifier[w]! // TODO: Bleargh. We don't support pointer types
			rankSpecifiers[w]			
		)
	;
	
pointerSpecifier [Object w]
	:	#(	STARS 
			( STAR 
			)* 
		)
	;
	
classType [Object w]
	:	qualifiedIdentifier[w]
	|!  OBJECT  { ## = #( [DOT, "DOT"], #( [IDENTIFIER, "System"] ),	#( [IDENTIFIER, "Object"] ) ); }			
	|/*str:*/STRING			
	;
	
interfaceType [Object w]
	:	qualifiedIdentifier[w]
	;

delegateType [Object w]
	:	qualifiedIdentifier[w]		// typeName
	;

/*	
pointerType
	:	unmanagedType STAR
	|	VOID STAR
	;
*/	
unmanagedType [Object w]
	:	qualifiedIdentifier[w]		// typeName
	;
	
//
// A.2.3 Variables
//

variableReference [Object w]
	:	expression[w]
	;

//
// A.2.4 Expressions
//
	
argumentList [Object w]  // Simplify by making it an EXPR_LIST
	:	#(	ARG_LIST { ##.setType(EXPR_LIST); ##.setText("EXPR_LIST"); } argument[w] ( argument[w] )*
		)
	;
	
argument [Object w]
	:	expression[w]
	|!	#(/*r:*/REF variableReference[w] )
	|!	#(/*o:*/OUT variableReference[w] )
	;

constantExpression [Object w]
	:	expression[w]
	;
	
booleanExpression [Object w]
	:	expression[w]
	;
	
expressionList [Object w]
	:	#(	EXPR_LIST expression[w] ( expression[w] )*
		)
	;

expression [Object w]
	:	#( EXPR expr[w] )
	;
	
expr [Object w]
		// assignmentExpression
		//
	:	#( ASSIGN         expr[w]  expr[w] )
	// We expand 
	|!	#( PLUS_ASSIGN    pal:expr[w]  par:expr[w]   { ## = #( [ASSIGN, "="], astFactory.dupTree(#pal), #( [PLUS, "+"], astFactory.dupTree(#pal), astFactory.dupTree(#par) ) ); }    )
	|!	#( MINUS_ASSIGN   mal:expr[w]  mar:expr[w]   { ## = #( [ASSIGN, "="], astFactory.dupTree(#mal), #( [MINUS, "-"], astFactory.dupTree(#mal), astFactory.dupTree(#mar) ) ); }   )
	|!	#( STAR_ASSIGN    sal:expr[w]  sar:expr[w]   { ## = #( [ASSIGN, "="], astFactory.dupTree(#sal), #( [STAR, "*"], astFactory.dupTree(#sal), astFactory.dupTree(#sar) ) ); }    )
	|!	#( DIV_ASSIGN     dal:expr[w]  dar:expr[w]   { ## = #( [ASSIGN, "="], astFactory.dupTree(#dal), #( [DIV, "/"], astFactory.dupTree(#dal), astFactory.dupTree(#dar) ) ); }     )
	|!	#( MOD_ASSIGN     moal:expr[w]  moar:expr[w] { ## = #( [ASSIGN, "="], astFactory.dupTree(#moal), #( [MOD, "%"], astFactory.dupTree(#moal), astFactory.dupTree(#moar) ) ); }  )
	|!	#( BIN_AND_ASSIGN baal:expr[w]  baar:expr[w] { ## = #( [ASSIGN, "="], astFactory.dupTree(#baal), #( [BIN_AND, "&"], astFactory.dupTree(#baal), astFactory.dupTree(#baar) ) ); }   )
	|!	#( BIN_OR_ASSIGN  boal:expr[w]  boar:expr[w] { ## = #( [ASSIGN, "="], astFactory.dupTree(#boal), #( [BIN_OR, "|"], astFactory.dupTree(#boal), astFactory.dupTree(#boar) ) ); }    )
	|!	#( BIN_XOR_ASSIGN bxal:expr[w]  bxar:expr[w] { ## = #( [ASSIGN, "="], astFactory.dupTree(#bxal), #( [BIN_XOR, "^"], astFactory.dupTree(#bxal), astFactory.dupTree(#bxar) ) ); }   )
	|!	#( SHIFTL_ASSIGN  slal:expr[w]  slar:expr[w] { ## = #( [ASSIGN, "="], astFactory.dupTree(#slal), #( [SHIFTL, "<<"], astFactory.dupTree(#slal), astFactory.dupTree(#slar) ) ); }    )
	|!	#( SHIFTR_ASSIGN  sral:expr[w]  srar:expr[w] { ## = #( [ASSIGN, "="], astFactory.dupTree(#sral), #( [SHIFTR, ">>"], astFactory.dupTree(#sral), astFactory.dupTree(#srar) ) ); }    )
		// conditionalExpression
		//
	|	#( QUESTION       expr[w]  expr[w]  expr[w] )
	
		// conditional-XXX-Expressions
		//
	|	#( LOG_OR         expr[w]  expr[w] )
	|	#( LOG_AND        expr[w]  expr[w] )
		
		// bitwise-XXX-Expressions
		//
	|	#( BIN_OR         expr[w]  expr[w] )
	|	#( BIN_XOR        expr[w]  expr[w] )
	|	#( BIN_AND        expr[w]  expr[w] )

		// equalityExpression
		//
	|	#( EQUAL          expr[w]  expr[w] )
	|	#( NOT_EQUAL      expr[w]  expr[w] )

		// relationalExpression
		//
	|	#( LTHAN          expr[w]  expr[w] )
	|	#( GTHAN          expr[w]  expr[w] )
	|	#( LTE            expr[w]  expr[w] )
	|	#( GTE            expr[w]  expr[w] )
	|	#( IS             expr[w]  type[w] ) {##.setType(INSTANCEOF); ##.setText("instanceof"); }
	|!	#( AS             e:expr[w]  t:type[w] ) { ASTNode e1 = (ASTNode) astFactory.dupTree(#e);
												   ASTNode e2 = (ASTNode) astFactory.dupTree(#e);
												   ASTNode t1 = (ASTNode) astFactory.dupTree(#t);
												   ASTNode t2 = (ASTNode) astFactory.dupTree(#t);
												   ASTNode t3 = (ASTNode) astFactory.dupTree(#t);
												   ## = #( [QUESTION, "?"], #( [INSTANCEOF, "instanceof"], e1, t1),
																			#( [CAST_EXPR], t2, e2),
																			#( [CAST_EXPR], t3, #( [NULL, "null"]))); }
		// shiftExpression
		//
	|	#( SHIFTL         expr[w]  expr[w] )
	|	#( SHIFTR         expr[w]  expr[w] )

		// additiveExpression
		//
	|	#( PLUS           expr[w]  expr[w] )
	|	#( MINUS          expr[w]  expr[w] )
		// multiplicativeExpression
		//
	|	#( STAR           expr[w]  expr[w] )
	|	#( DIV            expr[w]  expr[w] )
	|	#( MOD            expr[w]  expr[w] )
	|	unaryExpression[w]
	;	

unaryExpression [Object w]
	:	#( CAST_EXPR 			 type[w] expr[w] )
	|	#( INC              	 expr[w] )
	|	#( DEC              	 expr[w] )
	|	#( UNARY_PLUS       	 expr[w] )
	|	#( UNARY_MINUS      	 expr[w] )
	|	#( LOG_NOT             expr[w] )
	|	#( BIN_NOT			 expr[w] )
	|	#(/*p:*/PTR_INDIRECTION_EXPR  expr[w] )
	|	#(/*a:*/ADDRESS_OF_EXPR       expr[w] )
	|	primaryExpression[w]
	;
	
primaryExpression [Object w]
	:	#(	// invocationExpression ::= primaryExpression OPEN_PAREN ( argumentList )? CLOSE_PAREN 
			INVOCATION_EXPR			primaryExpression[w] 
									(a:argumentList[w])?   { if (a == null) ##.addChild( #( [EXPR_LIST, "EXPR_LIST"] ) ); }
		)
	|	#(	// elementAccess		::= primaryNoArrayCreationExpression OPEN_BRACK expressionList CLOSE_BRACK
		/*e:*/ELEMENT_ACCESS_EXPR		primaryExpression[w] 
										expressionList[w]    
		)
	|	#(	// pointerElementAccess ::= primaryNoArrayCreationExpression OPEN_BRACK expression     CLOSE_BRACK
		/*p:*/PTR_ELEMENT_ACCESS_EXPR	primaryExpression[w]  
										expressionList[w]    
		)
	|	#(	// memberAccess		    ::= primaryExpression DOT identifier
		    MEMBER_ACCESS_EXPR      									
			(	type[w]
			|	primaryExpression[w]
			)
			identifier[w]
		)
	|	// pointerMemberAccess
		#(/*d:*/DEREF                   primaryExpression[w]  identifier[w] )
	|	// postIncrementExpression
		#( POST_INC_EXPR          primaryExpression[w]  )
	|	// postDecrementExpression
		#( POST_DEC_EXPR          primaryExpression[w]  )
	|	basicPrimaryExpression[w]
	;

basicPrimaryExpression [Object w]
	:	literal[w]
	|	identifier[w]										// simpleName
	|!	// parenthesizedExpression
		//
		#( PAREN_EXPR  e:expr[w]  )    { ## = #e; }
	| THIS 		
	|!	#( BASE		
			(	i:identifier[w]   { ## = #( [MEMBER_ACCESS_EXPR, "MEMBER_ACCESS_EXPR"],
												[SUPER, "super"], #i); }
			|	expressionList[w]  // TODO:access to base class indexer ... 
			)
		) 
	|	newExpression[w]
	|!	// typeofExpression    rewrite to call <typename>.class
		//
		#( TYPEOF 	 t:type[w] )   
		     { 
		         string classCall = typeToString(#t, false) + ".class"; ## =  #( [JAVAWRAPPER], [IDENTIFIER, classCall] ); 
		         ##.DotNetType = new TypeRep();
		         ##.DotNetType.TypeName = "System.Type"; /* ugly, will fill this type in on the next pass when we have correct type context */ 
		     }
//	|!	// typeofExpression    rewrite to call Class.forName("type name");
//		//
//		#( TYPEOF 	 t:type[w] )   {## =  #( [INVOCATION_EXPR, "INVOCATION_EXPR"],
//												#( [JAVAWRAPPER],
//													#( [IDENTIFIER, "Class.forName"] )
//												  ),
//												#( [EXPR_LIST, "EXPR_LIST"],
//													#( [EXPR, "EXPR"],
//														[STRING_LITERAL, typeToString(#t)] ) )
//											);
//									addImport("RusticiSoftware.JavaSupport.ClassSupport");   // To get a forName that converts checked exception to unchecked
//									}
	|	#(/*s:*/SIZEOF    	 unmanagedType[w]  )
	|!	#(/*c:*/CHECKED   	 ce:expression[w]  )     { ## = (ASTNode) #ce.getFirstChild(); }          // keving: TODO RusticiSoftware.ScormContentPlayer.Logic\Types\ScormTimeSpan.cs uses checked expressions
	|!	#(/*u:*/UNCHECKED 	 ue:expression[w]  )     { ## = (ASTNode) #ue.getFirstChild(); }     
	;

newExpression [Object w]
		// objectCreationExpression	  
		//
	:	#(OBJ_CREATE_EXPR  type[w]  (al:argumentList[w])?  )  { if (#al == null) ##.addChild( #([EXPR_LIST, "EXPR_LIST"]) ); }
		// delegateCreationExpression	  
		//
	|	#(/*dl:*/DLG_CREATE_EXPR  type[w]  argumentList[w]  )
		// arrayCreationExpression
		//
	|	#( ARRAY_CREATE_EXPR 
			type[w]										// nonArrayType ( rankSpecifiers )?
			(	 arrayInitializer[w]
			|	 expressionList[w]  
				 rankSpecifiers[w] ( arrayInitializer[w] )? 
			)
		)
	;

literal [Object w]
	: TRUE				
	| FALSE			
	|! i:INT_LITERAL		{ long val = 0;
	                          if (#i.getText().TrimStart().StartsWith("0x"))
	                             val = Int64.Parse(#i.getText().TrimStart().Substring(2), NumberStyles.AllowHexSpecifier); 
	                           else
	                             val = Int64.Parse(#i.getText());
						      if (val > Int32.MaxValue) 
						        ## = #( [LONG_LITERAL, #i.getText()] );
						      else
						        ## = (ASTNode) astFactory.dupTree(#i); 
						    }		
	|! u:UINT_LITERAL	    { ulong val = 0;
	                          if (#i.getText().TrimStart().StartsWith("0x"))
	                             val = UInt64.Parse(#i.getText().TrimStart().Substring(2), NumberStyles.AllowHexSpecifier); 
	                           else
	                             val = UInt64.Parse(#i.getText());
						      if (val > UInt32.MaxValue) 
						        ## = #( [ULONG_LITERAL, #u.getText()] );
						      else
						        ## = (ASTNode) astFactory.dupTree(#u); 
						    }
	| LONG_LITERAL		
	| ULONG_LITERAL	
	| DECIMAL_LITERAL	
	| FLOAT_LITERAL	
	| DOUBLE_LITERAL	
	| CHAR_LITERAL		
	| s:STRING_LITERAL	{ String slit = #s.getText();
						  if (slit.StartsWith("@\"")) {
						     // escape string for Java
						     ##.setText("\"" + escapeJavaString(slit.Substring(2,slit.Length - 2)) + "\"");
						  }
						  // TODO: Check that C# escaped strings are Java escaped
						  ; }
	| NULL				
	;

predefinedType [Object w]
	: BOOL        {##.setText("boolean");}				 
	| BYTE		  {##.setText("byte");} // TODO: Not available		
	| CHAR				
	| DECIMAL	  {##.setText("Decimal");} // TODO: Not available		
	| DOUBLE			
	| FLOAT			
	| INT				
	| LONG				
	| OBJECT	  { ##.setText("Object"); }
	| SBYTE		  { ##.setText("byte"); }
	| SHORT			
	| STRING	 {##.setText("String");}			
	| UINT		{##.setText("int");} // TODO: Not available		
	| ULONG		{##.setText("long");} // TODO: Not available	
	| USHORT	{##.setText("short");} // TODO: Not available		
	;
	

//
// A.2.5 Statements
//

statement [Object w]
	:	#(/*l:*/LABEL_STMT identifier[w]  statement[w]
		)
	|	localVariableDeclaration[w] 
	|	localConstantDeclaration[w]
	|	embeddedStatement[w]
	|	preprocessorDirective[w, CodeMaskEnums.Statements]
	;
	
embeddedStatement [Object w]
	:	block[w]
	|   EMPTY_STMT	
	|	#( EXPR_STMT statementExpression[w]  )
	|	#(IF 
			 expression[w]  embeddedStatement[w]
			(	#(ELSE 
					embeddedStatement[w]
				)
			)? 
		)
	|!	// TODO: If the scrutinee has string type then we must convert to if-then-else :-( 
		{ ASTNode ret = #( [SWITCH, "switch"] ); }
	    #(   SWITCH 
			 se:expression[w] { ret.addChild(#se); } 
			#( OPEN_CURLY 
				( ss:switchSection[w] { ret.addChild(#ss); } )* 
			 CLOSE_CURLY 
			) 
		) { ## = ret; }
	|	#( FOR 
			#( FOR_INIT ( ( localVariableDeclaration[w] | ( statementExpression[w] )+ ) )? )
			
			#( FOR_COND ( booleanExpression[w] )? )
			
			#( FOR_ITER ( ( statementExpression[w] )+  )? )
			
			embeddedStatement[w]
		)
	|	#( WHILE booleanExpression[w] embeddedStatement[w] )
	|	#(/*doStmt:*/DO 
			embeddedStatement[w]  booleanExpression[w] 
		)
	|	#( FOREACH localVariableDeclaration[w]  expression[w] embeddedStatement[w] )
	|   BREAK		
	|   CONTINUE	
	|	#(/*gto:*/GOTO 	
			(	identifier[w] 
			|/*cse:*/CASE 	 constantExpression[w]
			|/*dfl:*/DEFAULT	
			)
			
		)
	|	#(/*rtn:*/RETURN  ( expression[w] )?  )
	|	{ bool throwExp = false; } #(  THROW   ( expression[w] { throwExp = true; } )?  ) 
	          { if (!throwExp) 
	            {
	               ASTNode hole = #( [IDENTIFIER, "ThrowHole"] );  
	               ##.addChild( #( [EXPR], hole ) );
	               // To be fixed up later in catchClause when we know what the exception var is 
	               currExHoles.Add(hole);
	            };
	          }
	|	tryStatement[w]
	|	#(/*checkedStmt:*/CHECKED 	   block[w]
		)
	|	#(/**/UNCHECKED	   block[w]
		)
	|	#(/*lockStmt:*/LOCK 
			 expression[w]  embeddedStatement[w]
		)
	|!	
		// rewrite 'using (<var decs> | <expr>) slist' to 
		// <var decs>; try <slist> finally { if (<var> != null) <var>.Dispose(); ... <expr>.Dispose(); }
	    { ASTNode decsAST = null; }  
	    #(USING 
			 rs:resourceAcquisition[w] 
			 {  // Collect all the resources from #rs that need to be Disposed of.
				ArrayList resources = new ArrayList();
				if (#rs.Type == EXPR)
				{
				   // A single resource indicated by an expression (EXPR e)
				   resources.Add(astFactory.dupTree(#rs.getFirstChild()));
				} 
				else if (#rs.Type == FIELD_DECL)
				{
				   // Collect all the variable names so that they can be disposed of later
				   ASTNode varsAST = (ASTNode) #rs.getFirstChild().getNextSibling().getNextSibling();
				   while (varsAST != null)
				   {
				      // (VAR_DECLARATOR identifier (init?))
				      resources.Add(astFactory.dupTree(varsAST.getFirstChild()));
				      varsAST = (ASTNode) varsAST.getNextSibling();
				   }   
				   // Emit variable declarations
				   decsAST = (ASTNode) astFactory.dupTree(#rs);
				}
				else
				{
				   Console.Error.WriteLine("ERROR -- (using): unexpected resource specification: ");
				}
			 }
			  slist:embeddedStatement[w]
		)	
		{
		   // Build up the try .. finally statement
		   ASTNode tryAST = null;
		   ASTNode fBlockAST = #( [BLOCK] );
		   if (slist.Type == BLOCK)
		      tryAST = #( [TRY, "try"], astFactory.dupTree(#slist));
		   else
		      tryAST = #( [TRY, "try"], #( [BLOCK], astFactory.dupTree(#slist) ) );
		   foreach (ASTNode expr in resources)
		   {
		      fBlockAST.addChild( #( [IF, "if"], 
		                                #( [EXPR], #( [NOT_EQUAL, "!="], astFactory.dupTree(expr), #( [NULL, "null"] ) ) ), 
		                                #( [BLOCK], #( [EXPR_STMT], #( [EXPR], #( [INVOCATION_EXPR], #( [MEMBER_ACCESS_EXPR], expr, [IDENTIFIER, "Dispose"] ), #( [EXPR_LIST] ) ) ) ) ) 
		                           ) );
		   }
		   tryAST.addChild( #( [FINALLY, "finally"], fBlockAST ) );  
		   ## = (decsAST == null ? tryAST : #( null, decsAST, tryAST) );
		}
	|	#(/*unsafeStmt:*/UNSAFE 
			block[w]
		)
	|	// fixedStatement
		#(/*fixedStmt:*/FIXED 
			 type[w] 
			fixedPointerDeclarator[w] (  fixedPointerDeclarator[w] )*
			
			embeddedStatement[w]
		)
	;
	
body [Object w]
	:	block[w]
	|   EMPTY_STMT
	;

block [Object w]
	:	#(BLOCK 
			( statement[w] )*
		 CLOSE_CURLY! 
		)
	;
	
statementList[Object w]
	:	#( STMT_LIST ( statement[w] )+ )
	;
	
localVariableDeclaration! [Object w]
	:	#(	LOCVAR_DECLS t:type[w] 
					{ ## = #( [FIELD_DECL, "FIELD_DECL"], 
								#( [MODIFIERS, "MODIFIERS"] ),
								#t); }
			(d:localVariableDeclarator[w] { ##.addChild(#d); } )+ 
			) 
	;
	
localVariableDeclarator [Object w]
	:	#(	VAR_DECLARATOR identifier[w] (localVariableInit[w])?	
		)
	;

localVariableInit [Object w]
	:
		#(	LOCVAR_INIT
			(	expression[w]
			|	arrayInitializer[w]
			)
		) { ##.setType(VAR_INIT); ##.setText("VAR_INIT"); }
	;
	
		
localConstantDeclaration! [Object w] // rewrite to field_decl with final modifier
	:	#(LOCAL_CONST  t:type[w] 
			{ ## = #( [FIELD_DECL, "FIELD_DECL"],
						  #( [MODIFIERS, "MODIFIERS"],
								#( [STATIC, "static"] ),
						        #( [FINAL, "final"] )
						    ), #t ); } 
			(	
				d:constantDeclarator[w] { ##.addChild(#d); }
			)+
		)
	;
	
constantDeclarator! [Object w]
	:	#(CONST_DECLARATOR i:identifier[w] c:constantExpression[w]
		) {## = #( [VAR_DECLARATOR, "VAR_DECLARATOR"], #i, #( [VAR_INIT, "VAR_INIT"], #c) ); }
	;
	
statementExpression! [Object w]
	:	e:expr[w] { ## = #([EXPR], #e); } 
	;
	
switchSection[Object w]
	:	#( SWITCH_SECTION switchLabels[w] statementList[w] )
	;
	
switchLabels [Object w]
	:	#(	SWITCH_LABELS 
			(	(	#(CASE  expression[w] )
				|  DEFAULT	
				)
				
			)+ 
		) { ## = (ASTNode) ##.getFirstChild(); }
	;
	
tryStatement [Object w]
	:	#( TRY 
			block[w]
			(	finallyClause[w]
			|	catchClauses[w] ( finallyClause[w] )?
			)
		)
	;
	
catchClauses [Object w]
	:	(	
			catchClause[w]
		)+
	;
	
catchClause [Object w]
 { bool guards = false; 
   ASTNode ret = #( [CATCH, "catch"] );
   ArrayList saveExHoles = currExHoles;
   currExHoles = new ArrayList();
   string exVarStr = "";
 }
	:! #( CATCH b:block[w] 
			(	t:type[w]						{ 
													exVarStr = "__dummyCatchallEx" + dummyCatchallCtr;
													dummyCatchallCtr++;
													ret.addChild( #( [FIELD_DECL, "FIELD_DECL"], 
																	#( [MODIFIERS, "MODIFIERS"] ),
																		#( [TYPE, "type"],
																		    astFactory.dupTree(#t.getFirstChild()),
																		    ( [ARRAY_RANKS] ) ),
																	    #( [VAR_DECLARATOR, "VAR_DECLARATOR"],
																			#( [IDENTIFIER, exVarStr] ) 
																				) ) ); 
												  guards = true; }
			|	l:localVariableDeclaration[w]	{ ret.addChild(#l); 
												  exVarStr = #l.getFirstChild().getNextSibling().getNextSibling().getFirstChild().getText(); 
												  guards = true; }
			)*
		) { if (!guards) {
				exVarStr = "__dummyCatchallEx" + dummyCatchallCtr;
				dummyCatchallCtr++;
				ret.addChild(#( [FIELD_DECL, "FIELD_DECL"], 
								#( [MODIFIERS, "MODIFIERS"] ),
								#( [TYPE, "type"],
									#( [JAVAWRAPPER], [IDENTIFIER, "Exception"] ),
									#( [ARRAY_RANKS] ) ),
								#( [VAR_DECLARATOR, "VAR_DECLARATOR"],
									#( [IDENTIFIER, exVarStr] ) 
									) ));
			} 
			// Fill in any holes
			foreach (ASTNode idAST in currExHoles)
			{
			    idAST.setText(exVarStr);
			}
			currExHoles = saveExHoles;
		    ret.addChild(#b);
		    ## = ret; }
	;
	
finallyClause [Object w]
	:	#( FINALLY 
			block[w]
		)
	;
	
resourceAcquisition[Object w]
	:	localVariableDeclaration[w]
	|	expression[w]
	;
	
//	
// A.2.6 Classes
//

classDeclaration [Object w]
  {string saveClass = this.ClassInProcess; }
	:!	#( CLASS
			attributes[w]     // todo: attributes 
			mod:classModifiers[w]  
			id:identifier[w]    { this.ClassInProcess = #id.getText(); }
			ext:classBase[w] 
			#(TYPE_BODY  mem:classMemberDeclarations[w] CLOSE_CURLY ) 
		) 
		 {
		    ASTNode firstParent = (ASTNode) #ext.getFirstChild(); 
		    if ( firstParent != null && firstParent.getNextSibling() == null && idToString(firstParent) == "System.Attribute" ) {
		      // A class that only inherits System.Attribute. Must be a an attribute definition.
		      // TODO: We don't handle annotations with parameters yet.
		      ## = #( [ANNOTATION], #mod, #id, #( [MEMBER_LIST]) );
		    }
		    else {
		      ## = #( [CLASS], #mod, #id, #ext, #mem );
		    } 
		  this.ClassInProcess = saveClass;
		 }
	;
	
classBase [Object w]
	:! { ## = #( [CLASS_BASE] ); }
	   #( CLASS_BASE
			(	
				t:type[w]! { ##.addChild( astFactory.dupTree(#t.getFirstChild()) ); } // Extract identifier from type info
			)* 
		)
	;
	
classMemberDeclarations [Object w]
	:	#(	MEMBER_LIST
			(	classMemberDeclaration [w]
			|	preprocessorDirective[w, CodeMaskEnums.ClassMemberDeclarations]
			)*
		)
	;
	
classMemberDeclaration [Object w]
	:	(	destructorDeclaration[w]
		|	typeMemberDeclaration[w]
		)
	;
	
typeMemberDeclaration [Object w]
	:	(	constantDeclaration[w]
		|	eventDeclaration[w]
		|	constructorDeclaration[w]
		|	staticConstructorDeclaration[w]
		|	propertyDeclaration[w]
		|	methodDeclaration[w]
		|	indexerDeclaration[w]
		|	fieldDeclaration[w]
		|	operatorDeclaration[w]
		|	typeDeclaration[w, null, null, false]
		)
	;
	
constantDeclaration! [Object w]
	:	#(CONST attributes[w]! m:modifiers[w] t:type[w] 
	          { #m.addChild( #( null, [STATIC, "static"], [FINAL, "final"] )); 
	            ## = #( [FIELD_DECL, "FIELD_DECL"], #m, #t ); 
	          } 
			( c:constantDeclarator[w] { ##.addChild(#c); } )+			
		)
	;
	
fieldDeclaration [Object w]
	:	#(	FIELD_DECL attributes[w]! modifiers[w] type[w]
			( variableDeclarator[w] )+
			
		)
	;
	
variableDeclarator [Object w]
	:	#(	VAR_DECLARATOR identifier[w]
			( variableInitializer[w] )? 
		)
	;
	
variableInitializer [Object w]
	:	#(	VAR_INIT
			(	expression[w]
			|	arrayInitializer[w]
			|	stackallocInitializer[w]
			)
		)
	;
		
methodDeclaration! [Object w]
	:	#(	METHOD_DECL a:attributes[w] m:modifiers[w] t:type[w] i:qualifiedIdentifier[w]
			b:methodBody[w]
			 ( p:formalParameterList[w] )? 
		) { 
			if (#p == null) #p = #( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"] ); 
			
			if (#i.getText() == "ToString" && #p.getFirstChild() == null)
			{
			  //TODO: keving: Note: Should generalize this, we also should trap equals, clone etc.
			  ## = #( [METHOD_DECL], #m, astFactory.dupTree(#t), #( [IDENTIFIER, "toString"] ), #p, #( [BLOCK], SmotherCheckedExceptions(#b, "RuntimeException") ));
			}
			else
			{
		      ## = #( [METHOD_DECL], #m, #t, #i, #p, #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ), #b);
		      ##.setNextSibling(WrapMainMethod(#m, #t, #i, #p));   // Adds wrapper if this is Main
		    }
		  }
	;
	
memberName [Object w]
	:	qualifiedIdentifier[w]					// interfaceType^ DOT! identifier
//	|	identifier
	;
	
methodBody [Object w]
	:	body[w]
	;
	
formalParameterList [Object w]
	:	#(	FORMAL_PARAMETER_LIST 
			(	fixedParameters[w] (  parameterArray[w] )?
			|	parameterArray[w]
			)
		)
	;
	
fixedParameters [Object w]
	:	fixedParameter[w] (  fixedParameter[w] )*
	;
	
fixedParameter [Object w]
	:	#( 	PARAMETER_FIXED attributes[w]! 
			type[w] identifier[w] 
			( parameterModifier[w] )?
		)
	;
	
parameterModifier [Object w]
	: REF		
	| OUT		
	;
	
// In C#      public void fred(int[]... arg)
//
// In Java 	  public void fred(int... arg)
//
// So we strip the rank off here and the netTranslator has to
// make sure arg is an array.
parameterArray! [Object w]
	:	#(PARAMS attributes[w]!  t:type[w] i:identifier[w]   { ## = #( [PARAMS], StripOneRank(#t), #i); } 
		)
	;
	
propertyDeclaration! [Object w]
	:	#(	PROPERTY_DECL attributes[w] m:modifiers[w] t:type[w] id:qualifiedIdentifier[w] 
			a:accessorDeclarations[w] CLOSE_CURLY 
		)     { ## = #( [IDENTIFIER, "dummy"] );
			    // Take each getter/setter
                ASTNode gs = #a;
                
                while ( gs != null ) {
                  if (gs.getText() == "get") {
					AST new_id = astFactory.dupTree(#id);
					prependStringToId(new_id, "get");
                    ASTNode myGetter = #( [METHOD_DECL, "METHOD_DECL"], 
											astFactory.dupTree(#m), 
											astFactory.dupTree(#t), 
											new_id,
											#( [FORMAL_PARAMETER_LIST] ), #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ),
											astFactory.dupTree(gs.getFirstChild()) );
                    if (## == null)
						## = myGetter;
				    else
				        ##.addChild(myGetter);
                  } else { 
					if (gs.getText() == "set") {
 						AST new_id = astFactory.dupTree(#id);
						prependStringToId(new_id, "set");
						ASTNode mySetter = #( [METHOD_DECL, "METHOD_DECL"], 
												astFactory.dupTree(#m), 
												#( [TYPE, "TYPE"],
													#( [VOID, "void"] ),
													#( [ARRAY_RANKS] ) ),
												new_id,
												#( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"],
													#( [PARAMETER_FIXED, "PARAMETER_FIXED"],
														astFactory.dupTree(#t),
														[IDENTIFIER, "value"] )), #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ),
												astFactory.dupTree(gs.getFirstChild()) );
						if (## == null)
						    ## = mySetter;
						 else
						    ##.addChild(mySetter);
                    }                 
                  }
                  gs = (ASTNode) gs.getNextSibling();
                } 
                ## = (ASTNode) ##.getFirstChild();
			}
	;
	
accessorDeclarations [Object w]
	:	(	accessorDeclaration[w]
		)*
	;
	
accessorDeclaration [Object w]
	:	#( "get" attributes[w]! accessorBody[w] )
	|   #( "set" attributes[w]! accessorBody[w] )
	;
	
	
accessorBody [Object w]
	:	block[w]
	|   EMPTY_STMT
	;
	
eventDeclaration [Object w]
	:	#(/*evt:*/EVENT attributes[w] modifiers[w]  type[w]
			(	qualifiedIdentifier[w] 
				eventAccessorDeclarations[w]/*cly:*/CLOSE_CURLY 
			|	variableDeclarator[w] (  variableDeclarator[w] )* 
				
			)
		)
	;
	
eventAccessorDeclarations [Object w]
	:	addAccessorDeclaration[w] removeAccessorDeclaration[w]
	|	removeAccessorDeclaration[w] addAccessorDeclaration[w]
	;
	
addAccessorDeclaration [Object w]
	:	#( "add"    attributes[w] block[w] )
	;
	
removeAccessorDeclaration [Object w]
	:	#( "remove" attributes[w] block[w] )
	;


// for an index getter create get___idx()
// for the setter set___idx()
// I am trying to avoid clashes with possible property names (__idx would clash)
indexerDeclaration! [Object w]
	:	#(	INDEXER_DECL attributes[w] m:modifiers[w]
			t:type[w] ( interfaceType[w]  )? THIS
			f:formalParameterList[w]  a:accessorDeclarations[w] 
		/*cly:*/CLOSE_CURLY
		)		{ ## = #( [IDENTIFIER, "dummy"] );
			    // Take each getter/setter
                ASTNode gs = #a;
                
                while ( gs != null ) {
                  if (gs.getText() == "get") {
                    ASTNode myGetter = #( [METHOD_DECL, "METHOD_DECL"], 
											astFactory.dupTree(#m), 
											astFactory.dupTree(#t), 
											#( [IDENTIFIER, "get___idx"]),
											astFactory.dupTree(#f), #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ),
											astFactory.dupTree(gs.getFirstChild()) );
				    ##.addChild(myGetter);
                  } else { 
					if (gs.getText() == "set") {
						ASTNode mySetter = #( [METHOD_DECL, "METHOD_DECL"], 
												astFactory.dupTree(#m), 
												#( [TYPE, "TYPE"],
													#( [VOID, "void"] ),
													#( [ARRAY_RANKS] ) ),
												#( [IDENTIFIER, "set___idx"]),
												astFactory.dupTree(#f), #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ),
												astFactory.dupTree(gs.getFirstChild()) );
						##.addChild(mySetter);
                    }                 
                  }
                  gs = (ASTNode) gs.getNextSibling();
                } 
                ## = (ASTNode) ##.getFirstChild();
			}
	;
	
operatorDeclaration [Object w]
	:	(	#(	UNARY_OP_DECL attributes[w]! modifiers[w]
				type[w]  overloadableUnaryOperator[w]
				 formalParameterList[w]  
	 			operatorBody[w]
		 	)
		|	#(	BINARY_OP_DECL attributes[w]! modifiers[w]
				type[w]  overloadableBinaryOperator[w]
				 formalParameterList[w] 
		 		operatorBody[w]
			 )
		|   #(	CONV_OP_DECL attributes[w]! modifiers[w]
				( IMPLICIT | EXPLICIT ) type[w]	   
					formalParameterList[w] operatorBody[w]
			 ) 
		)
	;
	
overloadableUnaryOperator [Object w]
	:  UNARY_PLUS	
	|  UNARY_MINUS	
	|  LOG_NOT		
	|  BIN_NOT		
	|  INC			
	|  DEC			
	|  TRUE			
	|  FALSE		
	;
	
overloadableBinaryOperator [Object w]
	:/*pl:*/PLUS			
	|/*ms:*/MINUS		
	|/*st:*/STAR			
	|/*dv:*/DIV 			
	|/*md:*/MOD 			
	|/*ba:*/BIN_AND 		
	|/*bo:*/BIN_OR 		
	|/*bx:*/BIN_XOR 		
	|/*sl:*/SHIFTL 		
	|/*sr:*/SHIFTR 		
	|/*eq:*/EQUAL		
	|/*nq:*/NOT_EQUAL 	
	|/*gt:*/GTHAN		
	|/*lt:*/LTHAN 		
	|/*ge:*/GTE 			
	|/*le:*/LTE 			
	;
	
operatorBody [Object w]
	:	body[w]
	;

constructorDeclaration [Object w]
	:!	#(	CTOR_DECL attributes[w]! m:modifiers[w] i:identifier[w]
			b:constructorBody[w]
			( p:formalParameterList[w] )? 
			( c:constructorInitializer[w] )? 
		) 
			{ 
				AST nbody = #( [BLOCK] );
				nbody.addChild(#c);
				nbody.addChild(#b.getFirstChild());
				if (#p == null)
					#p = #( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"] );
				## = #( CTOR_DECL, #m, #i, #p, #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ), nbody);
			}		       
	;
	
constructorInitializer [Object w]
	:	(	#( BASE  ( es1:argumentList[w] )? ) { ##.setText("super"); if (#es1 == null) ##.addChild( #( [EXPR_LIST] ) ); }
		|	#( THIS  ( es2:argumentList[w] )? ) { ##.setText("this"); if (#es2 == null) ##.addChild( #( [EXPR_LIST] ) ); }
		)
	;
	
constructorBody [Object w]
	:	body[w]
	;

staticConstructorDeclaration! [Object w]
	:	#(	STATIC_CTOR_DECL attributes[w] modifiers[w] identifier[w] 
			b:staticConstructorBody[w]
		) { ## = #( [STATIC_CTOR_DECL], #( [BLOCK], SmotherCheckedExceptions(#b, "ExceptionInInitializerError") ) ); }
	;
	
staticConstructorBody [Object w]
	:	body[w]
	;
	
destructorDeclaration! [Object w]
	:	#( 	DTOR_DECL attributes[w] modifiers[w] identifier[w] 
			b:destructorBody[w]
		) 
		{ ## = #( [METHOD_DECL], 
											#( [MODIFIERS], [PROTECTED, "protected"] ), 
											#( [TYPE], [VOID, "void"], #( [ARRAY_RANKS] ) ), 
											#( [IDENTIFIER, "finalize"]) ,
											#( [FORMAL_PARAMETER_LIST] ), #( [THROWS, "throws"], [IDENTIFIER, "Throwable"] ),
											astFactory.dupTree(#b) ); }
	;

destructorBody [Object w]
	:	body[w]
	;

	
//
// A.2.7 Structs
//


// Convert to a class
// A struct instance is an object without identity
// We add a default constructor that does nothing. In C# a default 
// constructor is not allowed.  If a struct is declared without
// an explicit constructor call then struct fields will be initialized
// to defalt values,  but anyway program must assign to them before
// they can be used.
// [TODO]: What problems can happen when passed as parameter due to
// value vs ref semantics.
structDeclaration [Object w]
  { string saveClass = this.ClassInProcess; }
	:!	#( STRUCT attributes[w] mod:modifiers[w] id:identifier[w] { this.ClassInProcess = #id.getText(); }
			imps:structImplements[w]			
			#( TYPE_BODY mem:structMemberDeclarations[w, #id] CLOSE_CURLY  )
		) { ## = #( [CLASS, "class"], #mod, #id, #imps, #mem );
		    this.ClassInProcess = saveClass; }
	;

structImplements [Object w]
	:	#(	STRUCT_BASE ( type[w] )* )
	       { ##.setType(CLASS_BASE); }
	;	
	
structMemberDeclarations [Object w, ASTNode id]
		// Add Default Constructor
	:! { ASTNode ret = #( [MEMBER_LIST],
				 #( [CTOR_DECL],
						#( [MODIFIERS], [PUBLIC, "public"] ),
						astFactory.dupTree(id),
						#( [FORMAL_PARAMETER_LIST] ),
						#( [BLOCK] ) )
						 ); }
		#(	MEMBER_LIST
			(	s:structMemberDeclaration[w] { ret.addChild(#s); }
			|	p:preprocessorDirective[w, CodeMaskEnums.StructMemberDeclarations] { ret.addChild(#p); }
			)*
		) { ## = ret; }
	;
	
structMemberDeclaration [Object w]
	:	typeMemberDeclaration[w]
	;

	
//
// A.2.8 Arrays
//

rankSpecifiers [Object w]
	:	#(	ARRAY_RANKS
			( 	rankSpecifier[w]
			)*
		)
	;
	
rankSpecifier  [Object w]
	:	#( ARRAY_RANK 
			(COMMA 
			)* 			
		)
	;
	
arrayInitializer [Object w]
	:	#( ARRAY_INIT  ( variableInitializerList[w] )? CLOSE_CURLY!  ) 
	;
	
variableInitializerList [Object w]
	:	#( VAR_INIT_LIST v:arrayvariableInitializer[w] ( arrayvariableInitializer[w] )* ) {## = #v; }
	;

arrayvariableInitializer! [Object w]
	:	v:variableInitializer[w] {## = (ASTNode) #v.getFirstChild(); }
	;

// 
// A.2.9 Interfaces
//

interfaceDeclaration [Object w]
  { string saveClass = this.ClassInProcess; }
	:!	#(INTERFACE attributes[w] mod:modifiers[w] id:identifier[w] { this.ClassInProcess = #id.getText(); }
			imps:interfaceImplements[w]
			#( TYPE_BODY mem:interfaceMemberDeclarations[w] CLOSE_CURLY  )
		) { ## = #( [INTERFACE], #mod, #id, #imps, #mem );
		    this.ClassInProcess = saveClass; }
	;
	
interfaceImplements [Object w]
    : #( INTERFACE_BASE ( type[w] )* )
        { ##.setType(IMPLEMENTS_CLAUSE); ##.setText("implements"); }
	;
	
interfaceMemberDeclarations [Object w]
	:	#(	MEMBER_LIST
			(	interfaceMemberDeclaration[w]
			|	preprocessorDirective[w, CodeMaskEnums.InterfaceMemberDeclarations]
			)*
		)
	;
	
interfaceMemberDeclaration [Object w]
	:	(	methodDeclaration[w]
		|	propertyDeclaration[w]
		|	eventDeclaration[w]
		|	indexerDeclaration[w]
		)
	;
	
interfaceMethodDeclaration [Object w]
	:	#(	METHOD_DECL attributes[w]! modifiers[w] type[w] qualifiedIdentifier[w]
		    EMPTY_STMT 
			( f:formalParameterList[w] )?
		) { if (#f == null) ##.addChild( #( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"] )); } 
	;
	
interfacePropertyDeclaration [Object w]
	:	#(	PROPERTY_DECL attributes[w] modifiers[w] type[w] identifier[w]
			accessorDeclarations[w]/*cc:*/CLOSE_CURLY 
		)
	;
	
interfaceEventDeclaration [Object w]
	:	#(/*evt:*/EVENT attributes[w] modifiers[w] 
			type[w] variableDeclarator[w]		 
		)
	;
	
interfaceIndexerDeclaration [Object w]
	:	#(	INDEXER_DECL attributes[w] modifiers[w] type[w]/*t:*/THIS 
			formalParameterList[w] 
			accessorDeclarations[w]/*cc:*/CLOSE_CURLY 
		)
	;

	
//
//	A.2.10 Enums
//

enumDeclaration [Object w]
  { string saveClass = this.ClassInProcess; }
	:!	#( ENUM attributes[w] mod:modifiers[w] id:identifier[w] { this.ClassInProcess = #id.getText(); }
			basetype:enumImplements[w]
			#(  TYPE_BODY 
					mem:enumMemberDeclarations[w]			  
			    CLOSE_CURLY 
			)
		) { ## = #( [ENUM], #mod, #id, #( [IMPLEMENTS_CLAUSE] ), #mem);
		    this.ClassInProcess = saveClass; } 
	;

// keving: ignored in enumDeclaration
enumImplements [Object w]
  { ASTNode t = null; }
	:! #( ENUM_BASE ( gt:type[w] { t = #gt; } )? )
	   { if (t == null)
	        ## =  #( [TYPE], [INT, "int"], #( [ARRAY_RANKS] ) );
	     else
	        ## = #gt; }
	;

// Convert the declarations to a simple list,  match their intended value to their ordinal position by adding in
// dummy enums	
enumMemberDeclarations! [Object w]
 { SortedList ExplicitEnums = new SortedList();
   ArrayList ImplicitEnums = new ArrayList();
   int dummyCounter = 0;
 }
	: #( MEMBER_LIST ( enumMemberDeclaration[w, ExplicitEnums, ImplicitEnums] )* ) 
	{
	   ## = #(MEMBER_LIST);
	   int ord = 0;
	   foreach (DictionaryEntry de in ExplicitEnums)
	   {
	      // entries are sorted by enum value
	      int enumValue = (int) de.Key;
	      ASTNode enumAST = (ASTNode) de.Value;
	      while (ord < enumValue)
	      {  
	         // We need some padding here
	         if (ImplicitEnums.Count > 0)
	         {
	            ##.addChild((ASTNode) ImplicitEnums[0]);
	            ImplicitEnums.RemoveAt(0);
	         }
	         else
	         {
	            string dummyEnum = "__dummyEnum" + dummyCounter;
	            dummyCounter++;
	            ##.addChild(#([IDENTIFIER, dummyEnum]));
	         }
	         ord++;
	      }    
	      ##.addChild(enumAST);
	      ord++;
	   }
	   // Add implicit enums that haven't yet been accounted for
	   foreach (ASTNode id in ImplicitEnums)
	      ##.addChild((ASTNode) #id);

	}
	;
		
enumMemberDeclaration! [Object w, SortedList e, ArrayList i]
 { bool init = false; }
	:	#( id:IDENTIFIER { fixBrokenIds(#id); } attributes[w]
				( c:constantExpression[w] 
				    { // If constantExpression is #(EXPR #( [INT_LITERAL, xxx] ) ) 
				      // then we maintain its value, otherwise print a warning and
				      // give it next available slot
				      bool hasValue = false;
				      int intValue = 0;
				      if (#c.Type == EXPR)
				      {
				         if (#c.getFirstChild().Type == INT_LITERAL)
				         {
				            // Assigned value
				            String strValue = #c.getFirstChild().getText();
				            if ( strValue.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) {
                              intValue = Int32.Parse(strValue.Substring(2), NumberStyles.HexNumber);
                            }
							else {
				              intValue = Int32.Parse(strValue);
				            }
				            if (!e.Contains(intValue))
				               hasValue = true;
				            else
				               Console.Error.WriteLine("ERROR -- (enumMemberDeclaration): repeated enum value: " + intValue);
				         }
				      }
				      if (hasValue)
				      {
				         init = true;
				         e[intValue] = #id;
				      }
				      else
				         Console.Out.WriteLine("WARNING -- (enumMemberDeclaration): Ignoring assigned value of " + #id.getText());
				    }
				   )?
				{ if (!init)
				    i.Add(#id);
				}
		)
 	;


//
// A.2.11 Delegates
//

delegateDeclaration [Object w]
	:	#(/*dlg:*/DELEGATE attributes[w] modifiers[w] 
			type[w] identifier[w] ( f:formalParameterList[w] )? 
		) { if (#f == null) ##.addChild( #( [FORMAL_PARAMETER_LIST, "FORMAL_PARAMETER_LIST"] )); } 
	;
	

//
// A.2.12 Attributes
//

globalAttributes [Object w]
	:	#(	GLOBAL_ATTRIBUTE_SECTIONS 
			(	globalAttributeSection[w]
			|	preprocessorDirective[w, CodeMaskEnums.GlobalAttributes]
			)*
		)
	;
	
globalAttributeSection [Object w]
	:	#(/*sect:*/GLOBAL_ATTRIBUTE_SECTION  
			( attribute[w] )+  
		)
	;

attributes [Object w]
	:	#(	ATTRIBUTE_SECTIONS 
			(	attributeSection[w]
			|	preprocessorDirective[w, CodeMaskEnums.Attributes]
			)*
		)
	;
	
attributeSection [Object w]
	:	#(/*sect:*/ATTRIBUTE_SECTION  ( attributeTarget[w] )?
			( attribute[w] )+  
		)
	;
	
attributeTarget[Object w]
	:	(/*fv:*/"field"			
		|/*ev:*/EVENT			
		|/*mv:*/"method"			
		|/*mo:*/"module"			
		|/*pa:*/"param"			
		|/*pr:*/"property"		
		|/*re:*/RETURN			
		|/*ty:*/"type"			
		)
	;

attribute [Object w]
	:	#( ATTRIBUTE typeName[w] attributeArguments[w] )
	;
	
attributeArguments [Object w]
	:	( positionalArgumentList[w] )? ( namedArgumentList[w] )? 
	;
	
positionalArgumentList [Object w]
	:	#(	POSITIONAL_ARGLIST positionalArgument[w]
			( positionalArgument[w] )* 
		)
	;
	
positionalArgument [Object w]
	:	#( POSITIONAL_ARG attributeArgumentExpression[w] )
	;
	
namedArgumentList [Object w]
	:	#(	NAMED_ARGLIST namedArgument[w]
			( namedArgument[w] )* 
		)
	;
	
namedArgument [Object w]
	:	#( NAMED_ARG identifier[w] attributeArgumentExpression[w] )
	;
	
attributeArgumentExpression [Object w]
	:	#( ATTRIB_ARGUMENT_EXPR expression[w] )
	;

//
// A.3 Grammar extensions for unsafe code
// 

fixedPointerDeclarator [Object w]
	:	#( PTR_DECLARATOR identifier[w] fixedPointerInitializer[w] )
	;
	
fixedPointerInitializer [Object w]
	:	#(	PTR_INIT
			(/*b:*/BIN_AND variableReference[w]
			|	expression[w]
			)
		)
	;	
	
stackallocInitializer [Object w]
	:	#(/*s:*/STACKALLOC unmanagedType[w]  expression[w]  )
	;

//======================================
// Preprocessor Directives
//======================================

justPreprocessorDirectives [Object w]
	:	#(	PP_DIRECTIVES 
			(	preprocessorDirective[w, CodeMaskEnums.PreprocessorDirectivesOnly] 
			)* 
		)
	;
	
preprocessorDirective [Object w, CodeMaskEnums codeMask]
	:!	#(PP_DEFINE   i:PP_IDENT  {defines[#i.getText()] = true;} )
	|!	#(/*u1:*/PP_UNDEFINE i2:PP_IDENT  {defines.Remove(#i2.getText()); } )
	|!	#(/*l1:*/PP_LINE    
			(/*l2:*/DEFAULT   
			|/*l3:*/PP_NUMBER  (/*l4:*/PP_FILENAME  )? 
			)
		)
	|!	#(/*e1:*/PP_ERROR    ppMessage[w] )
	|!	#(/*w1:*/PP_WARNING  ppMessage[w] )
	|	regionDirective[w, codeMask]
	|	conditionalDirective[w, codeMask]
	;
	
regionDirective! [Object w, CodeMaskEnums codeMask]
	:	#(PP_REGION  ppMessage[w] b:directiveBlock[w, codeMask]
			#(PP_ENDREGION  ppMessage[w] )
		) { ## = #b; }
	;

conditionalDirective! [Object w, CodeMaskEnums codeMask] 
  { ASTNode ret = null;
    bool c = false; }
	:	#(PP_COND_IF         c = preprocessExpression[w]  th:directiveBlock[w, codeMask] { if (c) ret = #th; }
			( #(PP_COND_ELIF  c = preprocessExpression[w]  ce:directiveBlock[w, codeMask] ) { if (c && ret == null) ret = #ce; } )*
			( #(PP_COND_ELSE  el:directiveBlock[w, codeMask] ) { if (ret == null) ret = #el; } )?
		  PP_COND_ENDIF     
		) { ## = ret; }
	;

directiveBlock [Object w, CodeMaskEnums codeMask]
	:	#(	PP_BLOCK
			(	{ NotExcluded(codeMask, CodeMaskEnums.UsingDirectives) }?				usingDirective[w]
			|	{ NotExcluded(codeMask, CodeMaskEnums.GlobalAttributes) }?				globalAttributeSection[w]
			|	{ NotExcluded(codeMask, CodeMaskEnums.Attributes) }?					attributeSection[w]
			|	{ NotExcluded(codeMask, CodeMaskEnums.NamespaceMemberDeclarations) }?	namespaceMemberDeclaration[w, null, null]
			|	{ NotExcluded(codeMask, CodeMaskEnums.ClassMemberDeclarations) }?		classMemberDeclaration[w]
			|	{ NotExcluded(codeMask, CodeMaskEnums.StructMemberDeclarations) }?		structMemberDeclaration[w]
			|	{ NotExcluded(codeMask, CodeMaskEnums.InterfaceMemberDeclarations) }?	interfaceMemberDeclaration[w]
			|	{ NotExcluded(codeMask, CodeMaskEnums.Statements) }?					statement[w]
			|	preprocessorDirective[w, codeMask]
			)*
		) {## = (ASTNode) ##.getFirstChild(); }
	;
	
ppMessage [Object w]
	:	#(	PP_MESSAGE
			(/*m1:*/PP_IDENT 		
			|/*m2:*/PP_STRING 		
			| /*m3:*/PP_FILENAME 		
			| /*m4:*/PP_NUMBER 		
			)*
		)
	;

preprocessExpression [Object w] returns [bool v]
  { v = false; }
	:	#( PP_EXPR v = preprocessExpr[w] )
	;

preprocessExpr [Object w] returns [bool v]
  { bool v1, v2;
    v = false;
  }
	:	#( LOG_OR    v1 = preprocessExpr[w]  v2 = preprocessExpr[w] )   { v = v1 | v2; }
	|	#( LOG_AND   v1 = preprocessExpr[w]  v2 = preprocessExpr[w] )   { v = v1 & v2; }
	|	#( EQUAL     v1 = preprocessExpr[w]  v2 = preprocessExpr[w] )   { v = v1 == v2; }
	|	#( NOT_EQUAL v1 = preprocessExpr[w]  v2 = preprocessExpr[w] )   { v = v1 != v2; }
	|	v = preprocessPrimaryExpression[w]
	;
	
preprocessPrimaryExpression [Object w] returns [bool v]
  { bool r;
    v = false;
  }
	:  i:PP_IDENT { v = (defines[#i.getText()] == null?false:(bool)defines[#i.getText()]); // Should look up in symbol table! 
	            } 		
	|  TRUE     { v = true; }			
	|  FALSE    { v = false; }		
	|	#( LOG_NOT 	 r = preprocessPrimaryExpression[w] )  { v = !r; }
	|	#( PAREN_EXPR  v = preprocessExpr[w]  )
	;