WIP: dev #1
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGraph moduleGraph)
|
public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGraph moduleGraph, bool compileLib)
|
||||||
{
|
{
|
||||||
public static string Emit(List<TypedNodeDefinitionFunc> functions, ModuleGraph moduleGraph)
|
public static string Emit(List<TypedNodeDefinitionFunc> functions, ModuleGraph moduleGraph, bool compileLib)
|
||||||
{
|
{
|
||||||
return new Generator(functions, moduleGraph).Emit();
|
return new Generator(functions, moduleGraph, compileLib).Emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IndentedTextWriter writer = new();
|
private readonly IndentedTextWriter writer = new();
|
||||||
@@ -79,10 +79,10 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
|
|||||||
|
|
||||||
writer.WriteLine();
|
writer.WriteLine();
|
||||||
|
|
||||||
var main = functions.FirstOrDefault(x => x.Module == "main" && x.Name.Ident == "main");
|
if (!compileLib)
|
||||||
|
|
||||||
if (main != null)
|
|
||||||
{
|
{
|
||||||
|
var main = functions.First(x => x.Module == "main" && x.Name.Ident == "main");
|
||||||
|
|
||||||
writer.WriteLine($$"""
|
writer.WriteLine($$"""
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
|
|||||||
return module != null;
|
return module != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Manifest CreateManifest()
|
||||||
|
{
|
||||||
|
return new Manifest(1, GetModules().Select(x => x.CreateManifestModule()).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Manifest(int Version, IReadOnlyList<Module.ManifestModule> Modules);
|
||||||
|
|
||||||
public sealed class Module(string name)
|
public sealed class Module(string name)
|
||||||
{
|
{
|
||||||
public string Name { get; } = name;
|
public string Name { get; } = name;
|
||||||
@@ -72,37 +79,105 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCustomType(string name, NubType type, bool exported)
|
public void AddCustomType(string name, CustomTypeInfo info)
|
||||||
{
|
{
|
||||||
customTypes.Add(name, new CustomTypeInfo(type, exported));
|
customTypes.Add(name, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddIdentifier(string name, NubType type, bool exported)
|
public void AddIdentifier(string name, IdentifierInfo info)
|
||||||
{
|
{
|
||||||
identifierTypes.Add(name, new IdentifierInfo(type, exported));
|
identifierTypes.Add(name, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private record CustomTypeInfo(NubType Type, bool Exported);
|
public ManifestModule CreateManifestModule()
|
||||||
private record IdentifierInfo(NubType Type, bool Exported);
|
{
|
||||||
|
var manifestCustomTypes = customTypes.ToDictionary(x => x.Key, x => x.Value.CreateManifestCustomTypeInfo());
|
||||||
|
var manifestIdentifiers = identifierTypes.ToDictionary(x => x.Key, x => x.Value.CreateManifestIdentifierInfo());
|
||||||
|
return new ManifestModule(Name, manifestCustomTypes, manifestIdentifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Source
|
||||||
|
{
|
||||||
|
Ast,
|
||||||
|
Lib,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomTypeInfo(NubType type, bool exported, Source source)
|
||||||
|
{
|
||||||
|
public NubType Type { get; } = type;
|
||||||
|
public bool Exported { get; } = exported;
|
||||||
|
public Source Source { get; } = source;
|
||||||
|
|
||||||
|
public ManifestCustomTypeInfo CreateManifestCustomTypeInfo()
|
||||||
|
{
|
||||||
|
return new ManifestCustomTypeInfo(Type, Exported);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IdentifierInfo(NubType type, bool exported, Source source)
|
||||||
|
{
|
||||||
|
public NubType Type { get; } = type;
|
||||||
|
public bool Exported { get; } = exported;
|
||||||
|
public Source Source { get; } = source;
|
||||||
|
|
||||||
|
public ManifestIdentifierInfo CreateManifestIdentifierInfo()
|
||||||
|
{
|
||||||
|
return new ManifestIdentifierInfo(Type, Exported);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ManifestModule(string Name, Dictionary<string, ManifestCustomTypeInfo> CustomTypes, Dictionary<string, ManifestIdentifierInfo> Identifiers);
|
||||||
|
public record ManifestCustomTypeInfo(NubType Type, bool Exported);
|
||||||
|
public record ManifestIdentifierInfo(NubType Type, bool Exported);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Builder
|
public class Builder
|
||||||
{
|
{
|
||||||
private readonly List<Ast> asts = [];
|
private readonly List<Ast> asts = [];
|
||||||
|
private readonly List<Manifest> manifests = [];
|
||||||
|
|
||||||
public void AddAst(Ast ast)
|
public void AddAst(Ast ast)
|
||||||
{
|
{
|
||||||
asts.Add(ast);
|
asts.Add(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddManifest(Manifest manifest)
|
||||||
|
{
|
||||||
|
manifests.Add(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
public ModuleGraph? Build(out List<Diagnostic> diagnostics)
|
public ModuleGraph? Build(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
diagnostics = [];
|
diagnostics = [];
|
||||||
|
|
||||||
var astModuleCache = new Dictionary<Ast, Module>();
|
|
||||||
var modules = new Dictionary<string, Module>();
|
var modules = new Dictionary<string, Module>();
|
||||||
|
|
||||||
// First pass: Register modules
|
// First pass: Register libraries
|
||||||
|
foreach (var manifest in manifests)
|
||||||
|
{
|
||||||
|
foreach (var manifestModule in manifest.Modules)
|
||||||
|
{
|
||||||
|
if (!modules.TryGetValue(manifestModule.Name, out var module))
|
||||||
|
{
|
||||||
|
module = new Module(manifestModule.Name);
|
||||||
|
modules.Add(manifestModule.Name, module);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var customType in manifestModule.CustomTypes)
|
||||||
|
{
|
||||||
|
module.AddCustomType(customType.Key, new Module.CustomTypeInfo(customType.Value.Type, customType.Value.Exported, Module.Source.Lib));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var customType in manifestModule.Identifiers)
|
||||||
|
{
|
||||||
|
module.AddIdentifier(customType.Key, new Module.IdentifierInfo(customType.Value.Type, customType.Value.Exported, Module.Source.Lib));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var astModuleCache = new Dictionary<Ast, Module>();
|
||||||
|
|
||||||
|
// First pass: Register modules from ast
|
||||||
foreach (var ast in asts)
|
foreach (var ast in asts)
|
||||||
{
|
{
|
||||||
if (!modules.ContainsKey(ast.ModuleName.Ident))
|
if (!modules.ContainsKey(ast.ModuleName.Ident))
|
||||||
@@ -119,7 +194,11 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
|
|||||||
var module = astModuleCache[ast];
|
var module = astModuleCache[ast];
|
||||||
|
|
||||||
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
||||||
module.AddCustomType(structDef.Name.Ident, new NubTypeStruct(module.Name, structDef.Name.Ident, structDef.Packed), structDef.Exported);
|
{
|
||||||
|
var type = new NubTypeStruct(module.Name, structDef.Name.Ident, structDef.Packed);
|
||||||
|
var info = new Module.CustomTypeInfo(type, structDef.Exported, Module.Source.Ast);
|
||||||
|
module.AddCustomType(structDef.Name.Ident, info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third pass: Resolve struct fields
|
// Third pass: Resolve struct fields
|
||||||
@@ -147,13 +226,15 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
|
|||||||
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList();
|
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList();
|
||||||
var returnType = ResolveType(funcDef.ReturnType, module.Name);
|
var returnType = ResolveType(funcDef.ReturnType, module.Name);
|
||||||
var funcType = NubTypeFunc.Get(parameters, returnType);
|
var funcType = NubTypeFunc.Get(parameters, returnType);
|
||||||
module.AddIdentifier(funcDef.Name.Ident, funcType, funcDef.Exported);
|
var info = new Module.IdentifierInfo(funcType, funcDef.Exported, Module.Source.Ast);
|
||||||
|
module.AddIdentifier(funcDef.Name.Ident, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var globalVariable in ast.Definitions.OfType<NodeDefinitionGlobalVariable>())
|
foreach (var globalVariable in ast.Definitions.OfType<NodeDefinitionGlobalVariable>())
|
||||||
{
|
{
|
||||||
var type = ResolveType(globalVariable.Type, module.Name);
|
var type = ResolveType(globalVariable.Type, module.Name);
|
||||||
module.AddIdentifier(globalVariable.Name.Ident, type, globalVariable.Exported);
|
var info = new Module.IdentifierInfo(type, globalVariable.Exported, Module.Source.Ast);
|
||||||
|
module.AddIdentifier(globalVariable.Name.Ident, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace Compiler;
|
|||||||
public abstract class NubType
|
public abstract class NubType
|
||||||
{
|
{
|
||||||
public abstract override string ToString();
|
public abstract override string ToString();
|
||||||
|
public string GetSignature() => TypeMangler.Encode(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class NubTypeVoid : NubType
|
public sealed class NubTypeVoid : NubType
|
||||||
@@ -214,7 +215,7 @@ static class TypeMangler
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NubTypeStruct st:
|
case NubTypeStruct st:
|
||||||
sb.Append($"T({st.Module}::{st.Name})").Append(st.Module).Append("::").Append(st.Name).Append(')');
|
sb.Append($"T({st.Module}::{st.Name})");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NubTypeFunc fn:
|
case NubTypeFunc fn:
|
||||||
|
|||||||
@@ -1,10 +1,77 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Text.Json;
|
||||||
using Compiler;
|
using Compiler;
|
||||||
|
|
||||||
|
var nubFiles = new List<string>();
|
||||||
|
var libFiles = new List<string>();
|
||||||
|
var compileLib = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
string arg = args[i];
|
||||||
|
|
||||||
|
if (arg.StartsWith("--type="))
|
||||||
|
{
|
||||||
|
var value = arg.Split("--type=")[1];
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case "lib":
|
||||||
|
compileLib = true;
|
||||||
|
break;
|
||||||
|
case "exe":
|
||||||
|
compileLib = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DiagnosticFormatter.Print(Diagnostic.Error("Type must be 'exe' or 'lib'").Build(), Console.Error);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (arg.EndsWith(".nub"))
|
||||||
|
{
|
||||||
|
nubFiles.Add(arg);
|
||||||
|
}
|
||||||
|
else if (arg.EndsWith(".nublib"))
|
||||||
|
{
|
||||||
|
libFiles.Add(arg);
|
||||||
|
}
|
||||||
|
else if (arg == "--help")
|
||||||
|
{
|
||||||
|
Console.WriteLine("""
|
||||||
|
Usage: nubc [options] <files>
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--type=exe Compile the input files into an executable (default)
|
||||||
|
--type=lib Compile the input files into a library
|
||||||
|
--help Show this help message
|
||||||
|
|
||||||
|
Files:
|
||||||
|
*.nub Nub source files to compile
|
||||||
|
*.nublib Precompiled Nub libraries to link
|
||||||
|
|
||||||
|
Example:
|
||||||
|
nubc --type=exe main.nub utils.nub math.nublib
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DiagnosticFormatter.Print(Diagnostic.Error($"Unrecognized option '{arg}'").Build(), Console.Error);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var moduleGraphBuilder = ModuleGraph.Create();
|
var moduleGraphBuilder = ModuleGraph.Create();
|
||||||
var asts = new List<Ast>();
|
var asts = new List<Ast>();
|
||||||
|
var archivePaths = new List<string>();
|
||||||
|
|
||||||
foreach (var fileName in args)
|
foreach (var libPath in libFiles)
|
||||||
|
{
|
||||||
|
var lib = ReadNublib(libPath);
|
||||||
|
archivePaths.Add(lib.ArchivePath);
|
||||||
|
moduleGraphBuilder.AddManifest(lib.Manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var fileName in nubFiles)
|
||||||
{
|
{
|
||||||
var file = File.ReadAllText(fileName);
|
var file = File.ReadAllText(fileName);
|
||||||
|
|
||||||
@@ -54,13 +121,97 @@ foreach (var ast in asts)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var output = Generator.Emit(functions, moduleGraph);
|
var output = Generator.Emit(functions, moduleGraph, compileLib);
|
||||||
|
|
||||||
Directory.Delete(".build", recursive: true);
|
if (Directory.Exists(".build"))
|
||||||
|
{
|
||||||
|
CleanDirectory(".build");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Directory.CreateDirectory(".build");
|
Directory.CreateDirectory(".build");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compileLib)
|
||||||
|
{
|
||||||
File.WriteAllText(".build/out.c", output);
|
File.WriteAllText(".build/out.c", output);
|
||||||
|
Process.Start("gcc", ["-Og", "-fvisibility=hidden", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit();
|
||||||
Process.Start("gcc", ["-Og", "-g", "-o", ".build/out", ".build/out.c", "-fvisibility=hidden","-fno-builtin"]);
|
Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit();
|
||||||
|
WriteNublib(".build/out.nublib", ".build/out.a", moduleGraph.CreateManifest());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
File.WriteAllText(".build/out.c", output);
|
||||||
|
Process.Start("gcc", ["-Og", "-fvisibility=hidden", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
static void CleanDirectory(string dirName)
|
||||||
|
{
|
||||||
|
var dir = new DirectoryInfo(dirName);
|
||||||
|
|
||||||
|
foreach (var file in dir.GetFiles())
|
||||||
|
{
|
||||||
|
file.Delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var subdir in dir.GetDirectories())
|
||||||
|
{
|
||||||
|
CleanDirectory(subdir.FullName);
|
||||||
|
subdir.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteNublib(string outputPath, string archivePath, ModuleGraph.Manifest manifest)
|
||||||
|
{
|
||||||
|
using var fs = new FileStream(outputPath, FileMode.Create);
|
||||||
|
using var zip = new ZipArchive(fs, ZipArchiveMode.Create);
|
||||||
|
|
||||||
|
var serialized = JsonSerializer.Serialize(manifest, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
WriteIndented = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var manifestEntry = zip.CreateEntry("manifest.json");
|
||||||
|
using (var writer = new StreamWriter(manifestEntry.Open()))
|
||||||
|
{
|
||||||
|
writer.Write(JsonSerializer.Serialize(manifest));
|
||||||
|
}
|
||||||
|
|
||||||
|
var archiveEntry = zip.CreateEntry("lib.a");
|
||||||
|
|
||||||
|
using var entryStream = archiveEntry.Open();
|
||||||
|
using var fileStream = File.OpenRead(archivePath);
|
||||||
|
|
||||||
|
fileStream.CopyTo(entryStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NublibLoadResult ReadNublib(string nublibPath)
|
||||||
|
{
|
||||||
|
using var fs = new FileStream(nublibPath, FileMode.Open, FileAccess.Read);
|
||||||
|
using var zip = new ZipArchive(fs, ZipArchiveMode.Read);
|
||||||
|
|
||||||
|
var manifestEntry = zip.GetEntry("manifest.json") ?? throw new FileNotFoundException("Manifest not found in nublib", "manifest.json");
|
||||||
|
|
||||||
|
ModuleGraph.Manifest manifest;
|
||||||
|
using (var reader = new StreamReader(manifestEntry.Open()))
|
||||||
|
{
|
||||||
|
var json = reader.ReadToEnd();
|
||||||
|
manifest = JsonSerializer.Deserialize<ModuleGraph.Manifest>(json) ?? throw new InvalidDataException("Failed to deserialize manifest.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
var archiveEntry = zip.Entries.FirstOrDefault(e => e.Name.EndsWith(".a")) ?? throw new FileNotFoundException("Archive not found in nublib", "*.a");
|
||||||
|
|
||||||
|
string tempArchivePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".a");
|
||||||
|
using (var entryStream = archiveEntry.Open())
|
||||||
|
using (var tempFile = File.Create(tempArchivePath))
|
||||||
|
{
|
||||||
|
entryStream.CopyTo(tempFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NublibLoadResult(manifest, tempArchivePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record NublibLoadResult(ModuleGraph.Manifest Manifest, string ArchivePath);
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public sealed class TypeChecker(string fileName, string moduleName, NodeDefiniti
|
|||||||
|
|
||||||
foreach (var parameter in function.Parameters)
|
foreach (var parameter in function.Parameters)
|
||||||
{
|
{
|
||||||
|
scope.DeclareIdentifier(parameter.Name.Ident, ResolveType(parameter.Type));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parameters.Add(CheckDefinitionFuncParameter(parameter));
|
parameters.Add(CheckDefinitionFuncParameter(parameter));
|
||||||
|
|||||||
11
examples/build.sh
Executable file
11
examples/build.sh
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
pushd math
|
||||||
|
|
||||||
|
dotnet run --project ../../compiler math.nub --type=lib
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd program
|
||||||
|
|
||||||
|
dotnet run --project ../../compiler main.nub ../math/.build/out.nublib
|
||||||
|
|
||||||
|
popd
|
||||||
BIN
examples/math/.build/out.a
Normal file
BIN
examples/math/.build/out.a
Normal file
Binary file not shown.
24
examples/math/.build/out.c
Normal file
24
examples/math/.build/out.c
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include <float.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct nub_core_string
|
||||||
|
{
|
||||||
|
const char *data;
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int32_t nub_math_add_cc3fc9d68812b10d(int32_t, int32_t);
|
||||||
|
|
||||||
|
|
||||||
|
int32_t nub_math_add_cc3fc9d68812b10d(int32_t a, int32_t b)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
return (a + b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BIN
examples/math/.build/out.nublib
Normal file
BIN
examples/math/.build/out.nublib
Normal file
Binary file not shown.
BIN
examples/math/.build/out.o
Normal file
BIN
examples/math/.build/out.o
Normal file
Binary file not shown.
6
examples/math/math.nub
Normal file
6
examples/math/math.nub
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module math
|
||||||
|
|
||||||
|
export func add(a: i32 b: i32): i32
|
||||||
|
{
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
6
examples/program/main.nub
Normal file
6
examples/program/main.nub
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
func main(): i32
|
||||||
|
{
|
||||||
|
return math::add(1, 2)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user