File size: 6,418 Bytes
1ae2e8e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace RobloxCS
{
    internal sealed class MainTransformer : BaseTransformer
    {
        public MainTransformer(SyntaxTree tree, ConfigData config)
            : base(tree, config)
        {
        }

        public override SyntaxNode? VisitCompilationUnit(CompilationUnitSyntax node)
        {
            var usings = node.Usings;
            usings = usings.Add(SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("Roblox")));
            usings = usings.Add(SyntaxFactory.UsingDirective(SyntaxFactory.Token(SyntaxKind.StaticKeyword), null, SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("Roblox"), SyntaxFactory.IdentifierName("Globals"))));
            return base.VisitCompilationUnit(node.WithUsings(usings));
        }

        public override SyntaxNode? VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node)
        {
            return VisitNamespaceDeclaration(SyntaxFactory.NamespaceDeclaration(node.AttributeLists, node.Modifiers, node.Name, node.Externs, node.Usings, node.Members));
        }

        public override SyntaxNode? VisitDoStatement(DoStatementSyntax node)
        {
            // invert condition
            return base.VisitDoStatement(node.WithCondition(SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, node.Condition)));
        }

        public override SyntaxNode? VisitBinaryExpression(BinaryExpressionSyntax node)
        {
            if (node.OperatorToken.Text == "is")
            {
                var pattern = SyntaxFactory.TypePattern(SyntaxFactory.ParseTypeName(((IdentifierNameSyntax)node.Right).Identifier.Text));
                return SyntaxFactory.IsPatternExpression(node.Left, pattern);
            }
            return base.VisitBinaryExpression(node);
        }

        public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            var newNode = node.Identifier.ValueText switch
            {
                "ToString" => node.WithIdentifier(CreateIdentifierToken("__tostring")),
                "Equals" => node.WithIdentifier(CreateIdentifierToken("__eq")),
                _ => node
            };
            if (node != newNode && HasSyntax(newNode.Modifiers, SyntaxKind.OverrideKeyword))
            {
                var newModifiers = newNode.Modifiers.RemoveAt(newNode.Modifiers.Select(token => token.Kind()).ToList().IndexOf(SyntaxKind.OverrideKeyword));
                newNode = newNode.WithModifiers(newModifiers);
            }
            return base.VisitMethodDeclaration(newNode);
        }

        public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
        {
            var identifierText = node.Identifier.Text;
            if (!identifierText.Contains("@") || identifierText == "var")
            {
                return base.VisitIdentifierName(node);
            }

            var fixedIdentifierText = identifierText.Replace("@", "");
            var newToken = CreateIdentifierToken(fixedIdentifierText);
            return base.VisitIdentifierName(node.WithIdentifier(newToken));
        }

        public override SyntaxNode? VisitArgument(ArgumentSyntax node)
        {
            if (node.Expression.IsKind(SyntaxKind.IdentifierName))
            {
                var newExpression = VisitIdentifierName((IdentifierNameSyntax)node.Expression);
                return base.VisitArgument(node.WithExpression((ExpressionSyntax)newExpression!));
            }
            return base.VisitArgument(node);
        }

        public override SyntaxNode? VisitConditionalAccessExpression(ConditionalAccessExpressionSyntax node)
        {
            var whenNotNull = ProcessWhenNotNull(node.Expression, node.WhenNotNull);
            if (whenNotNull != null)
            {
                return base.VisitConditionalAccessExpression(node.WithWhenNotNull(whenNotNull));
            }
            return base.VisitConditionalAccessExpression(node);
        }

        private static bool HasSyntax(SyntaxTokenList tokens, SyntaxKind syntax)
        {
            return tokens.Any(token => token.IsKind(syntax));
        }

        private ExpressionSyntax? ProcessWhenNotNull(ExpressionSyntax expression, ExpressionSyntax whenNotNull)
        {
            if (whenNotNull == null)
            {
                return null;
            }

            switch (whenNotNull)
            {
                case MemberBindingExpressionSyntax memberBinding:
                    return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expression, memberBinding.Name);
                case InvocationExpressionSyntax invocation:
                    return invocation.WithExpression((invocation.Expression switch
                    {
                        MemberAccessExpressionSyntax memberAccess => SyntaxFactory.MemberAccessExpression(
                            SyntaxKind.SimpleMemberAccessExpression,
                            expression,
                            memberAccess.Name
                        ),
                        ConditionalAccessExpressionSyntax nestedConditional => ProcessWhenNotNull(nestedConditional.WhenNotNull, expression),
                        MemberBindingExpressionSyntax memberBinding => SyntaxFactory.MemberAccessExpression(
                            SyntaxKind.SimpleMemberAccessExpression,
                            expression,
                            memberBinding.Name
                        ),
                        _ => SyntaxFactory.MemberAccessExpression(
                            SyntaxKind.SimpleMemberAccessExpression,
                            expression,
                            SyntaxFactory.IdentifierName(invocation.Expression.ToString())
                        )
                    })!);
                case ConditionalAccessExpressionSyntax conditionalAccess:
                    return conditionalAccess
                        .WithExpression(ProcessWhenNotNull(expression, conditionalAccess.Expression) ?? conditionalAccess.Expression)
                        .WithWhenNotNull(ProcessWhenNotNull(expression, conditionalAccess.WhenNotNull) ?? conditionalAccess.WhenNotNull);
                default:
                    return null;
            };
        }
    }
}