Spaces:
Running
Running
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));
}
}
}
} |