Files
nub-lang/src/compiler/NubLang.CLI/Program.cs
nub31 d993581361 ...
2025-07-22 23:20:56 +02:00

213 lines
4.9 KiB
C#

using System.Reflection;
using NubLang.CLI;
using NubLang.Code;
using NubLang.Common;
using NubLang.Diagnostics;
using NubLang.Generation;
using NubLang.Generation.QBE;
using NubLang.Parsing;
using NubLang.Parsing.Syntax;
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_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(new SourceFile(arg));
}
}
var diagnostics = new List<Diagnostic>();
var syntaxTrees = new List<SyntaxTree>();
foreach (var file in options.Files)
{
if (!File.Exists(file.Path))
{
Console.Error.WriteLine($"File '{file}' does not exist");
return 1;
}
}
foreach (var file in options.Files)
{
var tokenizer = new Tokenizer(file.GetText());
var tokens = tokenizer.Tokenize();
var parser = new Parser();
var syntaxTree = parser.Parse(tokens);
diagnostics.AddRange(parser.GetDiagnostics());
syntaxTrees.Add(syntaxTree);
}
var definitionTable = new DefinitionTable(syntaxTrees);
var typedSyntaxTrees = new List<TypedSyntaxTree>();
foreach (var syntaxTree in syntaxTrees)
{
var typeChecker = new TypeChecker(syntaxTree, definitionTable);
var typedSyntaxTree = typeChecker.Check();
diagnostics.AddRange(typeChecker.GetDiagnostics());
typedSyntaxTrees.Add(typedSyntaxTree);
}
foreach (var diagnostic in diagnostics)
{
Console.Error.WriteLine(diagnostic.FormatANSI());
}
if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error))
{
return 1;
}
var typedDefinitionTable = new TypedDefinitionTable(typedSyntaxTrees);
var objectFiles = new List<string>();
foreach (var syntaxTree in typedSyntaxTrees)
{
var outFileName = HexString.CreateUnique(8);
var generator = new QBEGenerator(syntaxTree, typedDefinitionTable);
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 NubLang.CLI
{
internal class RuntimeCreationException(string message) : Exception(message);
}