...
This commit is contained in:
2
run.sh
2
run.sh
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
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
|
./bin/out
|
||||||
52
src/CLI/GCC.cs
Normal file
52
src/CLI/GCC.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace CLI;
|
||||||
|
|
||||||
|
public static class GCC
|
||||||
|
{
|
||||||
|
public static async Task<bool> 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<bool> Link(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();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using CLI;
|
||||||
using Generation.QBE;
|
using Generation.QBE;
|
||||||
using Syntax;
|
using Syntax;
|
||||||
using Syntax.Parsing;
|
using Syntax.Parsing;
|
||||||
@@ -9,44 +10,35 @@ using Syntax.Typing;
|
|||||||
const string BIN_DIR = "bin";
|
const string BIN_DIR = "bin";
|
||||||
const string BIN_INT_DIR = "bin-int";
|
const string BIN_INT_DIR = "bin-int";
|
||||||
|
|
||||||
if (args.Length != 1)
|
if (Directory.Exists(BIN_DIR))
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Usage: nub <input-dir>");
|
Directory.Delete(BIN_DIR, true);
|
||||||
Console.Error.WriteLine("Example: nub src");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var srcDir = Path.GetFullPath(args[0]);
|
if (Directory.Exists(BIN_INT_DIR))
|
||||||
|
|
||||||
if (!Directory.Exists(srcDir))
|
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine($"Error: Input directory '{srcDir}' does not exist.");
|
Directory.Delete(BIN_INT_DIR, true);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(BIN_INT_DIR);
|
|
||||||
Directory.CreateDirectory(BIN_DIR);
|
Directory.CreateDirectory(BIN_DIR);
|
||||||
|
Directory.CreateDirectory(BIN_INT_DIR);
|
||||||
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;
|
var error = false;
|
||||||
var compilationUnits = new List<CompilationUnit>();
|
var compilationUnits = new List<CompilationUnit>();
|
||||||
var sourceTexts = new Dictionary<CompilationUnit, SourceText>();
|
var sourceTexts = new Dictionary<CompilationUnit, SourceText>();
|
||||||
var objectFiles = new List<string>();
|
|
||||||
|
|
||||||
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 content = File.ReadAllText(file);
|
||||||
|
|
||||||
var sourceText = new SourceText(file, content);
|
var sourceText = new SourceText(file, content);
|
||||||
|
|
||||||
var tokenizeResult = Tokenizer.Tokenize(sourceText);
|
var tokenizeResult = Tokenizer.Tokenize(sourceText);
|
||||||
tokenizeResult.PrintAllDiagnostics();
|
tokenizeResult.PrintAllDiagnostics();
|
||||||
error = error || tokenizeResult.HasErrors;
|
error = error || tokenizeResult.HasErrors;
|
||||||
@@ -81,21 +73,25 @@ if (error)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var objectFiles = new List<string>();
|
||||||
|
|
||||||
foreach (var compilationUnit in compilationUnits)
|
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 relativeFilePath = Path.GetRelativePath(Environment.CurrentDirectory, sourceTexts[compilationUnit].Path);
|
||||||
var sourceHash = Math.Abs(sourceTexts[compilationUnit].Path.GetHashCode()).ToString("x8");
|
var outputPath = Path.Combine(BIN_INT_DIR, "program", relativeFilePath);
|
||||||
var baseOutputName = $"{sourceFileName}_{sourceHash}";
|
|
||||||
|
|
||||||
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");
|
var asmPath = Path.ChangeExtension(outputPath, "s");
|
||||||
await File.WriteAllTextAsync(asmPath, assemblyCode);
|
await File.WriteAllTextAsync(asmPath, asm);
|
||||||
|
|
||||||
var objPath = Path.Combine(BIN_INT_DIR, $"{baseOutputName}.o");
|
var objPath = Path.ChangeExtension(outputPath, "o");
|
||||||
var asmSuccess = await InvokeAssembler(asmPath, objPath);
|
var asmSuccess = await GCC.Assemble(asmPath, objPath);
|
||||||
if (!asmSuccess)
|
if (!asmSuccess)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@@ -119,102 +115,25 @@ foreach (var resourceName in runtimeResources)
|
|||||||
}
|
}
|
||||||
|
|
||||||
using var reader = new StreamReader(stream);
|
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 fileName = resourceName.Split('.').Reverse().Skip(1).First();
|
||||||
var asmPath = Path.Combine(BIN_INT_DIR, $"runtime_{fileName}.s");
|
var outputDirectory = Path.Combine(BIN_INT_DIR, "runtime");
|
||||||
var objPath = Path.Combine(BIN_INT_DIR, $"runtime_{fileName}.o");
|
|
||||||
|
|
||||||
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)
|
if (!asmSuccess)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
objectFiles.Add(objPath);
|
objectFiles.Add(objPath);
|
||||||
|
|
||||||
File.Delete(asmPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputPath = Path.Combine(BIN_DIR, "out");
|
var linkSuccess = await GCC.Link(objectFiles, Path.Combine(BIN_DIR, "out"));
|
||||||
var linkSuccess = await InvokeLinker(objectFiles, outputPath);
|
|
||||||
return linkSuccess ? 0 : 1;
|
return linkSuccess ? 0 : 1;
|
||||||
|
|
||||||
static async Task<string> 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<bool> 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<bool> 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();
|
|
||||||
await gccProcess.WaitForExitAsync();
|
|
||||||
|
|
||||||
var gccErrors = await gccProcess.StandardError.ReadToEndAsync();
|
|
||||||
if (!string.IsNullOrWhiteSpace(gccErrors))
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("gcc error:\n" + gccErrors);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gccProcess.ExitCode == 0;
|
|
||||||
}
|
|
||||||
37
src/CLI/QBE.cs
Normal file
37
src/CLI/QBE.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace CLI;
|
||||||
|
|
||||||
|
public static class QBE
|
||||||
|
{
|
||||||
|
public static async Task<string> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user