1
0
mirror of https://github.com/twiglet/cs2j.git synced 2025-01-18 13:15:17 +01:00

1424 lines
48 KiB
Plaintext

header {
using System.IO;
using System.Collections;
using Directory = System.IO.Directory;
using System.Xml.Serialization;
using Path = System.IO.Path;
}
options {
language = "CSharp";
namespace = "RusticiSoftware.Translator";
}
/** NetTranslator: Converts .Net calls to Java API calls
*
* Author: Kevin Glynn <kevin.glynn@scorm.com>
*
* This grammar is based on the java tree walker included in ANTLR examples
*
*/
class NetTranslator extends TreeParser("RusticiSoftware.Translator.NetTranslatorBase");
options {
importVocab = CSharpJava;
buildAST = true;
ASTLabelType = "ASTNode";
defaultErrorHandler = true;
}
{
// track current namespace
private string nameSpace = "";
// track current level of switch statement so that we generate unique scrutinee variables
private int scrutineeCounter = 0;
// track current level of foreach statement so that we generate unique temp variables
private int foreachCounter = 0;
// expr.<field>
// accessAST:#( MEMBER_ACCESS_EXPR thisAST:expr fieldNm:IDENTIFIER )
private ASTNode ResolveGetter(ASTNode accessAST)
{
ASTNode retAST = null;
ASTNode thisAST = (ASTNode) accessAST.getFirstChild();
string fieldNm = thisAST.getNextSibling().getText();
FieldRep fldProp = thisAST.DotNetType.Resolve(fieldNm);
if (fldProp != null)
{
string getter = fldProp.Get;
this.addImport(fldProp.Imports);
bool thisIsDummyThis = thisAST.Type == THIS && thisAST.getText() == "DUMMYTHIS";
if (getter == null)
{
string newFieldNm = fieldNm;
if (fldProp is PropRep)
newFieldNm = "get" + newFieldNm + "()";
ASTNode newFieldAST = #( [IDENTIFIER, newFieldNm] );
if (thisIsDummyThis)
retAST = newFieldAST;
else
retAST = #( [MEMBER_ACCESS_EXPR], astFactory.dupTree(thisAST), newFieldAST );
}
else
retAST = (ASTNode) #( [JAVAWRAPPER], [IDENTIFIER, getter],
[IDENTIFIER, "${this}"],
astFactory.dupTree(thisAST) );
retAST.DotNetType = fldProp.Type;
}
return retAST;
}
// expr.<field> = value
// accessAST: #( ASSIGN targetAST:#( MEMBER_ACCESS_EXPR thisAST:expr fieldNm:IDENTIFIER ) valueAST:expr[w] )
private ASTNode ResolveSetter(ASTNode assignAST)
{
ASTNode retAST = null;
ASTNode targetAST = (ASTNode) assignAST.getFirstChild();
ASTNode valueAST = (ASTNode) targetAST.getNextSibling();
ASTNode thisAST = (ASTNode) targetAST.getFirstChild();
string fieldNm = thisAST.getNextSibling().getText();
FieldRep fldProp = thisAST.DotNetType.Resolve(fieldNm);
if (fldProp != null)
{
string setter = fldProp.Set;
this.addImport(fldProp.Imports);
bool thisIsDummyThis = thisAST.Type == THIS && thisAST.getText() == "DUMMYTHIS";
if (thisIsDummyThis)
thisAST.setText("this");
if (setter == null)
{
// assignment of an application property or field
if (fldProp is PropRep)
setter = (thisIsDummyThis ? "" : "${this}.") + "set" + fieldNm + "(${value})";
else
setter = (thisIsDummyThis ? "" : "${this}.") + fieldNm + " = ${value}";
}
retAST = (ASTNode) #( [JAVAWRAPPER], [IDENTIFIER, setter],
[IDENTIFIER, "${this}"],
astFactory.dupTree(thisAST),
[IDENTIFIER, "${value}"],
astFactory.dupTree(valueAST) );
retAST.DotNetType = mkType("System.Void");
}
return retAST;
}
// new T(<args>)
// newAST:#( OBJ_CREATE_EXPR #( TYPE type #( ARRAY_RANKS ) ) #( [ELIST] args ) )
private ASTNode ResolveNewObj(ASTNode newAST)
{
ASTNode retAST = null;
ASTNode typeAST = (ASTNode) newAST.getFirstChild().getFirstChild();
ArrayList argVs = new ArrayList();
ASTNode argAST = (ASTNode) newAST.getFirstChild().getNextSibling().getFirstChild();
while (argAST != null)
{
argVs.Add(astFactory.dupTree(argAST));
argAST = (ASTNode) argAST.getNextSibling();
}
ClassRep typeClass = typeAST.DotNetType as ClassRep; // If type wasn't found then we will have an InterfaceRep
ConstructorRep constructor = (typeClass == null ? null : typeClass.Resolve(argVs));
if (constructor != null && constructor.Java != null)
{
retAST = (ASTNode) #( [JAVAWRAPPER], [IDENTIFIER, constructor.Java]);
for (int i = 0; i < argVs.Count; i++)
{
string templateVar = "${" + constructor.Params[i].Name + "}";
retAST.addChild( #( [IDENTIFIER, templateVar] ) );
retAST.addChild( (ASTNode) argVs[i] );
}
this.addImport(constructor.Imports);
retAST.DotNetType = typeAST.DotNetType;
}
return retAST;
}
// expr.<method>(expr*)
// invokeAST:#( INVOCATION_EXPR #( MEMBER_ACCESS_EXPR thisAST:expr methodNm:IDENTIFIER)
// #( [ELIST] args ) )
private ASTNode ResolveMethod(ASTNode invokeAST)
{
ASTNode retAST = null;
ASTNode thisAST = (ASTNode) invokeAST.getFirstChild().getFirstChild();
string methodNm = thisAST.getNextSibling().getText();
ArrayList argVs = new ArrayList();
ASTNode argAST = (ASTNode) invokeAST.getFirstChild().getNextSibling().getFirstChild();
while (argAST != null)
{
argVs.Add(astFactory.dupTree(argAST));
argAST = (ASTNode) argAST.getNextSibling();
}
MethodRep method = thisAST.DotNetType.Resolve(methodNm, argVs);
if (method != null) {
bool thisIsDummyThis = thisAST.Type == THIS && thisAST.getText() == "DUMMYTHIS";
if (thisIsDummyThis)
thisAST.setText("this"); // if we need 'this' below, then it better not be 'dummythis'
if (method.Java == null)
{
ASTNode methodAST = (ASTNode) (thisIsDummyThis ? #([IDENTIFIER, methodNm]) : astFactory.dupTree(invokeAST.getFirstChild()));
ASTNode argsElistAST = (ASTNode) astFactory.dupTree(invokeAST.getFirstChild().getNextSibling());
retAST = #( [INVOCATION_EXPR], methodAST, argsElistAST);
}
else
{
retAST = (ASTNode) #( [JAVAWRAPPER], [IDENTIFIER, method.Java],
[IDENTIFIER, "${this}"],
astFactory.dupTree(thisAST));
for (int i = 0; i < argVs.Count; i++)
{
ASTNode arg = (ASTNode) argVs[i];
string templateVar = "${" + method.Params[i].Name + "}";
retAST.addChild( #( [IDENTIFIER, templateVar] ) );
retAST.addChild( astFactory.dupTree(arg) );
// Support for extracting type name from typeof() expressions
// typeof(<type>) has been transformed to #( [EXPR], #( [JAVAWRAPPER], [IDENTIFIER, "<JavaType>.class"] ) )
if (method.Params[i].Name.StartsWith("TYPEOF") &&
arg.getFirstChild().Type == JAVAWRAPPER &&
arg.getFirstChild().getFirstChild().getText().EndsWith(".class"))
{
string classCall = arg.getFirstChild().getFirstChild().getText();
string typeName = classCall.Substring(0, classCall.Length - 6); // remove trailing ".class"
retAST.addChild( #( [IDENTIFIER, "${TYPEOF_TYPE}"] ) );
retAST.addChild( #( [IDENTIFIER, typeName] ) );
}
}
}
retAST.DotNetType = method.Return;
this.addImport(method.Imports);
}
return retAST;
}
// (type) expr
// castAST:#( CAST_EXPR tyAST:typeSpec[w] exprAST:expr[w] )
private ASTNode ResolveCast(ASTNode castAST)
{
ASTNode retAST = null;
ASTNode tyAST = (ASTNode) castAST.getFirstChild();
ASTNode exprAST = (ASTNode) castAST.getFirstChild().getNextSibling();
string template = null;
// We first look for a way to cast expression TO type
CastRep cast = tyAST.DotNetType.ResolveCastFrom(exprAST.DotNetType);
if (cast != null)
{
// Initialize template
template = cast.Java;
if (template == null)
template = "${to_type}.__castTo${to_type_id}(${expr})";
}
else
{
// If we can't cast TO type, can we cast to type FROM expr' type?
cast = exprAST.DotNetType.ResolveCastTo(tyAST.DotNetType);
if (cast != null)
{
// Initialize Template
template = cast.Java;
if (template == null)
template = "${from_type}.__castTo${to_type_id}(${expr})";
}
}
if (cast != null)
{
// We have found an appropriate castercast.Java;
retAST = (ASTNode) #( [JAVAWRAPPER], [IDENTIFIER, template],
[IDENTIFIER, "${expr}"],
astFactory.dupTree(exprAST),
[IDENTIFIER, "${to_type}"],
[IDENTIFIER, tyAST.DotNetType.TypeName],
[IDENTIFIER, "${from_type}"],
[IDENTIFIER, exprAST.DotNetType.TypeName],
[IDENTIFIER, "${to_type_id}"],
[IDENTIFIER, typeNameToId(tyAST.DotNetType.TypeName)],
[IDENTIFIER, "${from_type_id}"],
[IDENTIFIER, typeNameToId(exprAST.DotNetType.TypeName)]
);
retAST.DotNetType = tyAST.DotNetType;
this.addImport(cast.Imports);
}
return retAST;
}
// true iff n is being assigned to
private bool SetterContext(ASTNode n)
{
int parentType = n.getParent().Type;
if (n.getPreviousSibling() != null)
return false;
return (parentType == ASSIGN);
}
// Note, that in Java 6.0 there is getTypeUtils() that could be used to do this conversion
private Hashtable boxTypeMap = new Hashtable();
// If typeAST is one of the Java unboxed types (char, int, etc.) convert to boxed equivalent (Character, Integer, etc.)
// updates type name in place!
private ASTNode boxedType(ASTNode typeAST)
{
string type = typeAST.getFirstChild().getText();
if (boxTypeMap.Contains(type))
typeAST.getFirstChild().setText((string)boxTypeMap[type]);
return typeAST;
}
private void netTranslatorInit()
{
// Initialize boxTypeMap (see JLS, ed 3 sec 5.1.7)
boxTypeMap["boolean"] = "Boolean";
boxTypeMap["byte"] = "Byte";
boxTypeMap["char"] = "Character";
boxTypeMap["short"] = "Short";
boxTypeMap["int"] = "Integer";
boxTypeMap["long"] = "Long";
boxTypeMap["float"] = "Float";
boxTypeMap["double"] = "Double";
//initialize(netLib);
initialize();
}
}
compilationUnit [object w, DirectoryHT env]
{
netTranslatorInit();
TypeRep.Initialize(env);
// TypeRep.Test();
//this.ExtendEnvFromNS("Predefined");
//this.appEnv = env;
//TypeRepTemplate.ExtendTranslationCache(env);
uPath.Push("System"); // C# assumes access to built-in types such as String, Decimal, ...
}
:! #( COMPILATION_UNIT
p:packageDefinition[w]
u:useDefinitions[w]
importDefinitions[w]
t:typeDefinition[w] { ## = #( [COMPILATION_UNIT], #p, #u, GetImports(), #t ); }
)
;
packageDefinition [object w]
: #( PACKAGE_DEF (id:identifier[w] {
// keving:
// current namespace should be on top of stack, add it when we do search
// uPath.Push(idToString(#id));
//ExtendSymTabFromNS(idToString(#id));
nameSpace = idToString(#id);
} )? )
;
useDefinitions [object w]
: #( USING_DIRECTIVES
(useDefinition[w])*
)
;
useDefinition [object w]
: #( USING_NAMESPACE_DIRECTIVE
use:identifier[w] { string ns = idToString(#use);
if (!ns.StartsWith("System") && !ns.StartsWith("Microsoft"))
this.addImport(ns + ".*");
uPath.Push(ns);
//ExtendSymTabFromNS(ns);
}
)
| #( USING_ALIAS_DIRECTIVE
alias:identifier[w]
aid:identifier[w] { uPath.Push(idToString(#alias) + "=" + idToString(#aid)); }
)
;
importDefinitions [object w]
: #( IMPORTS
( (importDefinition[w])+ )?
)
;
importDefinition [object w]
: #( IMPORT
id:identifier[w] { this.addImport(#id.getText()); }
)
;
typeDefinition [object w]
{
string saveClass = this.ClassInProcess;
TypeRep saveThis = symtab["this"];
TypeRep saveSuper = symtab["super"];
}
:! #(CLASS
m:modifiers[w]
cn:IDENTIFIER { uPath.Push(nameSpace); uPath.Push(nameSpace + "." + #cn.getText());
symtab["this"] = mkType(#cn.getText());
symtab["super"] = symtab["this"].Extends;
this.ClassInProcess = #cn.getText(); }
b:classBase[w]
o:objBlock[w] { uPath.Pop(); uPath.Pop(); }
)
{
ASTNode baseClause = #( [EXTENDS_CLAUSE, "extends"] );
ASTNode ifClause = #( [IMPLEMENTS_CLAUSE, "implements"] );
ASTNode inherits = (ASTNode) #b.getFirstChild();
while ( inherits != null)
{
if (inherits.DotNetType is ClassRep)
baseClause.addChild( astFactory.dupTree(inherits) );
else
ifClause.addChild( astFactory.dupTree(inherits) );
inherits = (ASTNode) inherits.getNextSibling();
}
## = #( [CLASS], #m, #cn, baseClause, ifClause, #o);
this.ClassInProcess = saveClass;
symtab["this"] = saveThis;
symtab["super"] = saveSuper;
}
| #(INTERFACE
modifiers[w]
ifn:IDENTIFIER { uPath.Push(nameSpace); uPath.Push(nameSpace + "." + #ifn.getText()); this.ClassInProcess = #ifn.getText(); }
implementsClause[w]
interfaceBlock[w]
{ this.ClassInProcess = saveClass; } )
| #(ENUM
modifiers[w]
en:IDENTIFIER { uPath.Push(nameSpace); uPath.Push(nameSpace + "." + #en.getText()); this.ClassInProcess = #en.getText(); }
implementsClause[w]
enumBlock[w]
{ this.ClassInProcess = saveClass; } )
| #(ANNOTATION
modifiers[w]
ann:IDENTIFIER { uPath.Push(nameSpace); uPath.Push(nameSpace + "." + #ann.getText()); this.ClassInProcess = #ann.getText(); }
anno:objBlock[w]
{ this.ClassInProcess = saveClass; } )
;
classBase [Object w]
:
#( CLASS_BASE ( type[w] )* )
;
typeSpec! [object w]
: #(TYPE
t:type[w]
rs:rankSpecifiers[w]
{
string typeName = #t.DotNetType.TypeName;
## = #( [TYPE], #t, #rs );
int numRanks = #rs.getNumberOfChildren();
for (int i = 0; i < numRanks; i++)
typeName += "[]";
##.DotNetType = mkType(typeName);
}
)
;
// Int[,][] arr;
rankSpecifiers [object w]
: #( ARRAY_RANKS (rankSpecifier[w])* )
;
rankSpecifier [object w]
: #(ARRAY_RANK
( COMMA // Notice, we ignore dimensions.
)*
)
;
typeSpecArray [object w]
: #( ARRAY_DECLARATOR
typeSpecArray[w]
)
| type[w]
;
type! [object w]
{ TypeRep tyRep = null; }
: id:identifier[w] { tyRep = mkType(idToString(#id));
this.addImport(tyRep.Imports);
if (tyRep.Java != null)
## = #([IDENTIFIER, tyRep.Java]);
else
## = #id;
##.DotNetType = tyRep;
}
| bt:builtInType[w] { tyRep = #bt.DotNetType;
this.addImport(tyRep.Imports);
if (tyRep.Java != null)
## = #([IDENTIFIER, tyRep.Java]);
else
## = #bt;
##.DotNetType = tyRep;
}
| #(JAVAWRAPPER jid:identifier[w]) { ## = #jid; ##.DotNetType = mkType("A JAVA TYPE"); }
;
builtInType [object w]
: VOID { ##.DotNetType = mkType("System.Void"); }
| OBJECT { ##.DotNetType = mkType("System.Object"); }
| BOOL { ##.DotNetType = mkType("System.Boolean"); }
| STRING { ##.DotNetType = mkType("System.String"); }
| SBYTE { ##.DotNetType = mkType("System.SByte"); }
| "char" { ##.DotNetType = mkType("System.Char"); }
| "short" { ##.DotNetType = mkType("System.Int16"); }
| "int" { ##.DotNetType = mkType("System.Int32"); }
| "float" { ##.DotNetType = mkType("System.Single"); }
| "double" { ##.DotNetType = mkType("System.Double"); }
| "long" { ##.DotNetType = mkType("System.Int64"); }
| UBYTE { ##.DotNetType = mkType("System.Byte"); }
| DECIMAL { ##.DotNetType = mkType("System.Decimal"); }
| UINT { ##.DotNetType = mkType("System.UInt32"); }
| ULONG { ##.DotNetType = mkType("System.UInt64"); }
| USHORT { ##.DotNetType = mkType("System.UInt16"); }
| BYTE { ##.DotNetType = mkType("System.Byte"); } // What to do?
;
modifiers [object w]
: #( MODIFIERS (modifier[w]
)* )
;
modifier [object w]
: "private"
| "public"
| "protected"
| "static"
| "transient"
| FINAL
| ABSTRACT
| "native"
| "threadsafe"
| "synchronized"
| "const"
| "volatile"
| "strictfp"
;
extendsClause [object w]
//OK, OK, really we can only extend 1 class, but the tree stores a list so ....
: #(EXTENDS_CLAUSE
( type[w] )*
)
;
implementsClause [object w]
: #(IMPLEMENTS_CLAUSE
( type[w] )*
)
;
interfaceBlock [object w]
: #( MEMBER_LIST
( methodDecl[w]
| variableDef[w, false]
| typeDefinition[w]
)*
)
;
objBlock [object w]
: #( MEMBER_LIST
( ctorDef[w]
| methodDef[w]
| variableDef[w, true]
| typeDefinition[w]
| operatorDef[w]
| #(STATIC_CTOR_DECL
slist[w] )
| #(INSTANCE_INIT
slist[w] )
)*
)
;
enumBlock [object w]
: #( MEMBER_LIST
( #( IDENTIFIER ( expression[w])?)
)*
)
;
ctorDef [object w]
: #(CTOR_DECL
modifiers[w]
methodHead[w]
(slist[w])?)
;
methodDecl [object w]
: #(METHOD_DECL
modifiers[w]
typeSpec[w]
methodHead[w])
;
methodDef [object w]
: #(METHOD_DECL
modifiers[w]
typeSpec[w] { symtab.PushLevel(); }
methodHead[w]
(slist[w])?
) { symtab.PopLevel(); }
;
variableDef [object w, bool isCreate]
: #(FIELD_DECL
modifiers[w]
t:typeSpec[w]
(variableDeclarator[w, #t, isCreate])+
//varInitializer[w]
)
;
operatorDef [object w]
{ ASTNode retAST = null;
}
: ( #( UNARY_OP_DECL modifiers[w]
typeSpec[w] overloadableUnaryOperator[w]
paramList[w]
slist[w]
)
| #( BINARY_OP_DECL modifiers[w]
typeSpec[w] overloadableBinaryOperator[w]
paramList[w]
slist[w]
)
|! // A Type conversion operator. We translate this to a method "__cast[To/From]<type>()" method
// We treat both types as EXPLICIT because (at least for now) we need the explicit cast to tell
// us to resolve the conversion
#( CONV_OP_DECL m:modifiers[w] { retAST = #( [METHOD_DECL], astFactory.dupTree(#m) ); }
( IMPLICIT | EXPLICIT ) it:typeSpec[w]
ips:paramList[w] ib:slist[w]
{ string convertTo = #it.DotNetType.TypeName;
string convertFrom = ((ASTNode) #ips.getFirstChild().getFirstChild()).DotNetType.TypeName;
string currentClass = (nameSpace != ""? nameSpace + ".":"") + ClassInProcess;
bool isTo = convertTo == currentClass;
if (!isTo && convertFrom != currentClass)
{
Console.Error.Write("ERROR -- (Converting Cast Operator " + convertFrom + " to " + convertTo + ") ");
Console.Error.WriteLine("should match enclosing class " + currentClass);
}
string methodNm = "__castTo" + typeNameToId(convertTo);
retAST.addChild( astFactory.dupTree(#it) );
retAST.addChild( #([IDENTIFIER, methodNm]) );
retAST.addChild( astFactory.dupTree(#ips) );
retAST.addChild( #( [THROWS, "throws"], [IDENTIFIER, "Exception"] ) );
retAST.addChild( astFactory.dupTree(#ib) );
}
) { ## = retAST; }
)
;
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
;
parameterDef [object w]
: #(PARAMETER_FIXED
t:typeSpec[w]
id:IDENTIFIER { symtab[#id.getText()] = #t.DotNetType; }
)
| #(PARAMS tp:typeSpec[w]
idp:IDENTIFIER
{ // idp is actually an array of tp
symtab[#idp.getText()] = mkType(#tp.DotNetType.TypeName + "[]")
; }
)
;
objectinitializer [object w]
: #(INSTANCE_INIT slist[w] )
;
variableDeclarator [object w, ASTNode t, bool isCreate]
{ bool initted = false; }
: #( VAR_DECLARATOR
id:IDENTIFIER
(varInitializer[w] { initted = true; } )?
{ symtab[#id.getText()] = t.DotNetType; // keving: I assume id is not valid in initializer
if (isCreate && !initted)
{
if ( t.DotNetType is StructRep )
##.addChild( #( [VAR_INIT], #( [EXPR], #( [OBJ_CREATE_EXPR, "new"],
astFactory.dupTree(t),
#( [EXPR_LIST] ) ) ) ) );
if ( t.DotNetType is EnumRep )
{
string enumZero = ((EnumRep) t.DotNetType).getField(0);
##.addChild( #( [VAR_INIT], #( [EXPR], #( [MEMBER_ACCESS_EXPR, "."],
astFactory.dupTree(t.getFirstChild()),
#( [IDENTIFIER, enumZero] ) ) ) ) );
}
}
}
)
// | LBRACK variableDeclarator[w]
;
varInitializer [object w]
: #(VAR_INIT
initializer[w])
;
initializer [object w]
: expression[w]
| arrayInitializer[w]
;
arrayInitializer [object w]
: #(ARRAY_INIT (initializer[w])* )
;
methodHead [object w]
: IDENTIFIER paramList[w] (throwsClause[w])?
;
paramList [object w]
: #( FORMAL_PARAMETER_LIST
( parameterDef[w] )*
)
;
throwsClause [object w]
: #( "throws"
( identifier[w] ( identifier[w] )* )?
)
;
identifier [object w]
: IDENTIFIER
| #( DOT IDENTIFIER identifier[w] )
;
identifierStar [object w]
: IDENTIFIER
| STAR
| #( DOT IDENTIFIER identifier[w] )
;
slist [object w]
: #( BLOCK { symtab.PushLevel(); } (stat[w])* { symtab.PopLevel(); } )
| EMPTY_STMT
;
// Like a slist[]. Appears in switch alternatives
statementList [object w]
: #( STMT_LIST (stat[w])* )
;
stat [object w]
: typeDefinition[w]
| variableDef[w, true]
| #(EXPR_STMT expression[w])
| #(LABEL_STMT IDENTIFIER stat[w])
| #(IF
expression[w]
stat[w]
( #(ELSE
stat[w])
)?
)
| #( "for"
#(FOR_INIT (variableDef[w, true])* (expression[w] ( expression[w])* )?)
#(FOR_COND (expression[w])?)
#(FOR_ITER (expression[w] ( expression[w])* )?)
stat[w]
)
|! { foreachCounter++; }
#("foreach"
def:variableDef[w, true]
ce:expression[w]
bod:stat[w]
)
{
foreachCounter--;
string tempVar = "__o" + (foreachCounter > 0 ? foreachCounter+"" : "");
ASTNode retAST;
ASTNode typeAST = (ASTNode) #def.getFirstChild().getNextSibling();
string typeOfVar = typeToString(typeAST, false);
ASTNode initVar = (ASTNode) #def.getFirstChild().getNextSibling().getNextSibling().getFirstChild();
// If target expression supports interface IDictionary then we will be getting back DictionaryEntry's, for Java
// assume the expression #CE implements interface Map and call entrySet()
ASTNode iterableAST;
if (mkType("System.Collections.IDictionary").IsA(#ce))
{
iterableAST = #( [EXPR], #( [JAVAWRAPPER], [IDENTIFIER, "${ce}.entrySet()"],
[IDENTIFIER, "${ce}"], astFactory.dupTree(#ce) ) );
} else
if (mkType("System.String").IsA(#ce))
{
iterableAST = #( [EXPR], #( [JAVAWRAPPER], [IDENTIFIER, "${ce}.toCharArray()"],
[IDENTIFIER, "${ce}"], astFactory.dupTree(#ce) ) );
} else
{
iterableAST = (ASTNode) astFactory.dupTree(#ce);
}
retAST = #( [FOREACH, "foreach"],
#( [FIELD_DECL], #([MODIFIERS]),
#( [TYPE], #([IDENTIFIER, "Object"]), #([ARRAY_RANKS]) ),
#( [VAR_DECLARATOR], #( [IDENTIFIER, tempVar] ) ) ),
iterableAST);
retAST.addChild( #( [BLOCK],
#( [FIELD_DECL], astFactory.dupTree(#def.getFirstChild()),
astFactory.dupTree(typeAST),
#( [VAR_DECLARATOR],
astFactory.dupTree(initVar),
#( [VAR_INIT],
#( [EXPR], #( [CAST_EXPR], astFactory.dupTree(boxedType(typeAST)), #( [IDENTIFIER, tempVar] ) ) ) ) ) ),
#bod ) );
## = retAST;
}
| #("while"
expression[w]
stat[w]
)
| #("do"
stat[w]
expression[w]
)
| #("goto" IDENTIFIER )
| #("break" ( IDENTIFIER)? )
| #("continue" ( IDENTIFIER)? )
| #("return" ( expression[w])? )
|! { ASTNode scrutinee = null;
ASTNode retAST = null;
ASTNode iteAST = null;
}
#("switch"
se:expression[w] { if (isValidScrutinee(#se))
{
retAST = #( [SWITCH, "switch"], #se );
}
else
{
// Declare a variable with the right type to hold the scrutinee
string scrutType;
string scrutVar;
if (#se.DotNetType.Java != null)
scrutType = #se.DotNetType.Java;
else
scrutType = #se.DotNetType.TypeName;
scrutVar = "__scrut" + scrutineeCounter;
scrutineeCounter++;
// Add to symtab
symtab[scrutVar] = #se.DotNetType;
scrutinee = #( [IDENTIFIER, scrutVar] );
retAST = #( [BLOCK], #( [FIELD_DECL], [MODIFIERS], #( [TYPE], [IDENTIFIER, scrutType], [ARRAY_RANKS] ),
#( [VAR_DECLARATOR], scrutinee, #( [VAR_INIT],
astFactory.dupTree(#se) ) ) ) );
};
}
(cg:caseGroup[w, scrutinee] { if (scrutinee == null)
{
retAST.addChild(#cg);
}
else
{
// cg is if (...) { ... }
if (iteAST == null)
retAST.addChild(#cg);
else
iteAST.addChild( #( [ELSE, "else"], ( [BLOCK], #cg ) ) );
iteAST = #cg;
};
} )*
{ if (scrutinee != null)
scrutineeCounter--;
## = retAST;
}
)
| #("throw" expression[w] )
| #("synchronized"
expression[w]
stat[w]
)
| tryBlock[w]
| slist[w]
// uncomment to make assert JDK 1.4 stuff work
// | #("assert" expression[w] (expression[w])?)
| ctorCall[w]
;
// If s is null then this is a real case, else we are transforming it to if-then-else statements
caseGroup! [Object w, ASTNode s]
{ ASTNode retAST = null;
ASTNode condAST = null;
}
: #(SWITCH_SECTION
( #("case" e1:expression[w] { if (s == null)
{ ASTNode constAST = null;
if (mkType("System.Enum").IsA(#e1.DotNetType))
{
// Enums must not be qualified
ASTNode strippedConst = stripQualifier((ASTNode)#e1.getFirstChild());
constAST = #( [EXPR], strippedConst);
}
else
constAST = (ASTNode) astFactory.dupTree(#e1);
retAST = #( [SWITCH_SECTION],
#( [CASE, "case"], constAST ) );
}
else
condAST = #( [JAVAWRAPPER], [IDENTIFIER, "${this}.equals(${arg})"],
[IDENTIFIER, "${this}"], astFactory.dupTree(s),
[IDENTIFIER, "${arg}"], astFactory.dupTree(#e1.getFirstChild()) ); }
)
| "default" { if (s == null)
retAST = #( [SWITCH_SECTION], #( [DEFAULT, "default"] ) );
else
condAST = #( [TRUE, "true"] );
}
)
( #("case" en:expression[w] { if (s == null)
{
ASTNode constAST = null;
if (mkType("System.Enum").IsA(#en.DotNetType))
{
// Enums must not be qualified
ASTNode strippedConst = stripQualifier((ASTNode)#en.getFirstChild());
constAST = #( [EXPR], strippedConst);
}
else
constAST = (ASTNode) astFactory.dupTree(#en);
retAST.addChild( #( [CASE, "case"], constAST ) );
}
else
{
if ( condAST == null || condAST.Type != TRUE )
condAST = #( [LOG_OR, "||"], condAST, #( [JAVAWRAPPER], [IDENTIFIER, "${this}.equals(${arg})"],
[IDENTIFIER, "${this}"], astFactory.dupTree(s),
[IDENTIFIER, "${arg}"], astFactory.dupTree(#en.getFirstChild()) ) ); }
}
)
| "default" { if (s == null)
retAST.addChild( #( [DEFAULT, "default"] ) );
else
condAST = #( [TRUE, "true"] ); // We must always take this option, ignore previous
}
)*
sl:statementList[w]
{ if (s == null)
retAST.addChild( astFactory.dupTree(#sl) );
else
{ // strip trailing break from sl
ASTNode slAST = (ASTNode) #sl.getFirstChild();
if (slAST != null)
{
ASTNode prevAST = null;
ASTNode nextAST = slAST;
while (nextAST.getNextSibling() != null)
{
prevAST = nextAST;
nextAST = (ASTNode) nextAST.getNextSibling();
}
if (nextAST.Type == BREAK)
{
// Strip break
if (prevAST == null)
slAST = null;
else
prevAST.setNextSibling(null);
}
}
if (condAST == null || condAST.Type == TRUE)
retAST = slAST;
else
retAST = #( [IF, "if"], #( [EXPR], condAST), #( [BLOCK], slAST ) );
}
## = retAST;
}
)
;
tryBlock [object w]
: #( "try"
slist[w]
(handler[w])*
(#("finally"
slist[w]
))?
)
;
handler [object w]
: #( "catch"
( // typeSpec[w] |
variableDef[w, false] )
slist[w]
)
;
elist [object w]
: #( EXPR_LIST
( expression[w] )*
)
;
expression [object w]
: #(EXPR e:expr[w]) { ##.DotNetType = #e.DotNetType; }
;
expr [object w]
// Set expression type to be the type of the 'then' part (but maybe it should be the least of 'then' and 'else'??)
: #( QUESTION expr[w] t1:expr[w] expr[w] ) { ##.DotNetType = #t1.DotNetType; }
// binary operators...
| assignOp[w]
| #( BIN_OR op1:expr[w] expr[w] ) { ##.DotNetType = #op1.DotNetType; }
| #( BIN_XOR op2:expr[w] expr[w] ) { ##.DotNetType = #op2.DotNetType; }
| #( BIN_AND op3:expr[w] expr[w] ) { ##.DotNetType = #op3.DotNetType; }
| #( SHIFTL op4:expr[w] expr[w] ) { ##.DotNetType = #op4.DotNetType; }
| #( SHIFTR op5:expr[w] expr[w] ) { ##.DotNetType = #op5.DotNetType; }
| #( BSR op6:expr[w] expr[w] ) { ##.DotNetType = #op6.DotNetType; }
| #( PLUS op7:expr[w] expr[w] ) { ##.DotNetType = #op7.DotNetType; }
| #( MINUS op8:expr[w] expr[w] ) { ##.DotNetType = #op8.DotNetType; }
| #( DIV op9:expr[w] expr[w] ) { ##.DotNetType = #op9.DotNetType; }
| #( MOD op10:expr[w] expr[w] ) { ##.DotNetType = #op10.DotNetType; }
| #( STAR op11:expr[w] expr[w] ) { ##.DotNetType = #op11.DotNetType; }
| #( INSTANCEOF expr[w] typeSpec[w] ) { ##.DotNetType = mkType("System.Boolean"); }
// In C# strings are always compared by content
|! #( ope:EQUAL eq1:expr[w] eq2:expr[w] )
{ ASTNode retAST = null;
if (#eq1.DotNetType.TypeName == "System.String" ||
#eq2.DotNetType.TypeName == "System.String" )
{
retAST = #( [JAVAWRAPPER], [IDENTIFIER, "StringSupport.equals(${s1},${s2})"],
[IDENTIFIER, "${s1}"], astFactory.dupTree(#eq1),
[IDENTIFIER, "${s2}"], astFactory.dupTree(#eq2) );
this.addImport("RusticiSoftware.System.StringSupport");
}
else
if (#eq1.DotNetType.TypeName == "System.DateTime" ||
#eq2.DotNetType.TypeName == "System.DateTime" )
{
retAST = #( [JAVAWRAPPER], [IDENTIFIER, "(${d1} == ${d2} || ${d1}.getTime() == ${d2}.getTime())"],
[IDENTIFIER, "${d1}"], astFactory.dupTree(#eq1),
[IDENTIFIER, "${d2}"], astFactory.dupTree(#eq2) );
}
else
retAST = #( #ope, #eq1, #eq2);
retAST.DotNetType = mkType("System.Boolean");
## = retAST;
}
|! #( opn:NOT_EQUAL ne1:expr[w] ne2:expr[w] )
{ ASTNode retAST = null;
if (#ne1.DotNetType.TypeName == "System.String" ||
#ne2.DotNetType.TypeName == "System.String")
{
retAST = #( [LOG_NOT, "!"],
#( [JAVAWRAPPER], [IDENTIFIER, "StringSupport.equals(${s1}, ${s2})"],
[IDENTIFIER, "${s1}"], astFactory.dupTree(#ne1),
[IDENTIFIER, "${s2}"], astFactory.dupTree(#ne2) ) );
this.addImport("RusticiSoftware.System.StringSupport");
}
else
if (#ne1.DotNetType.TypeName == "System.DateTime" ||
#ne2.DotNetType.TypeName == "System.DateTime" )
{
retAST = #( [JAVAWRAPPER], [IDENTIFIER, "(${d1} != ${d2} && ${d1}.getTime() != ${d2}.getTime())"],
[IDENTIFIER, "${d1}"], astFactory.dupTree(#ne1),
[IDENTIFIER, "${d2}"], astFactory.dupTree(#ne2) );
}
else
retAST = #( #opn, #ne1, #ne2);
retAST.DotNetType = mkType("System.Boolean");
## = retAST;
}
|! #( opl:LTHAN lt1:expr[w] lt2:expr[w] )
{ ASTNode retAST = null;
if (#lt1.DotNetType.TypeName == "System.DateTime" || #lt2.DotNetType.TypeName == "System.DateTime")
retAST = #( [JAVAWRAPPER], [IDENTIFIER, "${this}.before(${arg})"],
[IDENTIFIER, "${this}"], astFactory.dupTree(#lt1),
[IDENTIFIER, "${arg}"], astFactory.dupTree(#lt2) );
else
retAST = #( #opl, #lt1, #lt2);
retAST.DotNetType = mkType("System.Boolean");
## = retAST;
}
|! #( opg:GTHAN gt1:expr[w] gt2:expr[w] )
{ ASTNode retAST = null;
if (#gt1.DotNetType.TypeName == "System.DateTime" || #gt2.DotNetType.TypeName == "System.DateTime")
retAST = #( [JAVAWRAPPER], [IDENTIFIER, "${this}.after(${arg})"],
[IDENTIFIER, "${this}"], astFactory.dupTree(#gt1),
[IDENTIFIER, "${arg}"], astFactory.dupTree(#gt2) );
else
retAST = #( #opg, #gt1, #gt2);
retAST.DotNetType = mkType("System.Boolean");
## = retAST;
}
|! #( opge:GTE ge1:expr[w] ge2:expr[w] )
{ ASTNode retAST = null;
if (#ge1.DotNetType.TypeName == "System.DateTime" || #ge2.DotNetType.TypeName == "System.DateTime")
retAST = #( [JAVAWRAPPER], [IDENTIFIER, "(${this}.compareTo(${arg}) >= 0)"],
[IDENTIFIER, "${this}"], astFactory.dupTree(#ge1),
[IDENTIFIER, "${arg}"], astFactory.dupTree(#ge2) );
else
retAST = #( #opge, #ge1, #ge2);
retAST.DotNetType = mkType("System.Boolean");
## = retAST;
}
|! #( ople:LTE le1:expr[w] le2:expr[w] )
{ ASTNode retAST = null;
if (#le1.DotNetType.TypeName == "System.DateTime" || #le2.DotNetType.TypeName == "System.DateTime")
retAST = #( [JAVAWRAPPER], [IDENTIFIER, "(${this}.compareTo(${arg}) <= 0)"],
[IDENTIFIER, "${this}"], astFactory.dupTree(#le1),
[IDENTIFIER, "${arg}"], astFactory.dupTree(#le2) );
else
retAST = #( #ople, #le1, #le2);
retAST.DotNetType = mkType("System.Boolean");
## = retAST;
}
| #( LOG_OR expr[w] expr[w] ) { ##.DotNetType = mkType("System.Boolean"); }
| #( LOG_AND expr[w] expr[w] ) { ##.DotNetType = mkType("System.Boolean"); }
| #( INC op12:expr[w] ) { ##.DotNetType = #op12.DotNetType; }
| #( DEC op13:expr[w] ) { ##.DotNetType = #op13.DotNetType; }
| #( POST_INC_EXPR op14:expr[w] ) { ##.DotNetType = #op14.DotNetType; }
| #( POST_DEC_EXPR op15:expr[w] ) { ##.DotNetType = #op15.DotNetType; }
| #( UNARY_MINUS op16:expr[w] ) { ##.DotNetType = #op16.DotNetType; }
| #( UNARY_PLUS op17:expr[w] ) { ##.DotNetType = #op17.DotNetType; }
| #( BIN_NOT op18:expr[w] ) { ##.DotNetType = #op18.DotNetType; }
| #( LOG_NOT op19:expr[w] ) { ##.DotNetType = #op19.DotNetType; }
| primaryExpression[w]
;
assignOp! [Object w]
: #( op:ASSIGN left:expr[w] right:expr[w] )
{
ASTNode kosherInp = #( astFactory.dupTree(#op), astFactory.dupTree(#left), astFactory.dupTree(#right) );
ASTNode retAST = null;
if ( #left.Type == MEMBER_ACCESS_EXPR &&
#left.getFirstChild().getNextSibling().Type == IDENTIFIER)
{
retAST = ResolveSetter( kosherInp );
if (retAST == null)
retAST = kosherInp;
}
else if ( #left.Type == ELEMENT_ACCESS_EXPR )
{
TypeRep at = ((ASTNode)#left.getFirstChild()).DotNetType;
if (at.TypeName.EndsWith("[]") || at.TypeName == "System.Array")
{
// A real array :-)
retAST = kosherInp;
}
else
{
ASTNode objAST = (ASTNode) astFactory.dupTree(#left.getFirstChild());
ASTNode keyAST = (ASTNode) astFactory.dupTree(#left.getFirstChild().getNextSibling().getFirstChild());
retAST = ResolveMethod( #( [INVOCATION_EXPR], #( [MEMBER_ACCESS_EXPR], objAST,
[IDENTIFIER, "set___idx"]),
#( [EXPR_LIST], keyAST,
astFactory.dupTree(#right))) );
if (retAST == null)
{
retAST = kosherInp;
}
}
}
else if (#left.Type == IDENTIFIER)
{
// Try to resolve to a local variable / property
ASTNode thisNode = #( [THIS, "DUMMYTHIS"] );
thisNode.DotNetType = symtab["this"];
retAST = ResolveSetter( #( astFactory.dupTree(#op), #( [MEMBER_ACCESS_EXPR], thisNode, astFactory.dupTree(#left) ),
astFactory.dupTree(#right) ) );
}
if (retAST == null)
{
retAST = kosherInp;
}
## = retAST;
##.DotNetType = mkType("System.Void");
}
;
primaryExpression [object w]
: v:IDENTIFIER {
ASTNode retAST = null;
TypeRep retType = null;
// Look for identifier in symbol table
retType = symtab[#v.getText()];
if (retType != null)
{
retAST = #v;
retAST.DotNetType = retType;
}
else
{
if ( ##_in.getParent().Type != INVOCATION_EXPR &&
!SetterContext(##_in) )
{ // Looking for a property or field
ASTNode thisNode = #( [THIS, "DUMMYTHIS"] );
thisNode.DotNetType = symtab["this"];
retAST = ResolveGetter( #( [MEMBER_ACCESS_EXPR], thisNode, #v) );
}
if (retAST == null)
{
if ( ##_in.getParent().Type == INVOCATION_EXPR)
{
// method call
retAST = #v;
retAST.DotNetType = null;
}
else
{
// Might be part of a reference to a static member of a type
retType = mkType(#v.getText());
if (retType != null)
{
retAST = #v;
retAST.DotNetType = retType;
}
else
{
// TODO: Probably part of a qualified type name
retAST = #v;
retAST.DotNetType = mkType("UNKNOWN TYPE");
}
}
}
}
## = retAST;
}
| #( JAVAWRAPPER identifier[w] (identifier[w] (expression[w]|expr[w]|elist[w]))* )
{
if (##.DotNetType == null) {
##.DotNetType = mkType("System.Object");
}
else {
// We saved just the type name on the previous pass
##.DotNetType = mkType(##.DotNetType.TypeName);
}
}
| memberAccessExpr[w]
| arrayIndex[w]
|! #(INVOCATION_EXPR e:primaryExpression[w] args:elist[w] )
{
ASTNode kosherInp = #( [INVOCATION_EXPR], #e, #args);
ASTNode retAST = null;
if (#e != null && #e.Type == IDENTIFIER && symtab[#e.getText()] == null)
{
// Is it a local method call?
ASTNode thisNode = #( [THIS, "DUMMYTHIS"] );
thisNode.DotNetType = symtab["this"];
retAST = ResolveMethod( #( [INVOCATION_EXPR], #( [MEMBER_ACCESS_EXPR], thisNode, astFactory.dupTree(#e)),
astFactory.dupTree(#args)) );
}
else if (#e != null && #e.Type == MEMBER_ACCESS_EXPR && #e.getFirstChild().getNextSibling().Type == IDENTIFIER)
{ // resolve method call
retAST = ResolveMethod( kosherInp );
}
if (retAST == null) {
retAST = kosherInp;
retAST.DotNetType = mkType("System.Object");
}
## = retAST;
}
|! #( CAST_EXPR t:typeSpec[w] ce:expr[w] )
{
ASTNode kosherInp = #( [CAST_EXPR], #t, #ce );
ASTNode retAST = null;
retAST = ResolveCast( kosherInp );
if (retAST == null)
{
retAST = kosherInp;
}
## = retAST;
##.DotNetType = #t.DotNetType;
}
| newExpression[w]
| constant[w]
| "super" { ##.DotNetType = symtab["super"]; }
| "true" { ##.DotNetType = mkType("System.Boolean"); }
| "false" { ##.DotNetType = mkType("System.Boolean"); }
| "this" { ##.DotNetType = symtab["this"]; }
| NULL { ##.DotNetType = mkType("System.Object"); }
| typeSpec[w] // type name used with instanceof
;
memberAccessExpr [object w]
{ ASTNode ret = null; }
:! #( MEMBER_ACCESS_EXPR
( te:expr[w]
( m:IDENTIFIER {
ASTNode kosherInp = #( [MEMBER_ACCESS_EXPR], #te, #m);
ASTNode retAST = null;
if ( ##_in.getParent().Type != INVOCATION_EXPR &&
!SetterContext(##_in) )
{ // Looking for a property or field
retAST = ResolveGetter( kosherInp );
}
if ( retAST == null )
{
retAST = kosherInp;
retAST.DotNetType = mkType("System.Object");
}
## = retAST;
}
| ai:arrayIndex[w] { ## = #( [MEMBER_ACCESS_EXPR], #te, #ai); }
| th:"this" { ## = #( [MEMBER_ACCESS_EXPR], #te, #th); }
| cl:"class" { ## = #( [MEMBER_ACCESS_EXPR], #te, #cl); }
| #( n:"new" i:IDENTIFIER es:elist[w] ) { ## = #( [MEMBER_ACCESS_EXPR], #te, #( #n, #i, #es ) ); }
| sp:"super" { ## = #( [MEMBER_ACCESS_EXPR], #te, #sp); }
)
| #(ARRAY_DECLARATOR t:typeSpecArray[w] ) { ## = #( [MEMBER_ACCESS_EXPR], #([ARRAY_DECLARATOR], #t) ); }
| bt:builtInType[w] { ## = #( [MEMBER_ACCESS_EXPR], #bt); } (cl1:"class" { ##.addChild(#cl1); } )?
)
)
;
ctorCall [object w]
: #( THIS elist[w] )
| #( BASE elist[w] )
;
arrayIndex! [object w]
: #(ELEMENT_ACCESS_EXPR e:expr[w] idx:elist[w] ) // keving: Strips off one rank
{
ASTNode retAST = null;
TypeRep at = #e.DotNetType;
if (at.TypeName.EndsWith("[]") || at.TypeName == "System.Array")
{
// A real array, keep array notation
ArrayList ms = (ArrayList)at.MethodsD["GetValue"];
retAST = #( [ELEMENT_ACCESS_EXPR], #e, #idx);
retAST.DotNetType = ((MethodRep)ms[0]).Return;
}
else
{
// TODO: Setter or Getter?
if ( !SetterContext(##_in) )
{
retAST = ResolveMethod( #( [INVOCATION_EXPR], #( [MEMBER_ACCESS_EXPR], astFactory.dupTree(#e),
[IDENTIFIER, "get___idx"]),
astFactory.dupTree(#idx)) );
if (retAST == null)
{
retAST = #( [ELEMENT_ACCESS_EXPR], #e, #idx);
retAST.DotNetType = mkType("System.Object");
}
}
else
{
retAST = #( [ELEMENT_ACCESS_EXPR], #e, #idx);
retAST.DotNetType = mkType("System.Object");
}
}
## = retAST;
}
;
constant [object w]
: INT_LITERAL { ##.DotNetType = mkType("System.Int32"); }
| CHAR_LITERAL { ##.DotNetType = mkType("System.Char"); }
| STRING_LITERAL { ##.DotNetType = mkType("System.String"); }
| NUM_FLOAT { ##.DotNetType = mkType("System.Single"); }
| DOUBLE_LITERAL { ##.DotNetType = mkType("System.Double"); }
| FLOAT_LITERAL { ##.DotNetType = mkType("System.Single"); }
| LONG_LITERAL { ##.DotNetType = mkType("System.Int64"); }
| ULONG_LITERAL { ##.DotNetType = mkType("System.UInt64"); }
| DECIMAL_LITERAL { ##.DotNetType = mkType("System.Decimal"); }
;
newExpression [object w]
:! #( OBJ_CREATE_EXPR
t:typeSpec[w]
a:elist[w] {
ASTNode kosherInp = #( [OBJ_CREATE_EXPR], #t, #a);
ASTNode retAST = null;
retAST = ResolveNewObj( kosherInp );
if (retAST == null)
{
retAST = kosherInp;
retAST.DotNetType = #t.DotNetType;
}
## = retAST;
}
/* keving: This was in the pretty printer, but it is never generated and I can't find reference to it
(
objBlock[w]
)?
*/
)
| #( ARRAY_CREATE_EXPR
at:typeSpec[w]
( arrayInitializer[w] //rankSpecifiers[w]!
| elist[w]
rankSpecifiers[w] ( arrayInitializer[w] )?
)
) { ##.DotNetType = #at.DotNetType; }
;
// newArrayDeclarator [object w]
// : #( ARRAY_DECLARATOR (newArrayDeclarator[w])? (expression[w])? )
// ;