...
This commit is contained in:
17
src/compiler/CLI/CLI.csproj
Normal file
17
src/compiler/CLI/CLI.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>nub</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishAot>true</PublishAot>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Generation\Generation.csproj" />
|
||||
<ProjectReference Include="..\Syntax\Syntax.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
52
src/compiler/CLI/GCC.cs
Normal file
52
src/compiler/CLI/GCC.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CLI;
|
||||
|
||||
public static class GCC
|
||||
{
|
||||
public static async Task<bool> Assemble(string asmPath, string objPath)
|
||||
{
|
||||
using var gccProcess = new Process();
|
||||
gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-g", "-c", asmPath, "-o", objPath])
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
gccProcess.Start();
|
||||
await gccProcess.WaitForExitAsync();
|
||||
|
||||
var gccErrors = await gccProcess.StandardError.ReadToEndAsync();
|
||||
if (!string.IsNullOrWhiteSpace(gccErrors))
|
||||
{
|
||||
await Console.Error.WriteLineAsync("gcc error when assembling:\n" + gccErrors);
|
||||
}
|
||||
|
||||
return gccProcess.ExitCode == 0;
|
||||
}
|
||||
|
||||
public static async Task<bool> Link(List<string> objectFiles, string outputPath)
|
||||
{
|
||||
using var gccProcess = new Process();
|
||||
gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-g", "-nostartfiles", "-o", outputPath, ..objectFiles])
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
gccProcess.Start();
|
||||
await gccProcess.WaitForExitAsync();
|
||||
|
||||
var gccErrors = await gccProcess.StandardError.ReadToEndAsync();
|
||||
if (!string.IsNullOrWhiteSpace(gccErrors))
|
||||
{
|
||||
await Console.Error.WriteLineAsync("gcc error when linking:\n" + gccErrors);
|
||||
}
|
||||
|
||||
return gccProcess.ExitCode == 0;
|
||||
}
|
||||
}
|
||||
118
src/compiler/CLI/Program.cs
Normal file
118
src/compiler/CLI/Program.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Diagnostics;
|
||||
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 BIN_INT_DIR = "bin-int";
|
||||
|
||||
if (Directory.Exists(BIN_DIR))
|
||||
{
|
||||
Directory.Delete(BIN_DIR, true);
|
||||
}
|
||||
|
||||
if (Directory.Exists(BIN_INT_DIR))
|
||||
{
|
||||
Directory.Delete(BIN_INT_DIR, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(BIN_DIR);
|
||||
Directory.CreateDirectory(BIN_INT_DIR);
|
||||
|
||||
var diagnostics = new List<Diagnostic>();
|
||||
var syntaxTrees = new List<SyntaxTree>();
|
||||
|
||||
foreach (var file in args)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
Console.Error.WriteLine($"File '{file}' does not exist");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var file in args)
|
||||
{
|
||||
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, file, out var parseDiagnostics);
|
||||
diagnostics.AddRange(parseDiagnostics);
|
||||
|
||||
if (syntaxTree != null)
|
||||
{
|
||||
syntaxTrees.Add(syntaxTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
var definitionTable = new DefinitionTable(syntaxTrees);
|
||||
|
||||
var boundSyntaxTrees = new List<BoundSyntaxTree>();
|
||||
|
||||
foreach (var syntaxTree in syntaxTrees)
|
||||
{
|
||||
boundSyntaxTrees.Add(Binder.Bind(syntaxTree, definitionTable));
|
||||
}
|
||||
|
||||
var boundDefinitionTable = new BoundDefinitionTable(boundSyntaxTrees);
|
||||
|
||||
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>();
|
||||
|
||||
objectFiles.Add("/home/oliste/repos/nub-lang/src/runtime/runtime.o");
|
||||
|
||||
foreach (var boundSyntaxTree in boundSyntaxTrees)
|
||||
{
|
||||
var relativeFilePath = Path.GetRelativePath(Environment.CurrentDirectory, boundSyntaxTree.FilePath);
|
||||
var outputPath = Path.Combine(BIN_INT_DIR, relativeFilePath);
|
||||
|
||||
var outputDirectory = Path.GetDirectoryName(outputPath);
|
||||
Debug.Assert(!string.IsNullOrWhiteSpace(outputDirectory));
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
|
||||
var ssa = QBEGenerator.Emit(boundSyntaxTree, boundDefinitionTable);
|
||||
var ssaPath = Path.ChangeExtension(outputPath, "ssa");
|
||||
File.WriteAllText(ssaPath, ssa);
|
||||
|
||||
var asm = await QBE.Invoke(ssa);
|
||||
if (asm == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var asmPath = Path.ChangeExtension(outputPath, "s");
|
||||
await File.WriteAllTextAsync(asmPath, asm);
|
||||
|
||||
var objPath = Path.ChangeExtension(outputPath, "o");
|
||||
var asmSuccess = await GCC.Assemble(asmPath, objPath);
|
||||
if (!asmSuccess)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
objectFiles.Add(objPath);
|
||||
}
|
||||
|
||||
var linkSuccess = await GCC.Link(objectFiles, Path.Combine(BIN_DIR, "out"));
|
||||
return linkSuccess ? 0 : 1;
|
||||
37
src/compiler/CLI/QBE.cs
Normal file
37
src/compiler/CLI/QBE.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CLI;
|
||||
|
||||
public static class QBE
|
||||
{
|
||||
public static async Task<string?> Invoke(string ssa)
|
||||
{
|
||||
using var qbeProcess = new Process();
|
||||
qbeProcess.StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "qbe",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
qbeProcess.Start();
|
||||
|
||||
await qbeProcess.StandardInput.WriteAsync(ssa);
|
||||
qbeProcess.StandardInput.Close();
|
||||
|
||||
await qbeProcess.WaitForExitAsync();
|
||||
|
||||
var qbeErrors = await qbeProcess.StandardError.ReadToEndAsync();
|
||||
if (!string.IsNullOrWhiteSpace(qbeErrors))
|
||||
{
|
||||
await Console.Error.WriteLineAsync("qbe error:\n" + qbeErrors);
|
||||
}
|
||||
|
||||
var asm = await qbeProcess.StandardOutput.ReadToEndAsync();
|
||||
|
||||
return qbeProcess.ExitCode == 0 ? asm : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user