File size: 5,033 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
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace RobloxCS
{
    public sealed class Transpiler
    {
        public readonly ConfigData Config;

        private const string _includeFolderName = "Include";
        private List<SyntaxTree> _fileTrees = new List<SyntaxTree>();
        private readonly string _inputDirectory;
        private readonly string _sourceDirectory;
        private readonly string _outDirectory;

        public Transpiler(string inputDirectory)
        {
            Config = ConfigReader.Read(inputDirectory);
            _inputDirectory = inputDirectory;
            _sourceDirectory = inputDirectory + "/" + Config.SourceFolder;
            _outDirectory = inputDirectory + "/" + Config.OutputFolder;
        }

        public void Transpile()
        {
            ParseSource();
            var compiler = CompileASTs();
            CopyIncludedLua();
            WriteLuaOutput(compiler);
        }

        private void ParseSource()
        {
            if (!Directory.Exists(_sourceDirectory))
            {
                Logger.Error($"Source folder \"{Config.SourceFolder}\" does not exist!");
            }

            var sourceFiles = FileManager.GetSourceFiles(_sourceDirectory);
            foreach (var sourceFile in sourceFiles)
            {
                var fileContents = File.ReadAllText(sourceFile);
                var tree = TranspilerUtility.ParseTree(fileContents, sourceFile);
                HashSet<Func<SyntaxTree, ConfigData, SyntaxTree>> transformers = [BuiltInTransformers.Main()];

                foreach (var transformerName in Config.EnabledBuiltInTransformers)
                {
                    transformers.Add(BuiltInTransformers.Get(transformerName));
                }

                var transformedTree = TranspilerUtility.TransformTree(tree, transformers, Config);
                foreach (var diagnostic in transformedTree.GetDiagnostics())
                {
                    Logger.HandleDiagnostic(diagnostic);
                }

                _fileTrees.Add(transformedTree);
            }
        }
        
        private CSharpCompilation CompileASTs()
        {
            var compiler = TranspilerUtility.GetCompiler(_fileTrees, Config);
            foreach (var diagnostic in compiler.GetDiagnostics())
            {
                Logger.HandleDiagnostic(diagnostic);
            }

            return compiler;
        }

        private void CopyIncludedLua()
        {
            var rbxcsDirectory = Utility.GetRbxcsDirectory();
            if (rbxcsDirectory == null)
            {
                Logger.CompilerError("Failed to find RobloxCS directory");
                return;
            }

            var compilerDirectory = Utility.FixPathSep(Path.Combine(rbxcsDirectory, "RobloxCS"));
            var includeDirectory = Utility.FixPathSep(Path.Combine(compilerDirectory, _includeFolderName));
            var destinationIncludeDirectory = includeDirectory
                .Replace(compilerDirectory, _inputDirectory)
                .Replace(_includeFolderName, _includeFolderName.ToLower());

            try
            {
                FileManager.CopyDirectory(includeDirectory, destinationIncludeDirectory);
            }
            catch (Exception e)
            {
                Logger.Error($"Failed to copy included Lua files: {e.Message}");
            }
        }

        private void WriteLuaOutput(CSharpCompilation compiler)
        {
            var compiledFiles = new List<CompiledFile>();
            var memberCollector = new MemberCollector(_fileTrees);
            var members = memberCollector.Collect();
            if (Config.CSharpOptions.EntryPointRequired && _fileTrees.All(tree => !tree.GetRoot().DescendantNodes().Any(node => node is ClassDeclarationSyntax classDeclaration && Utility.GetNamesFromNode(classDeclaration).FirstOrDefault() == Config.CSharpOptions.EntryPointName)))
            {
                Logger.Error($"No entry point class \"{Config.CSharpOptions.EntryPointName}\" found!");
            }

            foreach (var tree in _fileTrees)
            {
                var generatedLua = TranspilerUtility.GenerateLua(tree, compiler, members, _inputDirectory, Config);
                var targetPath = tree.FilePath.Replace(Config.SourceFolder, Config.OutputFolder).Replace(".cs", ".lua");
                compiledFiles.Add(new CompiledFile(targetPath, generatedLua));
            }

            EnsureDirectoriesExist();
            FileManager.WriteCompiledFiles(_outDirectory, compiledFiles);
        }

        private void EnsureDirectoriesExist()
        {
            var subDirectories = Directory.GetDirectories(_sourceDirectory, "*", SearchOption.AllDirectories);
            foreach (string subDirectory in subDirectories)
            {
                Directory.CreateDirectory(subDirectory.Replace(Config.SourceFolder, Config.OutputFolder));
            }
        }
    }
}