/* Copyright 2010,2011 Kevin Glynn (kevin.glynn@twigletsoftware.com) */ // JavaMaker.g // // Convert C# parse tree to a Java parse tree // tree grammar JavaMaker; options { tokenVocab=cs; ASTLabelType=CommonTree; language=CSharp2; superClass='Twiglet.CS2J.Translator.Transform.CommonWalker'; output=AST; } // A scope to keep track of the namespaces available at any point in the program scope NSContext { int filler; string currentNS; // namespaces in scope List namespaces; // Alias map: these two lists are actually a map from alias to namespace // so aliases[i] -> namespaces[i] List aliasKeys; List aliasNamespaces; } // A scope to keep track of the current type context scope TypeContext { string typeName; } @namespace { Twiglet.CS2J.Translator.Transform } @header { using System; using System.Globalization; } @members { // Since a CS file may comtain multiple top level types (and so generate multiple Java // files) we build a map from type name to AST for each top level type // We also build a lit of type names so that we can maintain the order (so comments // at the end of the file will get included when we emit the java for the last type) public IDictionary CUMap { get; set; } public IList CUKeys { get; set; } protected string ParentNameSpace { get { return ((NSContext_scope)$NSContext.ToArray()[1]).currentNS; } } protected List CollectSearchPath { get { List ret = new List(); Object[] nsCtxtArr = $NSContext.ToArray(); for (int i = nsCtxtArr.Length - 1; i >= 0; i--) { foreach (string v in ((NSContext_scope)nsCtxtArr[i]).namespaces) { ret.Add(v); } } return ret; } } protected List CollectAliasKeys { get { List ret = new List(); Object[] nsCtxtArr = $NSContext.ToArray(); for (int i = nsCtxtArr.Length - 1; i >= 0; i--) { foreach (string v in ((NSContext_scope)nsCtxtArr[i]).aliasKeys) { ret.Add(v); } } return ret; } } protected List CollectAliasNamespaces { get { List ret = new List(); Object[] nsCtxtArr = $NSContext.ToArray(); for (int i = nsCtxtArr.Length - 1; i >= 0; i--) { foreach (string v in ((NSContext_scope)nsCtxtArr[i]).aliasNamespaces) { ret.Add(v); } } return ret; } } // TREE CONSTRUCTION protected CommonTree mkPayloadList(List payloads) { CommonTree root = (CommonTree)adaptor.Nil; foreach (string p in payloads) { adaptor.AddChild(root, (CommonTree)adaptor.Create(PAYLOAD, p)); } return root; } protected CommonTree mangleModifiersForType(CommonTree modifiers) { if (modifiers == null || modifiers.Children == null) return modifiers; CommonTree stripped = (CommonTree)modifiers.DupNode(); for (int i = 0; i < modifiers.Children.Count; i++) { if (((CommonTree)modifiers.Children[i]).Token.Text != "static") { adaptor.AddChild(stripped, modifiers.Children[i]); } } return stripped; } protected CommonTree addConstModifiers(IToken tok, List filter) { CommonTree root = (CommonTree)adaptor.Nil; if (filter == null || !filter.Contains("static") ) { adaptor.AddChild(root, (CommonTree)adaptor.Create(STATIC, tok, "static")); } if (filter == null || !filter.Contains("final")) { adaptor.AddChild(root, (CommonTree)adaptor.Create(FINAL, tok, "final")); } root = (CommonTree)adaptor.RulePostProcessing(root); return root; } // embedded statement is ";", or, "{" ... "}", or a single statement. In the latter case we wrap with braces protected CommonTree embeddedStatementToBlock(IToken tok, CommonTree embedStat) { if ((!embedStat.IsNil && adaptor.GetType(embedStat) == SEMI) || (embedStat.IsNil && adaptor.GetChildCount(embedStat) >= 1 && adaptor.GetType(embedStat) == SEMI)) { // Do Nothing, already a block return embedStat; } CommonTree root = (CommonTree)adaptor.Nil; adaptor.AddChild(root, (CommonTree)adaptor.Create(OPEN_BRACE, tok, "{")); adaptor.AddChild(root, dupTree(embedStat)); adaptor.AddChild(root, (CommonTree)adaptor.Create(CLOSE_BRACE, tok, "}")); root = (CommonTree)adaptor.RulePostProcessing(root); return root; } // for ["conn", "conn1", "conn2"] generate: // // if (conn != null) // ((IDisposable)conn).Dispose(); // // // if (conn2 != null) // ((IDisposable)conn2).Dispose(); // // // if (conn3 != null) // ((IDisposable)conn3).Dispose(); // used in the finally block of the using translation protected CommonTree addDisposeVars(IToken tok, List vars) { CommonTree root = (CommonTree)adaptor.Nil; foreach (string var in vars) { CommonTree root_1 = (CommonTree)adaptor.Nil; root_1 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(IF, tok, "if"), root_1); CommonTree root_2 = (CommonTree)adaptor.Nil; root_2 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(NOT_EQUAL, tok, "!="), root_2); adaptor.AddChild(root_2, (CommonTree)adaptor.Create(IDENTIFIER, tok, var)); adaptor.AddChild(root_2, (CommonTree)adaptor.Create(NULL, tok, "null")); adaptor.AddChild(root_1, root_2); adaptor.AddChild(root_1, (CommonTree)adaptor.Create(SEP, "SEP")); root_2 = (CommonTree)adaptor.Nil; root_2 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(APPLY, tok, "APPLY"), root_2); CommonTree root_3 = (CommonTree)adaptor.Nil; root_3 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(DOT, tok, "."), root_3); CommonTree root_4 = (CommonTree)adaptor.Nil; root_4 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(CAST_EXPR, tok, "CAST"), root_4); CommonTree root_5 = (CommonTree)adaptor.Nil; root_5 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(TYPE, tok, "TYPE"), root_5); adaptor.AddChild(root_5, (CommonTree)adaptor.Create(IDENTIFIER, tok, "IDisposable")); adaptor.AddChild(root_4, root_5); adaptor.AddChild(root_4, (CommonTree)adaptor.Create(IDENTIFIER, tok, var)); adaptor.AddChild(root_3, root_4); adaptor.AddChild(root_3, (CommonTree)adaptor.Create(IDENTIFIER, tok, "Dispose")); adaptor.AddChild(root_2, root_3); adaptor.AddChild(root_1, root_2); adaptor.AddChild(root_1, (CommonTree)adaptor.Create(SEMI, tok, ";")); adaptor.AddChild(root, root_1); } return (CommonTree)adaptor.RulePostProcessing(root); } // TODO: Read reserved words from a file so that they can be extended by customer private readonly static string[] javaReserved = new string[] { "int", "protected", "package" }; protected string fixBrokenId(string id) { // Console.WriteLine(id); foreach (string k in javaReserved) { if (k == id) { return "__" + id; } } return id; } // Map of C# built in types to Java equivalents Dictionary predefined_type_map = new Dictionary() { {"bool", "boolean"}, {"decimal", "double"}, {"object", "Object"}, {"string", "String"} }; protected CommonTree mkHole() { return mkHole(null); } protected CommonTree mkHole(IToken tok) { return (CommonTree)adaptor.Create(KGHOLE, tok, "KGHOLE"); } // counter to ensure that the catch vars we introduce are unique protected int dummyCatchVarCtr = 0; protected int newVarCtr = 0; protected CommonTree dupTree(CommonTree t) { return (CommonTree)adaptor.DupTree(t); } } /******************************************************************************************** Parser section *********************************************************************************************/ /////////////////////////////////////////////////////// public compilation_unit scope NSContext; @init { $NSContext::currentNS = ""; $NSContext::namespaces = new List(); $NSContext::aliasKeys = new List(); $NSContext::aliasNamespaces = new List(); } : namespace_body; namespace_declaration scope NSContext; @init { $NSContext::currentNS = ""; $NSContext::namespaces = new List(); $NSContext::aliasKeys = new List(); $NSContext::aliasNamespaces = new List(); }: 'namespace' qi=qualified_identifier { // extend parent namespace $NSContext::currentNS = this.ParentNameSpace + $qi.thetext; $NSContext::namespaces.Add($NSContext::currentNS); } namespace_block ';'? ; namespace_block: '{' namespace_body '}' ; namespace_body: extern_alias_directives? using_directives? global_attributes? namespace_member_declarations? ; extern_alias_directives: extern_alias_directive+ ; extern_alias_directive: e='extern' 'alias' i=identifier ';' { Warning($e.line, "[UNSUPPORTED] External Alias " + $i.text); } ; using_directives: using_directive+ ; using_directive: (using_alias_directive | using_namespace_directive) ; using_alias_directive: 'using' identifier '=' namespace_or_type_name ';' {$NSContext::aliasKeys.Add($identifier.text);$NSContext::aliasNamespaces.Add($namespace_or_type_name.thetext); } ; using_namespace_directive: 'using' namespace_name ';' {$NSContext::namespaces.Add($namespace_name.thetext); }; namespace_member_declarations: namespace_member_declaration+ ; namespace_member_declaration @init { string ns = $NSContext::currentNS; bool isCompUnit = false; } @after { if (isCompUnit) { if (CUKeys.Contains(ns+"."+$ty.name)) { { Warning($ty.start.Token.Line, "[UNSUPPORTED] Cannot have a class with multiple generic type overloadings: " + ns+"."+$ty.name); } } else { CUMap.Add(ns+"."+$ty.name, new CUnit($namespace_member_declaration.tree,CollectSearchPath,CollectAliasKeys,CollectAliasNamespaces)); CUKeys.Add(ns+"."+$ty.name); } }; } : namespace_declaration | attributes? modifiers? ty=type_declaration[$attributes.tree, mangleModifiersForType($modifiers.tree)] { isCompUnit = true; } -> ^(PACKAGE[$ty.start.Token, "package"] PAYLOAD[ns] $ty); // type_declaration is only called at the top level, so each of the types declared // here will become a Java compilation unit (and go to its own file) type_declaration[CommonTree atts, CommonTree mods] returns [string name] : ('partial') => p='partial'! { Warning($p.line, "[UNSUPPORTED] 'partial' definition"); } (pc=class_declaration[$atts, $mods] { $name=$pc.name; } | ps=struct_declaration[$atts, $mods] { $name=$ps.name; } | pi=interface_declaration[$atts, $mods] { $name=$pi.name; }) | c=class_declaration[$atts, $mods] { $name=$c.name; } | s=struct_declaration[$atts, $mods] { $name=$s.name; } | i=interface_declaration[$atts, $mods] { $name=$i.name; } | e=enum_declaration[$atts, $mods] { $name=$e.name; } | d=delegate_declaration[$atts, $mods] { $name=$d.name; } ; // Identifiers qualified_identifier returns [string thetext]: i1=identifier { $thetext = $i1.text; } ('.' ip=identifier { $thetext += "." + $ip.text; } )*; namespace_name returns [string thetext] : namespace_or_type_name { $thetext = $namespace_or_type_name.thetext; }; modifiers returns [List modList] @init { $modList = new List(); }: (modifier { if ($modifier.tree != null) $modList.Add( $modifier.tree.Text); })+ ; modifier: 'new' -> /* No new in Java*/ | 'public' | 'protected' | 'private' | 'internal' -> /* translate to package-private */| 'unsafe' -> | 'abstract' | 'sealed' -> FINAL["final"] | 'static' | 'readonly' -> /* no equivalent in C# (this is like a const that can be initialized separately in the constructor) */ | 'volatile' | e='extern' { Warning($e.line, "[UNSUPPORTED] 'extern' modifier"); } | 'virtual' -> | 'override' -> /* not in Java, maybe convert to override annotation */; class_member_declaration: a=attributes? m=modifiers? ( c='const' ct=type constant_declarators ';' -> ^(FIELD[$c.token, "FIELD"] $a? $m? { addConstModifiers($c.token, $m.modList) } $ct constant_declarators) | ev=event_declaration -> ^(EVENT[$ev.start.Token, "EVENT"] $a? $m? $ev) | p='partial' { Warning($p.line, "[UNSUPPORTED] 'partial' definition"); } (v1=void_type m3=method_declaration[$a.tree, $m.tree, $m.modList, $v1.tree, $v1.text] -> $m3 | pi=interface_declaration[$a.tree, $m.tree] -> $pi | pc=class_declaration[$a.tree, $m.tree] -> $pc | ps=struct_declaration[$a.tree, $m.tree] -> $ps) | i=interface_declaration[$a.tree, $m.tree] -> $i | v2=void_type m1=method_declaration[$a.tree, $m.tree, $m.modList, $v2.tree, $v2.text] -> $m1 | t=type ( (member_name type_parameter_list? '(') => m2=method_declaration[$a.tree, $m.tree, $m.modList, $t.tree, $t.text] -> $m2 | (member_name '{') => pd=property_declaration[$a.tree, $m.tree, $t.tree] -> $pd | (member_name '.' 'this') => type_name '.' ix1=indexer_declaration[$a.tree, $m.tree, $t.tree] -> $ix1 | ix2=indexer_declaration[$a.tree, $m.tree, $t.tree] -> $ix2 //this | field_declaration -> ^(FIELD[$t.start.Token, "FIELD"] $a? $m? $t field_declaration) // qid | operator_declaration -> ^(OPERATOR[$t.start.Token, "OPERATOR"] $a? $m? $t operator_declaration) ) // common_modifiers// (method_modifiers | field_modifiers) | cd=class_declaration[$a.tree, $m.tree] -> $cd | sd=struct_declaration[$a.tree, $m.tree] -> $sd | ed=enum_declaration[$a.tree, $m.tree] -> $ed | dd=delegate_declaration[$a.tree, $m.tree] -> $dd | co3=conversion_operator_declaration -> ^(CONVERSION_OPERATOR[$co3.start.Token, "CONVERSION"] $a? $m? $co3) | con3=constructor_declaration[$a.tree, $m.tree, $m.modList] -> $con3 | de3=destructor_declaration -> $de3 ) ; primary_expression: ('this' brackets[null]) => (t='this' -> $t) (b1=brackets[$primary_expression.tree] -> $b1) (pp1=primary_expression_part[$primary_expression.tree] -> $pp1) * | ('base' brackets[null]) => (b='base' -> SUPER[$b.token, "super"]) (b2=brackets[$primary_expression.tree] -> $b2) (pp2=primary_expression_part[$primary_expression.tree] -> $pp2) * | (primary_expression_start -> primary_expression_start) (pp3=primary_expression_part[$primary_expression.tree] -> $pp3 )* // keving:TODO fixup | 'new' ( (object_creation_expression ('.'|'->'|'[')) => (oc1=object_creation_expression -> $oc1) (pp4=primary_expression_part[ $primary_expression.tree ] -> $pp4 )+ // new Foo(arg, arg).Member // try the simple one first, this has no argS and no expressions // symantically could be object creation // keving: No, try object_creation_expression first, it could be new type ( xx ) {} // can also match delegate_creation, will have to distinguish in NetMaker.g | (object_creation_expression) => oc2=object_creation_expression -> $oc2 | delegate_creation_expression -> delegate_creation_expression // new FooDelegate (MyFunction) | anonymous_object_creation_expression -> anonymous_object_creation_expression) // new {int X, string Y} | sizeof_expression // sizeof (struct) | checked_expression // checked (... | unchecked_expression // unchecked {...} | default_value_expression // default | anonymous_method_expression // delegate (int foo) {} ; primary_expression_start: predefined_type | (identifier generic_argument_list) => identifier generic_argument_list | identifier ((c='::'^ identifier { Warning($c.line, "[UNSUPPORTED] external aliases are not yet supported"); })?)! | 'this' | b='base' -> SUPER[$b.token, "super"] | paren_expression | typeof_expression // typeof(Foo).Name | literal ; primary_expression_part [CommonTree lhs]: access_identifier[$lhs] | brackets_or_arguments[$lhs] | p='++' -> ^(POSTINC[$p.token, "++"] { dupTree($lhs) } ) | m='--' -> ^(POSTDEC[$m.token, "--"] { dupTree($lhs) } ) ; access_identifier [CommonTree lhs]: access_operator type_or_generic -> ^(access_operator { dupTree($lhs) } type_or_generic); access_operator: '.' | '->' ; brackets_or_arguments [CommonTree lhs]: brackets[$lhs] | arguments[$lhs] ; brackets [CommonTree lhs]: '[' expression_list? ']' -> ^(INDEX { dupTree($lhs) } expression_list?); // keving: TODO: drop this. paren_expression: '(' expression ')' -> ^(PARENS expression); arguments [CommonTree lhs]: '(' argument_list? ')' -> ^(APPLY { dupTree($lhs) } argument_list?); argument_list: a1=argument (',' an+=argument)* -> ^(ARGS[$a1.start.Token,"ARGS"] $a1 $an*); // 4.0 argument: argument_name argument_value | argument_value; argument_name: identifier ':'; argument_value: expression | ref_variable_reference | 'out' variable_reference ; ref_variable_reference: 'ref' (('(' type ')') => '(' type ')' (ref_variable_reference | variable_reference) // SomeFunc(ref (int) ref foo) // SomeFunc(ref (int) foo) | variable_reference); // SomeFunc(ref foo) // lvalue variable_reference: expression; rank_specifiers: rank_specifier+ ; // convert dimension separators into additional dimensions, so [,,] -> [] [] [] rank_specifier: o='[' dim_separators? c=']' -> $o $c dim_separators?; dim_separators @init { CommonTree ret = (CommonTree)adaptor.Nil; } @after { $dim_separators.tree = ret; }: (c=',' { adaptor.AddChild(ret, adaptor.Create(OPEN_BRACKET, $c.token, "["));adaptor.AddChild(ret, adaptor.Create(CLOSE_BRACKET, $c.token, "]")); })+ -> ; delegate_creation_expression: // 'new' t1=type_name '(' t2=type_name ')' -> ^(NEW_DELEGATE[$t1.start.Token, "new(delegate)"] ^(TYPE[$t1.start.Token, "TYPE"] $t1) ^(ARGS[$t2.start.Token, "ARGS"] $t2)); anonymous_object_creation_expression: // 'new' i=anonymous_object_initializer -> ^(NEW_ANON_OBJECT[$i.tree.Token, "new(anonobj)"] anonymous_object_initializer); anonymous_object_initializer: '{' (member_declarator_list ','?)? '}'; member_declarator_list: member_declarator (',' member_declarator)* ; member_declarator: qid ('=' expression)? ; primary_or_array_creation_expression: (array_creation_expression) => array_creation_expression | primary_expression ; // new Type[2] { } array_creation_expression @init { bool removeDimensions = false; CommonTree ret = null; } @after { if (ret != null) $array_creation_expression.tree = (CommonTree)adaptor.RulePostProcessing(ret); if (removeDimensions) { if ($array_creation_expression.tree != null && adaptor.GetChildCount($array_creation_expression.tree) > 3 && adaptor.GetType(adaptor.GetChild($array_creation_expression.tree, 1)) == OPEN_BRACKET) { // Delete until CLOSE_BRACKET while (adaptor.GetType(adaptor.GetChild($array_creation_expression.tree, 2)) != CLOSE_BRACKET) { adaptor.DeleteChild($array_creation_expression.tree, 2); } // push open / close bracket into type. CommonTree type = (CommonTree)adaptor.GetChild($array_creation_expression.tree, 0); adaptor.AddChild(type, adaptor.DupTree(adaptor.GetChild($array_creation_expression.tree, 1))); adaptor.AddChild(type, adaptor.DupTree(adaptor.GetChild($array_creation_expression.tree, 2))); adaptor.DeleteChild($array_creation_expression.tree, 2); adaptor.DeleteChild($array_creation_expression.tree, 1); } } }: n=NEW_ARRAY (type ((o='[' expression_list c=']' -> ^($n type $o expression_list $c)) { ret = (CommonTree)adaptor.RulePostProcessing($array_creation_expression.tree); } ( (rank_specifiers { adaptor.AddChild(ret, $rank_specifiers.tree); })? (ai1=array_initializer { adaptor.AddChild(ret, $ai1.tree); removeDimensions = true; /* If an initializer is provided then drop the dimensions */} )? // new int[4] // | invocation_part* | ( ((arguments[null] ('['|'.'|'->')) => as1=arguments[ ret ] ip=invocation_part[ $as1.tree ] { ret = $ip.tree; })// new object[2].GetEnumerator() | ip2=invocation_part[ret] { ret = $ip2.tree; })* as2=arguments[ ret ] { ret = $as2.tree; } ) // new int[4]() | array_initializer -> ^($n type array_initializer) ) | rank_specifier // [,] (array_initializer // var a = new[] { 1, 10, 100, 1000 }; // int[] ) -> ^($n rank_specifier array_initializer) ) ; array_initializer: '{' variable_initializer_list? ','? '}' ; variable_initializer_list: variable_initializer (',' variable_initializer)* ; variable_initializer: expression | array_initializer ; sizeof_expression: 'sizeof'^ '('! unmanaged_type ')'!; checked_expression: 'checked'^ '('! expression ')'! ; unchecked_expression: 'unchecked'^ '('! expression ')'! ; default_value_expression: 'default'^ '('! type ')'! ; anonymous_method_expression: 'delegate'^ explicit_anonymous_function_signature? block; explicit_anonymous_function_signature: '(' explicit_anonymous_function_parameter_list? ')' ; explicit_anonymous_function_parameter_list: explicit_anonymous_function_parameter (',' explicit_anonymous_function_parameter)* ; explicit_anonymous_function_parameter: anonymous_function_parameter_modifier? type identifier; anonymous_function_parameter_modifier: 'ref' | 'out'; /////////////////////////////////////////////////////// object_creation_expression: // 'new' type ( '(' argument_list? ')' o1=object_or_collection_initializer? -> ^(NEW[$type.start.Token, "new"] type argument_list? $o1?) | o2=object_or_collection_initializer -> ^(NEW[$type.start.Token, "new"] type $o2)) ; object_or_collection_initializer: '{' (object_initializer | collection_initializer) ; collection_initializer: element_initializer_list ','? '}' ; element_initializer_list: element_initializer (',' element_initializer)* ; element_initializer: non_assignment_expression | '{' expression_list '}' ; // object-initializer eg's // Rectangle r = new Rectangle { // P1 = new Point { X = 0, Y = 1 }, // P2 = new Point { X = 2, Y = 3 } // }; // TODO: comma should only follow a member_initializer_list object_initializer: member_initializer_list? ','? '}' ; member_initializer_list: member_initializer (',' member_initializer)* ; member_initializer: identifier '=' initializer_value ; initializer_value: expression | object_or_collection_initializer ; /////////////////////////////////////////////////////// typeof_expression: 'typeof'^ '('! ((unbound_type_name) => unbound_type_name | type | void_type) ')'! ; // unbound type examples //foo>> //bar::foo<> //foo1::foo2.foo3<,,> unbound_type_name: // qualified_identifier v2 // unbound_type_name_start unbound_type_name_part* ; unbound_type_name_start (((generic_dimension_specifier '.') => generic_dimension_specifier unbound_type_name_part) | unbound_type_name_part)* generic_dimension_specifier ; unbound_type_name_start: identifier ('::' identifier)?; unbound_type_name_part: '.' identifier; generic_dimension_specifier: '<' commas? '>' ; commas: ','+ ; /////////////////////////////////////////////////////// // Type Section /////////////////////////////////////////////////////// type_name returns [string thetext]: namespace_or_type_name { $thetext = $namespace_or_type_name.thetext; }; namespace_or_type_name returns [string thetext]: t1=type_or_generic { $thetext=t1.type+formatTyargs($t1.generic_arguments); } ('::'^ tc=type_or_generic { $thetext+="::"+tc.type+formatTyargs($tc.generic_arguments); })? ('.'^ tn=type_or_generic { $thetext+="."+tn.type+formatTyargs($tn.generic_arguments); } )* ; type_or_generic returns [string type, List generic_arguments] @init { $generic_arguments = new List(); } @after{ $type = $t.text; }: (identifier generic_argument_list) => t=identifier ga=generic_argument_list { $generic_arguments = $ga.tyargs; } | t=identifier ; // keving: as far as I can see this is (.)?identifier ()? at lease for C# 3.0 and less. qid returns [string name, List tyargs]: // qualified_identifier v2 (qs=qid_start -> $qs) (qp=qid_part[$qid.tree] -> $qp)* { $name=$qid_start.name; $tyargs = $qid_start.tyargs; } ; qid_start returns [string name, List tyargs]: predefined_type { $name = $predefined_type.thetext; } | (identifier generic_argument_list) => identifier generic_argument_list { $name = $identifier.text; $tyargs = $generic_argument_list.tyargs; } // | 'this' // | 'base' | i1=identifier { $name = $i1.text; } ('::' inext=identifier { $name+="::" + $inext.text; })? | literal { $name = $literal.text; } ; // 0.ToString() is legal qid_part[CommonTree lhs]: access_identifier[ $lhs ] ; generic_argument_list returns [List tyargs] @after { $tyargs = $ta.tyargs; } : '<' ta=type_arguments '>' ; type_arguments returns [List tyargs] @init { $tyargs = new List(); } : t1=type { $tyargs.Add($t1.thetext); } (',' tn=type { $tyargs.Add($tn.thetext); })* ; type returns [string thetext]: ((predefined_type | type_name) rank_specifiers) => (p1=predefined_type { $thetext = $p1.thetext; } | tn1=type_name { $thetext = $tn1.thetext; }) rs=rank_specifiers { $thetext += $rs.text; } ('*' { $thetext += "*"; })* -> ^(TYPE $p1? $tn1? $rs '*'*) | ((predefined_type | type_name) ('*'+ | '?')) => (p2=predefined_type { $thetext = $p2.thetext; } | tn2=type_name { $thetext = $tn2.thetext; }) (('*' { $thetext += "*"; })+ | o2='?' { $thetext += "?"; }) -> ^(TYPE $p2? $tn2? '*'* $o2?) | (p3=predefined_type { $thetext = $p3.thetext; } | tn3=type_name { $thetext = $tn3.thetext; }) -> ^(TYPE $p3? $tn3?) | v='void' { $thetext = "System.Void"; } ('*' { $thetext += "*"; })+ -> ^(TYPE[$v.token, "TYPE"] $v '*'+) ; non_nullable_type: (p=predefined_type | t=type_name) rs=rank_specifiers? '*'* -> ^(TYPE["TYPE"] $p? $t? $rs? '*'*) | v='void' '*'+ -> ^(TYPE[$v.token,"TYPE"] $v '*'+) ; non_array_type: type; array_type: type; unmanaged_type: type; class_type: type; pointer_type: type; /////////////////////////////////////////////////////// // Statement Section /////////////////////////////////////////////////////// block: ';' | '{' statement_list? '}'; statement_list: statement+ ; /////////////////////////////////////////////////////// // Expression Section /////////////////////////////////////////////////////// expression: (unary_expression assignment_operator) => assignment | non_assignment_expression ; expression_list: expression (',' expression)* ; assignment: unary_expression assignment_operator expression ; unary_expression: //('(' arguments ')' ('[' | '.' | '(')) => primary_or_array_creation_expression (cast_expression) => cast_expression | primary_or_array_creation_expression -> primary_or_array_creation_expression | p='+' unary_expression -> ^(MONOPLUS[$p.token,"+"] unary_expression) | m='-' unary_expression -> ^(MONOMINUS[$m.token, "-"] unary_expression) | n='!' unary_expression -> ^(MONONOT[$n.token, "!"] unary_expression) | t='~' unary_expression -> ^(MONOTWIDDLE[$t.token, "~"] unary_expression) | pre_increment_expression -> pre_increment_expression | pre_decrement_expression -> pre_decrement_expression | pointer_indirection_expression -> pointer_indirection_expression | addressof_expression -> addressof_expression ; cast_expression: // //'(' type ')' unary_expression ; l='(' type ')' unary_expression -> ^(CAST_EXPR[$l.token, "CAST"] type unary_expression); assignment_operator: '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | r='>' '>=' -> RIGHT_SHIFT_ASSIGN[$r.token, ">>="] ; pre_increment_expression: s='++' unary_expression -> ^(PREINC[$s.token, "++"] unary_expression) ; pre_decrement_expression: s='--' unary_expression -> ^(PREDEC[$s.token, "--"] unary_expression); pointer_indirection_expression: s='*' unary_expression -> ^(MONOSTAR[$s.token, "*"] unary_expression); addressof_expression: a='&' unary_expression -> ^(ADDRESSOF[$a.token, "&"] unary_expression); non_assignment_expression: //'non ASSIGNment' (anonymous_function_signature '=>') => lambda_expression | (query_expression) => query_expression | conditional_expression ; /////////////////////////////////////////////////////// // Conditional Expression Section /////////////////////////////////////////////////////// multiplicative_expression: (u1=unary_expression -> $u1) ((op='*'|op='/'|op='%') un=unary_expression -> ^($op $multiplicative_expression $un) )* ; additive_expression: multiplicative_expression (('+'|'-')^ multiplicative_expression)* ; // >> check needed (no whitespace) shift_expression: (a1=additive_expression -> $a1) ((so='<<' a3=additive_expression -> ^($so $shift_expression $a3)) | (r='>' '>' a2=additive_expression -> ^(RIGHT_SHIFT[$r.token, ">>"] $shift_expression $a2)) )* ; relational_expression: (s1=shift_expression -> $s1) ( ((o='<'|o='>'|o='>='|o='<=') s2=shift_expression -> ^($o $relational_expression $s2)) | (i='is' t=non_nullable_type -> ^(INSTANCEOF[$i.Token,"instanceof"] $relational_expression $t) | i1='as' t1=non_nullable_type -> ^(COND_EXPR[$i1.Token, "?:"] ^(INSTANCEOF[$i1.Token,"instanceof"] { dupTree($relational_expression.tree) } { dupTree($t1.tree) } ) ^(CAST_EXPR[$i1.Token, "(cast)"] { dupTree($t1.tree) } { dupTree($relational_expression.tree) }) ^(CAST_EXPR[$i1.Token, "(cast)"] { dupTree($t1.tree) } NULL[$i1.Token, "null"]))) )* ; equality_expression: relational_expression (('=='|'!=')^ relational_expression)* ; and_expression: equality_expression ('&'^ equality_expression)* ; exclusive_or_expression: and_expression ('^'^ and_expression)* ; inclusive_or_expression: exclusive_or_expression ('|'^ exclusive_or_expression)* ; conditional_and_expression: inclusive_or_expression ('&&'^ inclusive_or_expression)* ; conditional_or_expression: conditional_and_expression ('||'^ conditional_and_expression)* ; null_coalescing_expression: (e1=conditional_or_expression -> $e1) (qq='??' e2=conditional_or_expression -> ^(COND_EXPR[$qq.token, "?:"] ^(NOT_EQUAL[$qq.token, "!="] { dupTree($null_coalescing_expression.tree) } NULL[$qq.Token, "null"]) { dupTree($null_coalescing_expression.tree) } { dupTree($e2.tree) }) )* ; conditional_expression: (ne=null_coalescing_expression -> $ne) (q='?' te=expression ':' ee=expression -> ^(COND_EXPR[$q.token, "?:"] $conditional_expression $te $ee))? ; // (null_coalescing_expression '?' expression ':') => e1=null_coalescing_expression q='?' e2=expression ':' e3=expression -> ^(COND_EXPR[$q.token, "?:"] $e1 $e2 $e3) // | null_coalescing_expression ; /////////////////////////////////////////////////////// // lambda Section /////////////////////////////////////////////////////// lambda_expression: anonymous_function_signature '=>' anonymous_function_body; anonymous_function_signature: '(' (explicit_anonymous_function_parameter_list | implicit_anonymous_function_parameter_list)? ')' | implicit_anonymous_function_parameter_list ; implicit_anonymous_function_parameter_list: implicit_anonymous_function_parameter (',' implicit_anonymous_function_parameter)* ; implicit_anonymous_function_parameter: identifier; anonymous_function_body: expression | block ; /////////////////////////////////////////////////////// // LINQ Section /////////////////////////////////////////////////////// query_expression: from_clause query_body ; query_body: // match 'into' to closest query_body query_body_clauses? select_or_group_clause (('into') => query_continuation)? ; query_continuation: 'into' identifier query_body; query_body_clauses: query_body_clause+ ; query_body_clause: from_clause | let_clause | where_clause | join_clause | orderby_clause; from_clause: 'from' type? identifier 'in' expression ; join_clause: 'join' type? identifier 'in' expression 'on' expression 'equals' expression ('into' identifier)? ; let_clause: 'let' identifier '=' expression; orderby_clause: 'orderby' ordering_list ; ordering_list: ordering (',' ordering)* ; ordering: expression ordering_direction ; ordering_direction: 'ascending' | 'descending' ; select_or_group_clause: select_clause | group_clause ; select_clause: 'select' expression ; group_clause: 'group' expression 'by' expression ; where_clause: 'where' boolean_expression ; boolean_expression: expression; /////////////////////////////////////////////////////// // B.2.13 Attributes /////////////////////////////////////////////////////// global_attributes: global_attribute+ ; global_attribute: '[' global_attribute_target_specifier attribute_list ','? ']' ; global_attribute_target_specifier: global_attribute_target ':' ; global_attribute_target: 'assembly' | 'module' ; attributes: attribute_sections ; attribute_sections: attribute_section+ ; attribute_section: '[' attribute_target_specifier? attribute_list ','? ']' ; attribute_target_specifier: attribute_target ':' ; attribute_target: 'field' | 'event' | 'method' | 'param' | 'property' | 'return' | 'type' ; attribute_list: attribute (',' attribute)* ; attribute: type_name attribute_arguments? ; // TODO: allows a mix of named/positional arguments in any order attribute_arguments: '(' (')' // empty | (positional_argument ((',' identifier '=') => named_argument |',' positional_argument)* ) ')' ) ; positional_argument_list: a1=positional_argument (',' an+=positional_argument)* -> ^(ARGS[$a1.start.Token,"ARGS"] $a1 $an*); positional_argument: attribute_argument_expression ; named_argument_list: a1=named_argument (',' an+=named_argument)* -> ^(ARGS[$a1.start.Token,"ARGS"] $a1 $an*); named_argument: identifier '=' attribute_argument_expression ; attribute_argument_expression: expression ; /////////////////////////////////////////////////////// // Class Section /////////////////////////////////////////////////////// class_declaration[CommonTree atts, CommonTree mods] returns [string name] scope TypeContext; : c='class' identifier { $TypeContext::typeName = $identifier.text; } type_parameter_list? { $name = mkGenericTypeAlias($identifier.text, $type_parameter_list.names); } class_base? type_parameter_constraints_clauses? class_body ';'? -> ^(CLASS[$c.Token] { dupTree($atts) } { dupTree($mods) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base? class_body ); type_parameter_list returns [List names] @init { List names = new List(); }: '<'! attributes? t1=type_parameter { names.Add($t1.name); } ( ','! attributes? tn=type_parameter { names.Add($tn.name); })* '>'! ; type_parameter returns [string name]: identifier { $name = $identifier.text; } ; class_base: // just put all types in a single list. In NetMaker we will extract the base class if necessary ':' ts+=type (',' ts+=type)* -> ^(IMPLEMENTS $ts)*; //interface_type_list: // ts+=type (',' ts+=type)* -> $ts+; class_body: '{' class_member_declarations? '}' ; class_member_declarations: class_member_declaration+ ; /////////////////////////////////////////////////////// constant_declaration: 'const' type constant_declarators ';' ; constant_declarators: constant_declarator (',' constant_declarator)* ; constant_declarator: identifier ('=' constant_expression)? ; constant_expression: expression; /////////////////////////////////////////////////////// field_declaration: variable_declarators ';'! ; variable_declarators: variable_declarator (',' variable_declarator)* ; variable_declarator: type_name ('=' variable_initializer)? ; // eg. event EventHandler IInterface.VariableName = Foo; /////////////////////////////////////////////////////// method_declaration [CommonTree atts, CommonTree mods, List modList, CommonTree type, string typeText] @init { bool isToString = false; CommonTree exceptions = null; CommonTree optMain = null; bool isVoid = $typeText == "void"; bool isInt = $typeText == "int" || $typeText == "System.Int32" || $typeText == "Int32"; bool isMain = isVoid || isInt; bool isMainHasArg = false; }: // TODO: According to the spec the C# Main() method should be static and not public. We aren't checking for lack of public // we can check the modifiers in modList if we want to enforce that. member_name { isToString = $member_name.text == "ToString"; isMain &= $member_name.text == "Main"; } (type_parameter_list { isToString = false; isMain = false; })? '(' // We are looking for ToString(), and Main(string[] args), where arg is optional. (formal_parameter_list { isToString = false; if (isMain) { isMain = false; // since we have an argument, must check its an array of String if ($formal_parameter_list.tree != null && $formal_parameter_list.tree.Children != null && $formal_parameter_list.tree.Children.Count == 2) { // parameter list children size is 2 (type arg) CommonTree argTy = (CommonTree)$formal_parameter_list.tree.Children[0]; if (argTy != null && argTy.Children != null && argTy.Children.Count == 3) { // Looking for Children of "String" "[" "]" if (argTy.Children[0].Text.ToLower() == "string" && argTy.Children[1].Text == "[" && argTy.Children[2].Text == "]") { // Bingo! isMain = true; isMainHasArg = true; } } } } } )? ')' ( type_parameter_constraints_clauses { isToString = false; isMain = false; })? b=method_body[isToString] // build main method if required argParam=magicMainArgs[isMain && isMainHasArg, $member_name.tree.Token] mainApply=magicMainApply[isMain, $member_name.tree.Token, $TypeContext::typeName, $argParam.tree] mainCall=magicMainExit[isMain, isInt, $member_name.tree.Token, $mainApply.tree] mainMethod=magicMainWrapper[isMain, $member_name.tree.Token, $mainCall.tree] { if (isToString) { $member_name.tree.Token.Text = "toString"; } exceptions = $b.exceptionList; } -> $mainMethod? ^(METHOD { dupTree($atts) } { dupTree($mods) } { dupTree($type) } member_name type_parameter_constraints_clauses? type_parameter_list? formal_parameter_list? $b { exceptions }); method_body [bool smotherExceptions] returns [CommonTree exceptionList]: {smotherExceptions}? b=block nb=magicSmotherExceptions[dupTree($b.tree) ] -> $nb | b=block el=magicThrowsException[true,$b.tree.Token] { $exceptionList=$el.tree; } -> $b ; member_name returns [string rawId]: (type_or_generic '.')* i=identifier { $rawId = $i.text; } // keving [interface_type.identifier] | type_name '.' identifier ; member_name_orig returns [string name, List tyargs]: qid { $name = $qid.name; $tyargs = $qid.tyargs; } ; // IInterface.Method logic added. /////////////////////////////////////////////////////// property_declaration [CommonTree atts, CommonTree mods, CommonTree type] scope { bool emptyGetterSetter; } @init { $property_declaration::emptyGetterSetter = false; CommonTree privateVar = null; } : i=member_name '{' ads=accessor_declarations[atts, mods, type, $i.text, $i.rawId] '}' v=magicMkPropertyVar[type, "__" + $i.tree.Text] { privateVar = $property_declaration::emptyGetterSetter ? $v.tree : null; }-> { privateVar } $ads ; accessor_declarations [CommonTree atts, CommonTree mods, CommonTree type, string propName, string rawVarName]: accessor_declaration[atts, mods, type, propName, rawVarName]+; accessor_declaration [CommonTree atts, CommonTree mods, CommonTree type, string propName, string rawVarName] @init { CommonTree propBlock = null; bool mkBody = false; }: la=attributes? lm=accessor_modifier? (g='get' ((';')=> gbe=';' { $property_declaration::emptyGetterSetter = true; propBlock = $gbe.tree; mkBody = true; rawVarName = "__" + rawVarName; } | gb=block { propBlock = $gb.tree; } ) getm=magicPropGetter[atts, $la.tree, mods, $lm.tree, type, $g.token, propBlock, propName, mkBody, rawVarName] -> $getm | s='set' ((';')=> sbe=';' { $property_declaration::emptyGetterSetter = true; propBlock = $sbe.tree; mkBody = true; rawVarName = "__" + rawVarName; } | sb=block { propBlock = $sb.tree; } ) setm=magicPropSetter[atts, $la.tree, mods, $lm.tree, type, $s.token, propBlock, propName, mkBody, rawVarName] -> $setm) ; accessor_modifier: 'protected' 'internal'? | 'private' | 'internal' 'protected'?; /////////////////////////////////////////////////////// event_declaration: 'event' type ((member_name '{') => member_name '{' event_accessor_declarations '}' | variable_declarators ';') // typename=foo; ; event_modifiers: modifier+ ; event_accessor_declarations: attributes? ((add_accessor_declaration attributes? remove_accessor_declaration) | (remove_accessor_declaration attributes? add_accessor_declaration)) ; add_accessor_declaration: 'add' block ; remove_accessor_declaration: 'remove' block ; /////////////////////////////////////////////////////// // enum declaration /////////////////////////////////////////////////////// enum_declaration[CommonTree atts, CommonTree mods] returns [string name] scope TypeContext; : e='enum' identifier { $name = $identifier.text; $TypeContext::typeName = $identifier.text; } enum_base? enum_body ';'? -> ^(ENUM[$e.token, "ENUM"] { dupTree($atts) } { dupTree($mods) } identifier enum_base? enum_body); enum_base: ':' integral_type ; enum_body: '{' (enum_member_declarations ','?)? '}' -> ^(ENUM_BODY enum_member_declarations) ; enum_member_declarations @init { SortedList members = new SortedList(); int next = 0; } @after{ $enum_member_declarations.tree = (CommonTree)adaptor.Nil; if (next > 0 && next < MAX_DUMMY_ENUMS) { int dummyCounter = 0; for (int i = 0; i < next; i++) { if (members.ContainsKey(i)) { adaptor.AddChild($enum_member_declarations.tree, members[i]); } else { adaptor.AddChild($enum_member_declarations.tree, adaptor.Create(IDENTIFIER, $e.start.Token, "__dummyEnum__" + dummyCounter++)); } } } else { Warning($e.tree.Line, "[UNSUPPORTED] We do not yet generate dummy enum members for enums that need more than " + MAX_DUMMY_ENUMS + " entries."); foreach (CommonTree en in members.Values) { adaptor.AddChild($enum_member_declarations.tree, en); } } } : e=enum_member_declaration[members,ref next] (',' enum_member_declaration[members, ref next])* -> ; enum_member_declaration[ SortedList members, ref int next] @init { int calcValue = 0; }: // Fill in members, a map from enum's value to AST attributes? identifier { $members[$next] = $identifier.tree; $next++; } ((eq='=' ( ((NUMBER | Hex_number) (','|'}')) => { $members.Remove($next-1); } (i=NUMBER { calcValue = Int32.Parse($i.text); } | i=Hex_number { calcValue = Int32.Parse($i.text.Substring(2), NumberStyles.AllowHexSpecifier); } ) { if (calcValue < 0 || calcValue > Int32.MaxValue) { Warning($eq.line, "[UNSUPPORTED] enum member's value initialization ignored, only numeric literals in the range 0..MAXINT supported for enum values"); calcValue = $next-1; } else if (calcValue < $next-1) { Warning($eq.line, "[UNSUPPORTED] enum member's value initialization ignored, value has already been assigned and enum values must be unique"); calcValue = $next-1; } $members[calcValue] = $identifier.tree; $next = calcValue + 1; } | expression { Warning($eq.line, "[UNSUPPORTED] enum member's value initialization ignored, only numeric literals supported for enum values"); } ))?)! ; //enum_modifiers: // enum_modifier+ ; //enum_modifier: // 'new' | 'public' | 'protected' | 'internal' | 'private' ; integral_type: 'sbyte' | 'byte' | 'short' | 'ushort' | 'int' | 'uint' | 'long' | 'ulong' | 'char' ; // B.2.12 Delegates delegate_declaration[CommonTree atts, CommonTree mods] returns [string name] scope TypeContext; : d='delegate' return_type identifier { $name = $identifier.text; $TypeContext::typeName = $identifier.text; } variant_generic_parameter_list? '(' formal_parameter_list? ')' type_parameter_constraints_clauses? ';' -> ^(DELEGATE[$d.token, "DELEGATE"] { dupTree($atts) } { dupTree($mods) } return_type identifier type_parameter_constraints_clauses? variant_generic_parameter_list? '(' formal_parameter_list? ')' ); delegate_modifiers: modifier+ ; // 4.0 variant_generic_parameter_list returns [List tyargs] @init { $tyargs = new List(); }: '<'! variant_type_parameters[$tyargs] '>'! ; variant_type_parameters [List tyargs]: v1=variant_type_variable_name { tyargs.Add($v1.text); } (',' vn=variant_type_variable_name { tyargs.Add($vn.text); })* -> variant_type_variable_name+ ; variant_type_variable_name: attributes? variance_annotation? type_variable_name ; variance_annotation: 'in' -> IN | 'out' -> OUT; type_parameter_constraints_clauses: type_parameter_constraints_clause (',' type_parameter_constraints_clause)* -> type_parameter_constraints_clause+ ; type_parameter_constraints_clause: 'where' type_variable_name ':' type_parameter_constraint_list -> ^(TYPE_PARAM_CONSTRAINT type_variable_name type_parameter_constraint_list?) ; // class, Circle, new() type_parameter_constraint_list: ('class' | 'struct') (',' secondary_constraint_list)? (',' constructor_constraint)? -> secondary_constraint_list? | secondary_constraint_list (',' constructor_constraint)? -> secondary_constraint_list | constructor_constraint -> ; //primary_constraint: // class_type // | 'class' // | 'struct' ; secondary_constraint_list: secondary_constraint (',' secondary_constraint)* -> secondary_constraint+ ; secondary_constraint: type_name ; // | type_variable_name) ; type_variable_name: identifier ; // keving: TOTEST we drop new constraints, but what will happen in Java for this case? constructor_constraint: 'new' '(' ')' ; return_type: type | void_type ; formal_parameter_list: formal_parameter (',' formal_parameter)* -> ^(PARAMS formal_parameter+); formal_parameter: attributes? (fixed_parameter | parameter_array) | '__arglist'; // __arglist is undocumented, see google fixed_parameters: fixed_parameter (',' fixed_parameter)* ; // 4.0 fixed_parameter: parameter_modifier? type identifier default_argument? ; // 4.0 default_argument: '=' expression; parameter_modifier: 'ref' | 'out' | 'this' ; parameter_array: p='params'^ t=type identifier { // type will be an array, need to strip the final [] for java int numComponents = adaptor.GetChildCount($t.tree); // sanity check if (numComponents >= 3 && adaptor.GetType(adaptor.GetChild($t.tree, numComponents-2)) == OPEN_BRACKET && adaptor.GetType(adaptor.GetChild($t.tree, numComponents-1)) == CLOSE_BRACKET) { adaptor.DeleteChild($t.tree, numComponents-1); adaptor.DeleteChild($t.tree, numComponents-2); } else { Error($p.line, "[SOURCE ERROR] params type must be an array"); } } ; /////////////////////////////////////////////////////// interface_declaration[CommonTree atts, CommonTree mods] returns [string name] scope TypeContext; : c='interface' identifier { $name = $identifier.text; $TypeContext::typeName = $identifier.text; } variant_generic_parameter_list? interface_base? type_parameter_constraints_clauses? interface_body ';'? -> ^(INTERFACE[$c.Token] { dupTree($atts) } { dupTree($mods) } identifier type_parameter_constraints_clauses? variant_generic_parameter_list? interface_base? interface_body ); interface_base: c=':' ts+=type (',' ts+=type)* -> ^(EXTENDS[$c.token,"extends"] $ts)*; interface_modifiers: modifier+ ; interface_body: '{' interface_member_declarations? '}' ; interface_member_declarations: interface_member_declaration+ ; interface_member_declaration: a=attributes? m=modifiers? (vt=void_type im1=interface_method_declaration[$a.tree, $m.tree, $vt.tree] -> $im1 | ie=interface_event_declaration[$a.tree, $m.tree] -> $ie | t=type ( (identifier type_parameter_list? '(') => im2=interface_method_declaration[$a.tree, $m.tree, $t.tree] -> $im2 // property will rewrite to one, or two method headers | (member_name '{') => ip=interface_property_declaration[$a.tree, $m.tree, $t.tree] -> $ip //^(PROPERTY[$t.start.Token, "PROPERTY"] $a? $m? $t interface_property_declaration) | ii=interface_indexer_declaration[$a.tree, $m.tree, $t.tree] -> $ii) ) ; interface_property_declaration [CommonTree atts, CommonTree mods, CommonTree type]: i=identifier '{' iads=interface_accessor_declarations[atts, mods, type, $i.text] '}' -> $iads ; interface_method_declaration [CommonTree atts, CommonTree mods, CommonTree type]: identifier type_parameter_list? '(' formal_parameter_list? ')' type_parameter_constraints_clauses? s=';' magicThrowsException[true,$s.token] -> ^(METHOD { dupTree($atts) } { dupTree($mods) } { dupTree($type) } identifier type_parameter_constraints_clauses? type_parameter_list? formal_parameter_list? magicThrowsException); interface_event_declaration [CommonTree atts, CommonTree mods]: //attributes? 'new'? e='event' type identifier ';' -> ^(EVENT[$e.token, "EVENT"] { dupTree($atts) } { dupTree($mods) } type identifier) ; interface_indexer_declaration [CommonTree atts, CommonTree mods, CommonTree type]: // attributes? 'new'? type 'this' '[' formal_parameter_list ']' '{' indexer_accessor_declarations[atts,mods,type,$formal_parameter_list.tree] '}' ; interface_accessor_declarations [CommonTree atts, CommonTree mods, CommonTree type, string propName]: interface_accessor_declaration[atts, mods, type, propName]+ ; interface_accessor_declaration [CommonTree atts, CommonTree mods, CommonTree type, string propName]: la=attributes? (g='get' semi=';' magicPropGetter[atts, $la.tree, mods, null, type, $g.token, $semi.tree, propName, false, ""] -> magicPropGetter | s='set' semi=';' magicPropSetter[atts, $la.tree, mods, null, type, $s.token, $semi.tree, propName, false, ""] -> magicPropSetter) ; /////////////////////////////////////////////////////// struct_declaration[CommonTree atts, CommonTree mods] returns [string name] scope TypeContext; : c='struct' identifier { $TypeContext::typeName = $identifier.text; } type_parameter_list? { $name = mkGenericTypeAlias($identifier.text, $type_parameter_list.names); } class_base? type_parameter_constraints_clauses? struct_body[$identifier.text] ';'? -> ^(CLASS[$c.Token, "class"] { dupTree($atts) } { dupTree($mods) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base? struct_body ); struct_body [string structName]: o='{' magicDefaultConstructor[$o.token, structName] class_member_declarations? '}' ; /////////////////////////////////////////////////////// indexer_declaration [CommonTree atts, CommonTree mods, CommonTree type]: 'this' '[' formal_parameter_list ']' '{' indexer_accessor_declarations[atts, mods, type, $formal_parameter_list.tree] '}' -> indexer_accessor_declarations ; //indexer_declarator: //(type_name '.')? // 'this' '[' formal_parameter_list ']' ; indexer_accessor_declarations [CommonTree atts, CommonTree mods, CommonTree type, CommonTree idxparams]: indexer_accessor_declaration[atts, mods, type, idxparams]+; indexer_accessor_declaration [CommonTree atts, CommonTree mods, CommonTree type, CommonTree idxparams] @init { CommonTree idxBlock = null; }: la=attributes? lm=accessor_modifier? (g='get' ((';')=> gbe=';' { idxBlock = $gbe.tree; } | gb=block { idxBlock = $gb.tree; } ) geti=magicIdxGetter[atts, $la.tree, mods, $lm.tree, type, $g.token, idxBlock, idxparams] -> $geti | s='set' ((';')=> sbe=';' { idxBlock = $sbe.tree; } | sb=block { idxBlock = $sb.tree; } ) seti=magicIdxSetter[atts, $la.tree, mods, $lm.tree, type, $s.token, idxBlock, idxparams] -> $seti) ; /////////////////////////////////////////////////////// operator_declaration: operator_declarator operator_body ; operator_declarator: 'operator' (('+' | '-') '(' type identifier (binary_operator_declarator | unary_operator_declarator) | overloadable_unary_operator '(' type identifier unary_operator_declarator | overloadable_binary_operator '(' type identifier binary_operator_declarator) ; unary_operator_declarator: ')' ; overloadable_unary_operator: /*'+' | '-' | */ '!' | '~' | '++' | '--' | 'true' | 'false' ; binary_operator_declarator: ',' type identifier ')' ; // >> check needed overloadable_binary_operator: /*'+' | '-' | */ '*' | '/' | '%' | '&' | '|' | '^' | '<<' | '>' '>' | '==' | '!=' | '>' | '<' | '>=' | '<=' ; conversion_operator_declaration: conversion_operator_declarator operator_body ; conversion_operator_declarator: ('implicit' | 'explicit') 'operator' type '(' type identifier ')' ; operator_body: block ; /////////////////////////////////////////////////////// constructor_declaration[CommonTree atts, CommonTree mods, List modList]: i=identifier '(' p=formal_parameter_list? s=')' init=constructor_initializer? b=constructor_body[$init.tree] magicThrowsException[true,$s.token] -> ^(CONSTRUCTOR[$i.tree.Token, "CONSTRUCTOR"] { dupTree($atts) } { dupTree($mods) } $i $p? $b magicThrowsException); constructor_initializer: ':' tok='this' '(' argument_list? ')' -> ^(APPLY[$tok.token, "APPLY"] $tok argument_list?) SEMI[$tok.token, ";"] | ':' tok='base' '(' argument_list? ')' -> ^(APPLY[$tok.token, "APPLY"] SUPER[$tok.token, "super"] argument_list?) SEMI[$tok.token, ";"] ; constructor_body[CommonTree init]: {init == null}?=> s=';' -> $s | s1=';' -> OPEN_BRACE[$s1.token, "{"] { dupTree(init) } CLOSE_BRACE[$s1.token, "}"] | a='{' ss+=statement* b='}' -> $a { dupTree(init) } $ss* $b ; /////////////////////////////////////////////////////// //static_constructor_declaration: // identifier '(' ')' static_constructor_body ; //static_constructor_body: // block ; /////////////////////////////////////////////////////// destructor_declaration: t='~' identifier '(' ')' destructor_body f=magicFinalize[$t.token, $destructor_body.tree] -> $f; destructor_body: block ; /////////////////////////////////////////////////////// // invocation_expression: // invocation_start (((arguments[null] ('['|'.'|'->')) => arguments[ (CommonTree)adaptor.Create(KGHOLE, "KGHOLE") ] invocation_part) // | invocation_part)* arguments[ (CommonTree)adaptor.Create(KGHOLE, "KGHOLE") ] ; // invocation_start: // predefined_type // | (identifier generic_argument_list) => identifier generic_argument_list // | 'this' // | b='base' -> SUPER[$b.token, "super"] // | identifier ('::' identifier)? // | typeof_expression // typeof(Foo).Name // ; invocation_part [CommonTree start]: access_identifier[ $start ] | brackets[ $start ] ; /////////////////////////////////////////////////////// statement: (declaration_statement) => declaration_statement | (identifier ':') => labeled_statement | embedded_statement ; embedded_statement: block | selection_statement // if, switch | iteration_statement // while, do, for, foreach | jump_statement // break, continue, goto, return, throw | try_statement | checked_statement | unchecked_statement | lock_statement | using_statement | yield_statement | unsafe_statement | fixed_statement | expression_statement // expression! ; fixed_statement: 'fixed' '(' pointer_type fixed_pointer_declarators ')' embedded_statement ; fixed_pointer_declarators: fixed_pointer_declarator (',' fixed_pointer_declarator)* ; fixed_pointer_declarator: identifier '=' fixed_pointer_initializer ; fixed_pointer_initializer: //'&' variable_reference // unary_expression covers this expression; unsafe_statement: 'unsafe'^ block; labeled_statement: identifier ':'^ statement ; declaration_statement: (local_variable_declaration | local_constant_declaration) ';' ; local_variable_declaration returns [List variableNames]: local_variable_type local_variable_declarators { $variableNames = $local_variable_declarators.variableNames; }; local_variable_type: ('var') => v='var' -> TYPE_VAR[$v.token, "var"] | ('dynamic') => d='dynamic' -> TYPE_DYNAMIC[$d.token,"dynamic"] | type ; local_variable_declarators returns [List variableNames] @init { $variableNames = new List(); }: i1=local_variable_declarator { $variableNames.Add($i1.variableName); } (',' ip=local_variable_declarator { $variableNames.Add($ip.variableName); })* ; local_variable_declarator returns [string variableName]: identifier { $variableName = $identifier.text; } ('=' local_variable_initializer)? ; local_variable_initializer: expression | array_initializer | stackalloc_initializer; stackalloc_initializer: 'stackalloc' unmanaged_type '[' expression ']' ; local_constant_declaration: 'const' type constant_declarators ; expression_statement: expression ';' ; // TODO: should be assignment, call, increment, decrement, and new object expressions statement_expression: expression ; selection_statement: if_statement | switch_statement ; if_statement: // else goes with closest if i='if' '(' boolean_expression ')' embedded_statement (('else') => else_statement)? -> ^(IF[$i.Token] boolean_expression SEP embedded_statement else_statement?) // 'if' '(' boolean_expression ')' embedded_statement (('else') => else_statement)? ; else_statement: 'else' embedded_statement ; switch_statement: s='switch' '(' expression ')' switch_block -> ^($s expression switch_block); switch_block: '{'! switch_section* '}'! ; //switch_sections: // switch_section+ ; switch_section: switch_label+ statement_list -> ^(SWITCH_SECTION switch_label+ statement_list); //switch_labels: // switch_label+ ; switch_label: ('case'^ constant_expression ':'!) | ('default' ':'!); iteration_statement: while_statement | do_statement | for_statement | foreach_statement ; while_statement: w='while' '(' boolean_expression ')' embedded_statement -> ^($w boolean_expression SEP embedded_statement); do_statement: 'do' embedded_statement 'while' '(' boolean_expression ')' ';' ; for_statement: f='for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')' embedded_statement -> ^($f for_initializer? SEP for_condition? SEP for_iterator? SEP embedded_statement); for_initializer: (local_variable_declaration) => local_variable_declaration | statement_expression_list ; for_condition: boolean_expression ; for_iterator: statement_expression_list ; statement_expression_list: statement_expression (',' statement_expression)* ; foreach_statement: f='foreach' '(' local_variable_type identifier 'in' expression ')' embedded_statement -> ^($f local_variable_type identifier expression SEP embedded_statement); jump_statement: break_statement | continue_statement | goto_statement | return_statement | throw_statement ; break_statement: 'break' ';' ; continue_statement: 'continue' ';' ; goto_statement: 'goto' ( identifier | 'case' constant_expression | 'default') ';' ; return_statement: 'return'^ expression? ';'! ; throw_statement // If throw exp is missing then it is the var from closest enclosing catch @init { CommonTree var = null; bool missingThrowExp = true; }: t='throw' (e=expression { missingThrowExp = false;})? { var = missingThrowExp ? dupTree($catch_clause::throwVar) : $e.tree; } ';' -> ^($t { var }); try_statement: t='try' block ( catch_clauses finally_clause? | finally_clause) -> ^($t block catch_clauses? finally_clause?); // We rewrite the catch clauses so that they all have the form "(catch Type Var)" by introducing // Throwable and dummy vars as necessary catch_clauses: catch_clause+; catch_clause scope { CommonTree throwVar; } @init { CommonTree ty = null, var = null; }: c='catch' ('(' given_t=class_type { ty = $given_t.tree; } (given_v=identifier { var = $given_v.tree; } | magic_v=magicCatchVar { var = $magic_v.tree; } ) ')' | magic_t=magicThrowableType[true,$c.token] magic_v=magicCatchVar { ty = $magic_t.tree; var = $magic_v.tree; }) { $catch_clause::throwVar = var; } block -> ^($c { ty } { var } block) ; finally_clause: 'finally'^ block ; checked_statement: 'checked' block ; unchecked_statement: 'unchecked' block ; lock_statement: 'lock' '(' expression ')' embedded_statement ; // TODO: Can we avoid surrounding this with braces if not needed? using_statement @init { CommonTree disposers = null; }: // see http://msdn.microsoft.com/en-us/library/yh598w02.aspx for translation u='using' '(' resource_acquisition c=')' embedded_statement { disposers = addDisposeVars($c.token, $resource_acquisition.resourceNames); } f=magicFinally[$c.token, disposers] magicTry[$u.token, state.backtracking == 0 ? embeddedStatementToBlock($u.token, $embedded_statement.tree) : null, null, $f.tree] -> OPEN_BRACE[$u.token, "{"] resource_acquisition SEMI[$c.token, ";"] magicTry CLOSE_BRACE[$u.token, "}"] ; resource_acquisition returns [List resourceNames] @init { $resourceNames = new List(); }: (local_variable_declaration) => local_variable_declaration { $resourceNames = $local_variable_declaration.variableNames; } | expression { $resourceNames.Add("__newVar"+newVarCtr); } -> ^(TYPE[$expression.tree.Token, "TYPE"] IDENTIFIER[$expression.tree.Token, "IDisposable"]) IDENTIFIER[$expression.tree.Token, "__newVar"+newVarCtr++] ASSIGN[$expression.tree.Token, "="] expression //SEMI[$expression.tree.Token, ";"] ; yield_statement: 'yield' ('return' expression ';' | 'break' ';') ; /////////////////////////////////////////////////////// // Lexar Section /////////////////////////////////////////////////////// predefined_type returns [string thetext] @after{ string newText; if (predefined_type_map.TryGetValue($predefined_type.tree.Token.Text, out newText)) { $predefined_type.tree.Token.Text = newText; } }: 'bool' { $thetext = "System.Boolean"; } | 'byte' { $thetext = "System.Byte"; } | 'char' { $thetext = "System.Char"; } | 'decimal' { $thetext = "System.Decimal"; } | 'double' { $thetext = "System.Double"; } | 'float' { $thetext = "System.Single"; } | 'int' { $thetext = "System.Int32"; } | 'long' { $thetext = "System.Int64"; } | 'object' { $thetext = "System.Object"; } | 'sbyte' { $thetext = "System.SByte"; } | 'short' { $thetext = "System.Int16"; } | 'string' { $thetext = "System.String"; } | 'uint' { $thetext = "System.UInt32"; } | 'ulong' { $thetext = "System.UInt64"; } | 'ushort' { $thetext = "System.UInt16"; } ; identifier @after { string fixedId = fixBrokenId($identifier.tree.Token.Text); $identifier.tree.Token.Text = fixedId; }: IDENTIFIER | also_keyword; keyword: 'abstract' | 'as' | 'base' | 'bool' | 'break' | 'byte' | 'case' | 'catch' | 'char' | 'checked' | 'class' | 'const' | 'continue' | 'decimal' | 'default' | 'delegate' | 'do' | 'double' | 'else' | 'enum' | 'event' | 'explicit' | 'extern' | 'false' | 'finally' | 'fixed' | 'float' | 'for' | 'foreach' | 'goto' | 'if' | 'implicit' | 'in' | 'int' | 'interface' | 'internal' | 'is' | 'lock' | 'long' | 'namespace' | 'new' | 'null' | 'object' | 'operator' | 'out' | 'override' | 'params' | 'private' | 'protected' | 'public' | 'readonly' | 'ref' | 'return' | 'sbyte' | 'sealed' | 'short' | 'sizeof' | 'stackalloc' | 'static' | 'string' | 'struct' | 'switch' | 'this' | 'throw' | 'true' | 'try' | 'typeof' | 'uint' | 'ulong' | 'unchecked' | 'unsafe' | 'ushort' | 'using' | 'virtual' | 'void' | 'volatile' ; also_keyword: 'add' | 'alias' | 'assembly' | 'module' | 'field' | 'method' | 'param' | 'property' | 'type' | 'yield' | 'from' | 'into' | 'join' | 'on' | 'where' | 'orderby' | 'group' | 'by' | 'ascending' | 'descending' | 'equals' | 'select' | 'pragma' | 'let' | 'remove' | 'get' | 'set' | 'var' | '__arglist' | 'dynamic' | 'elif' | 'endif' | 'define' | 'undef'; literal: Real_literal | n=NUMBER -> {UInt64.Parse($n.text) > Int32.MaxValue}? LONGNUMBER[$n.token, $n.text] -> $n | Hex_number | Character_literal | STRINGLITERAL | Verbatim_string_literal | TRUE | FALSE | NULL ; void_type: v='void' -> ^(TYPE[$v.token, "TYPE"] $v); magicThrowableType[bool isOn, IToken tok]: -> {isOn}? ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, Cfg.TranslatorExceptionIsThrowable ? "Throwable" : "Exception"]) -> ; magicCatchVar: -> IDENTIFIER["__dummyCatchVar" + dummyCatchVarCtr++]; magicPropGetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken getTok, CommonTree body, string propName, bool mkBody, string varName] @init { CommonTree realBody = body; CommonTree exceptionList = null; }: b=magicGetterBody[mkBody,getTok,varName] { if (mkBody) realBody = $b.tree; } e=magicThrowsException[!mkBody,getTok] { if (!mkBody) exceptionList = $e.tree; } -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } { dupTree(type)} IDENTIFIER[getTok, "get"+propName] { dupTree(realBody) } { exceptionList }) ; magicPropSetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken setTok, CommonTree body, string propName, bool mkBody, string varName] @init { CommonTree realBody = body; CommonTree exceptionList = null; }: b=magicSetterBody[mkBody,setTok,varName] { if (mkBody) realBody = $b.tree; } e=magicThrowsException[!mkBody,setTok] { if (!mkBody) exceptionList = $e.tree; } -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } ^(TYPE[setTok, "TYPE"] IDENTIFIER[setTok, "void"] ) IDENTIFIER[setTok, "set"+propName] ^(PARAMS[setTok, "PARAMS"] { dupTree(type)} IDENTIFIER[setTok, "value"]) { dupTree(realBody) } { exceptionList } ) ; magicSemi: -> SEMI; magicMkPropertyVar[CommonTree type, string varText] : -> ^(FIELD[$type.token, "FIELD"] PRIVATE[$type.token, "private"] { dupTree(type) } IDENTIFIER[$type.token, varText]) ; magicGetterBody[bool isOn, IToken getTok, string varName]: -> { isOn }? OPEN_BRACE[getTok,"{"] ^(RETURN[getTok, "return"] IDENTIFIER[getTok, varName]) CLOSE_BRACE[getTok,"}"] -> ; magicSetterBody[bool isOn, IToken setTok, string varName]: -> { isOn }? OPEN_BRACE[setTok,"{"] IDENTIFIER[setTok, varName] ASSIGN[setTok,"="] IDENTIFIER[setTok, "value"] SEMI[setTok, ";"] CLOSE_BRACE[setTok,"}"] -> ; magicIdxGetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken getTok, CommonTree body, CommonTree idxparams] : magicThrowsException[true,getTok] -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } { dupTree(type)} IDENTIFIER[getTok, "get___idx"] { dupTree(idxparams) } { dupTree(body) } magicThrowsException) ; magicIdxSetter[CommonTree atts, CommonTree localatts, CommonTree mods, CommonTree localmods, CommonTree type, IToken setTok, CommonTree body, CommonTree idxparams] @init { CommonTree augParams = dupTree(idxparams); adaptor.AddChild(augParams, dupTree($type)); adaptor.AddChild(augParams, (CommonTree)adaptor.Create(IDENTIFIER, setTok, "value")); } : magicThrowsException[true,setTok] -> ^(METHOD[$type.token, "METHOD"] { dupTree(mods) } ^(TYPE[setTok, "TYPE"] IDENTIFIER[setTok, "void"] ) IDENTIFIER[setTok, "set___idx"] { augParams } { dupTree(body) } magicThrowsException ) ; // keving: can't get this to work reasonably //magicMkConstModifiers[IToken tok, List filter]: // ({ !filter.Contains("static") }?=> -> STATIC[tok, "static"] ) ( { !filter.Contains("public") }?=> -> $magicMkConstModifiers FINAL[tok, "final"] ); magicSmotherExceptions[CommonTree body]: magicSmotherExceptionsThrow[body, "RuntimeException"] ; magicSmotherExceptionsThrow[CommonTree body, string exception]: v=magicCatchVar magicThrowableType[true, body.Token] -> OPEN_BRACE["{"] ^(TRY["try"] { dupTree(body) } ^(CATCH["catch"] magicThrowableType { dupTree($v.tree) } OPEN_BRACE["{"] ^(THROW["throw"] ^(NEW["new"] ^(TYPE["TYPE"] IDENTIFIER[exception]) ^(ARGS["ARGS"] { dupTree($v.tree) }))) CLOSE_BRACE["}"])) CLOSE_BRACE["}"] ; // METHOD{ public static TYPE{ void } main PARAMS{ TYPE{ String [ ] } args } { APPLY{ .{ System exit } ARGS{ APPLY{ .{ Program Main } } } } ; magicMainArgs[bool isOn, IToken tok]: -> { isOn }? ^(ARGS[tok, "ARGS"] IDENTIFIER[tok, "args"]) -> ; magicMainApply[bool isOn, IToken tok, string klass, CommonTree args]: -> { isOn }? ^(APPLY[tok, "APPLY"] ^(DOT[tok,"."] IDENTIFIER[tok,klass] IDENTIFIER[tok,"Main"]) { dupTree(args) } ) -> ; magicMainExit[bool isOn, bool retInt, IToken tok, CommonTree body]: -> { isOn && retInt }? ^(APPLY[tok, "APPLY"] ^(DOT[tok,"."] IDENTIFIER[tok,"System"] IDENTIFIER[tok,"exit"]) ^(ARGS[tok, "ARGS"] { dupTree(body) } ) ) -> { isOn }? { dupTree(body) } -> ; magicMainWrapper[bool isOn, IToken tok, CommonTree body]: magicThrowsException[isOn,tok] -> { isOn }? ^(METHOD[tok, "METHOD"] PUBLIC[tok, "public"] STATIC[tok,"static"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "void"]) IDENTIFIER[tok, "main"] ^(PARAMS[tok, "PARAMS"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok,"String"] OPEN_BRACKET[tok, "["] CLOSE_BRACKET[tok, "]"]) IDENTIFIER[tok, "args"]) OPEN_BRACE[tok, "{"] { dupTree(body) } SEMI[tok, ";"] CLOSE_BRACE[tok, "}"] magicThrowsException) -> ; magicTry[IToken tok, CommonTree body, CommonTree catches, CommonTree fin]: -> ^(TRY[tok, "try"] { dupTree(body) } { dupTree(catches) } { dupTree(fin) }) ; magicDispose[IToken tok, string var]: -> ^(IF[tok, "if"] ^(NOT_EQUAL[tok, "!="] IDENTIFIER[tok, var] NULL[tok, "null"]) SEP ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] ^(CAST_EXPR[tok, "CAST"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "IDisposable"]) IDENTIFIER[tok, var]) IDENTIFIER[tok, "Dispose"])) SEMI[tok, ";"]) ; magicFinally[IToken tok, CommonTree statement_list]: -> ^(FINALLY[tok, "finally"] OPEN_BRACE[tok, "{"] { dupTree(statement_list) } CLOSE_BRACE[tok, "}"]) ; magicFinalize[IToken tok, CommonTree body]: magicThrowsException[true,tok] -> ^(METHOD[tok, "METHOD"] PROTECTED[tok, "protected"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "void"]) IDENTIFIER[tok, "finalize"] OPEN_BRACE[tok, "{"] ^(TRY[tok, "try"] { dupTree(body) } ^(FINALLY[tok, "finally"] OPEN_BRACE[tok, "{"] ^(APPLY[tok, "APPLY"] ^(DOT[tok,"."] SUPER[tok,"super"] IDENTIFIER[tok,"finalize"])) SEMI[tok, ";"] CLOSE_BRACE[tok, "}"])) CLOSE_BRACE[tok, "}"] // Always throws Throwable to match Object.finalize() EXCEPTION[tok, "Throwable"]) ; magicDefaultConstructor[IToken tok, string name]: -> ^(CONSTRUCTOR[tok, "CONSTRUCTOR"] PUBLIC[tok, "public"] IDENTIFIER[tok, name] OPEN_BRACE[tok, "{"] CLOSE_BRACE[tok, "}"] ) ; magicThrowsException[bool isOn, IToken tok]: -> {isOn}? EXCEPTION[tok, Cfg.TranslatorExceptionIsThrowable ? "Throwable" : "Exception"] -> ;