This repository has been archived on 2025-10-23. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nub-lang-archive/src/compiler/CLI/Program.cs
nub31 a29cb6d9db ...
2025-07-02 18:58:28 +02:00

210 lines
5.0 KiB
C#

using System.Reflection;
using CLI;
using Generation.QBE;
using Syntax;
using Syntax.Diagnostics;
using Syntax.Parsing;
using Syntax.Tokenization;
using Syntax.Typing;
using Binder = Syntax.Typing.Binder;
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_DEBUG_DIR = Path.Join(INT_DIR, "debug");
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_DEBUG_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")
{
i++;
if (i >= args.Length - 1)
{
Console.Error.WriteLine($"{arg} must be followed by a value");
return 1;
}
options.OutputPath = args[i];
}
else if (arg == "-c")
{
i++;
options.Link = false;
}
else
{
options.Files.Add(arg);
}
}
var diagnostics = new List<Diagnostic>();
var syntaxTrees = new Dictionary<string, SyntaxTree>();
foreach (var file in options.Files)
{
if (!File.Exists(file))
{
Console.Error.WriteLine($"File '{file}' does not exist");
return 1;
}
}
foreach (var file in options.Files)
{
var content = File.ReadAllText(file);
var sourceText = new SourceText(file, content);
var tokenizeResult = Tokenizer.Tokenize(sourceText, out var tokenizerDiagnostics);
diagnostics.AddRange(tokenizerDiagnostics);
var syntaxTree = Parser.ParseFile(tokenizeResult, out var parseDiagnostics);
diagnostics.AddRange(parseDiagnostics);
if (syntaxTree != null)
{
syntaxTrees[file] = syntaxTree;
}
}
var definitionTable = new DefinitionTable(syntaxTrees.Values);
var boundSyntaxTrees = new Dictionary<string, BoundSyntaxTree>();
foreach (var (file, syntaxTree) in syntaxTrees)
{
var boundSyntaxTree = Binder.Bind(syntaxTree, definitionTable, out var binderDiagnostics);
diagnostics.AddRange(binderDiagnostics);
boundSyntaxTrees[file] = boundSyntaxTree;
}
var boundDefinitionTable = new BoundDefinitionTable(boundSyntaxTrees.Values);
foreach (var diagnostic in diagnostics)
{
Console.Error.WriteLine(diagnostic.FormatANSI());
}
if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error))
{
return 1;
}
var objectFiles = new List<string>();
foreach (var file in options.Files)
{
var outFileName = $"{HexString.CreateUnique(8)}_{Path.GetFileNameWithoutExtension(file)}";
var ssa = QBEGenerator.Emit(boundSyntaxTrees[file], boundDefinitionTable);
File.WriteAllText(Path.Join(INT_DEBUG_DIR, $"{outFileName}.ssa"), ssa);
var asm = await QBE.Invoke(ssa);
if (asm == null)
{
return 1;
}
File.WriteAllText(Path.Join(INT_DEBUG_DIR, $"{outFileName}.s"), asm);
var fileName = Path.Join(INT_OBJECT_DIR, $"{outFileName}.o");
var asmSuccess = await GCC.Assemble(asm, fileName);
if (!asmSuccess)
{
return 1;
}
objectFiles.Add(fileName);
}
if (options.CustomRuntime == 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'");
return 1;
}
objectFiles.Add(options.CustomRuntime);
}
if (options.Link)
{
var outPath = options.OutputPath ?? Path.Join(BIN_DIR, "out");
var linkResult = await GCC.Link(outPath, objectFiles);
if (!linkResult)
{
return 1;
}
}
else
{
var outPath = options.OutputPath ?? Path.Join(BIN_DIR, "out.a");
var archiveResult = await Archive.Invoke(outPath, objectFiles);
if (!archiveResult)
{
return 1;
}
}
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;
}
internal class RuntimeCreationException(string message) : Exception(message);