diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs index 26bbcf5..ddce2aa 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs @@ -45,6 +45,8 @@ namespace Twiglet.CS2J.Translator public delegate void FileProcessor(string fName); + private static Dictionary partialTypes = new Dictionary(); + private static void showVersion() { Console.Out.WriteLine(Path.GetFileNameWithoutExtension(System.Environment.GetCommandLineArgs()[0]) + ": " + VERSION); @@ -207,6 +209,10 @@ namespace Twiglet.CS2J.Translator foreach (string r in csDir) doFile(r, ".cs", translateFile, cfg.Exclude); // translate it + if (cfg.DebugLevel >= 1) Console.Out.WriteLine("Writing out collected partial types"); + foreach (KeyValuePair entry in partialTypes) + emitPartialType(entry.Key, entry.Value); + if (cfg.DumpEnums) { enumXmlWriter.WriteEndElement(); @@ -531,12 +537,31 @@ namespace Twiglet.CS2J.Translator outputMaker.Cfg = cfg; outputMaker.EmittedCommentTokenIdx = saveEmittedCommentTokenIdx; + outputMaker.IsPartial = javaMaker.CUMap[typeName].IsPartial; + if (outputMaker.IsPartial) + { + if (!partialTypes.ContainsKey(typeName)) + { + partialTypes[typeName] = new ClassDescriptorSerialized(claName); + partialTypes[typeName].FileName = javaFName; + } + outputMaker.PartialDescriptor = partialTypes[typeName]; + } + outputMaker.IsLast = i == (javaMaker.CUKeys.Count - 1); - if (cfg.DebugLevel >= 1) Console.Out.WriteLine("Writing out {0}", javaFName); - StreamWriter javaW = new StreamWriter(javaFName); - javaW.Write(limit(outputMaker.compilation_unit().ToString())); - javaW.Close(); + if (!outputMaker.IsPartial) + { + if (cfg.DebugLevel >= 1) Console.Out.WriteLine("Writing out {0}", javaFName); + StreamWriter javaW = new StreamWriter(javaFName); + javaW.Write(limit(outputMaker.compilation_unit().ToString())); + javaW.Close(); + } + else + { + // fill out partialTypes[typeName] + outputMaker.compilation_unit(); + } saveEmittedCommentTokenIdx = outputMaker.EmittedCommentTokenIdx; } } @@ -546,5 +571,29 @@ namespace Twiglet.CS2J.Translator System.Console.Out.WriteLine(""); System.Console.Out.WriteLine(""); } + + public static void emitPartialType(string name, ClassDescriptorSerialized serTy) + { + + // Pretty print as text + Dictionary args = new Dictionary(); + args["now"] = DateTime.Now; + args["includeDate"] = cfg.TranslatorAddTimeStamp; + args["packageName"] = serTy.Package; + args["imports"] = serTy.Imports; + args["modifiers"] = serTy.Mods; + args["name"] = serTy.Identifier; + args["extends"] = serTy.ClassBase; + args["imps"] = serTy.ClassImplements; + args["body"] = serTy.ClassBody; + + StringTemplate st = templates.GetInstanceOf("partial_type", args); + +// new STAttrMap().Add("now", DateTime.Now).Add("includeDate", Cfg.TranslatorAddTimeStamp).Add("packageName", (((nm != null) ? nm.Text : null) != null && ((nm != null) ? nm.Text : null).Length > 0 ? ((nm != null) ? nm.Text : null) : null)).Add("imports", ((imports1 != null) ? imports1.ST : null)).Add("type", ((type_declaration2 != null) ? type_declaration2.ST : null)).Add("endComments", CollectedComments )); + if (cfg.DebugLevel >= 1) Console.Out.WriteLine("Writing out {0}", serTy.FileName); + StreamWriter javaW = new StreamWriter(serTy.FileName); + javaW.Write(limit(st.ToString())); + javaW.Close(); + } } } diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Templates.cs b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Templates.cs index f3efd91..3c8f1f9 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Templates.cs +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Templates.cs @@ -40,8 +40,27 @@ package(now, includeDate, packageName, imports, comments, modifiers, type, endCo >> +import_list(nss) ::= << +;}; separator=""\n""> +>> + import_template(ns) ::= ""import ;"" +// ****** output partial type ****** +partial_type(now, includeDate, packageName, imports, modifiers, comments, attributes, name, typeparams, extends, imps, body) ::= << + +package ; + + + + +class implements +{ + +} + +>> + // ******* CLASSES *********** class(modifiers, comments, attributes, name, typeparams, extends, imps, body) ::= << @@ -347,3 +366,4 @@ verbatim_string(payload) ::= << } } } + diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs index 5fd2e13..2015e43 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs @@ -234,12 +234,19 @@ namespace Twiglet.CS2J.Translator.Transform // Wraps a compilation unit with its imports search path public class CUnit { - public CUnit(CommonTree inTree, List inSearchPath, List inAliasKeys, List inAliasValues) { + public CUnit(CommonTree inTree, List inSearchPath, List inAliasKeys, List inAliasValues) + : this(inTree, inSearchPath, inAliasKeys, inAliasValues, false) + { + } + + public CUnit(CommonTree inTree, List inSearchPath, List inAliasKeys, List inAliasValues, bool inIsPartial) { Tree = inTree; SearchPath = inSearchPath; NameSpaceAliasKeys = inAliasKeys; NameSpaceAliasValues = inAliasValues; + IsPartial = inIsPartial; } + public CommonTree Tree {get; set;} // namespaces in scope @@ -248,6 +255,64 @@ namespace Twiglet.CS2J.Translator.Transform // aliases for namespaces public List NameSpaceAliasKeys {get; set;} public List NameSpaceAliasValues {get; set;} + public bool IsPartial {get; set;} + } + + public class ClassDescriptor { + public IToken Token { get;set; } + public String Comments { get;set; } + public CommonTree Atts { get;set; } + public CommonTree Mods { get;set; } + public CommonTree Identifier { get;set; } + public CommonTree TypeParameterList { get;set; } + public CommonTree ClassBase { get;set; } + public CommonTree TypeParameterConstraintsClauses { get;set; } + public CommonTree ClassBody { get;set; } + public Dictionary PartialTypes { get;set; } + + public ClassDescriptor(IToken inToken, String inComments, CommonTree inAtts, CommonTree inMods, CommonTree inIdentifier, CommonTree inTypeParameterList, CommonTree inClassBase, CommonTree inTypeParameterConstraintsClauses, CommonTree inClassBody, Dictionary inPartialTypes) { + Token = inToken; + Comments = inComments; + Atts = inAtts; + Mods = inMods; + Identifier = inIdentifier; + TypeParameterList = inTypeParameterList; + ClassBase = inClassBase; + TypeParameterConstraintsClauses = inTypeParameterConstraintsClauses; + ClassBody = inClassBody; + PartialTypes = inPartialTypes; + } + } + + public class ClassDescriptorSerialized { + public String FileName { get;set; } + public List Imports { get;set; } + public String Package { get;set; } + public String Comments { get;set; } + public String Atts { get;set; } + public List Mods { get;set; } + public String Identifier { get;set; } + public String TypeParameterList { get;set; } + public String ClassBase { get;set; } + public List ClassImplements { get;set; } + public String TypeParameterConstraintsClauses { get;set; } + public String ClassBody { get;set; } + + public ClassDescriptorSerialized(string name) + { + FileName = ""; + Comments = ""; + Imports = new List(); + Package = ""; + Atts = ""; + Mods = new List(); + Identifier = name; + TypeParameterList = ""; + ClassBase = ""; + ClassImplements = new List(); + TypeParameterConstraintsClauses = ""; + ClassBody = ""; + } } } diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g index fb33f0b..b49aa3a 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g @@ -33,6 +33,7 @@ scope NSContext { // A scope to keep track of the current type context scope TypeContext { string typeName; + Dictionary partialTypes; } @namespace { Twiglet.CS2J.Translator.Transform } @@ -42,6 +43,7 @@ scope TypeContext { using System; using System.Text; using System.Globalization; + using System.Text.RegularExpressions; } @members @@ -49,11 +51,29 @@ scope TypeContext { // 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 + // We also build a list 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; } + // we keep track of the end position of the previous class / struct member + // so that we can collect comments for a partial type definition + private int prevMemberEndTokenIndex = -1; + + protected string snaffleComments(int startIndex, int endIndex) { + StringBuilder ret = new StringBuilder(); + List toks = ((CommonTokenStream)this.GetTreeNodeStream().TokenStream).GetTokens(startIndex,endIndex); + if (toks != null) { + foreach (IToken tok in toks) { + if (tok.Channel == TokenChannels.Hidden) { + ret.Append(new Regex("(\\n|\\r)+").Replace(tok.Text, Environment.NewLine).Trim()); + // Hide from Pretty Printer + tok.Channel = TokenChannels.Hidden - 1; + } + } + } + return ret.ToString(); + } protected string ParentNameSpace { get { return ((NSContext_scope)$NSContext.ToArray()[1]).currentNS; @@ -140,7 +160,7 @@ scope TypeContext { // null, or // a single token, or // a list of tokens (^(NIL ......)) - protected CommonTree addModifier(IToken tok, CommonTree mods, CommonTree modToAdd) { + protected CommonTree addModifier(CommonTree mods, CommonTree modToAdd) { CommonTree root = (CommonTree)adaptor.Nil; @@ -149,7 +169,7 @@ scope TypeContext { if (mods != null) { // Is root node the one we are looking for? if (!mods.IsNil) { - if (adaptor.GetType(mods) == adaptor.GetType(modToAdd)) { + if (adaptor.GetType(mods) == adaptor.GetType(modToAdd) && adaptor.GetText(mods) == adaptor.GetText(modToAdd)) { modSeen = true; } adaptor.AddChild(root, (CommonTree)adaptor.DupTree(mods)); @@ -157,7 +177,7 @@ scope TypeContext { else { for (int i = 0; i < adaptor.GetChildCount(mods); i++) { CommonTree child = (CommonTree)adaptor.GetChild(mods,i); - if (adaptor.GetType(child) == adaptor.GetType(modToAdd)) { + if (adaptor.GetType(child) == adaptor.GetType(modToAdd) && adaptor.GetText(child) == adaptor.GetText(modToAdd)) { modSeen = true; } adaptor.AddChild(root, (CommonTree)adaptor.DupTree(child)); @@ -172,6 +192,24 @@ scope TypeContext { return root; } + // add all modifiers in modToAdd tree + protected CommonTree mergeModifiers(CommonTree mods, CommonTree modToAdd) { + + if (modToAdd == null) + return mods; + + if (!modToAdd.IsNil) { + return addModifier(mods, modToAdd); + } + + CommonTree ret = mods; + for (int i = 0; i < adaptor.GetChildCount(modToAdd); i++) { + ret = addModifier(mods, (CommonTree)adaptor.GetChild(modToAdd, i)); + } + + return ret; + } + // mods is a list of modifiers. removes is a list of token types, we remove all modifiers appearing in removes protected CommonTree mkRemoveMods(CommonTree mods, int[] removes) { @@ -467,6 +505,73 @@ scope TypeContext { } return ret.ToString(); } + + // Merges part into combined + protected void mergePartialTypes(ClassDescriptor combined, ClassDescriptor part) { + + // append comments + combined.Comments += part.Comments; + + // union all attributes + CommonTree attRoot = (CommonTree)adaptor.Nil; + adaptor.AddChild(attRoot, combined.Atts); + adaptor.AddChild(attRoot, part.Atts); + combined.Atts = (CommonTree)adaptor.RulePostProcessing(attRoot); + + // merge all modifiers + combined.Mods = mergeModifiers(combined.Mods, part.Mods); + + // type parameter list must be the same on all parts + + // all parts that have a TypeParameterConstraintsClauses must agree + if (combined.TypeParameterConstraintsClauses == null) + combined.TypeParameterConstraintsClauses = part.TypeParameterConstraintsClauses; + + // merge all base classes, interfaces + combined.ClassBase = mergeModifiers(combined.ClassBase, part.ClassBase); + + // union all class_body + CommonTree bodyRoot = (CommonTree)adaptor.Nil; + adaptor.AddChild(bodyRoot, combined.ClassBody); + adaptor.AddChild(bodyRoot, part.ClassBody); + combined.ClassBody = (CommonTree)adaptor.RulePostProcessing(bodyRoot); + + // merge partial sub-types + foreach (string key in combined.PartialTypes.Keys) { + if (part.PartialTypes.ContainsKey(key)) { + mergePartialTypes(combined.PartialTypes[key], part.PartialTypes[key]); + } + } + // Add types in part but not combined + foreach (string key in part.PartialTypes.Keys) { + if (!combined.PartialTypes.ContainsKey(key)) { + combined.PartialTypes[key] = part.PartialTypes[key]; + } + } + } + + protected CommonTree emitPartialTypes(Dictionary partialTypes) { + CommonTree root = (CommonTree)adaptor.Nil; + foreach (ClassDescriptor part in partialTypes.Values) { + root.AddChild((CommonTree)magicClassFromDescriptor(part.Token, part).Tree); + } + return (CommonTree)adaptor.RulePostProcessing(root); + } + + protected void mergeCompUnits(CUnit cu, List searchPath, List aliasKeys, List aliasNamespaces) { + foreach (string s in searchPath) { + if (!cu.SearchPath.Contains(s)) { + cu.SearchPath.Add(s); + } + for (int i = 0; i < aliasKeys.Count; i++) { + // TODO: ?? Assume alias -> namespace mapping is the same in all files .... + if (!cu.NameSpaceAliasKeys.Contains(aliasKeys[i])) { + cu.NameSpaceAliasKeys.Add(aliasKeys[i]); + cu.NameSpaceAliasValues.Add(aliasNamespaces[i]); + } + } + } + } } @@ -477,12 +582,15 @@ scope TypeContext { /////////////////////////////////////////////////////// public compilation_unit -scope NSContext; +scope NSContext, TypeContext; @init { $NSContext::currentNS = ""; $NSContext::namespaces = new List(); $NSContext::aliasKeys = new List(); $NSContext::aliasNamespaces = new List(); + + $TypeContext::typeName = null; + $TypeContext::partialTypes = new Dictionary(); } : namespace_body; @@ -523,24 +631,37 @@ using_namespace_directive: namespace_member_declarations: namespace_member_declaration+ ; namespace_member_declaration +scope TypeContext; @init { string ns = $NSContext::currentNS; bool isCompUnit = false; CommonTree atts = null; CommonTree mods = null; + $TypeContext::partialTypes = new Dictionary(); } @after { if (isCompUnit) { foreach (KeyValuePair treeEntry in $ty.compUnits) { if (treeEntry.Value != null) { - if (CUKeys.Contains(ns+"."+treeEntry.Key)) { - { Warning(treeEntry.Value.Token.Line, "[UNSUPPORTED] Cannot have a class with multiple generic type overloadings: " + ns+"."+treeEntry.Key); } + string fqn = ns+(String.IsNullOrEmpty(ns) ? "" : ".")+treeEntry.Key; + if (CUKeys.Contains(fqn)) { + Warning(treeEntry.Value.Token.Line, "[UNSUPPORTED] Cannot have a class with multiple generic type overloadings: " + fqn); } else { - CUMap.Add(ns+"."+treeEntry.Key, new CUnit(mkPackage(treeEntry.Value.Token, treeEntry.Value, ns),CollectSearchPath,CollectAliasKeys,CollectAliasNamespaces)); - CUKeys.Add(ns+"."+treeEntry.Key); + CUMap.Add(fqn, new CUnit(mkPackage(treeEntry.Value.Token, treeEntry.Value, ns),CollectSearchPath,CollectAliasKeys,CollectAliasNamespaces)); + CUKeys.Add(fqn); } } - }; + } + foreach (KeyValuePair partialEntry in $TypeContext::partialTypes) { + string fqn = ns+(String.IsNullOrEmpty(ns) ? "" : ".")+partialEntry.Key; + if (CUKeys.Contains(fqn)) { + Warning(partialEntry.Value.Token.Line, "[UNSUPPORTED] Cannot have a class with multiple generic type overloadings: " + fqn); + } + else { + CUMap.Add(fqn, new CUnit(mkPackage(partialEntry.Value.Token, (CommonTree)magicClassFromDescriptor(partialEntry.Value.Token, partialEntry.Value).Tree, ns), CollectSearchPath, CollectAliasKeys, CollectAliasNamespaces, true)); + CUKeys.Add(fqn); + } + } } }: namespace_declaration @@ -553,12 +674,11 @@ type_declaration[CommonTree atts, CommonTree mods] returns [Dictionary(); } : - ('partial') => p='partial'! { Warning($p.line, "[UNSUPPORTED] 'partial' definition"); } - (pc=class_declaration[$atts, $mods, true /* toplevel */] { $compUnits.Add($pc.name, $pc.tree); } - | ps=struct_declaration[$atts, $mods, true /* toplevel */] { $compUnits.Add($ps.name, $ps.tree); } + ('partial') => p='partial'! (pc=class_declaration[$atts, $mods, true /* toplevel */, true /* isPartial */] + | ps=struct_declaration[$atts, $mods, true /* toplevel */, true /* isPartial */] | pi=interface_declaration[$atts, $mods] { $compUnits.Add($pi.name, $pi.tree); } ) - | c=class_declaration[$atts, $mods, true /* toplevel */] { $compUnits.Add($c.name, $c.tree); } - | s=struct_declaration[$atts, $mods, true /* toplevel */] { $compUnits.Add($s.name, $s.tree); } + | c=class_declaration[$atts, $mods, true /* toplevel */, false /* isPartial */] { $compUnits.Add($c.name, $c.tree); } + | s=struct_declaration[$atts, $mods, true /* toplevel */, false /* isPartial */] { $compUnits.Add($s.name, $s.tree); } | i=interface_declaration[$atts, $mods] { $compUnits.Add($i.name, $i.tree); } | e=enum_declaration[$atts, $mods] { $compUnits.Add($e.name, $e.tree); } | d=delegate_declaration[$atts, $mods, true /* toplevel */] { $compUnits = $d.compUnits; } @@ -578,18 +698,24 @@ 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: +class_member_declaration +@after { + this.prevMemberEndTokenIndex = adaptor.GetTokenStopIndex($class_member_declaration.tree); +}: 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[$a.tree, $m.tree] -> $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 + | p='partial' (v1=void_type m3=method_declaration[$a.tree, $m.tree, $m.modList, $v1.tree, $v1.text, true /* isPartial */] -> { $m3.tree != null}? $m3 + -> | pi=interface_declaration[$a.tree, $m.tree] -> $pi - | pc=class_declaration[$a.tree, $m.tree, false /* toplevel */] -> $pc - | ps=struct_declaration[$a.tree, $m.tree, false /* toplevel */] -> $ps) + | pc=class_declaration[$a.tree, $m.tree, false /* toplevel */, true /* isPartial */] -> { $pc.tree != null}? $pc + -> + | ps=struct_declaration[$a.tree, $m.tree, false /* toplevel */, true /* isPartial */] -> { ps.tree != null}? $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 + | v2=void_type m1=method_declaration[$a.tree, $m.tree, $m.modList, $v2.tree, $v2.text, false /* isPartial */] -> $m1 + | t=type ( (member_name type_parameter_list? '(') => m2=method_declaration[$a.tree, $m.tree, $m.modList, $t.tree, $t.text, false /* isPartial */] -> $m2 | (member_name '{') => pd=property_declaration[$a.tree, $m.tree, $t.tree] -> $pd | (type_name '.' 'this') => tn=type_name '.' ix1=indexer_declaration[$a.tree, $m.tree, $t.tree, $tn.tree] -> $ix1 | ix2=indexer_declaration[$a.tree, $m.tree, $t.tree, null] -> $ix2 //this @@ -598,8 +724,8 @@ class_member_declaration: ) // common_modifiers// (method_modifiers | field_modifiers) - | cd=class_declaration[$a.tree, $m.tree, false /* toplevel */] -> $cd - | sd=struct_declaration[$a.tree, $m.tree, false /* toplevel */] -> $sd + | cd=class_declaration[$a.tree, $m.tree, false /* toplevel */, false /* isPartial */] -> $cd + | sd=struct_declaration[$a.tree, $m.tree, false /* toplevel */, false /* isPartial */] -> $sd | ed=enum_declaration[$a.tree, $m.tree] -> $ed | dd=delegate_declaration[$a.tree, $m.tree, false /* toplevel */] -> { mkFlattenDictionary($dd.tree.Token,$dd.compUnits) } | co3=conversion_operator_declaration -> ^(CONVERSION_OPERATOR[$co3.start.Token, "CONVERSION"] $a? $m? $co3) @@ -923,9 +1049,9 @@ pointer_type: /////////////////////////////////////////////////////// // Statement Section /////////////////////////////////////////////////////// -block: - ';' - | '{' statement_list? '}'; +block returns [bool isEmpty]: + ';' { $isEmpty = true; } + | '{' statement_list? '}' { $isEmpty = false; }; statement_list: statement[/* isStatementListCtxt */ true]+ ; @@ -1132,11 +1258,42 @@ attribute_argument_expression: // Class Section /////////////////////////////////////////////////////// -class_declaration[CommonTree atts, CommonTree mods, bool toplevel] returns [string name] +class_declaration[CommonTree atts, CommonTree mods, bool toplevel, bool isPartial] returns [string name] scope TypeContext; +@init{ + $TypeContext::partialTypes = new Dictionary(); + int prevMemberEndIndex = this.prevMemberEndTokenIndex; + IToken endToken = null; +} : - 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) } { toplevel ? dupTree($mods) : addModifier($c.token, $mods, (CommonTree)adaptor.Create(STATIC, $c.token, "static")) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base? class_body ); + c='class' identifier { $TypeContext::typeName = $identifier.thetext; } type_parameter_list? { $name = mkGenericTypeAlias($identifier.thetext, $type_parameter_list.names); } class_base? type_parameter_constraints_clauses? class_body[$isPartial] { endToken = $class_body.endToken; } (s=';' { endToken = $s.token; })? + { + if ($isPartial) { + + // Strip off braces + CommonTree newClassBody = dupTree($class_body.tree); + int bodyChildren = adaptor.GetChildCount(newClassBody); + if (bodyChildren >= 2 && ((CommonTree)adaptor.GetChild(newClassBody, 0)).Type == OPEN_BRACE && ((CommonTree)adaptor.GetChild(newClassBody, bodyChildren - 1)).Type == CLOSE_BRACE) { + adaptor.DeleteChild(newClassBody, bodyChildren - 1); + adaptor.DeleteChild(newClassBody, 0); + newClassBody = (CommonTree)adaptor.RulePostProcessing(newClassBody); + } + if (!toplevel) { + $mods = addModifier($mods, (CommonTree)adaptor.Create(STATIC, $c.token, "static")); + } + ClassDescriptor klass = new ClassDescriptor($c.token, snaffleComments(prevMemberEndIndex, $c.token.TokenIndex), $atts, $mods, $identifier.tree, $type_parameter_list.tree, $class_base.tree, $type_parameter_constraints_clauses.tree, newClassBody, $TypeContext::partialTypes); + // add to parent's context + Dictionary parentPartialTypes = ((TypeContext_scope)$TypeContext.ToArray()[1]).partialTypes; + if (parentPartialTypes.ContainsKey($identifier.thetext)) { + mergePartialTypes(parentPartialTypes[$identifier.thetext], klass); + } + else { + parentPartialTypes[$identifier.thetext] = klass; + } + } + } + -> {$isPartial}? + -> ^(CLASS[$c.Token] { dupTree($atts) } { toplevel ? dupTree($mods) : addModifier($mods, (CommonTree)adaptor.Create(STATIC, $c.token, "static")) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base? class_body ); type_parameter_list returns [List names] @init { @@ -1145,7 +1302,7 @@ type_parameter_list returns [List names] '<'! 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; } ; + identifier { $name = $identifier.thetext; } ; class_base: // just put all types in a single list. In NetMaker we will extract the base class if necessary @@ -1154,8 +1311,10 @@ class_base: //interface_type_list: // ts+=type (',' ts+=type)* -> $ts+; -class_body: - '{' class_member_declarations? '}' ; +class_body[bool isPartial] returns [IToken endToken]: + '{' class_member_declarations? e='}' { $endToken = $e.token; } + -> {!$isPartial}? '{' class_member_declarations? { emitPartialTypes($TypeContext::partialTypes) } '}' + -> '{' class_member_declarations? '}' ; class_member_declarations: class_member_declaration+ ; @@ -1178,7 +1337,7 @@ variable_declarator: type_name ('=' variable_initializer)? ; // eg. event EventHandler IInterface.VariableName = Foo; /////////////////////////////////////////////////////// -method_declaration [CommonTree atts, CommonTree mods, List modList, CommonTree type, string typeText] +method_declaration [CommonTree atts, CommonTree mods, List modList, CommonTree type, string typeText, bool isPartial] @init { bool isToString = false; bool isEquals = false; @@ -1247,14 +1406,17 @@ method_declaration [CommonTree atts, CommonTree mods, List modList, Comm } exceptions = IsJavaish ? $throw_exceptions.tree : $b.exceptionList; } - -> $mainMethod? + -> {!($isPartial && $b.isEmpty)}? + $mainMethod? ^(METHOD { dupTree($atts) } { dupTree($mods) } { dupTree($type) } - magicIdentifier type_parameter_constraints_clauses? type_parameter_list? formal_parameter_list? $b { exceptions }); + magicIdentifier type_parameter_constraints_clauses? type_parameter_list? formal_parameter_list? $b { exceptions }) + -> +; -method_body [bool smotherExceptions] returns [CommonTree exceptionList]: +method_body [bool smotherExceptions] returns [CommonTree exceptionList, bool isEmpty]: {smotherExceptions}? b=block nb=magicSmotherExceptions[dupTree($b.tree) ] -> $nb - | b=block el=magicThrowsException[true,$b.tree.Token] { $exceptionList=$el.tree; } + | b=block el=magicThrowsException[true,$b.tree.Token] { $exceptionList=$el.tree; $isEmpty = $b.isEmpty; } -> $b ; @@ -1328,7 +1490,7 @@ remove_accessor_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 ';'? + e='enum' identifier { $name = $identifier.thetext; $TypeContext::typeName = $identifier.thetext; } enum_base? enum_body ';'? -> ^(ENUM[$e.token, "ENUM"] { dupTree($atts) } { dupTree($mods) } identifier enum_base? enum_body); enum_base: ':' integral_type ; @@ -1404,7 +1566,7 @@ scope TypeContext; $compUnits.Add(delName, dupTree($delegate_declaration.tree)); } : - d='delegate' return_type identifier { delName = $identifier.text; $TypeContext::typeName = $identifier.text; } variant_generic_parameter_list? {ifTree = mkType($d.token, $identifier.tree, $variant_generic_parameter_list.tyargs); } + d='delegate' return_type identifier { delName = $identifier.thetext; $TypeContext::typeName = $identifier.thetext; } variant_generic_parameter_list? {ifTree = mkType($d.token, $identifier.tree, $variant_generic_parameter_list.tyargs); } '(' formal_parameter_list? ')' type_parameter_constraints_clauses? ';' magicDelegateInterface[$d.token, $return_type.tree, $identifier.tree, $formal_parameter_list.tree, $variant_generic_parameter_list.tyargs] magicMultiInvokerMethod[$d.token, $return_type.tree, $return_type.thetext == "Void" || $return_type.thetext == "System.Void", ifTree, $formal_parameter_list.tree, mkArgsFromParams($d.token, $formal_parameter_list.tree), $variant_generic_parameter_list.tyargs] @@ -1415,14 +1577,14 @@ scope TypeContext; AddToImports("java.util.ArrayList"); AddToImports("CS2JNet.JavaSupport.util.ListSupport"); } - magicMultiDelClass[$d.token, $atts, toplevel ? dupTree($mods) : addModifier($d.token, $mods, (CommonTree)adaptor.Create(STATIC, $d.token, "static")), multiDelName, ifTree, $type_parameter_constraints_clauses.tree, $variant_generic_parameter_list.tree, $magicMultiInvokerMethod.tree, delClassMemberNodes] + magicMultiDelClass[$d.token, $atts, toplevel ? dupTree($mods) : addModifier($mods, (CommonTree)adaptor.Create(STATIC, $d.token, "static")), multiDelName, ifTree, $type_parameter_constraints_clauses.tree, $variant_generic_parameter_list.tree, $magicMultiInvokerMethod.tree, delClassMemberNodes] { $compUnits.Add(multiDelName, $magicMultiDelClass.tree); } -> // ^(DELEGATE[$d.token, "DELEGATE"] { dupTree($atts) } { dupTree($mods) } return_type identifier type_parameter_constraints_clauses? variant_generic_parameter_list? // '(' formal_parameter_list? ')' ); - ^(INTERFACE[$d.token, "interface"] { dupTree($atts) } { toplevel ? dupTree($mods) : addModifier($d.token, $mods, (CommonTree)adaptor.Create(STATIC, $d.token, "static")) } identifier { dupTree($type_parameter_constraints_clauses.tree) } { dupTree($variant_generic_parameter_list.tree) } magicDelegateInterface) + ^(INTERFACE[$d.token, "interface"] { dupTree($atts) } { toplevel ? dupTree($mods) : addModifier($mods, (CommonTree)adaptor.Create(STATIC, $d.token, "static")) } identifier { dupTree($type_parameter_constraints_clauses.tree) } { dupTree($variant_generic_parameter_list.tree) } magicDelegateInterface) ; delegate_modifiers: modifier+ ; @@ -1505,7 +1667,7 @@ parameter_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? + c='interface' identifier { $name = $identifier.thetext; $TypeContext::typeName = $identifier.thetext; } variant_generic_parameter_list? interface_base? type_parameter_constraints_clauses? interface_body ';'? -> ^(INTERFACE[$c.Token, "interface"] { dupTree($atts) } { dupTree($mods) } identifier type_parameter_constraints_clauses? variant_generic_parameter_list? interface_base? interface_body ); @@ -1551,14 +1713,48 @@ interface_accessor_declaration [CommonTree atts, CommonTree mods, CommonTree typ ; /////////////////////////////////////////////////////// -struct_declaration[CommonTree atts, CommonTree mods, bool toplevel] returns [string name] +struct_declaration[CommonTree atts, CommonTree mods, bool toplevel, bool isPartial] returns [string name] scope TypeContext; +@init { + $TypeContext::partialTypes = new Dictionary(); + IToken endToken = null; +} : - 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) } { toplevel ? dupTree($mods) : addModifier($c.token, $mods, (CommonTree)adaptor.Create(STATIC, $c.token, "static")) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base? struct_body ); + c='struct' identifier { $TypeContext::typeName = $identifier.thetext; } type_parameter_list? { $name = mkGenericTypeAlias($identifier.thetext, $type_parameter_list.names); } class_base? type_parameter_constraints_clauses? struct_body[$isPartial, $identifier.thetext] { endToken = $struct_body.endToken; } (s=';' { endToken = $s.token; })? + { + if ($isPartial) { -struct_body [string structName]: - o='{' magicDefaultConstructor[$o.token, structName] class_member_declarations? '}' ; + // Strip off braces + CommonTree newClassBody = dupTree($struct_body.tree); + int bodyChildren = adaptor.GetChildCount(newClassBody); + if (bodyChildren >= 2 && ((CommonTree)adaptor.GetChild(newClassBody, 0)).Type == OPEN_BRACE && ((CommonTree)adaptor.GetChild(newClassBody, bodyChildren - 1)).Type == CLOSE_BRACE) { + adaptor.DeleteChild(newClassBody, bodyChildren - 1); + adaptor.DeleteChild(newClassBody, 0); + newClassBody = (CommonTree)adaptor.RulePostProcessing(newClassBody); + } + if (!toplevel) { + $mods = addModifier($mods, (CommonTree)adaptor.Create(STATIC, $c.token, "static")); + } + + ClassDescriptor klass = new ClassDescriptor($c.token, snaffleComments(this.prevMemberEndTokenIndex, $c.token.TokenIndex), $atts, $mods, $identifier.tree, $type_parameter_list.tree, $class_base.tree, $type_parameter_constraints_clauses.tree, newClassBody, $TypeContext::partialTypes); + + // add to parent's context + Dictionary parentPartialTypes = ((TypeContext_scope)$TypeContext.ToArray()[1]).partialTypes; + if (!parentPartialTypes.ContainsKey($identifier.thetext)) { + mergePartialTypes(parentPartialTypes[$identifier.thetext], klass); + } + else { + parentPartialTypes[$identifier.thetext] = klass; + } + } + } + -> {$isPartial}? + -> ^(CLASS[$c.Token, "class"] { dupTree($atts) } { toplevel ? dupTree($mods) : addModifier($mods, (CommonTree)adaptor.Create(STATIC, $c.token, "static")) } identifier type_parameter_constraints_clauses? type_parameter_list? class_base? struct_body ); + +struct_body [bool isPartial, string structName] returns [IToken endToken]: + o='{' magicDefaultConstructor[$o.token, structName] class_member_declarations? e='}' { $endToken = $e.token; } + -> {$isPartial}? '{' magicDefaultConstructor class_member_declarations? { emitPartialTypes($TypeContext::partialTypes) } '}' + -> '{' magicDefaultConstructor class_member_declarations? '}' ; /////////////////////////////////////////////////////// @@ -1702,7 +1898,7 @@ local_variable_declarators returns [List variableNames] }: 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)? ; + identifier { $variableName = $identifier.thetext; } ('=' local_variable_initializer)? ; local_variable_initializer: expression | array_initializer @@ -1867,10 +2063,11 @@ predefined_type returns [string thetext] | 'ushort' { $thetext = "System.UInt16"; } ; -identifier +identifier returns [string thetext] @after { string fixedId = fixBrokenId($identifier.tree.Token.Text); $identifier.tree.Token.Text = fixedId; + $thetext = $identifier.tree.Token.Text; }: IDENTIFIER | also_keyword; @@ -2155,3 +2352,5 @@ magicMultiDelClass[IToken tok, CommonTree atts, CommonTree mods, string classNam OPEN_BRACE[tok, "{"] {dupTree(invokeMethod)} { dupTree(members) } CLOSE_BRACE[tok, "}"]) ; +magicClassFromDescriptor[IToken tok, ClassDescriptor klass]: +-> ^(CLASS[tok] PAYLOAD[tok, klass.Comments] { dupTree(klass.Atts) } { dupTree(klass.Mods) } { dupTree(klass.Identifier) } { dupTree(klass.TypeParameterConstraintsClauses) } { dupTree(klass.TypeParameterList) } { dupTree(klass.ClassBase) } OPEN_BRACE[tok, "{"] { dupTree(klass.ClassBody) } { emitPartialTypes(klass.PartialTypes) } CLOSE_BRACE[tok, "}"] ); diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g index 0b47ef8..4fcaffd 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g @@ -25,9 +25,11 @@ options { @members { + public bool IsPartial { get; set; } public bool IsLast { get; set; } public int EmittedCommentTokenIdx { get; set; } - + public ClassDescriptorSerialized PartialDescriptor { get; set; } + private List collectedComments = null; List CollectedComments { get { @@ -288,7 +290,9 @@ options { return ret; } - + protected string mkString(object s) { + return (s == null ? String.Empty : s.ToString()); + } } @@ -297,14 +301,30 @@ public compilation_unit initPrecedence(); } : - ^(PACKAGE nm=PAYLOAD imports? type_declaration { if (IsLast) collectComments(); }) -> + ^(PACKAGE nm=PAYLOAD imports? type_declaration) + + { + if (IsLast) collectComments(); + if (IsPartial) { + // Merge into existing descriptor + PartialDescriptor.Package = ($nm.text != null && $nm.text.Length > 0 ? $nm.text : null); + if ($imports.importList != null && $imports.importList.Count > 0) { + foreach (string m in $imports.importList) { + if (!PartialDescriptor.Imports.Contains(m)) { + PartialDescriptor.Imports.Add(m); + } + } + } + } + } + -> package(now = {DateTime.Now}, includeDate = {Cfg.TranslatorAddTimeStamp}, packageName = {($nm.text != null && $nm.text.Length > 0 ? $nm.text : null)}, imports = {$imports.st}, type = {$type_declaration.st}, endComments = { CollectedComments }); type_declaration: - class_declaration -> { $class_declaration.st } + class_declaration[true] -> { $class_declaration.st } | interface_declaration -> { $interface_declaration.st } | enum_declaration -> { $enum_declaration.st } | annotation_declaration -> { $annotation_declaration.st } @@ -315,21 +335,36 @@ qualified_identifier: namespace_name : namespace_or_type_name ; -modifiers: - ms+=modifier+ -> modifiers(mods={$ms}); -modifier +modifiers returns [List modList] @init { - string thetext = null; -} -: - (m='new' | m='public' | m='protected' | m='private' | m='abstract' | m='sealed' | m='static' - | m='readonly' | m='volatile' | m='extern' { thetext = "/* [UNSUPPORTED] 'extern' modifier not supported */"; } | m='virtual' | m='override' | m=FINAL) - -> string(payload={ (thetext == null ? $m.text : thetext) }); + $modList = new List(); +}: + (modifier { $modList.Add($modifier.thetext); })+ -> modifiers(mods={$modList}); -imports: - imps+=importns+ -> seplist(items = { $imps }, sep= { "\n" }); -importns: - IMPORT PAYLOAD -> import_template(ns = { $PAYLOAD.text }); +modifier returns [string thetext] +: + (m='new' { $thetext = "new"; } + | m='public' { $thetext = "public"; } + | m='protected' { $thetext = "protected"; } + | m='private' { $thetext = "private"; } + | m='abstract' { $thetext = "abstract"; } + | m='sealed' { $thetext = "sealed"; } + | m='static' { $thetext = "static"; } + | m='readonly' { $thetext = "readonly"; } + | m='volatile' { $thetext = "volatile"; } + | m='extern' { $thetext = "/* [UNSUPPORTED] 'extern' modifier not supported */"; } + | m='virtual' { $thetext = "virtual"; } + | m='override' { $thetext = "override"; } + | m=FINAL{ $thetext = "final"; }) + -> string(payload= { $thetext }); + +imports returns [List importList] +@init { + $importList = new List(); +}: + (importns { $importList.Add($importns.thetext); })+ -> import_list(nss = { $importList }); +importns returns [string thetext]: + IMPORT PAYLOAD { $thetext = $PAYLOAD.text; } -> import_template(ns = { $PAYLOAD.text }); class_member_declaration returns [List preComments]: ^(CONST attributes? modifiers? type { $preComments = CollectedComments; } constant_declarators) @@ -338,7 +373,7 @@ class_member_declaration returns [List preComments]: { $preComments = CollectedComments; } method_body exception*) -> method(modifiers={$modifiers.st}, type={$type.st}, name={ $member_name.st }, typeparams = { $type_parameter_list.st }, params={ $formal_parameter_list.st }, exceptions = { $exception.st }, bodyIsSemi = { $method_body.isSemi }, body={ $method_body.st }) | interface_declaration -> { $interface_declaration.st } - | class_declaration -> { $class_declaration.st } + | class_declaration[false] -> { $class_declaration.st } | ^(FIELD attributes? modifiers? type { $preComments = CollectedComments; } field_declaration) -> field(modifiers={$modifiers.st}, type={$type.st}, field={$field_declaration.st}) | ^(OPERATOR attributes? modifiers? type { $preComments = CollectedComments; } operator_declaration) | enum_declaration -> { $enum_declaration.st } @@ -884,9 +919,9 @@ global_attribute_target_specifier: global_attribute_target: 'assembly' | 'module' ; attributes: - attribute_sections ; + attribute_sections -> { $attribute_sections.st } ; attribute_sections: - attribute_section+ ; + ass+=attribute_section+ ; attribute_section: ^(ATTRIBUTE attribute_target_specifier? attribute_list) ; attribute_target_specifier: @@ -919,13 +954,58 @@ attribute_argument_expression: // Class Section /////////////////////////////////////////////////////// -class_declaration +class_declaration[bool topLevel] @init { List preComments = null; }: - ^(c=CLASS + ^(c=CLASS PAYLOAD? attributes? modifiers? identifier type_parameter_constraints_clauses? type_parameter_list[$type_parameter_constraints_clauses.tpConstraints]? - class_extends? class_implements? { preComments = CollectedComments; } class_body ) + class_extends? class_implements? { preComments = CollectedComments; preComments.Add($PAYLOAD.text); } class_body ) + { + if (IsPartial && topLevel) { + // Merge into existing descriptor + if (preComments != null) { + foreach (String comment in preComments) { + PartialDescriptor.Comments += comment ; + } + } + // Union all attributes + // we don't push through attributes yet + PartialDescriptor.Atts += mkString($attributes.st); + + // Merge modifiers + if ($modifiers.modList != null && $modifiers.modList.Count > 0) { + foreach (string m in $modifiers.modList) { + if (!PartialDescriptor.Mods.Contains(m)) { + PartialDescriptor.Mods.Add(m); + } + } + } + + if (String.IsNullOrEmpty(PartialDescriptor.TypeParameterList)) { + PartialDescriptor.TypeParameterList = mkString($type_parameter_list.st); + } + + if (String.IsNullOrEmpty(PartialDescriptor.TypeParameterConstraintsClauses)) { + PartialDescriptor.TypeParameterConstraintsClauses = mkString($type_parameter_constraints_clauses.st); + } + + if ($class_implements.implementList != null && $class_implements.implementList.Count > 0) { + foreach (string m in $class_implements.implementList) { + if (!PartialDescriptor.ClassImplements.Contains(m)) { + PartialDescriptor.ClassImplements.Add(m); + } + } + } + + if (String.IsNullOrEmpty(PartialDescriptor.ClassBase)) { + PartialDescriptor.ClassBase = mkString($class_extends.st); + } + + // Union the class bodies + PartialDescriptor.ClassBody += mkString($class_body.st); + } + } -> class(modifiers = { $modifiers.st }, name={ $identifier.st }, typeparams= {$type_parameter_list.st}, comments = { preComments }, extends = { $class_extends.st }, imps = { $class_implements.st }, body={$class_body.st}) ; @@ -942,10 +1022,13 @@ class_extends: ts+=class_extend+ -> extends(types = { $ts }) ; class_extend: ^(EXTENDS ts=type) -> { $ts.st } ; -class_implements: - ts+=class_implement+ -> imps(types = { $ts }) ; -class_implement: - ^(IMPLEMENTS ts=type) -> { $ts.st }; +class_implements returns [List implementList] +@init { + $implementList = new List(); +}: + (class_implement {$implementList.Add($class_implement.st.ToString()); })+ -> imps(types = { $implementList }) ; +class_implement returns [string implement]: + ^(IMPLEMENTS ts=type) { $implement = $ts.st.ToString(); } -> { $ts.st }; interface_type_list: ts+=type (',' ts+=type)* -> commalist(items={ $ts }); diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g index e031406..f55770f 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g @@ -2212,7 +2212,7 @@ scope NSContext,SymTab; $SymTab::symtab = new Dictionary(); } : - ^(c=CLASS attributes? modifiers? identifier type_parameter_constraints_clauses? + ^(c=CLASS PAYLOAD? attributes? modifiers? identifier type_parameter_constraints_clauses? type_parameter_list? { $NSContext::currentNS = NSPrefix(ParentNameSpace) + mkGenericTypeAlias($identifier.thetext, $type_parameter_list.tyParams); if (CompUnitName == null) CompUnitName = $NSContext::currentNS; } class_implements? @@ -2251,7 +2251,7 @@ scope NSContext,SymTab; } class_body magicAnnotation[$modifiers.tree, $identifier.tree, null, $c.token]) -> {$class_implements.hasExtends && $class_implements.extendDotNetType.IsA(AppEnv.Search("System.Attribute", new UnknownRepTemplate("System.Attribute")), AppEnv)}? magicAnnotation - -> ^($c attributes? modifiers? identifier type_parameter_constraints_clauses? type_parameter_list? class_implements? class_body); + -> ^($c PAYLOAD? attributes? modifiers? identifier type_parameter_constraints_clauses? type_parameter_list? class_implements? class_body); type_parameter_list returns [List tyParams] @init { @@ -2265,20 +2265,27 @@ type_parameter returns [string thetext]: class_extends: class_extend+ ; class_extend: - ^(EXTENDS type) ; + ^(EXTENDS type); // If first implements type is a class then convert to extends -class_implements returns [bool hasExtends, TypeRepTemplate extendDotNetType]: - class_implement_or_extend { $hasExtends = $class_implement_or_extend.hasExtends; $extendDotNetType = $class_implement_or_extend.extendDotNetType; } - class_implement* ; - -class_implement_or_extend returns [bool hasExtends, TypeRepTemplate extendDotNetType] +class_implements returns [bool hasExtends, TypeRepTemplate extendDotNetType] @init { - $hasExtends = false; + CommonTree extends = null; }: - ^(i=IMPLEMENTS t=type - { if ($t.dotNetType is ClassRepTemplate) { - $hasExtends = true; + (class_implement_or_extend[extends == null] { if ($class_implement_or_extend.extends != null) { + extends = $class_implement_or_extend.extends; + $hasExtends = true; + $extendDotNetType = $class_implement_or_extend.extendDotNetType; + }})+ + -> { extends } class_implement_or_extend*; + +class_implement_or_extend[bool lookingForBase] returns [CommonTree extends, TypeRepTemplate extendDotNetType] +@init { + $extends = null; +}: + ^(i=IMPLEMENTS t=type magicExtends[$lookingForBase && $t.dotNetType is ClassRepTemplate, $i.token, $t.tree] + { if ($lookingForBase && $t.dotNetType is ClassRepTemplate) { + $extends = $magicExtends.tree; $extendDotNetType = $t.dotNetType; } if($t.dotNetType.IsA(ICollectionType,AppEnv)) $NSContext::IsICollection = true; @@ -2287,19 +2294,9 @@ class_implement_or_extend returns [bool hasExtends, TypeRepTemplate extendDotNet $NSContext::GenericICollectionTyVar = $t.dotNetType.TypeParams[0]; } } ) - -> { $t.dotNetType is ClassRepTemplate }? ^(EXTENDS[$i.token, "extends"] type) + -> { $lookingForBase && $t.dotNetType is ClassRepTemplate }? -> ^($i $t); -class_implement: - ^(IMPLEMENTS t=type - { - if($t.dotNetType.IsA(ICollectionType,AppEnv)) $NSContext::IsICollection = true; - if($t.dotNetType.IsA(GenericICollectionType,AppEnv) && $t.dotNetType.TypeParams.Length > 0) { - $NSContext::IsGenericICollection = true; - $NSContext::GenericICollectionTyVar = $t.dotNetType.TypeParams[0]; - } - }) ; - class_body @init { CommonTree collectorNodes = null; @@ -3295,3 +3292,8 @@ magicTypeFromTemplate[bool isOn, IToken tok, TypeRepTemplate dotNetType]: -> { $isOn }? ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, $dotNetType.mkFormattedTypeName(false, "<",">")]) -> ; + +magicExtends[bool isOn, IToken tok, CommonTree type]: + -> { $isOn }? ^(EXTENDS[tok, "extends"] { dupTree($type) }) + -> + ;