From b16d4d64352b7787c9f92820c3f54e25c14fd197 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sat, 14 Jun 2025 17:12:19 +0200 Subject: [PATCH] ... --- {input => example}/c.nub | 0 {input => example}/main.nub | 0 run.sh | 2 +- src/CLI/GCC.cs | 52 ++++++++++++ src/CLI/Program.cs | 153 +++++++++--------------------------- src/CLI/QBE.cs | 37 +++++++++ 6 files changed, 126 insertions(+), 118 deletions(-) rename {input => example}/c.nub (100%) rename {input => example}/main.nub (100%) create mode 100644 src/CLI/GCC.cs create mode 100644 src/CLI/QBE.cs diff --git a/input/c.nub b/example/c.nub similarity index 100% rename from input/c.nub rename to example/c.nub diff --git a/input/main.nub b/example/main.nub similarity index 100% rename from input/main.nub rename to example/main.nub diff --git a/run.sh b/run.sh index 649c577..58dfd46 100755 --- a/run.sh +++ b/run.sh @@ -1,4 +1,4 @@ #!/bin/bash set -e -dotnet run --project src/CLI/CLI.csproj input +dotnet run --project src/CLI/CLI.csproj example/main.nub example/c.nub ./bin/out \ No newline at end of file diff --git a/src/CLI/GCC.cs b/src/CLI/GCC.cs new file mode 100644 index 0000000..354f030 --- /dev/null +++ b/src/CLI/GCC.cs @@ -0,0 +1,52 @@ +using System.Diagnostics; + +namespace CLI; + +public static class GCC +{ + public static async Task Assemble(string asmPath, string objPath) + { + using var gccProcess = new Process(); + gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-c", asmPath, "-o", objPath]) + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + + gccProcess.Start(); + await gccProcess.WaitForExitAsync(); + + var gccErrors = await gccProcess.StandardError.ReadToEndAsync(); + if (!string.IsNullOrWhiteSpace(gccErrors)) + { + await Console.Error.WriteLineAsync("gcc error when assembling:\n" + gccErrors); + } + + return gccProcess.ExitCode == 0; + } + + public static async Task Link(List objectFiles, string outputPath) + { + using var gccProcess = new Process(); + gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-nostartfiles", "-o", outputPath, ..objectFiles]) + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + + gccProcess.Start(); + await gccProcess.WaitForExitAsync(); + + var gccErrors = await gccProcess.StandardError.ReadToEndAsync(); + if (!string.IsNullOrWhiteSpace(gccErrors)) + { + await Console.Error.WriteLineAsync("gcc error when linking:\n" + gccErrors); + } + + return gccProcess.ExitCode == 0; + } +} diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index fcd203b..e059f27 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Reflection; +using CLI; using Generation.QBE; using Syntax; using Syntax.Parsing; @@ -9,44 +10,35 @@ using Syntax.Typing; const string BIN_DIR = "bin"; const string BIN_INT_DIR = "bin-int"; -if (args.Length != 1) +if (Directory.Exists(BIN_DIR)) { - Console.Error.WriteLine("Usage: nub "); - Console.Error.WriteLine("Example: nub src"); - return 1; + Directory.Delete(BIN_DIR, true); } -var srcDir = Path.GetFullPath(args[0]); - -if (!Directory.Exists(srcDir)) +if (Directory.Exists(BIN_INT_DIR)) { - Console.Error.WriteLine($"Error: Input directory '{srcDir}' does not exist."); - return 1; + Directory.Delete(BIN_INT_DIR, true); } -Directory.CreateDirectory(BIN_INT_DIR); Directory.CreateDirectory(BIN_DIR); - -foreach (var file in Directory.GetFiles(BIN_INT_DIR)) -{ - File.Delete(file); -} - -foreach (var file in Directory.GetFiles(BIN_DIR)) -{ - File.Delete(file); -} +Directory.CreateDirectory(BIN_INT_DIR); var error = false; var compilationUnits = new List(); var sourceTexts = new Dictionary(); -var objectFiles = new List(); -foreach (var file in Directory.EnumerateFiles(srcDir, "*.nub", SearchOption.AllDirectories)) +foreach (var file in args) { + if (!File.Exists(file)) + { + Console.Error.WriteLine($"File '{file}' does not exist"); + return 1; + } + var content = File.ReadAllText(file); var sourceText = new SourceText(file, content); + var tokenizeResult = Tokenizer.Tokenize(sourceText); tokenizeResult.PrintAllDiagnostics(); error = error || tokenizeResult.HasErrors; @@ -81,21 +73,25 @@ if (error) return 1; } +var objectFiles = new List(); + foreach (var compilationUnit in compilationUnits) { - var ssaCode = QBEGenerator.Generate(compilationUnit, definitionTable); + var ssa = QBEGenerator.Generate(compilationUnit, definitionTable); + var asm = await QBE.Invoke(ssa); - var sourceFileName = Path.GetFileNameWithoutExtension(sourceTexts[compilationUnit].Path); - var sourceHash = Math.Abs(sourceTexts[compilationUnit].Path.GetHashCode()).ToString("x8"); - var baseOutputName = $"{sourceFileName}_{sourceHash}"; + var relativeFilePath = Path.GetRelativePath(Environment.CurrentDirectory, sourceTexts[compilationUnit].Path); + var outputPath = Path.Combine(BIN_INT_DIR, "program", relativeFilePath); - var assemblyCode = await InvokeQBE(ssaCode); + var outputDirectory = Path.GetDirectoryName(outputPath); + Debug.Assert(!string.IsNullOrWhiteSpace(outputDirectory)); + Directory.CreateDirectory(outputDirectory); - var asmPath = Path.Combine(BIN_INT_DIR, $"{baseOutputName}.s"); - await File.WriteAllTextAsync(asmPath, assemblyCode); + var asmPath = Path.ChangeExtension(outputPath, "s"); + await File.WriteAllTextAsync(asmPath, asm); - var objPath = Path.Combine(BIN_INT_DIR, $"{baseOutputName}.o"); - var asmSuccess = await InvokeAssembler(asmPath, objPath); + var objPath = Path.ChangeExtension(outputPath, "o"); + var asmSuccess = await GCC.Assemble(asmPath, objPath); if (!asmSuccess) { return 1; @@ -119,102 +115,25 @@ foreach (var resourceName in runtimeResources) } using var reader = new StreamReader(stream); - var assemblyCode = await reader.ReadToEndAsync(); + var asm = await reader.ReadToEndAsync(); var fileName = resourceName.Split('.').Reverse().Skip(1).First(); - var asmPath = Path.Combine(BIN_INT_DIR, $"runtime_{fileName}.s"); - var objPath = Path.Combine(BIN_INT_DIR, $"runtime_{fileName}.o"); + var outputDirectory = Path.Combine(BIN_INT_DIR, "runtime"); - await File.WriteAllTextAsync(asmPath, assemblyCode); + Directory.CreateDirectory(outputDirectory); - var asmSuccess = await InvokeAssembler(asmPath, objPath); + var asmPath = Path.Combine(outputDirectory, fileName + ".s"); + await File.WriteAllTextAsync(asmPath, asm); + + var objPath = Path.Combine(outputDirectory, fileName + ".o"); + var asmSuccess = await GCC.Assemble(asmPath, objPath); if (!asmSuccess) { return 1; } objectFiles.Add(objPath); - - File.Delete(asmPath); } -var outputPath = Path.Combine(BIN_DIR, "out"); -var linkSuccess = await InvokeLinker(objectFiles, outputPath); +var linkSuccess = await GCC.Link(objectFiles, Path.Combine(BIN_DIR, "out")); return linkSuccess ? 0 : 1; - -static async Task InvokeQBE(string ssa) -{ - using var qbeProcess = new Process(); - qbeProcess.StartInfo = new ProcessStartInfo - { - FileName = "qbe", - UseShellExecute = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - qbeProcess.Start(); - - await qbeProcess.StandardInput.WriteAsync(ssa); - qbeProcess.StandardInput.Close(); - - var assemblyCode = await qbeProcess.StandardOutput.ReadToEndAsync(); - var qbeErrors = await qbeProcess.StandardError.ReadToEndAsync(); - - await qbeProcess.WaitForExitAsync(); - - if (qbeProcess.ExitCode != 0) - { - throw new Exception($"QBE errors:\n{qbeErrors}"); - } - - return assemblyCode; -} - -static async Task InvokeAssembler(string asmPath, string objPath) -{ - using var gccProcess = new Process(); - gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-c", asmPath, "-o", objPath]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - gccProcess.Start(); - await gccProcess.WaitForExitAsync(); - - var gccErrors = await gccProcess.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(gccErrors)) - { - Console.Error.WriteLine("gcc error:\n" + gccErrors); - } - - return gccProcess.ExitCode == 0; -} - -static async Task InvokeLinker(List objectFiles, string outputPath) -{ - using var gccProcess = new Process(); - gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-nostartfiles", "-o", outputPath, ..objectFiles]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - gccProcess.Start(); - await gccProcess.WaitForExitAsync(); - - var gccErrors = await gccProcess.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(gccErrors)) - { - Console.Error.WriteLine("gcc error:\n" + gccErrors); - } - - return gccProcess.ExitCode == 0; -} \ No newline at end of file diff --git a/src/CLI/QBE.cs b/src/CLI/QBE.cs new file mode 100644 index 0000000..c5c35e0 --- /dev/null +++ b/src/CLI/QBE.cs @@ -0,0 +1,37 @@ +using System.Diagnostics; + +namespace CLI; + +public static class QBE +{ + public static async Task Invoke(string ssa) + { + using var qbeProcess = new Process(); + qbeProcess.StartInfo = new ProcessStartInfo + { + FileName = "qbe", + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + + qbeProcess.Start(); + + await qbeProcess.StandardInput.WriteAsync(ssa); + qbeProcess.StandardInput.Close(); + + var assemblyCode = await qbeProcess.StandardOutput.ReadToEndAsync(); + var qbeErrors = await qbeProcess.StandardError.ReadToEndAsync(); + + await qbeProcess.WaitForExitAsync(); + + if (qbeProcess.ExitCode != 0) + { + throw new Exception($"QBE error:\n{qbeErrors}"); + } + + return assemblyCode; + } +}