This repository has been archived on 2025-10-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nub-lang-archive-2/src/compiler/CLI/Program.cs
nub31 69f93cac03 ...
2025-07-06 23:25:54 +02:00

215 lines
5.1 KiB
C#

using System.Reflection;
using CLI;
using NubLang;
using NubLang.Diagnostics;
using NubLang.Generation.QBE;
using NubLang.Syntax.Binding.Node;
using NubLang.Syntax.Parsing;
using NubLang.Syntax.Parsing.Node;
using NubLang.Syntax.Tokenization;
using Binder = NubLang.Syntax.Binding.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 tokenizer = new Tokenizer(sourceText);
var tokenizeResult = tokenizer.Tokenize(out var tokenizerDiagnostics);
diagnostics.AddRange(tokenizerDiagnostics);
var parser = new Parser(tokenizeResult);
var syntaxTree = parser.Parse();
diagnostics.AddRange(syntaxTree.Diagnostics);
syntaxTrees[file] = syntaxTree;
}
var definitionTable = new DefinitionTable(syntaxTrees.Values);
var boundSyntaxTrees = new Dictionary<string, BoundSyntaxTree>();
foreach (var (file, syntaxTree) in syntaxTrees)
{
var binder = new Binder(syntaxTree, definitionTable);
var boundSyntaxTree = binder.Bind();
diagnostics.AddRange(boundSyntaxTree.Diagnostics);
boundSyntaxTrees[file] = boundSyntaxTree;
}
foreach (var diagnostic in diagnostics)
{
Console.Error.WriteLine(diagnostic.FormatANSI());
}
if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error))
{
return 1;
}
var boundDefinitionTable = new BoundDefinitionTable(boundSyntaxTrees.Values);
var objectFiles = new List<string>();
foreach (var file in options.Files)
{
var outFileName = $"{HexString.CreateUnique(8)}_{Path.GetFileNameWithoutExtension(file)}";
var generator = new QBEGenerator(boundSyntaxTrees[file], boundDefinitionTable, file);
var ssa = generator.Emit();
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;
}
namespace CLI
{
internal class RuntimeCreationException(string message) : Exception(message);
}