1
0
mirror of https://github.com/twiglet/cs2j.git synced 2025-01-18 13:15:17 +01:00
cs2j/CSharpTranslator/antlr2/Translator/CSharpTranslator.g
Kevin Glynn 7069b42023 Turn C# destructors into finalize() methods.
TODO: probably should call super.finalize()
2010-07-12 20:11:50 -05:00

1895 lines
58 KiB
Plaintext

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] )
;