diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs index 0f500d9..8f60d2b 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JMain.cs @@ -129,6 +129,7 @@ namespace Twiglet.CS2J.Translator .Add ("translator-timestamp-files=", v => cfg.TranslatorAddTimeStamp = Boolean.Parse(v)) .Add ("translator-exception-is-throwable=", v => cfg.TranslatorExceptionIsThrowable = Boolean.Parse(v)) .Add ("experimental-transforms=", v => cfg.ExperimentalTransforms = Boolean.Parse(v)) + .Add ("internal-isjavaish", v => cfg.InternalIsJavaish = true) ; //TODO: fix enum dump @@ -191,6 +192,7 @@ namespace Twiglet.CS2J.Translator w.Close(); } } + // load in T.stg template group, put in templates variable string templateLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.Combine("templates", "java.stg")); if (File.Exists(templateLocation)) { @@ -279,6 +281,7 @@ namespace Twiglet.CS2J.Translator // } csParser p = new csParser(tokens); p.TraceDestination = Console.Error; + p.IsJavaish = cfg.InternalIsJavaish; csParser.compilation_unit_return parser_rt = p.compilation_unit(); @@ -439,6 +442,7 @@ namespace Twiglet.CS2J.Translator javaMaker.Cfg = cfg; javaMaker.CUMap = new Dictionary(); javaMaker.CUKeys = new List(); + javaMaker.IsJavaish = cfg.InternalIsJavaish; if (cfg.DebugLevel >= 1) Console.Out.WriteLine("Translating {0} to Java", fullName); @@ -514,6 +518,8 @@ namespace Twiglet.CS2J.Translator netMaker.AliasKeys = javaMaker.CUMap[typeName].NameSpaceAliasKeys; netMaker.AliasNamespaces = javaMaker.CUMap[typeName].NameSpaceAliasValues; + netMaker.IsJavaish = cfg.InternalIsJavaish; + if (cfg.DebugLevel > 5) Console.Out.WriteLine("Translating {0} Net Calls to Java", javaFName); NetMaker.compilation_unit_return javaCompilationUnit = netMaker.compilation_unit(); diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JSettings.cs b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JSettings.cs index fef08f8..3fca330 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JSettings.cs +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/CS2JSettings.cs @@ -61,6 +61,11 @@ namespace Twiglet.CS2J.Translator get; set; } + public bool InternalIsJavaish + { + get; set; + } + public CS2JSettings () { @@ -95,6 +100,8 @@ namespace Twiglet.CS2J.Translator TranslatorExceptionIsThrowable = false; ExperimentalTransforms = false; + + InternalIsJavaish = false; } } } diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs index ea30765..4a9bc92 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/CommonWalker.cs @@ -2,9 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Reflection; + using Antlr.Runtime.Tree; using Antlr.Runtime; +using AntlrCSharp; + using Twiglet.CS2J.Translator.Utils; using Twiglet.CS2J.Translator.TypeRep; using Twiglet.CS2J.Translator; @@ -119,6 +123,84 @@ namespace Twiglet.CS2J.Translator.Transform return (String.IsNullOrEmpty(ns) ? "" : ns + "."); } + // Routines to parse strings to ANTLR Trees on the fly, used to generate fragments needed by the transformation + public CommonTree parseString(string startRule, string inStr) + { + + if (Cfg.Verbosity > 5) Console.WriteLine("Parsing fragment "); + + ICharStream input = new ANTLRStringStream(inStr); + + PreProcessor lex = new PreProcessor(); + lex.AddDefine(Cfg.MacroDefines); + lex.CharStream = input; + lex.TraceDestination = Console.Error; + + CommonTokenStream tokens = new CommonTokenStream(lex); + + csParser p = new csParser(tokens); + p.TraceDestination = Console.Error; + p.IsJavaish = true; + + // Try and call a rule like CSParser.namespace_body() + // Use reflection to find the rule to use. + MethodInfo mi = p.GetType().GetMethod(startRule); + + if (mi == null) + { + throw new Exception("Could not find start rule " + startRule + " in csParser"); + } + + ParserRuleReturnScope csRet = (ParserRuleReturnScope) mi.Invoke(p, new object[0]); + + CommonTreeNodeStream csTreeStream = new CommonTreeNodeStream(csRet.Tree); + csTreeStream.TokenStream = tokens; + + JavaMaker javaMaker = new JavaMaker(csTreeStream); + javaMaker.TraceDestination = Console.Error; + javaMaker.Cfg = Cfg; + javaMaker.IsJavaish = true; + + // Try and call a rule like CSParser.namespace_body() + // Use reflection to find the rule to use. + mi = javaMaker.GetType().GetMethod(startRule); + + if (mi == null) + { + throw new Exception("Could not find start rule " + startRule + " in javaMaker"); + } + + TreeRuleReturnScope javaSyntaxRet = (TreeRuleReturnScope) mi.Invoke(javaMaker, new object[0]); + + CommonTree javaSyntaxAST = (CommonTree)javaSyntaxRet.Tree; + +// CommonTreeNodeStream javaSyntaxNodes = new CommonTreeNodeStream(javaSyntaxAST); +// +// javaSyntaxNodes.TokenStream = csTree.TokenStream; +// +// NetMaker netMaker = new NetMaker(javaSyntaxNodes); +// netMaker.TraceDestination = Console.Error; +// +// netMaker.Cfg = Cfg; +// netMaker.AppEnv = AppEnv; +// +// CommonTree javaAST = (CommonTree)netMaker.class_member_declarations().Tree; +// + return javaSyntaxAST; + } + + // If true, then we are parsing some JavaIsh fragment + private bool isJavaish = false; + public bool IsJavaish + { + get { + return isJavaish; + } + set { + isJavaish = value; + } + } + } // Wraps a compilation unit with its imports search path diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g index e1efbd5..dd601cf 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaMaker.g @@ -40,6 +40,7 @@ scope TypeContext { @header { using System; + using System.Text; using System.Globalization; } @@ -707,7 +708,17 @@ type_arguments returns [List tyargs] $tyargs = new List(); } : - t1=type { $tyargs.Add($t1.thetext); } (',' tn=type { $tyargs.Add($tn.thetext); })* ; + t1=type_argument { $tyargs.Add($t1.thetext); } (',' tn=type_argument { $tyargs.Add($tn.thetext); })* ; + +public type_argument returns [string thetext]: + {this.IsJavaish}?=> javaish_type_argument {$thetext = $javaish_type_argument.thetext; } + | type {$thetext = $type.thetext; } +; +public javaish_type_argument returns [string thetext]: + ('?' 'extends')=> '?' 'extends' type {$thetext = "? extends " + $type.thetext; } + | '?' {$thetext = "?"; } + | type {$thetext = $type.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 '*'*) @@ -1029,11 +1040,14 @@ method_declaration [CommonTree atts, CommonTree mods, List modList, Comm )? ')' ( type_parameter_constraints_clauses { isToString = false; isMain = false; })? + // Only have throw Exceptions if IsJavaish + throw_exceptions? + 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] + mainApply=magicMainApply[isMain, $member_name.tree.Token, (isMain ? $TypeContext::typeName : null), $argParam.tree] mainCall=magicMainExit[isMain, isInt, $member_name.tree.Token, $mainApply.tree] mainMethod=magicMainWrapper[isMain, $member_name.tree.Token, $mainCall.tree] @@ -1041,7 +1055,7 @@ method_declaration [CommonTree atts, CommonTree mods, List modList, Comm { if (isToString) { $member_name.tree.Token.Text = "toString"; } - exceptions = $b.exceptionList; + exceptions = IsJavaish ? $throw_exceptions.tree : $b.exceptionList; } -> $mainMethod? ^(METHOD { dupTree($atts) } { dupTree($mods) } { dupTree($type) } @@ -1053,8 +1067,16 @@ method_body [bool smotherExceptions] returns [CommonTree exceptionList]: | 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; } + +throw_exceptions: + {IsJavaish}?=> 'throws'! t1=identifier { $t1.tree.Type = EXCEPTION; } (','! tn=identifier { $tn.tree.Type = EXCEPTION; } )* + ; +member_name returns [string rawId, string full_name] +@init { + $full_name = ""; +}: + (type_or_generic '.' {$full_name += mkTypeOrGenericString($type_or_generic.type, $type_or_generic.generic_arguments) + ".";})* + i=identifier { $rawId = $i.text; $full_name += $i.text; } // keving [interface_type.identifier] | type_name '.' identifier ; diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g index 0769cc9..9e5b28b 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/JavaPrettyPrint.g @@ -670,8 +670,13 @@ qid_part: generic_argument_list: '<' type_arguments '>' -> generic_args(args={ $type_arguments.st }); type_arguments: - ts+=type (',' ts+=type)* -> commalist(items = { $ts }); + ts+=type_argument (',' ts+=type_argument)* -> commalist(items = { $ts }); +public type_argument: + ('?' 'extends')=> '?' 'extends' type -> op(pre={"?"},op={" extends "},post={$type.st}) + | '?' -> string(payload={"?"}) + | type -> { $type.st } +; type @init { StringTemplate nm = null; @@ -1349,16 +1354,19 @@ predefined_type: | t='short' | t='string' | t='uint' | t='ulong' | t='ushort') { collectComments($t.TokenStartIndex); } -> string(payload={$t.text}); identifier: - i=IDENTIFIER { collectComments($i.TokenStartIndex); } -> string(payload= { $IDENTIFIER.text }) | also_keyword -> string(payload= { $also_keyword.text }); + i=IDENTIFIER { collectComments($i.TokenStartIndex); } -> string(payload= { $IDENTIFIER.text }) | also_keyword -> { $also_keyword.st }; 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'; + ( + t='add' | t='alias' | t='assembly' | t='module' | t='field' | t='method' | t='param' | t='property' | t='type' | t='yield' + | t='from' | t='into' | t='join' | t='on' | t='where' | t='orderby' | t='group' | t='by' | t='ascending' | t='descending' + | t='equals' | t='select' | t='pragma' | t='let' | t='remove' | t='get' | t='set' | t='var' | t='__arglist' | t='dynamic' | t='elif' + | t='endif' | t='define' | t='undef' + ) -> string(payload={$t.text}) +; literal: Real_literal -> string(payload={$Real_literal.text}) diff --git a/CSharpTranslator/antlr3/src/CSharpParser/cs.g b/CSharpTranslator/antlr3/src/CSharpParser/cs.g index e09432e..e5e43c4 100644 --- a/CSharpTranslator/antlr3/src/CSharpParser/cs.g +++ b/CSharpTranslator/antlr3/src/CSharpParser/cs.g @@ -15,7 +15,6 @@ tokens { INTERFACE; FINAL; /* final modifier */ ANNOTATION; - IN; OUT; CONST; EVENT; @@ -66,6 +65,9 @@ tokens { ELSE = 'else'; BREAK = 'break'; OBJECT = 'object'; + THIS = 'this'; + FOREACH = 'foreach'; + IN = 'in'; OPEN_BRACKET='['; CLOSE_BRACKET=']'; @@ -176,6 +178,19 @@ tokens { { return false; } + + // We have a fragments library for strings that we want to splice in to the generated code. + // This is Java, so to parse it we need to set IsJavaish so that we are a bit more lenient ... + private bool isJavaish = false; + public bool IsJavaish + { + get { + return isJavaish; + } + set { + isJavaish = value; + } + } } public compilation_unit: @@ -485,8 +500,16 @@ public qid_part: public generic_argument_list: '<' type_arguments '>' ; public type_arguments: - type (',' type)* ; - + type_argument (',' type_argument)* ; +public type_argument: + {this.IsJavaish}?=> javaish_type_argument + | type +; +public javaish_type_argument: + ('?' 'extends')=> '?' 'extends' type + | '?' + | type +; public type: ((predefined_type | type_name) rank_specifiers) => (predefined_type | type_name) rank_specifiers '*'* | ((predefined_type | type_name) ('*'+ | '?')) => (predefined_type | type_name) ('*'+ | '?') @@ -1127,7 +1150,10 @@ public for_iterator: public statement_expression_list: statement_expression (',' statement_expression)* ; public foreach_statement: - 'foreach' '(' local_variable_type identifier 'in' expression ')' embedded_statement ; + 'foreach' '(' local_variable_type identifier 'in' expression ')' embedded_statement + | {this.IsJavaish}? f='for' '(' local_variable_type identifier i=':' expression ')' embedded_statement + -> FOREACH[$f,"foreach"] '(' local_variable_type identifier IN[$i,"in"] expression ')' embedded_statement +; public jump_statement: break_statement | continue_statement