This commit is contained in:
nub31
2025-06-12 23:21:09 +02:00
parent 32a341508c
commit a56e150769
2 changed files with 154 additions and 55 deletions

View File

@@ -1,19 +1,2 @@
#!/bin/bash #!/bin/bash
set -e dotnet run --project src/lang/Nub.Lang.CLI/ example
dotnet build src/lang/Nub.Lang.CLI
mkdir -p bin-int bin
rm -rf bin-int/* bin/*
nub example
find bin-int -name '*.s' | while read -r file; do
as "$file" -o "bin-int/$(basename "${file}" .s).o"
done
find src/runtime -name '*.s' | while read -r file; do
as "$file" -o "bin-int/$(basename "${file}" .s).o"
done
gcc -nostartfiles -o bin/out bin-int/*.o

View File

@@ -6,6 +6,9 @@ using Nub.Lang.Frontend.Lexing;
using Nub.Lang.Frontend.Parsing; using Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Typing; using Nub.Lang.Frontend.Typing;
const string BIN_DIR = "bin";
const string BIN_INT_DIR = "bin-int";
if (args.Length != 1) if (args.Length != 1)
{ {
Console.Error.WriteLine("Usage: nub <input-dir>"); Console.Error.WriteLine("Usage: nub <input-dir>");
@@ -21,17 +24,29 @@ if (!Directory.Exists(srcDir))
return 1; return 1;
} }
var error = false; Directory.CreateDirectory(BIN_INT_DIR);
Directory.CreateDirectory(BIN_DIR);
Dictionary<CompilationUnit, SourceText> sourceTexts = []; foreach (var file in Directory.GetFiles(BIN_INT_DIR))
{
File.Delete(file);
}
foreach (var file in Directory.GetFiles(BIN_DIR))
{
File.Delete(file);
}
var error = false;
List<CompilationUnit> compilationUnits = []; List<CompilationUnit> compilationUnits = [];
Dictionary<CompilationUnit, SourceText> sourceTexts = [];
List<string> objectFiles = [];
foreach (var file in Directory.EnumerateFiles(srcDir, "*.nub", SearchOption.AllDirectories)) foreach (var file in Directory.EnumerateFiles(srcDir, "*.nub", SearchOption.AllDirectories))
{ {
var content = File.ReadAllText(file); var content = File.ReadAllText(file);
var sourceText = new SourceText(file, content); var sourceText = new SourceText(file, content);
var tokenizeResult = Lexer.Tokenize(sourceText); var tokenizeResult = Lexer.Tokenize(sourceText);
tokenizeResult.PrintAllDiagnostics(); tokenizeResult.PrintAllDiagnostics();
error = error || tokenizeResult.HasErrors; error = error || tokenizeResult.HasErrors;
@@ -72,11 +87,76 @@ foreach (var compilationUnit in compilationUnits)
var sourceFileName = Path.GetFileNameWithoutExtension(sourceTexts[compilationUnit].Path); var sourceFileName = Path.GetFileNameWithoutExtension(sourceTexts[compilationUnit].Path);
var sourceHash = Math.Abs(sourceTexts[compilationUnit].Path.GetHashCode()).ToString("x8"); var sourceHash = Math.Abs(sourceTexts[compilationUnit].Path.GetHashCode()).ToString("x8");
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString("x"); var baseOutputName = $"{sourceFileName}_{sourceHash}";
var baseOutputName = $"{sourceFileName}_{sourceHash}_{timestamp}";
try try
{ {
var assemblyCode = await InvokeQBE(ssaCode);
var asmPath = Path.Combine(BIN_INT_DIR, $"{baseOutputName}.s");
await File.WriteAllTextAsync(asmPath, assemblyCode);
Console.Out.WriteLine($"Generated: {asmPath}");
var objPath = Path.Combine(BIN_INT_DIR, $"{baseOutputName}.o");
await InvokeAssembler(asmPath, objPath);
objectFiles.Add(objPath);
Console.Out.WriteLine($"Assembled: {objPath}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error processing {sourceTexts[compilationUnit].Path}: {ex.Message}");
error = true;
}
}
if (error)
{
return 1;
}
const string RUNTIME_DIR = "src/runtime";
if (Directory.Exists(RUNTIME_DIR))
{
foreach (var runtimeFile in Directory.EnumerateFiles(RUNTIME_DIR, "*.s", SearchOption.AllDirectories))
{
try
{
var runtimeFileName = Path.GetFileNameWithoutExtension(runtimeFile);
var objPath = Path.Combine(BIN_INT_DIR, $"runtime_{runtimeFileName}.o");
await InvokeAssembler(runtimeFile, objPath);
objectFiles.Add(objPath);
Console.Out.WriteLine($"Runtime assembled: {objPath}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error assembling runtime file {runtimeFile}: {ex.Message}");
error = true;
}
}
}
if (error)
{
return 1;
}
try
{
var outputPath = Path.Combine(BIN_DIR, "out");
await InvokeLinker(objectFiles, outputPath);
Console.Out.WriteLine($"Linked executable: {outputPath}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error linking: {ex.Message}");
return 1;
}
return 0;
static async Task<string> InvokeQBE(string ssa)
{
using var qbeProcess = new Process(); using var qbeProcess = new Process();
qbeProcess.StartInfo = new ProcessStartInfo qbeProcess.StartInfo = new ProcessStartInfo
{ {
@@ -90,7 +170,7 @@ foreach (var compilationUnit in compilationUnits)
qbeProcess.Start(); qbeProcess.Start();
await qbeProcess.StandardInput.WriteAsync(ssaCode); await qbeProcess.StandardInput.WriteAsync(ssa);
qbeProcess.StandardInput.Close(); qbeProcess.StandardInput.Close();
var assemblyCode = await qbeProcess.StandardOutput.ReadToEndAsync(); var assemblyCode = await qbeProcess.StandardOutput.ReadToEndAsync();
@@ -100,20 +180,56 @@ foreach (var compilationUnit in compilationUnits)
if (qbeProcess.ExitCode != 0) if (qbeProcess.ExitCode != 0)
{ {
Console.Error.WriteLine($"QBE failed for {sourceTexts[compilationUnit].Path}:"); throw new Exception($"QBE errors:\n{qbeErrors}");
Console.Error.WriteLine(qbeErrors);
error = true;
continue;
} }
var asmPath = Path.Combine("bin-int", $"{baseOutputName}.s"); return assemblyCode;
await File.WriteAllTextAsync(asmPath, assemblyCode); }
}
catch (Exception ex) static async Task InvokeAssembler(string asmPath, string objPath)
{
using var asProcess = new Process();
asProcess.StartInfo = new ProcessStartInfo("gcc", ["-c", asmPath, "-o", objPath])
{ {
Console.Error.WriteLine($"Error invoking QBE for {sourceTexts[compilationUnit].Path}: {ex.Message}"); UseShellExecute = false,
error = true; RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
asProcess.Start();
var asOutput = await asProcess.StandardOutput.ReadToEndAsync();
var asErrors = await asProcess.StandardError.ReadToEndAsync();
await asProcess.WaitForExitAsync();
if (asProcess.ExitCode != 0)
{
throw new Exception($"Assembler failed for {asmPath}:\n{asErrors}");
} }
} }
return error ? 1 : 0; static async Task InvokeLinker(List<string> 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();
var gccOutput = await gccProcess.StandardOutput.ReadToEndAsync();
var gccErrors = await gccProcess.StandardError.ReadToEndAsync();
await gccProcess.WaitForExitAsync();
if (gccProcess.ExitCode != 0)
{
throw new Exception($"Linker failed:\n{gccErrors}");
}
}