186 lines
5.1 KiB
C#
186 lines
5.1 KiB
C#
using System.Diagnostics;
|
|
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.CreateBuilder();
|
|
var asts = new List<Ast>();
|
|
var archivePaths = new List<string>();
|
|
|
|
foreach (var libPath in libFiles)
|
|
{
|
|
var lib = NubLib.Unpack(libPath);
|
|
archivePaths.Add(lib.ArchivePath);
|
|
moduleGraphBuilder.AddManifest(lib.Manifest);
|
|
}
|
|
|
|
foreach (var fileName in nubFiles)
|
|
{
|
|
var file = File.ReadAllText(fileName);
|
|
|
|
var tokens = Tokenizer.Tokenize(fileName, file, out var tokenizerDiagnostics);
|
|
|
|
foreach (var diagnostic in tokenizerDiagnostics)
|
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
|
|
|
if (tokens == null)
|
|
return 1;
|
|
|
|
var ast = Parser.Parse(fileName, tokens, out var parserDiagnostics);
|
|
|
|
foreach (var diagnostic in parserDiagnostics)
|
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
|
|
|
if (ast == null)
|
|
return 1;
|
|
|
|
moduleGraphBuilder.AddAst(ast);
|
|
asts.Add(ast);
|
|
}
|
|
|
|
var moduleGraph = moduleGraphBuilder.Build(out var moduleGraphDiagnostics);
|
|
|
|
foreach (var diagnostic in moduleGraphDiagnostics)
|
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
|
|
|
if (moduleGraph == null)
|
|
return 1;
|
|
|
|
var functions = new List<TypedNodeDefinitionFunc>();
|
|
|
|
foreach (var ast in asts)
|
|
{
|
|
foreach (var func in ast.Definitions.OfType<NodeDefinitionFunc>())
|
|
{
|
|
var typedFunction = TypeChecker.CheckFunction(ast.FileName, ast.ModuleName.Ident, func, moduleGraph, out var typeCheckerDiagnostics);
|
|
|
|
foreach (var diagnostic in typeCheckerDiagnostics)
|
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
|
|
|
if (typedFunction == null)
|
|
return 1;
|
|
|
|
functions.Add(typedFunction);
|
|
}
|
|
}
|
|
|
|
if (Directory.Exists(".build"))
|
|
{
|
|
CleanDirectory(".build");
|
|
}
|
|
else
|
|
{
|
|
Directory.CreateDirectory(".build");
|
|
}
|
|
|
|
string? entryPoint = null;
|
|
|
|
if (!compileLib)
|
|
{
|
|
if (!moduleGraph.TryResolveIdentifier("main", "main", true, out var info) || info.Type is not NubTypeFunc entryPointType)
|
|
{
|
|
DiagnosticFormatter.Print(Diagnostic.Error("func main::main(): i32 is not defined. If you wanted to compile as a library, specify --type=lib").Build(), Console.Error);
|
|
return 1;
|
|
}
|
|
|
|
if (!entryPointType.ReturnType.IsAssignableTo(NubTypeSInt.Get(32)))
|
|
{
|
|
DiagnosticFormatter.Print(Diagnostic.Error($"Entrypoint must return an i32 (currently '{entryPointType.ReturnType}')").Build(), Console.Error);
|
|
return 1;
|
|
}
|
|
|
|
if (entryPointType.Parameters.Any())
|
|
{
|
|
DiagnosticFormatter.Print(Diagnostic.Error($"Entrypoint must not take any parameters").Build(), Console.Error);
|
|
return 1;
|
|
}
|
|
|
|
entryPoint = info.MangledName;
|
|
}
|
|
|
|
var output = Generator.Emit(functions, moduleGraph, entryPoint);
|
|
File.WriteAllText(".build/out.c", output);
|
|
|
|
if (compileLib)
|
|
{
|
|
Process.Start("gcc", ["-Og", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit();
|
|
Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit();
|
|
NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph));
|
|
}
|
|
else
|
|
{
|
|
Process.Start("gcc", ["-Og", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit();
|
|
}
|
|
|
|
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();
|
|
}
|
|
} |