This commit is contained in:
nub31
2025-08-18 16:56:20 +02:00
parent 860e1fd0e4
commit f80be58fed
5 changed files with 73 additions and 153 deletions

View File

@@ -1,11 +1,17 @@
CFLAGS = -g
NUBC = ../src/compiler/NubLang.CLI/bin/Debug/net9.0/nubc
bin/out: src/main.nub
dotnet build ../src/compiler/NubLang.CLI/NubLang.CLI.csproj -c Release
../src/compiler/NubLang.CLI/bin/Release/net9.0/nubc src/main.nub
out: $(NUBC) main.nub
$(NUBC) main.nub
run: bin/out
bin/out
$(NUBC):
dotnet build ../src/compiler/NubLang.CLI/NubLang.CLI.csproj
run: out
out
clean:
rm -r bin-int bin
@rm out 2>/dev/null || true
@rm out.a 2>/dev/null || true
@find . -name "*.o" -type f -delete
@find . -name "*.s" -type f -delete
@find . -name "*.ssa" -type f -delete

View File

@@ -1,49 +0,0 @@
// c
extern func puts(text: cstring)
// raylib
extern func InitWindow(width: i32, height: i32, title: cstring)
extern func CloseWindow()
extern func WindowShouldClose(): bool
extern func BeginDrawing()
extern func EndDrawing()
extern func ClearBackground(color: Color)
extern func DrawCircle(centerX: i32, centerY: i32, radius: f32, color: Color)
struct Color
{
r: u8
g: u8
b: u8
a: u8
}
struct Vector2
{
x: f32
y: f32
}
struct Vector3
{
x: f32
y: f32
z: f32
}
func main(args: []cstring): i64
{
InitWindow(800, 600, "Hello from nub!")
while !WindowShouldClose()
{
BeginDrawing()
ClearBackground(struct { r = 55 g = 55 b = 55 a = 255 })
DrawCircle(400, 300, 50, struct { r = 255 g = 255 b = 0 a = 0 })
EndDrawing()
}
CloseWindow()
return 0
}

View File

@@ -6,12 +6,6 @@ public static class GCC
{
public static async Task<bool> Assemble(string asmPath, string outPath)
{
var dir = Path.GetDirectoryName(outPath);
if (dir != null)
{
Directory.CreateDirectory(dir);
}
using var process = new Process();
process.StartInfo = new ProcessStartInfo("gcc", ["-xassembler", "-c", "-o", outPath, asmPath])
{
@@ -36,12 +30,6 @@ public static class GCC
public static async Task<bool> Link(string executablePath, params IEnumerable<string> objectFiles)
{
var dir = Path.GetDirectoryName(executablePath);
if (dir != null)
{
Directory.CreateDirectory(dir);
}
using var process = new Process();
process.StartInfo = new ProcessStartInfo("gcc", ["-nostartfiles", "-o", executablePath, ..objectFiles])
{

View File

@@ -4,8 +4,10 @@ namespace NubLang.CLI;
public class Options
{
public string? CustomRuntime { get; set; }
public string? OutputPath { get; set; }
public bool Link { get; set; } = true;
public List<SourceFile> Files { get; } = [];
public bool EmitSsa { get; set; } = false;
public bool EmitAsm { get; set; } = false;
public bool EmitObj { get; set; } = false;
}

View File

@@ -10,41 +10,13 @@ using NubLang.Tokenization;
using NubLang.TypeChecking;
using NubLang.TypeChecking.Node;
const string BIN_DIR = "bin";
const string INT_DIR = "bin-int";
var INT_BUILTIN_DIR = Path.Join(INT_DIR, "builtin");
var INT_OBJECT_DIR = Path.Join(INT_DIR, "obj");
var INT_SSA_DIR = Path.Join(INT_DIR, "ssa");
var INT_ASM_DIR = Path.Join(INT_DIR, "asm");
if (Directory.Exists(INT_DIR))
{
Directory.Delete(INT_DIR, true);
}
Directory.CreateDirectory(BIN_DIR);
Directory.CreateDirectory(INT_BUILTIN_DIR);
Directory.CreateDirectory(INT_OBJECT_DIR);
Directory.CreateDirectory(INT_SSA_DIR);
Directory.CreateDirectory(INT_ASM_DIR);
var options = new Options();
for (var i = 0; i < args.Length; i++)
{
var arg = args[i];
if (arg == "-r")
{
i++;
if (i >= args.Length - 1)
{
Console.Error.WriteLine($"{arg} must be followed by a value");
return 1;
}
options.CustomRuntime = args[i];
}
else if (arg == "-o")
if (arg == "-o")
{
i++;
if (i >= args.Length - 1)
@@ -59,6 +31,18 @@ for (var i = 0; i < args.Length; i++)
{
options.Link = false;
}
else if (arg == "-ssa")
{
options.EmitSsa = true;
}
else if (arg == "-asm")
{
options.EmitAsm = true;
}
else if (arg == "-obj")
{
options.EmitObj = true;
}
else
{
options.Files.Add(new SourceFile(arg));
@@ -120,23 +104,35 @@ var objectFiles = new List<string>();
for (var i = 0; i < typedSyntaxTrees.Count; i++)
{
var syntaxTree = typedSyntaxTrees[i];
var outFileName = Path.GetFileNameWithoutExtension(options.Files[i].Path);
var outFileName = Path.ChangeExtension(options.Files[i].Path, null);
var generator = new QBEGenerator(syntaxTree, typedDefinitionTable);
var ssa = generator.Emit();
var ssaPath = Path.Join(INT_SSA_DIR, $"{outFileName}.ssa");
File.WriteAllText(ssaPath, ssa);
var ssaFilePath = $"{outFileName}.ssa";
File.WriteAllText(ssaFilePath, ssa);
var asmFilePath = $"{outFileName}.s";
var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath);
if (!options.EmitSsa)
{
File.Delete(ssaFilePath);
}
var asmFilePath = Path.Join(INT_ASM_DIR, $"{outFileName}.s");
var qbeSuccess = await QBE.Invoke(ssaPath, asmFilePath);
if (!qbeSuccess)
{
return 1;
}
var objFilePath = Path.Join(INT_OBJECT_DIR, $"{outFileName}.o");
var objFilePath = $"{outFileName}.o";
var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath);
if (!options.EmitAsm)
{
File.Delete(asmFilePath);
}
if (!asmSuccess)
{
return 1;
@@ -145,32 +141,28 @@ for (var i = 0; i < typedSyntaxTrees.Count; i++)
objectFiles.Add(objFilePath);
}
if (options.CustomRuntime == null)
var runtimeFilePath = "libruntime_x64.a";
var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
var runtime = resources.First(r => r.EndsWith(runtimeFilePath));
await using var reader = Assembly.GetExecutingAssembly().GetManifestResourceStream(runtime);
if (reader == null)
{
try
{
objectFiles.Add(await CreateBuiltinRuntime());
}
catch (RuntimeCreationException e)
{
Console.Error.WriteLine(e.Message);
return 1;
}
}
else
{
if (!File.Exists(options.CustomRuntime))
{
Console.Error.WriteLine($"file '{options.CustomRuntime}' does not exist'");
Console.Error.WriteLine($"Cannot open stream to '{runtimeFilePath}'");
return 1;
}
objectFiles.Add(options.CustomRuntime);
}
await using var writer = new FileStream(runtimeFilePath, FileMode.Create);
reader.CopyTo(writer);
objectFiles.Add(runtimeFilePath);
if (options.Link)
{
var outPath = options.OutputPath ?? Path.Join(BIN_DIR, "out");
var outPath = options.OutputPath ?? "out";
var linkResult = await GCC.Link(outPath, objectFiles);
if (!linkResult)
{
@@ -179,7 +171,7 @@ if (options.Link)
}
else
{
var outPath = options.OutputPath ?? Path.Join(BIN_DIR, "out.a");
var outPath = options.OutputPath ?? "out.a";
var archiveResult = await Archive.Invoke(outPath, objectFiles);
if (!archiveResult)
{
@@ -187,31 +179,12 @@ else
}
}
if (!options.EmitObj)
{
foreach (var objectFile in objectFiles)
{
File.Delete(objectFile);
}
}
return 0;
async Task<string> CreateBuiltinRuntime()
{
const string RUNTIME_NAME = "libruntime_x64.a";
var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
var runtime = resources.First(r => r.EndsWith(RUNTIME_NAME));
await using var reader = Assembly.GetExecutingAssembly().GetManifestResourceStream(runtime);
if (reader == null)
{
throw new RuntimeCreationException($"Cannot open stream to '{RUNTIME_NAME}'");
}
var runtimePath = Path.Combine(INT_BUILTIN_DIR, RUNTIME_NAME);
await using var writer = new FileStream(runtimePath, FileMode.Create);
reader.CopyTo(writer);
return runtimePath;
}
namespace NubLang.CLI
{
internal class RuntimeCreationException(string message) : Exception(message);
}