From d435c7666e4571a11c8488450cde89ce812afee0 Mon Sep 17 00:00:00 2001 From: Adriano Carlos Verona Date: Fri, 18 Aug 2017 15:53:34 -0300 Subject: [PATCH] [scripting] support for preserving as many comments as possible --- UnityScript2CSharp.Tests/Tests.cs | 7 +- UnityScript2CSharp/CommandLineArguments.cs | 4 + UnityScript2CSharp/OrphanCommentVisitor.cs | 556 ++++++++++++++++++ UnityScript2CSharp/Program.cs | 2 +- UnityScript2CSharp/Steps/AttachComments.cs | 88 +-- UnityScript2CSharp/UnityScript2CSharp.csproj | 1 + .../UnityScript2CSharpConverter.cs | 38 +- .../UnityScript2CSharpConverterVisitor.cs | 219 ++++++- UnityScript2CSharp/Writer.cs | 10 +- 9 files changed, 836 insertions(+), 89 deletions(-) create mode 100644 UnityScript2CSharp/OrphanCommentVisitor.cs diff --git a/UnityScript2CSharp.Tests/Tests.cs b/UnityScript2CSharp.Tests/Tests.cs index 80253c0..cef9d68 100644 --- a/UnityScript2CSharp.Tests/Tests.cs +++ b/UnityScript2CSharp.Tests/Tests.cs @@ -719,13 +719,16 @@ namespace UnityScript2CSharp.Tests yield return new TestCaseData($"{comment}\r\nfunction F() {{ }}", $"{comment}\r\npublic virtual void F() {{ }}").SetName("Above SingleLine"); // Multiline - comment = "/* C1 */ "; + comment = "/* C1 */"; yield return new TestCaseData($"function F() {{ return 42; {comment}\r\n}}", $"public virtual int F() {{ return 42 {comment}; }}").SetName("Right MultiLine"); - yield return new TestCaseData($"{comment}\nfunction F() {{ }}", $"{comment}\npublic virtual void F() {{ }}").SetName("Above MultiLine"); + yield return new TestCaseData($"{comment}\r\nfunction F() {{ }}", $"{comment}\r\npublic virtual void F() {{ }}").SetName("Above MultiLine"); yield return new TestCaseData($"function F() {{ var x = 42;\r\n{comment}\r\nreturn x; }}", $"public virtual int F() {{ int x = 42;\r\n{comment}\r\nreturn x; }}").SetName("Below MultiLine"); yield return new TestCaseData($"function F() : int {{ {comment} return 42; }}", $"public virtual int F() {{ {comment} return 42; }}").SetName("Left MultiLine"); yield return new TestCaseData("function F() : int { return 42 /*1*/; /*2*/ }", "public virtual int F() { return 42 /*1*/ /*2*/; }").SetName("Multiple multiLine comments"); + + yield return new TestCaseData("function F(/*B*/ i:int /*A*/ ) { }", "public virtual void F(/*B*/int /*A*/ i) { }").SetName("In Parameters"); + yield return new TestCaseData("// 1\r\nprivate var i:int;\r\nfunction F() { }", "// 1\r\nprivate int i;\r\npublic virtual void F() { }").SetName("Above Fields"); } private static IEnumerable CSharpNonClashingKeywords() diff --git a/UnityScript2CSharp/CommandLineArguments.cs b/UnityScript2CSharp/CommandLineArguments.cs index 9adb88f..c0a8d9e 100644 --- a/UnityScript2CSharp/CommandLineArguments.cs +++ b/UnityScript2CSharp/CommandLineArguments.cs @@ -30,6 +30,10 @@ namespace UnityScript2CSharp [Option('i', HelpText = "Ignore compilation errors. This allows the conversion process to continue instead of aborting.")] public bool IgnoreErrors { get; set; } + [Option(HelpText = "Do not try to preserve comments (Use this option if processing comments cause any issues).", DefaultValue = false)] public bool SkipComments { get; set; } + + [Option(HelpText = "Show a list of comments that were not written to the converted sources (used to help identifying issues with the comment processing code).")] public bool ShowOrphanComments { get; set; } + [Option('n', "dry-run", HelpText = "Run the conversion but do not change/create any files on disk.")] public bool DryRun { get; set; } [Option('v', "verbose", HelpText = "Show verbose messages.")] public bool Verbose { get; set; } diff --git a/UnityScript2CSharp/OrphanCommentVisitor.cs b/UnityScript2CSharp/OrphanCommentVisitor.cs new file mode 100644 index 0000000..c185b75 --- /dev/null +++ b/UnityScript2CSharp/OrphanCommentVisitor.cs @@ -0,0 +1,556 @@ +using System; +using System.Collections.Generic; +using Boo.Lang.Compiler.Ast; +using Attribute = Boo.Lang.Compiler.Ast.Attribute; + +namespace UnityScript2CSharp +{ + internal class OrphanCommentVisitor : DepthFirstVisitor + { + private void Check(Node node) + { + if (node.ContainsAnnotation("COMMENTS")) + { + var comments = (IList) node["COMMENTS"]; + foreach (var comment in comments) + { + Console.WriteLine($"[ORPHAN COMMENT | {node.NodeType}, {comment.AnchorKind}] {node.LexicalInfo} : {comment.Token.getText()}\r\n\t{node}"); + } + } + } + + public override void OnCompileUnit(CompileUnit node) + { + base.OnCompileUnit(node); + Check(node); + } + + public override void OnTypeMemberStatement(TypeMemberStatement node) + { + base.OnTypeMemberStatement(node); + Check(node); + } + + public override void OnExplicitMemberInfo(ExplicitMemberInfo node) + { + base.OnExplicitMemberInfo(node); + Check(node); + } + + public override void OnSimpleTypeReference(SimpleTypeReference node) + { + base.OnSimpleTypeReference(node); + Check(node); + } + + public override void OnArrayTypeReference(ArrayTypeReference node) + { + base.OnArrayTypeReference(node); + Check(node); + } + + public override void OnCallableTypeReference(CallableTypeReference node) + { + base.OnCallableTypeReference(node); + Check(node); + } + + public override void OnGenericTypeReference(GenericTypeReference node) + { + base.OnGenericTypeReference(node); + Check(node); + } + + public override void OnGenericTypeDefinitionReference(GenericTypeDefinitionReference node) + { + base.OnGenericTypeDefinitionReference(node); + Check(node); + } + + public override void OnCallableDefinition(CallableDefinition node) + { + base.OnCallableDefinition(node); + Check(node); + } + + public override void OnNamespaceDeclaration(NamespaceDeclaration node) + { + base.OnNamespaceDeclaration(node); + Check(node); + } + + public override void OnImport(Import node) + { + base.OnImport(node); + Check(node); + } + + public override void OnModule(Module node) + { + base.OnModule(node); + Check(node); + } + + public override void OnClassDefinition(ClassDefinition node) + { + base.OnClassDefinition(node); + Check(node); + } + + public override void OnStructDefinition(StructDefinition node) + { + base.OnStructDefinition(node); + Check(node); + } + + public override void OnInterfaceDefinition(InterfaceDefinition node) + { + base.OnInterfaceDefinition(node); + Check(node); + } + + public override void OnEnumDefinition(EnumDefinition node) + { + base.OnEnumDefinition(node); + Check(node); + } + + public override void OnEnumMember(EnumMember node) + { + base.OnEnumMember(node); + Check(node); + } + + public override void OnField(Field node) + { + base.OnField(node); + Check(node); + } + + public override void OnProperty(Property node) + { + base.OnProperty(node); + Check(node); + } + + public override void OnEvent(Event node) + { + base.OnEvent(node); + Check(node); + } + + public override void OnLocal(Local node) + { + base.OnLocal(node); + Check(node); + } + + public override void OnBlockExpression(BlockExpression node) + { + base.OnBlockExpression(node); + Check(node); + } + + public override void OnMethod(Method node) + { + base.OnMethod(node); + Check(node); + } + + public override void OnConstructor(Constructor node) + { + base.OnConstructor(node); + Check(node); + } + + public override void OnDestructor(Destructor node) + { + base.OnDestructor(node); + Check(node); + } + + public override void OnParameterDeclaration(ParameterDeclaration node) + { + base.OnParameterDeclaration(node); + Check(node); + } + + public override void OnGenericParameterDeclaration(GenericParameterDeclaration node) + { + base.OnGenericParameterDeclaration(node); + Check(node); + } + + public override void OnDeclaration(Declaration node) + { + base.OnDeclaration(node); + Check(node); + } + + public override void OnAttribute(Attribute node) + { + base.OnAttribute(node); + Check(node); + } + + public override void OnStatementModifier(StatementModifier node) + { + base.OnStatementModifier(node); + Check(node); + } + + public override void OnGotoStatement(GotoStatement node) + { + base.OnGotoStatement(node); + Check(node); + } + + public override void OnLabelStatement(LabelStatement node) + { + base.OnLabelStatement(node); + Check(node); + } + + public override void OnBlock(Block node) + { + base.OnBlock(node); + Check(node); + } + + public override void OnDeclarationStatement(DeclarationStatement node) + { + base.OnDeclarationStatement(node); + Check(node); + } + + public override void OnMacroStatement(MacroStatement node) + { + base.OnMacroStatement(node); + Check(node); + } + + public override void OnTryStatement(TryStatement node) + { + base.OnTryStatement(node); + Check(node); + } + + public override void OnExceptionHandler(ExceptionHandler node) + { + base.OnExceptionHandler(node); + Check(node); + } + + public override void OnIfStatement(IfStatement node) + { + base.OnIfStatement(node); + Check(node); + } + + public override void OnUnlessStatement(UnlessStatement node) + { + base.OnUnlessStatement(node); + Check(node); + } + + public override void OnForStatement(ForStatement node) + { + base.OnForStatement(node); + Check(node); + } + + public override void OnWhileStatement(WhileStatement node) + { + base.OnWhileStatement(node); + Check(node); + } + + public override void OnBreakStatement(BreakStatement node) + { + base.OnBreakStatement(node); + Check(node); + } + + public override void OnContinueStatement(ContinueStatement node) + { + base.OnContinueStatement(node); + Check(node); + } + + public override void OnReturnStatement(ReturnStatement node) + { + base.OnReturnStatement(node); + Check(node); + } + + public override void OnYieldStatement(YieldStatement node) + { + base.OnYieldStatement(node); + Check(node); + } + + public override void OnRaiseStatement(RaiseStatement node) + { + base.OnRaiseStatement(node); + Check(node); + } + + public override void OnUnpackStatement(UnpackStatement node) + { + base.OnUnpackStatement(node); + Check(node); + } + + public override void OnExpressionStatement(ExpressionStatement node) + { + base.OnExpressionStatement(node); + Check(node); + } + + public override void OnOmittedExpression(OmittedExpression node) + { + base.OnOmittedExpression(node); + Check(node); + } + + public override void OnExpressionPair(ExpressionPair node) + { + base.OnExpressionPair(node); + Check(node); + } + + public override void OnMethodInvocationExpression(MethodInvocationExpression node) + { + base.OnMethodInvocationExpression(node); + Check(node); + } + + public override void OnUnaryExpression(UnaryExpression node) + { + base.OnUnaryExpression(node); + Check(node); + } + + public override void OnBinaryExpression(BinaryExpression node) + { + base.OnBinaryExpression(node); + Check(node); + } + + public override void OnConditionalExpression(ConditionalExpression node) + { + base.OnConditionalExpression(node); + Check(node); + } + + public override void OnReferenceExpression(ReferenceExpression node) + { + base.OnReferenceExpression(node); + Check(node); + } + + public override void OnMemberReferenceExpression(MemberReferenceExpression node) + { + base.OnMemberReferenceExpression(node); + Check(node); + } + + public override void OnGenericReferenceExpression(GenericReferenceExpression node) + { + base.OnGenericReferenceExpression(node); + Check(node); + } + + public override void OnQuasiquoteExpression(QuasiquoteExpression node) + { + base.OnQuasiquoteExpression(node); + Check(node); + } + + public override void OnStringLiteralExpression(StringLiteralExpression node) + { + base.OnStringLiteralExpression(node); + Check(node); + } + + public override void OnCharLiteralExpression(CharLiteralExpression node) + { + base.OnCharLiteralExpression(node); + Check(node); + } + + public override void OnTimeSpanLiteralExpression(TimeSpanLiteralExpression node) + { + base.OnTimeSpanLiteralExpression(node); + Check(node); + } + + public override void OnIntegerLiteralExpression(IntegerLiteralExpression node) + { + base.OnIntegerLiteralExpression(node); + Check(node); + } + + public override void OnDoubleLiteralExpression(DoubleLiteralExpression node) + { + base.OnDoubleLiteralExpression(node); + Check(node); + } + + public override void OnNullLiteralExpression(NullLiteralExpression node) + { + base.OnNullLiteralExpression(node); + Check(node); + } + + public override void OnSelfLiteralExpression(SelfLiteralExpression node) + { + base.OnSelfLiteralExpression(node); + Check(node); + } + + public override void OnSuperLiteralExpression(SuperLiteralExpression node) + { + base.OnSuperLiteralExpression(node); + Check(node); + } + + public override void OnBoolLiteralExpression(BoolLiteralExpression node) + { + base.OnBoolLiteralExpression(node); + Check(node); + } + + public override void OnRELiteralExpression(RELiteralExpression node) + { + base.OnRELiteralExpression(node); + Check(node); + } + + public override void OnSpliceExpression(SpliceExpression node) + { + base.OnSpliceExpression(node); + Check(node); + } + + public override void OnSpliceTypeReference(SpliceTypeReference node) + { + base.OnSpliceTypeReference(node); + Check(node); + } + + public override void OnSpliceMemberReferenceExpression(SpliceMemberReferenceExpression node) + { + base.OnSpliceMemberReferenceExpression(node); + Check(node); + } + + public override void OnSpliceTypeMember(SpliceTypeMember node) + { + base.OnSpliceTypeMember(node); + Check(node); + } + + public override void OnSpliceTypeDefinitionBody(SpliceTypeDefinitionBody node) + { + base.OnSpliceTypeDefinitionBody(node); + Check(node); + } + + public override void OnSpliceParameterDeclaration(SpliceParameterDeclaration node) + { + base.OnSpliceParameterDeclaration(node); + Check(node); + } + + public override void OnExpressionInterpolationExpression(ExpressionInterpolationExpression node) + { + base.OnExpressionInterpolationExpression(node); + Check(node); + } + + public override void OnHashLiteralExpression(HashLiteralExpression node) + { + base.OnHashLiteralExpression(node); + Check(node); + } + + public override void OnListLiteralExpression(ListLiteralExpression node) + { + base.OnListLiteralExpression(node); + Check(node); + } + + public override void OnCollectionInitializationExpression(CollectionInitializationExpression node) + { + base.OnCollectionInitializationExpression(node); + Check(node); + } + + public override void OnArrayLiteralExpression(ArrayLiteralExpression node) + { + base.OnArrayLiteralExpression(node); + Check(node); + } + + public override void OnGeneratorExpression(GeneratorExpression node) + { + base.OnGeneratorExpression(node); + Check(node); + } + + public override void OnExtendedGeneratorExpression(ExtendedGeneratorExpression node) + { + base.OnExtendedGeneratorExpression(node); + Check(node); + } + + public override void OnSlice(Slice node) + { + base.OnSlice(node); + Check(node); + } + + public override void OnSlicingExpression(SlicingExpression node) + { + base.OnSlicingExpression(node); + Check(node); + } + + public override void OnTryCastExpression(TryCastExpression node) + { + base.OnTryCastExpression(node); + Check(node); + } + + public override void OnCastExpression(CastExpression node) + { + base.OnCastExpression(node); + Check(node); + } + + public override void OnTypeofExpression(TypeofExpression node) + { + base.OnTypeofExpression(node); + Check(node); + } + + public override void OnCustomStatement(CustomStatement node) + { + base.OnCustomStatement(node); + Check(node); + } + + public override void OnCustomExpression(CustomExpression node) + { + base.OnCustomExpression(node); + Check(node); + } + + public override void OnStatementTypeMember(StatementTypeMember node) + { + base.OnStatementTypeMember(node); + Check(node); + } + } +} \ No newline at end of file diff --git a/UnityScript2CSharp/Program.cs b/UnityScript2CSharp/Program.cs index 7854315..e7cc3ac 100644 --- a/UnityScript2CSharp/Program.cs +++ b/UnityScript2CSharp/Program.cs @@ -61,7 +61,7 @@ namespace UnityScript2CSharp DumpScripts("Plugin/Editor", pluginEditorScritps); } - var converter = new UnityScript2CSharpConverter(options.Value.IgnoreErrors); + var converter = new UnityScript2CSharpConverter(options.Value.IgnoreErrors, options.Value.SkipComments, options.Value.ShowOrphanComments); var references = AssemblyReferencesFrom(options); diff --git a/UnityScript2CSharp/Steps/AttachComments.cs b/UnityScript2CSharp/Steps/AttachComments.cs index fac27df..da94bad 100644 --- a/UnityScript2CSharp/Steps/AttachComments.cs +++ b/UnityScript2CSharp/Steps/AttachComments.cs @@ -24,25 +24,12 @@ namespace UnityScript2CSharp.Steps base.OnModule(node); - foreach (var comment in _sourceComments.ToArray()) - { - var attachedComments = GetAttachedCommentsFrom(comment.BestCandidate); - attachedComments.Add(comment); - - _sourceComments.Remove(comment); - } + AttachRemainingComments(node); } public override void LeaveMethod(Method node) { - foreach (var comment in _sourceComments.ToArray()) - { - var attachedComments = GetAttachedCommentsFrom(comment.BestCandidate); - attachedComments.Add(comment); - - _sourceComments.Remove(comment); - } - + AttachRemainingComments(node); base.LeaveMethod(node); } @@ -58,15 +45,22 @@ namespace UnityScript2CSharp.Steps var commentsAboveNode = _sourceComments.Where(candidate => candidate.Token.getLine() < node.LexicalInfo.Line).ToArray(); foreach (var comment in commentsAboveNode) { - comment.BestCandidate = comment.BestCandidate ?? node; - comment.AnchorKind = AnchorKind.Above; - + if (comment.BestCandidate == null) + { + comment.BestCandidate = node; + comment.AnchorKind = AnchorKind.Above; + } + var attachedComments = GetAttachedCommentsFrom(comment.BestCandidate); attachedComments.Add(comment); _sourceComments.Remove(comment); } + + if (node.Entity != null && node.Entity.FullName == "Boo.Lang.Builtins.array") + return; + // Handle comments in the same line var foundOnSameLine = _sourceComments.Where(candidate => candidate.Token.getLine() == node.LexicalInfo.Line); foreach (var comment in foundOnSameLine) @@ -75,55 +69,79 @@ namespace UnityScript2CSharp.Steps { comment.BestCandidate = node; comment.Distance = Int32.MaxValue; - continue; } + int distance = 0; if (node.LexicalInfo.Column > comment.Token.getColumn()) // comment is on left of the AST node { var endOfCommentCollumn = comment.Token.getColumn() + comment.Token.getText().Length; - var distance = node.LexicalInfo.Column - endOfCommentCollumn; - if (distance <= comment.Distance) + distance = node.LexicalInfo.Column - endOfCommentCollumn; + if (distance <= comment.Distance && distance >= 0) { comment.BestCandidate = node; comment.Distance = distance; comment.AnchorKind = AnchorKind.Left; } } - else + + // comment sould be on RIGHT of the AST node + var endOfNodeColumn = EndColumnOf(node); + distance = comment.Token.getColumn() - endOfNodeColumn; + if (distance <= comment.Distance && (distance >= 0 || comment.CommentKind == CommentKind.SingleLine)) { - // comment is on RIGHT of the AST node - var endOfNodeColumn = node.LexicalInfo.Column + TokenLengthFor(node); - var distance = comment.Token.getColumn() - endOfNodeColumn; - if (distance <= comment.Distance) - { - comment.BestCandidate= node; - comment.Distance = distance; - comment.AnchorKind = AnchorKind.Right; - } + comment.BestCandidate = node; + comment.Distance = distance; + comment.AnchorKind = AnchorKind.Right; } } base.OnNode(node); } + private void AttachRemainingComments(Node node) + { + if (!node.EndSourceLocation.IsValid) + return; + + foreach (var comment in _sourceComments.Where(comment => comment.Token.getLine() <= node.EndSourceLocation.Line).ToArray()) + { + if (comment.BestCandidate == null) + { + comment.BestCandidate = node; + comment.AnchorKind = AnchorKind.Below; + } + + var attachedComments = GetAttachedCommentsFrom(comment.BestCandidate); + attachedComments.Add(comment); + _sourceComments.Remove(comment); + } + } + private IList GetAttachedCommentsFrom(Node node) { if (!node.ContainsAnnotation(COMMENTS_KEY)) { - node.Annotate(COMMENTS_KEY, new List()); + node.Annotate(COMMENTS_KEY, new System.Collections.Generic.List()); } return (IList) node[COMMENTS_KEY]; } - private int TokenLengthFor(Node node) + // This method returns an aproximation for the *end column* of the passed node. + private int EndColumnOf(Node node) { switch (node.NodeType) { - case NodeType.ReturnStatement: return "return".Length; + case NodeType.BinaryExpression: return ((BinaryExpression) node).Left.LexicalInfo.Column + node.ToString().Length; + case NodeType.ReturnStatement: return node.LexicalInfo.Column + "return".Length; + case NodeType.IfStatement: + { + var condition = ((IfStatement)node).Condition; + return condition.LexicalInfo.Column + condition.ToString().Length + 1; // consider the ')' after the condition + } } - return node.ToString().Length; + return node.LexicalInfo.Column + node.ToString().Length; } } } \ No newline at end of file diff --git a/UnityScript2CSharp/UnityScript2CSharp.csproj b/UnityScript2CSharp/UnityScript2CSharp.csproj index d9603f3..aeb4257 100644 --- a/UnityScript2CSharp/UnityScript2CSharp.csproj +++ b/UnityScript2CSharp/UnityScript2CSharp.csproj @@ -110,6 +110,7 @@ + diff --git a/UnityScript2CSharp/UnityScript2CSharpConverter.cs b/UnityScript2CSharp/UnityScript2CSharpConverter.cs index d7d055c..2eb475d 100644 --- a/UnityScript2CSharp/UnityScript2CSharpConverter.cs +++ b/UnityScript2CSharp/UnityScript2CSharpConverter.cs @@ -20,15 +20,20 @@ namespace UnityScript2CSharp class UnityScript2CSharpConverter { private readonly bool _ignoreErrors; + private readonly bool _skipComments; + private readonly bool _checkOrphanComments; - public UnityScript2CSharpConverter(bool ignoreErrors = false) + public UnityScript2CSharpConverter(bool ignoreErrors = false, bool skipComments = false, bool checkOrphanComments = true) { _ignoreErrors = ignoreErrors; + _skipComments = skipComments; + _checkOrphanComments = checkOrphanComments; } public void Convert(IEnumerable inputs, IEnumerable definedSymbols, IEnumerable referencedAssemblies, Action onScriptConverted) { - var comp = CreatAndInitializeCompiler(inputs, definedSymbols, referencedAssemblies); + var comments = CollectCommentsFrom(inputs, definedSymbols); + var comp = CreatAndInitializeCompiler(inputs, definedSymbols, referencedAssemblies, comments); var result = comp.Run(); HandleCompilationResult(result); @@ -36,14 +41,20 @@ namespace UnityScript2CSharp var visitor = new UnityScript2CSharpConverterVisitor(); visitor.ScriptConverted += onScriptConverted; result.CompileUnit.Accept(visitor); + + if (_checkOrphanComments) + result.CompileUnit.Accept(new OrphanCommentVisitor()); } private IDictionary> CollectCommentsFrom(IEnumerable inputs, IEnumerable definedSymbols) { var comments = new Dictionary>(); - foreach (var source in inputs) + if (!_skipComments) { - comments[source.FileName] = CollectCommentsFor(source, definedSymbols); + foreach (var source in inputs) + { + comments[source.FileName] = CollectCommentsFor(source, definedSymbols); + } } return comments; @@ -68,18 +79,10 @@ namespace UnityScript2CSharp IToken last = null; while (token != null && token.Type != UnityScriptLexer.EOF) { - IToken next = null; try { if (token.Type == UnityScriptLexer.SL_COMMENT || token.Type == UnityScriptLexer.ML_COMMENT) - { - next = lexer.nextToken(); - if (next.getLine() > token.getLine()) - { - token.setText(token.getText() + Environment.NewLine); - } comments.Add(new Comment(token, token.Type == UnityScriptLexer.SL_COMMENT ? CommentKind.SingleLine : CommentKind.MultipleLine, last)); - } else last = token; } @@ -91,7 +94,7 @@ namespace UnityScript2CSharp { try { - token = next ?? lexer.nextToken(); + token = lexer.nextToken(); } catch (TokenStreamRecognitionException tre) { @@ -129,12 +132,11 @@ namespace UnityScript2CSharp CompilerWarnings = CompilerWarnings ?? new List(); } - internal UnityScriptCompiler CreatAndInitializeCompiler(IEnumerable inputs, IEnumerable definedSymbols, IEnumerable referencedAssemblies) + internal UnityScriptCompiler CreatAndInitializeCompiler(IEnumerable inputs, IEnumerable definedSymbols, IEnumerable referencedAssemblies, IDictionary> comments) { _compiler = new UnityScriptCompiler(); _compiler.Parameters.TabSize = 4; - var comments = CollectCommentsFrom(inputs, definedSymbols); - + SetupCompilerParameters(definedSymbols, referencedAssemblies); SetupCompilerPipeline(comments); foreach (var input in inputs) @@ -226,7 +228,9 @@ namespace UnityScript2CSharp adjustedPipeline.Add(new PromoteImplicitBooleanConversionsToExplicitComparisons()); adjustedPipeline.Add(new InstanceToTypeReferencedStaticMemberReference()); adjustedPipeline.Add(new TransforwmKnownUnityEngineMethods()); - adjustedPipeline.Add(new AttachComments(comments)); + + if (!_skipComments) + adjustedPipeline.Add(new AttachComments(comments)); _compiler.Parameters.Pipeline = adjustedPipeline; } diff --git a/UnityScript2CSharp/UnityScript2CSharpConverterVisitor.cs b/UnityScript2CSharp/UnityScript2CSharpConverterVisitor.cs index ab6d028..3a26736 100644 --- a/UnityScript2CSharp/UnityScript2CSharpConverterVisitor.cs +++ b/UnityScript2CSharp/UnityScript2CSharpConverterVisitor.cs @@ -44,12 +44,12 @@ namespace UnityScript2CSharp public override void OnSimpleTypeReference(SimpleTypeReference node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Left); var typeName = TypeNameFor(node.Entity); _writer.Write(typeName ?? node.Name); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } private void _builderAppendIdented(string str) @@ -75,12 +75,20 @@ namespace UnityScript2CSharp public override void OnArrayTypeReference(ArrayTypeReference node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + node.ElementType.Accept(this); _writer.Write($"[{new String(',', (int) (node.Rank.Value -1))}]"); + + WriteComments(node, AnchorKind.Right); } public override void OnCallableTypeReference(CallableTypeReference node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + var types = new List(node.Parameters.Select(p => p.Type)); if (node.ReturnType == null) { @@ -98,6 +106,7 @@ namespace UnityScript2CSharp WriteCommaSeparatedList(types); _writer.Write(">"); } + WriteComments(node, AnchorKind.Right); } public override void OnGenericTypeReference(GenericTypeReference node) @@ -147,6 +156,9 @@ namespace UnityScript2CSharp if (IsSyntheticDelegateUsedByCallable(node)) return; + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.WriteLine("[System.Serializable]"); // Every class in UnityScript is serializable WriteAttributes(node.Attributes); @@ -159,6 +171,8 @@ namespace UnityScript2CSharp _writer.WriteLine("{"); WriteMembersOf(node); _writer.WriteLine("}"); + + WriteComments(node, AnchorKind.Right); } public override void OnStructDefinition(StructDefinition node) @@ -186,8 +200,15 @@ namespace UnityScript2CSharp public override void OnEnumDefinition(EnumDefinition node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.IndentNextWrite = true; - _writer.WriteLine($"{ModifiersToString(node.Modifiers)} enum {node.Name}"); + _writer.Write($"{ModifiersToString(node.Modifiers)} enum {node.Name}"); + + WriteComments(node, AnchorKind.Right); + + _writer.WriteLine(); _writer.WriteLine("{"); using (new BlockIdentation(_writer)) { @@ -203,16 +224,24 @@ namespace UnityScript2CSharp public override void OnEnumMember(EnumMember node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.Write(node.Name); if (node.Initializer != null) { _writer.Write(" = "); node.Initializer.Accept(this); } + + WriteComments(node, AnchorKind.Right); } public override void OnField(Field node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + WriteAttributes(node.Attributes); _builderAppend(ModifiersToString(node.Modifiers)); @@ -226,6 +255,8 @@ namespace UnityScript2CSharp _builderAppend(" = "); } + WriteComments(node, AnchorKind.Right); + _writer.WriteLine(";"); } @@ -256,10 +287,14 @@ namespace UnityScript2CSharp public override void OnMethod(Method node) { - HandleComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); if (node.Name == "Main" || node.IsSynthetic) + { + WriteComments(node, AnchorKind.Below); return; + } WriteAttributes(node.Attributes); @@ -273,10 +308,14 @@ namespace UnityScript2CSharp _builderAppend(node.Name); WriteParameterList(node.Parameters); + WriteComments(node, AnchorKind.Right); + if (isInterface) _writer.WriteLine(";"); else node.Body.Accept(this); + + WriteComments(node, AnchorKind.Below); } public override bool EnterBlock(Block node) @@ -311,6 +350,9 @@ namespace UnityScript2CSharp public override void OnConstructor(Constructor node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + var stmts = CtorStatementsWithoutParameterlessSuperInvocation(node); if (stmts.Count == 0) return; @@ -325,6 +367,8 @@ namespace UnityScript2CSharp WriteCtorChainningFor(node); + WriteComments(node, AnchorKind.Right); + node.Body.Accept(this); } @@ -336,9 +380,14 @@ namespace UnityScript2CSharp public override void OnParameterDeclaration(ParameterDeclaration node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + node.Type.Accept(this); _builderAppend(' '); _builderAppend(node.Name); + + WriteComments(node, AnchorKind.Right); } public override void OnGenericParameterDeclaration(GenericParameterDeclaration node) @@ -373,6 +422,9 @@ namespace UnityScript2CSharp public override void OnAttribute(Attribute node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + if (node.Name == "System.SerializableAttribute") return; @@ -397,6 +449,8 @@ namespace UnityScript2CSharp _writer.Write(")"); _writer.WriteLine("]"); + + WriteComments(node, AnchorKind.Right); } public override void OnStatementModifier(StatementModifier node) @@ -462,10 +516,15 @@ namespace UnityScript2CSharp public override void OnIfStatement(IfStatement node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _builderAppendIdented("if ("); node.Condition.Accept(this); _builderAppend(")"); + WriteComments(node, AnchorKind.Right); + node.TrueBlock.Accept(this); if (node.FalseBlock != null) { @@ -482,6 +541,9 @@ namespace UnityScript2CSharp public override void OnForStatement(ForStatement node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.Write("foreach ("); VisitPossibleClashingDeclaration(node.Declarations[0]); @@ -489,42 +551,56 @@ namespace UnityScript2CSharp node.Iterator.Accept(this); _writer.Write(")"); + WriteComments(node, AnchorKind.Right); + node.Block.Accept(this); } public override void OnWhileStatement(WhileStatement node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _builderAppendIdented("while ("); node.Condition.Accept(this); _builderAppend(")"); + + WriteComments(node, AnchorKind.Right); + node.Block.Accept(this); } public override void OnBreakStatement(BreakStatement node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.WriteLine("break;"); - HandleComments(node, AnchorKind.Right); + + WriteComments(node, AnchorKind.Right); } public override void OnContinueStatement(ContinueStatement node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.WriteLine("continue;"); - HandleComments(node, AnchorKind.Right); + + WriteComments(node, AnchorKind.Right); } public override void OnReturnStatement(ReturnStatement node) { - HandleComments(node, AnchorKind.Above); - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); if (TryHandleYieldBreak(node)) return; _builderAppendIdented("return"); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); if (node.Expression != null) { @@ -537,6 +613,9 @@ namespace UnityScript2CSharp public override void OnYieldStatement(YieldStatement node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.Write("yield return "); if (node.Expression != null) node.Expression.Accept(this); @@ -544,6 +623,8 @@ namespace UnityScript2CSharp _writer.Write("null"); _writer.WriteLine(";"); + + WriteComments(node, AnchorKind.Right); } public override void OnRaiseStatement(RaiseStatement node) @@ -560,11 +641,15 @@ namespace UnityScript2CSharp public override void OnExpressionStatement(ExpressionStatement node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + node.Expression.Accept(this); if (!_lastIgnored) _writer.WriteLine(";"); _lastIgnored = false; + WriteComments(node, AnchorKind.Right); } public override void OnOmittedExpression(OmittedExpression node) @@ -575,14 +660,22 @@ namespace UnityScript2CSharp public override void OnExpressionPair(ExpressionPair node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + node.First.Accept(this); _writer.Write(" = "); node.Second.Accept(this); + + WriteComments(node, AnchorKind.Right); } public override void OnMethodInvocationExpression(MethodInvocationExpression node) { - if (node.Target.Entity != null && node.Target.Entity.EntityType == EntityType.BuiltinFunction) + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + + if (node.Target.Entity != null && node.Target.Entity.EntityType == EntityType.BuiltinFunction && node.Target.Entity != BuiltinFunction.Quack) { _lastIgnored = true; return; @@ -618,6 +711,8 @@ namespace UnityScript2CSharp WriteParameterList(node.Arguments, refOutWriter); _brackets.Pop(); + + WriteComments(node, AnchorKind.Right); } private void HandleNewExpression(MethodInvocationExpression node) @@ -630,6 +725,9 @@ namespace UnityScript2CSharp public override void OnUnaryExpression(UnaryExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + bool postOperator = AstUtil.IsPostUnaryOperator(node.Operator); var operatorText = node.Operator == UnaryOperatorType.LogicalNot ? "!" : BooPrinterVisitor.GetUnaryOperatorText(node.Operator); if (!postOperator) @@ -641,10 +739,15 @@ namespace UnityScript2CSharp { _builderAppend(operatorText); } + + WriteComments(node, AnchorKind.Right); } public override void OnBinaryExpression(BinaryExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + var needParensAround = node.Operator != BinaryOperatorType.Assign; if (needParensAround) { @@ -671,10 +774,15 @@ namespace UnityScript2CSharp _writer.Write($" {CSharpOperatorFor(node.Operator)} "); node.Right.Accept(this); }); + + WriteComments(node, AnchorKind.Right); } public override void OnConditionalExpression(ConditionalExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + var parent = node.ParentNode as BinaryExpression; var needsParens = parent != null && (parent.Right != node || parent.Operator != BinaryOperatorType.Assign); @@ -686,15 +794,21 @@ namespace UnityScript2CSharp _writer.Write(" : "); VisitWrapping(node.FalseValue, node.FalseValue.NodeType == NodeType.ConditionalExpression, "(", ")"); }); + + WriteComments(node, AnchorKind.Right); } public override void OnReferenceExpression(ReferenceExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + if (node.ContainsAnnotation("VALUE_TYPE_INITIALIZATON_MARKER")) { _writer.Write("default("); _writer.Write(TypeNameFor(node.Entity) ?? node.Name); _writer.Write(")"); + WriteComments(node, AnchorKind.Right); return; } @@ -702,16 +816,26 @@ namespace UnityScript2CSharp _writer.Write("object"); else _writer.Write(TypeNameFor(node.Entity) ?? node.Name); + + WriteComments(node, AnchorKind.Right); } public override void OnMemberReferenceExpression(MemberReferenceExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + node.Target.Accept(this); _builderAppend($".{node.Name}"); + + WriteComments(node, AnchorKind.Right); } public override void OnGenericReferenceExpression(GenericReferenceExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + if (node.IsArrayInstantiation()) { _writer.Write("new "); @@ -731,6 +855,8 @@ namespace UnityScript2CSharp _writer.Write(", "); } _writer.Write(">"); + + WriteComments(node, AnchorKind.Right); } public override void OnQuasiquoteExpression(QuasiquoteExpression node) @@ -760,9 +886,10 @@ namespace UnityScript2CSharp value.Replace(replacement.Key, replacement.Value); } - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); _writer.Write($"\"{value}\""); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } public override void OnCharLiteralExpression(CharLiteralExpression node) @@ -779,44 +906,50 @@ namespace UnityScript2CSharp public override void OnIntegerLiteralExpression(IntegerLiteralExpression node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); _writer.Write(node.Value); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } public override void OnDoubleLiteralExpression(DoubleLiteralExpression node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); _writer.Write($"{node.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)}f"); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } public override void OnNullLiteralExpression(NullLiteralExpression node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); _builderAppend("null"); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } public override void OnSelfLiteralExpression(SelfLiteralExpression node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); _writer.Write("this"); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } public override void OnSuperLiteralExpression(SuperLiteralExpression node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); _writer.Write("base"); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } public override void OnBoolLiteralExpression(BoolLiteralExpression node) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); _writer.Write(node.Value ? "true" : "false"); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); } public override void OnRELiteralExpression(RELiteralExpression node) @@ -869,6 +1002,9 @@ namespace UnityScript2CSharp public override void OnHashLiteralExpression(HashLiteralExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.Write("new Hashtable() {"); foreach (var item in node.Items) { @@ -879,6 +1015,8 @@ namespace UnityScript2CSharp _writer.Write(" }, "); } _writer.Write("}"); + + WriteComments(node, AnchorKind.Right); } public override void OnListLiteralExpression(ListLiteralExpression node) @@ -916,23 +1054,36 @@ namespace UnityScript2CSharp public override void OnSlicingExpression(SlicingExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + node.Target.Accept(this); _writer.Write("["); WriteCommaSeparatedList(node.Indices); _writer.Write("]"); + + WriteComments(node, AnchorKind.Right); } public override void OnTryCastExpression(TryCastExpression node) { var isTargetOfMemberReferenceExpression = NeedParensAround(node); + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + VisitWrapping(node.Target, isTargetOfMemberReferenceExpression, "("); _writer.Write(" as "); VisitWrapping(node.Type, isTargetOfMemberReferenceExpression, posfix: ")"); + + WriteComments(node, AnchorKind.Right); } public override void OnCastExpression(CastExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + WrapWith(NeedParensAround(node), "(", ")", delegate { _writer.Write("("); @@ -944,13 +1095,20 @@ namespace UnityScript2CSharp node.Target.Accept(this); }); }); + + WriteComments(node, AnchorKind.Right); } public override void OnTypeofExpression(TypeofExpression node) { + WriteComments(node, AnchorKind.Above); + WriteComments(node, AnchorKind.Left); + _writer.Write("typeof("); node.Type.Accept(this); _writer.Write(")"); + + WriteComments(node, AnchorKind.Right); } public override void OnCustomStatement(CustomStatement node) @@ -978,9 +1136,9 @@ namespace UnityScript2CSharp if (isReturningIEnumerable) { - HandleComments(node, AnchorKind.Left); + WriteComments(node, AnchorKind.Left); _writer.Write("yield break;"); - HandleComments(node, AnchorKind.Right); + WriteComments(node, AnchorKind.Right); _writer.WriteLine(); } @@ -1335,7 +1493,7 @@ namespace UnityScript2CSharp return "Label" + label.Replace("$", "_"); } - private void HandleComments(Node node, AnchorKind anchorKind) + private void WriteComments(Node node, AnchorKind anchorKind) { if (!node.ContainsAnnotation(COMMENT_KEY)) return; @@ -1353,6 +1511,9 @@ namespace UnityScript2CSharp _writer.WriteBeforeNextNewLine(commentText); else _writer.Write(commentText); + + if (comment.AnchorKind == AnchorKind.Above) + _writer.WriteLine(); } if (comments.Count == 0) diff --git a/UnityScript2CSharp/Writer.cs b/UnityScript2CSharp/Writer.cs index 84d1cef..a571e55 100644 --- a/UnityScript2CSharp/Writer.cs +++ b/UnityScript2CSharp/Writer.cs @@ -7,7 +7,7 @@ namespace UnityScript2CSharp { private StringBuilder _builder; private int _indentation; - private string _x; + private string _toBeWrittenBeforeNextNewLine; private static readonly string _newLine = Environment.NewLine; public Writer(string contents) @@ -49,10 +49,10 @@ namespace UnityScript2CSharp public void WriteLine() { - if (_x != null) + if (_toBeWrittenBeforeNextNewLine != null) { - _builder.Append(_x); - _x = null; + _builder.Append(_toBeWrittenBeforeNextNewLine); + _toBeWrittenBeforeNextNewLine = null; } _builder.Append(_newLine); @@ -66,7 +66,7 @@ namespace UnityScript2CSharp } public void WriteBeforeNextNewLine(string text) { - _x = text; + _toBeWrittenBeforeNextNewLine = text; } public static string NewLine { get { return _newLine; } }