diff --git a/example/.gitignore b/example/.gitignore index 145bd1e..a8b1f05 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,2 +1,3 @@ .build +out.a out \ No newline at end of file diff --git a/example/makefile b/example/makefile index 4932cb8..f63b491 100644 --- a/example/makefile +++ b/example/makefile @@ -1,18 +1,16 @@ -CC = gcc -NUBC = ../src/compiler/NubLang.CLI/bin/Debug/net9.0/nubc +NUBC = ../compiler/NubLang.CLI/bin/Debug/net9.0/nubc -.build/out: .build/out.a - $(CC) -ffreestanding -g -o .build/out .build/out.a +out: out.a + gcc -nostartfiles -o out x86_64.s out.a + rm out.a -.build/out.a: $(NUBC) src/main.nub - $(NUBC) src/main.nub +out.a: $(NUBC) main.nub + $(NUBC) main.nub +.PHONY: $(NUBC) $(NUBC): - dotnet build ../src/compiler/NubLang.CLI/NubLang.CLI.csproj - -run: .build/out - ./.build/out + dotnet build ../compiler/NubLang.CLI/NubLang.CLI.csproj clean: @rm -r .build 2>/dev/null || true - @rm out 2>/dev/null || true + @rm out.a 2>/dev/null || true diff --git a/example/src/main.nub b/example/src/main.nub deleted file mode 100644 index 0372339..0000000 --- a/example/src/main.nub +++ /dev/null @@ -1,15 +0,0 @@ -module main - -extern func puts(text: cstring) - -struct Test -{ - -} - -func main(args: []cstring): i64 -{ - puts("test") - - return 0 -} diff --git a/src/compiler/.gitignore b/src/compiler/.gitignore deleted file mode 100644 index c6cc67a..0000000 --- a/src/compiler/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# Common IntelliJ Platform excludes - -# User specific -**/.idea/**/workspace.xml -**/.idea/**/tasks.xml -**/.idea/shelf/* -**/.idea/dictionaries -**/.idea/httpRequests/ - -# Sensitive or high-churn files -**/.idea/**/dataSources/ -**/.idea/**/dataSources.ids -**/.idea/**/dataSources.xml -**/.idea/**/dataSources.local.xml -**/.idea/**/sqlDataSources.xml -**/.idea/**/dynamic.xml - -# Rider -# Rider auto-generates .iml files, and contentModel.xml -**/.idea/**/*.iml -**/.idea/**/contentModel.xml -**/.idea/**/modules.xml - -*.suo -*.user -.vs/ -[Bb]in/ -[Oo]bj/ -_UpgradeReport_Files/ -[Pp]ackages/ - -Thumbs.db -Desktop.ini -.DS_Store \ No newline at end of file diff --git a/src/compiler/.idea/.idea.Compiler/.idea/.gitignore b/src/compiler/.idea/.idea.Compiler/.idea/.gitignore deleted file mode 100644 index cda1cf4..0000000 --- a/src/compiler/.idea/.idea.Compiler/.idea/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Rider ignored files -/modules.xml -/projectSettingsUpdater.xml -/contentModel.xml -/.idea.Compiler.iml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/src/compiler/.idea/.idea.Compiler/.idea/.name b/src/compiler/.idea/.idea.Compiler/.idea/.name deleted file mode 100644 index b92b7a3..0000000 --- a/src/compiler/.idea/.idea.Compiler/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -Compiler \ No newline at end of file diff --git a/src/compiler/.idea/.idea.Compiler/.idea/encodings.xml b/src/compiler/.idea/.idea.Compiler/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/src/compiler/.idea/.idea.Compiler/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml b/src/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml deleted file mode 100644 index 2135b43..0000000 --- a/src/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Runtime - - - - - \ No newline at end of file diff --git a/src/compiler/.idea/.idea.Compiler/.idea/vcs.xml b/src/compiler/.idea/.idea.Compiler/.idea/vcs.xml deleted file mode 100644 index b2bdec2..0000000 --- a/src/compiler/.idea/.idea.Compiler/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/compiler/Compiler.sln b/src/compiler/Compiler.sln deleted file mode 100644 index 292d374..0000000 --- a/src/compiler/Compiler.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang", "NubLang\NubLang.csproj", "{5047E21F-590D-4CB3-AFF3-064316485009}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5047E21F-590D-4CB3-AFF3-064316485009}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5047E21F-590D-4CB3-AFF3-064316485009}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5047E21F-590D-4CB3-AFF3-064316485009}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5047E21F-590D-4CB3-AFF3-064316485009}.Release|Any CPU.Build.0 = Release|Any CPU - {A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/src/compiler/NubLang.CLI/Archive.cs b/src/compiler/NubLang.CLI/Archive.cs deleted file mode 100644 index 59919c5..0000000 --- a/src/compiler/NubLang.CLI/Archive.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Diagnostics; - -namespace NubLang.CLI; - -public static class Archive -{ - public static async Task Invoke(string fileName, params IEnumerable objectFiles) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("ar", ["rcs", fileName, ..objectFiles]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - process.Start(); - await process.WaitForExitAsync(); - - var errors = await process.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(errors)) - { - await Console.Error.WriteLineAsync("ar error when archiving:\n" + errors); - } - - return process.ExitCode == 0; - } -} \ No newline at end of file diff --git a/src/compiler/NubLang.CLI/GCC.cs b/src/compiler/NubLang.CLI/GCC.cs deleted file mode 100644 index db54016..0000000 --- a/src/compiler/NubLang.CLI/GCC.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Diagnostics; - -namespace NubLang.CLI; - -public static class GCC -{ - public static async Task Assemble(string asmPath, string outPath) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("gcc", ["-xassembler", "-g", "-c", "-o", outPath, asmPath]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - process.Start(); - - await process.WaitForExitAsync(); - - var errors = await process.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(errors)) - { - await Console.Error.WriteLineAsync("gcc error when assembling:\n" + errors); - } - - return process.ExitCode == 0; - } -} \ No newline at end of file diff --git a/src/compiler/NubLang.CLI/NubLang.CLI.csproj b/src/compiler/NubLang.CLI/NubLang.CLI.csproj deleted file mode 100644 index 2f25112..0000000 --- a/src/compiler/NubLang.CLI/NubLang.CLI.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - nubc - Exe - net9.0 - enable - enable - true - - - - - - - - - - - - - - diff --git a/src/compiler/NubLang.CLI/Options.cs b/src/compiler/NubLang.CLI/Options.cs deleted file mode 100644 index acef2bc..0000000 --- a/src/compiler/NubLang.CLI/Options.cs +++ /dev/null @@ -1,9 +0,0 @@ -using NubLang.Code; - -namespace NubLang.CLI; - -public class Options -{ - public string? OutputPath { get; set; } - public List Files { get; } = []; -} \ No newline at end of file diff --git a/src/compiler/NubLang.CLI/Program.cs b/src/compiler/NubLang.CLI/Program.cs deleted file mode 100644 index 55dc9d0..0000000 --- a/src/compiler/NubLang.CLI/Program.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Reflection; -using NubLang.CLI; -using NubLang.Code; -using NubLang.Diagnostics; -using NubLang.Generation.QBE; -using NubLang.Parsing; -using NubLang.Parsing.Syntax; -using NubLang.Tokenization; -using NubLang.TypeChecking; -using Module = NubLang.TypeChecking.Module; - -var options = new Options(); - -for (var i = 0; i < args.Length; i++) -{ - var arg = args[i]; - switch (arg) - { - case "-o": - { - ++i; - if (i >= args.Length) - { - return 1; - } - - options.OutputPath = args[i]; - break; - } - default: - { - options.Files.Add(new SourceFile(arg)); - break; - } - } -} - -foreach (var file in options.Files) -{ - if (!File.Exists(file.Path)) - { - Console.Error.WriteLine($"File '{file}' does not exist"); - return 1; - } -} - -var diagnostics = new List(); - -var syntaxTrees = new List(); -foreach (var file in options.Files) -{ - var tokenizer = new Tokenizer(file); - var tokens = tokenizer.Tokenize().ToList(); - diagnostics.AddRange(tokenizer.GetDiagnostics()); - - var parser = new Parser(); - var syntaxTree = parser.Parse(tokens); - - diagnostics.AddRange(parser.GetDiagnostics()); - - syntaxTrees.Add(syntaxTree); -} - -var moduleSignatures = ModuleSignature.CollectFromSyntaxTrees(syntaxTrees); -var modules = Module.CollectFromSyntaxTrees(syntaxTrees); - -var typedModules = new List(); - -foreach (var module in modules) -{ - var typeChecker = new TypeChecker(module, moduleSignatures); - var typedModule = typeChecker.CheckModule(); - diagnostics.AddRange(typeChecker.GetDiagnostics()); - typedModules.Add(typedModule); -} - -foreach (var diagnostic in diagnostics) -{ - Console.Error.WriteLine(diagnostic.FormatANSI()); -} - -if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)) -{ - return 1; -} - -var objectFiles = new List(); - -for (var i = 0; i < typedModules.Count; i++) -{ - var typedModule = typedModules[i]; - var outFileName = Path.Combine(".build", "code", Path.ChangeExtension(options.Files[i].Path, null)); - - var outFileDir = Path.GetDirectoryName(outFileName); - if (!string.IsNullOrEmpty(outFileDir)) - { - Directory.CreateDirectory(outFileDir); - } - - var generator = new QBEGenerator(typedModule, moduleSignatures); - var ssa = generator.Emit(); - - var ssaFilePath = Path.ChangeExtension(outFileName, "ssa"); - File.WriteAllText(ssaFilePath, ssa); - - var asmFilePath = Path.ChangeExtension(outFileName, "s"); - var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath); - - if (!qbeSuccess) - { - return 1; - } - - var objFilePath = Path.ChangeExtension(outFileName, "o"); - var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath); - - if (!asmSuccess) - { - return 1; - } - - objectFiles.Add(objFilePath); -} - -var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames(); - -string[] runtimeObjects = ["runtime.o", "x64.o"]; - -foreach (var runtimeObject in runtimeObjects) -{ - var runtime = resources.First(r => r.EndsWith(runtimeObject)); - - await using var reader = Assembly - .GetExecutingAssembly() - .GetManifestResourceStream(runtime); - - if (reader == null) - { - Console.Error.WriteLine($"Cannot open read stream to '{runtimeObject}'"); - return 1; - } - - var runtimePath = Path.Combine(".build", "runtime", runtimeObject); - var runtimeDir = Path.GetDirectoryName(runtimePath); - if (!string.IsNullOrEmpty(runtimeDir)) - { - Directory.CreateDirectory(runtimeDir); - } - - await using var writer = new FileStream(runtimePath, FileMode.Create); - - reader.CopyTo(writer); - - objectFiles.Add(runtimePath); -} - -var outPath = options.OutputPath ?? Path.Combine(".build", "out.a"); -var outDir = Path.GetDirectoryName(outPath); -if (!string.IsNullOrEmpty(outDir)) -{ - Directory.CreateDirectory(outDir); -} - -var archiveResult = await Archive.Invoke(outPath, objectFiles); -if (!archiveResult) -{ - return 1; -} - -return 0; \ No newline at end of file diff --git a/src/compiler/NubLang.CLI/QBE.cs b/src/compiler/NubLang.CLI/QBE.cs deleted file mode 100644 index 52e37c5..0000000 --- a/src/compiler/NubLang.CLI/QBE.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Diagnostics; - -namespace NubLang.CLI; - -public static class QBE -{ - public static async Task Invoke(string ssaPath, string outPath) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("qbe", ["-o", outPath, ssaPath]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - process.Start(); - - await process.WaitForExitAsync(); - - var errors = await process.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(errors)) - { - await Console.Error.WriteLineAsync("qbe error:\n" + errors); - } - - return process.ExitCode == 0; - } -} \ No newline at end of file diff --git a/src/compiler/NubLang.CLI/assets/runtime.o b/src/compiler/NubLang.CLI/assets/runtime.o deleted file mode 100644 index 3024a90..0000000 Binary files a/src/compiler/NubLang.CLI/assets/runtime.o and /dev/null differ diff --git a/src/compiler/NubLang.CLI/assets/x64.o b/src/compiler/NubLang.CLI/assets/x64.o deleted file mode 100644 index 6724856..0000000 Binary files a/src/compiler/NubLang.CLI/assets/x64.o and /dev/null differ diff --git a/src/compiler/NubLang/Code/SourceFile.cs b/src/compiler/NubLang/Code/SourceFile.cs deleted file mode 100644 index 48370c4..0000000 --- a/src/compiler/NubLang/Code/SourceFile.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace NubLang.Code; - -public class SourceFile -{ - private string? _content; - - public SourceFile(string path) - { - Path = path ?? throw new ArgumentNullException(nameof(path)); - } - - public string Path { get; } - - public string GetText() => _content ??= File.ReadAllText(Path); - public override string ToString() => Path; - - public override bool Equals(object? obj) - { - return obj is SourceFile other && other.Path == Path; - } - - public override int GetHashCode() - { - return HashCode.Combine(typeof(SourceFile), Path); - } - - public static bool operator ==(SourceFile? left, SourceFile? right) => Equals(left, right); - public static bool operator !=(SourceFile? left, SourceFile? right) => !Equals(left, right); -} - -public class SourceFileSpan -{ - public SourceFileSpan(SourceFile sourceFile, SourceSpan span) - { - SourceFile = sourceFile; - Span = span; - } - - public SourceFile SourceFile { get; } - public SourceSpan Span { get; } -} \ No newline at end of file diff --git a/src/compiler/NubLang/Code/SourceLocation.cs b/src/compiler/NubLang/Code/SourceLocation.cs deleted file mode 100644 index 38bf900..0000000 --- a/src/compiler/NubLang/Code/SourceLocation.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace NubLang.Code; - -public readonly struct SourceLocation : IEquatable -{ - public static SourceLocation Zero => new(0, 0); - - public SourceLocation(int line, int column) - { - Line = line; - Column = column; - } - - public int Line { get; } - public int Column { get; } - - public override string ToString() - { - return $"{Line}:{Column}"; - } - - public override bool Equals(object? obj) - { - return obj is SourceLocation other && Equals(other); - } - - public bool Equals(SourceLocation other) - { - return Line == other.Line && Column == other.Column; - } - - public override int GetHashCode() - { - return HashCode.Combine(typeof(SourceLocation), Line, Column); - } - - public static bool operator ==(SourceLocation left, SourceLocation right) => Equals(left, right); - public static bool operator !=(SourceLocation left, SourceLocation right) => !Equals(left, right); - public static bool operator <(SourceLocation left, SourceLocation right) => left.Line < right.Line || (left.Line == right.Line && left.Column < right.Column); - public static bool operator >(SourceLocation left, SourceLocation right) => left.Line > right.Line || (left.Line == right.Line && left.Column > right.Column); - public static bool operator <=(SourceLocation left, SourceLocation right) => left.Line <= right.Line || (left.Line == right.Line && left.Column <= right.Column); - public static bool operator >=(SourceLocation left, SourceLocation right) => left.Line >= right.Line || (left.Line == right.Line && left.Column >= right.Column); -} \ No newline at end of file diff --git a/src/compiler/NubLang/Code/SourceSpan.cs b/src/compiler/NubLang/Code/SourceSpan.cs deleted file mode 100644 index 3e7a89b..0000000 --- a/src/compiler/NubLang/Code/SourceSpan.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace NubLang.Code; - -public readonly struct SourceSpan : IEquatable -{ - public static SourceSpan Zero => new(SourceLocation.Zero, SourceLocation.Zero); - - public static SourceSpan Merge(params IEnumerable spans) - { - var spanArray = spans as SourceSpan[] ?? spans.ToArray(); - - if (spanArray.Length == 0) - { - return Zero; - } - - var minStart = spanArray.Min(s => s.Start); - var maxEnd = spanArray.Max(s => s.End); - - return new SourceSpan(minStart, maxEnd); - } - - public SourceSpan(SourceLocation start, SourceLocation end) - { - if (start > end) - { - throw new ArgumentException("Start location cannot be after end location"); - } - - Start = start; - End = end; - } - - public SourceLocation Start { get; } - public SourceLocation End { get; } - - public override string ToString() - { - if (Start == End) - { - return $"{Start}"; - } - - if (Start.Line == End.Line) - { - return Start.Column == End.Column ? $"{Start}" : $"{Start.Line}:{Start.Column}-{End.Column}"; - } - - return $"{Start}-{End}"; - } - - public bool Equals(SourceSpan other) => Start == other.Start && End == other.End; - public override bool Equals(object? obj) => obj is SourceSpan other && Equals(other); - public override int GetHashCode() => HashCode.Combine(typeof(SourceSpan), Start, End); - - public static bool operator ==(SourceSpan left, SourceSpan right) => Equals(left, right); - public static bool operator !=(SourceSpan left, SourceSpan right) => !Equals(left, right); -} \ No newline at end of file diff --git a/src/compiler/NubLang/Diagnostics/ConsoleColors.cs b/src/compiler/NubLang/Diagnostics/ConsoleColors.cs deleted file mode 100644 index 1a6f7a3..0000000 --- a/src/compiler/NubLang/Diagnostics/ConsoleColors.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace NubLang.Diagnostics; - -public static class ConsoleColors -{ - public const string Reset = "\e[0m"; - public const string Bold = "\e[1m"; - public const string Faint = "\e[2m"; - public const string Italic = "\e[3m"; - public const string Underline = "\e[4m"; - public const string SlowBlink = "\e[5m"; - public const string RapidBlink = "\e[6m"; - public const string SwapBgAndFg = "\e[7m"; - public const string Conceal = "\e[8m"; - public const string CrossedOut = "\e[9m"; - - public const string DefaultFont = "\e[10m"; - public const string AltFont1 = "\e[11m"; - public const string AltFont2 = "\e[12m"; - public const string AltFont3 = "\e[13m"; - public const string AltFont4 = "\e[14m"; - public const string AltFont5 = "\e[15m"; - public const string AltFont6 = "\e[16m"; - public const string AltFont7 = "\e[17m"; - public const string AltFont8 = "\e[18m"; - public const string AltFont9 = "\e[19m"; - - public const string Black = "\e[30m"; - public const string Red = "\e[31m"; - public const string Green = "\e[32m"; - public const string Yellow = "\e[33m"; - public const string Blue = "\e[34m"; - public const string Magenta = "\e[35m"; - public const string Cyan = "\e[36m"; - public const string White = "\e[37m"; - - public const string BrightBlack = "\e[90m"; - public const string BrightRed = "\e[91m"; - public const string BrightGreen = "\e[92m"; - public const string BrightYellow = "\e[93m"; - public const string BrightBlue = "\e[94m"; - public const string BrightMagenta = "\e[95m"; - public const string BrightCyan = "\e[96m"; - public const string BrightWhite = "\e[97m"; - - private static bool IsColorSupported() - { - var term = Environment.GetEnvironmentVariable("TERM"); - var colorTerm = Environment.GetEnvironmentVariable("COLORTERM"); - return !string.IsNullOrEmpty(term) || !string.IsNullOrEmpty(colorTerm) || !Console.IsOutputRedirected; - } - - public static string Colorize(string text, string color) - { - return IsColorSupported() ? $"{color}{text}{Reset}" : text; - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/Diagnostics/Diagnostic.cs b/src/compiler/NubLang/Diagnostics/Diagnostic.cs deleted file mode 100644 index b1fdd5f..0000000 --- a/src/compiler/NubLang/Diagnostics/Diagnostic.cs +++ /dev/null @@ -1,325 +0,0 @@ -using System.Text; -using NubLang.Code; -using NubLang.Parsing.Syntax; -using NubLang.Tokenization; - -namespace NubLang.Diagnostics; - -public class Diagnostic -{ - public class DiagnosticBuilder - { - private readonly DiagnosticSeverity _severity; - private readonly string _message; - private SourceFileSpan? _fileSpan; - private string? _help; - - public DiagnosticBuilder(DiagnosticSeverity severity, string message) - { - _severity = severity; - _message = message; - } - - public DiagnosticBuilder At(SyntaxNode? node) - { - if (node != null) - { - var first = node.Tokens.FirstOrDefault(); - if (first?.FileSpan != null) - { - var span = SourceSpan.Merge(node.Tokens.Select(x => x.FileSpan?.Span ?? SourceSpan.Zero)); - At(new SourceFileSpan(first.FileSpan.SourceFile, span)); - } - } - - return this; - } - - public DiagnosticBuilder At(Token? token) - { - if (token != null) - { - At(token.FileSpan); - } - - return this; - } - - public DiagnosticBuilder At(SourceFileSpan? fileSpan) - { - if (fileSpan != null) - { - _fileSpan = fileSpan; - } - - return this; - } - - public DiagnosticBuilder WithHelp(string help) - { - _help = help; - return this; - } - - public Diagnostic Build() => new(_severity, _message, _help, _fileSpan); - } - - public static DiagnosticBuilder Error(string message) => new(DiagnosticSeverity.Error, message); - public static DiagnosticBuilder Warning(string message) => new(DiagnosticSeverity.Warning, message); - public static DiagnosticBuilder Info(string message) => new(DiagnosticSeverity.Info, message); - - public DiagnosticSeverity Severity { get; } - public string Message { get; } - public string? Help { get; } - public SourceFileSpan? FileSpan { get; } - - private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceFileSpan? fileSpan) - { - Severity = severity; - Message = message; - Help = help; - FileSpan = fileSpan; - } - - public string FormatANSI() - { - var sb = new StringBuilder(); - - sb.Append(Severity switch - { - DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red), - DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow), - DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue), - _ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White) - }); - - if (FileSpan != null) - { - sb.Append(ConsoleColors.Colorize($" at {FileSpan.SourceFile.Path}:{FileSpan.Span}", ConsoleColors.Faint)); - } - - sb.Append(": "); - sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite)); - - if (FileSpan != null) - { - sb.AppendLine(); - var text = FileSpan.SourceFile.GetText(); - - var lines = text.Split('\n'); - - var startLine = FileSpan.Span.Start.Line; - var endLine = FileSpan.Span.End.Line; - - const int CONTEXT_LINES = 3; - - var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES); - var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES); - - var numberPadding = contextEndLine.ToString().Length; - var codePadding = lines.Skip(contextStartLine - 1).Take(contextEndLine - contextStartLine).Max(x => x.Length); - - sb.Append('╭'); - sb.Append(new string('─', numberPadding + 2)); - sb.Append('┬'); - sb.Append(new string('─', codePadding + 2)); - sb.Append('╮'); - sb.AppendLine(); - - var tokenizer = new Tokenizer(FileSpan.SourceFile); - var tokens = tokenizer.Tokenize().ToList(); - - for (var i = contextStartLine; i <= contextEndLine; i++) - { - var line = lines[i - 1]; - - sb.Append("│ "); - sb.Append(i.ToString().PadRight(numberPadding)); - sb.Append(" │ "); - sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokens)); - sb.Append(" │"); - sb.AppendLine(); - - if (i >= startLine && i <= endLine) - { - var markerStartColumn = 1; - var markerEndColumn = line.Length + 1; - - if (i == startLine) - { - markerStartColumn = FileSpan.Span.Start.Column; - } - - if (i == endLine) - { - markerEndColumn = FileSpan.Span.End.Column; - } - - var markerLength = markerEndColumn - markerStartColumn; - var marker = new string('^', markerLength); - - var markerColor = Severity switch - { - DiagnosticSeverity.Info => ConsoleColors.Blue, - DiagnosticSeverity.Warning => ConsoleColors.Yellow, - DiagnosticSeverity.Error => ConsoleColors.Red, - _ => ConsoleColors.White - }; - - sb.Append("│ "); - sb.Append(new string(' ', numberPadding)); - sb.Append(" │ "); - sb.Append(new string(' ', markerStartColumn - 1)); - sb.Append(ConsoleColors.Colorize(marker, markerColor)); - sb.Append(new string(' ', codePadding - markerEndColumn + 1)); - sb.Append(" │"); - sb.AppendLine(); - } - } - - sb.Append('╰'); - sb.Append(new string('─', numberPadding + 2)); - sb.Append('┴'); - sb.Append(new string('─', codePadding + 2)); - sb.Append('╯'); - } - - if (Help != null) - { - sb.AppendLine(); - sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan)); - } - - return sb.ToString(); - } - - private static string ApplySyntaxHighlighting(string line, int lineNumber, List tokens) - { - var sb = new StringBuilder(); - var lineTokens = tokens - .Where(t => t.FileSpan.Span.Start.Line == lineNumber) - .OrderBy(t => t.FileSpan.Span.Start.Column) - .ToList(); - - if (lineTokens.Count == 0) - { - return line; - } - - var currentColumn = 1; - - foreach (var token in lineTokens) - { - var tokenStart = token.FileSpan.Span.Start.Column; - var tokenEnd = token.FileSpan.Span.End.Column; - - if (tokenStart > currentColumn) - { - var beforeToken = line.Substring(currentColumn - 1, tokenStart - currentColumn); - sb.Append(beforeToken); - } - - var tokenLength = tokenEnd - tokenStart; - if (tokenStart - 1 + tokenLength <= line.Length) - { - var tokenText = line.Substring(tokenStart - 1, tokenLength); - - var coloredToken = ColorizeToken(token, tokenText); - sb.Append(coloredToken); - } - - currentColumn = tokenEnd; - } - - if (currentColumn <= line.Length) - { - var remaining = line[(currentColumn - 1)..]; - sb.Append(remaining); - } - - return sb.ToString(); - } - - private static string ColorizeToken(Token token, string tokenText) - { - switch (token) - { - case IdentifierToken: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightWhite); - } - case LiteralToken literal: - { - if (literal.Kind == LiteralKind.String) - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.Green); - } - - return ConsoleColors.Colorize(tokenText, ConsoleColors.Magenta); - } - case SymbolToken symbolToken: - { - switch (symbolToken.Symbol) - { - case Symbol.Func: - case Symbol.Return: - case Symbol.If: - case Symbol.Else: - case Symbol.While: - case Symbol.Break: - case Symbol.Continue: - case Symbol.Struct: - case Symbol.Let: - case Symbol.Calls: - case Symbol.Interface: - case Symbol.For: - case Symbol.Extern: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.Bold + ConsoleColors.Blue); - } - case Symbol.Assign: - case Symbol.Bang: - case Symbol.Equal: - case Symbol.NotEqual: - case Symbol.LessThan: - case Symbol.LessThanOrEqual: - case Symbol.GreaterThan: - case Symbol.GreaterThanOrEqual: - case Symbol.Plus: - case Symbol.Minus: - case Symbol.Star: - case Symbol.ForwardSlash: - case Symbol.Caret: - case Symbol.Ampersand: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.Yellow); - } - case Symbol.Colon: - case Symbol.OpenParen: - case Symbol.CloseParen: - case Symbol.OpenBrace: - case Symbol.CloseBrace: - case Symbol.OpenBracket: - case Symbol.CloseBracket: - case Symbol.Comma: - case Symbol.Period: - case Symbol.Semi: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightBlack); - } - } - - break; - } - } - - return tokenText; - } -} - -public enum DiagnosticSeverity -{ - Info, - Warning, - Error -} \ No newline at end of file diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs deleted file mode 100644 index 4f956e2..0000000 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ /dev/null @@ -1,1440 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Text; -using NubLang.Tokenization; -using NubLang.TypeChecking; -using NubLang.TypeChecking.Node; - -namespace NubLang.Generation.QBE; - -public class QBEGenerator -{ - private readonly QBEWriter _writer; - private readonly TypedModule _module; - private readonly IReadOnlyDictionary _moduleSignatures; - - private readonly List _cStringLiterals = []; - private readonly List _stringLiterals = []; - private readonly Stack _breakLabels = []; - private readonly Stack _continueLabels = []; - private int _tmpIndex; - private int _labelIndex; - private int _cStringLiteralIndex; - private int _stringLiteralIndex; - private bool _codeIsReachable = true; - - public QBEGenerator(TypedModule module, IReadOnlyDictionary moduleSignatures) - { - _module = module; - _moduleSignatures = moduleSignatures; - _writer = new QBEWriter(); - } - - public string Emit() - { - _cStringLiterals.Clear(); - _stringLiterals.Clear(); - _breakLabels.Clear(); - _continueLabels.Clear(); - _tmpIndex = 0; - _labelIndex = 0; - _cStringLiteralIndex = 0; - _stringLiteralIndex = 0; - _codeIsReachable = true; - - foreach (var (module, signature) in _moduleSignatures) - { - foreach (var structType in signature.StructTypes) - { - EmitStructType(module, structType); - _writer.NewLine(); - } - } - - foreach (var structDef in _module.Definitions.OfType()) - { - EmitStructDefinition(structDef); - _writer.NewLine(); - } - - foreach (var funcDef in _module.Definitions.OfType()) - { - EmitLocalFuncDefinition(funcDef); - _writer.NewLine(); - } - - // foreach (var structDef in _module.Definitions.OfType().Where(x => x.InterfaceImplementations.Count > 0)) - // { - // _writer.Write($"data {StructVtableName(_module.Name, structDef.Name)} = {{ "); - // - // foreach (var interfaceImplementation in structDef.InterfaceImplementations) - // { - // var interfaceDef = _definitionTable.LookupInterface(interfaceImplementation.Name); - // foreach (var func in interfaceDef.Functions) - // { - // _writer.Write($"l {StructFuncName(_module.Name, structDef.Name, func.Name)}, "); - // } - // } - // - // _writer.WriteLine("}"); - // } - - foreach (var cStringLiteral in _cStringLiterals) - { - _writer.WriteLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}"); - } - - foreach (var stringLiteral in _stringLiterals) - { - var bytes = Encoding.UTF8.GetBytes(stringLiteral.Value).Select(b => $"b {b}"); - _writer.WriteLine($"data {stringLiteral.Name} = {{ l {stringLiteral.Value.Length}, {string.Join(", ", bytes)} }}"); - } - - return _writer.ToString(); - } - - private static string QBEAssign(TypeNode type) - { - if (type.IsSimpleType(out var simpleType, out _)) - { - return simpleType.StorageSize switch - { - StorageSize.I8 or StorageSize.U8 or StorageSize.I16 or StorageSize.U16 or StorageSize.I32 or StorageSize.U32 => "=w", - StorageSize.I64 or StorageSize.U64 => "=l", - StorageSize.F32 => "=s", - StorageSize.F64 => "=d", - _ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize)) - }; - } - - return "=l"; - } - - private void EmitStore(TypeNode type, string value, string destination) - { - string store; - - if (type.IsSimpleType(out var simpleType, out _)) - { - store = simpleType.StorageSize switch - { - StorageSize.I8 or StorageSize.U8 => "storeb", - StorageSize.I16 or StorageSize.U16 => "storeh", - StorageSize.I32 or StorageSize.U32 => "storew", - StorageSize.I64 or StorageSize.U64 => "storel", - StorageSize.F32 => "stores", - StorageSize.F64 => "stored", - _ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize)) - }; - } - else - { - store = "storel"; - } - - _writer.Indented($"{store} {value}, {destination}"); - } - - private string EmitLoad(TypeNode type, string from) - { - string load; - - if (type.IsSimpleType(out var simpleType, out _)) - { - load = simpleType.StorageSize switch - { - StorageSize.I64 or StorageSize.U64 => "loadl", - StorageSize.I32 or StorageSize.U32 => "loadw", - StorageSize.I16 => "loadsh", - StorageSize.I8 => "loadsb", - StorageSize.U16 => "loaduh", - StorageSize.U8 => "loadub", - StorageSize.F64 => "loadd", - StorageSize.F32 => "loads", - _ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize)) - }; - } - else - { - load = "loadl"; - } - - var into = TmpName(); - - _writer.Indented($"{into} {QBEAssign(type)} {load} {from}"); - - return into; - } - - private void EmitMemset(string destination, int value, string length) - { - var count = TmpName(); - _writer.Indented($"{count} =l copy 0"); - - var loopLabel = LabelName(); - _writer.WriteLine(loopLabel); - - var continueLabel = LabelName(); - var doneLabel = LabelName(); - var condition = TmpName(); - _writer.Indented($"{condition} =w cultl {count}, {length}"); - _writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}"); - - _writer.WriteLine(continueLabel); - - var destinationAddress = TmpName(); - _writer.Indented($"{destinationAddress} =l add {destination}, {count}"); - - _writer.Indented($"storeb {value}, {destinationAddress}"); - - _writer.Indented($"{count} =l add {count}, 1"); - _writer.Indented($"jmp {loopLabel}"); - - _writer.WriteLine(doneLabel); - } - - private void EmitMemcpy(string source, string destination, string length) - { - var count = TmpName(); - _writer.Indented($"{count} =l copy 0"); - - var loopLabel = LabelName(); - _writer.WriteLine(loopLabel); - - var continueLabel = LabelName(); - var doneLabel = LabelName(); - var condition = TmpName(); - _writer.Indented($"{condition} =w cultl {count}, {length}"); - _writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}"); - - _writer.WriteLine(continueLabel); - - var sourceAddress = TmpName(); - _writer.Indented($"{sourceAddress} =l add {source}, {count}"); - - var destinationAddress = TmpName(); - _writer.Indented($"{destinationAddress} =l add {destination}, {count}"); - - var value = TmpName(); - _writer.Indented($"{value} =w loadub {sourceAddress}"); - _writer.Indented($"storeb {value}, {destinationAddress}"); - - _writer.Indented($"{count} =l add {count}, 1"); - _writer.Indented($"jmp {loopLabel}"); - - _writer.WriteLine(doneLabel); - } - - private string EmitArraySizeInBytes(ArrayTypeNode type, string array) - { - var size = TmpName(); - _writer.Indented($"{size} =l loadl {array}"); - _writer.Indented($"{size} =l mul {size}, {SizeOf(type.ElementType)}"); - _writer.Indented($"{size} =l add {size}, 8"); - return size; - } - - private string EmitCStringSizeInBytes(string cstring) - { - var count = TmpName(); - _writer.Indented($"{count} =l copy 0"); - - var loopLabel = LabelName(); - _writer.WriteLine(loopLabel); - - var address = TmpName(); - _writer.Indented($"{address} =l add {cstring}, {count}"); - - var value = TmpName(); - _writer.Indented($"{value} =w loadub {address}"); - - var notZeroLabel = LabelName(); - var zeroLabel = LabelName(); - _writer.Indented($"jnz {value}, {notZeroLabel}, {zeroLabel}"); - _writer.WriteLine(notZeroLabel); - _writer.Indented($"{count} =l add {count}, 1"); - _writer.Indented($"jmp {loopLabel}"); - _writer.WriteLine(zeroLabel); - - return count; - } - - private string EmitStringSizeInBytes(string nubstring) - { - var size = TmpName(); - _writer.Indented($"{size} =l loadl {nubstring}"); - _writer.Indented($"{size} =l add {size}, 8"); - return size; - } - - private void EmitCopyInto(ExpressionNode source, string destination) - { - // Simple types are passed in registers and can therefore just be stored - if (source.Type.IsSimpleType(out var simpleType, out var complexType)) - { - var value = EmitExpression(source); - EmitStore(simpleType, value, destination); - return; - } - - // Structs and interfaces has known sizes at compile time - if (complexType is StructTypeNode or InterfaceTypeNode) - { - var value = EmitExpression(source); - _writer.Indented($"blit {value}, {destination}, {SizeOf(complexType)}"); - } - // The rest of the complex types has unknown sizes - else - { - var value = EmitExpression(source); - var size = complexType switch - { - ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value), - CStringTypeNode => EmitCStringSizeInBytes(value), - StringTypeNode => EmitStringSizeInBytes(value), - _ => throw new ArgumentOutOfRangeException(nameof(source.Type)) - }; - - var buffer = TmpName(); - _writer.Indented($"{buffer} =l alloc8 {size}"); - EmitMemcpy(value, buffer, size); - EmitStore(complexType, buffer, destination); - } - } - - private string EmitCopy(ExpressionNode source) - { - // Allowlist for types which are safe to not copy - if (source is ArrayInitializerNode or StructInitializerNode or ConvertToInterfaceNode or LiteralNode) - { - return EmitExpression(source); - } - - // Simple types are passed in registers and therefore always copied - if (source.Type.IsSimpleType(out _, out var complexType)) - { - return EmitExpression(source); - } - - // For the rest, we figure out the size of the type and shallow copy them - var value = EmitExpression(source); - var destination = TmpName(); - - // Structs and interfaces has known sizes at compile time - if (complexType is StructTypeNode or InterfaceTypeNode) - { - var size = SizeOf(complexType); - _writer.Indented($"{destination} =l alloc8 {size}"); - _writer.Indented($"blit {value}, {destination}, {size}"); - } - // The rest of the complex types has unknown sizes - else - { - var size = complexType switch - { - ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value), - CStringTypeNode => EmitCStringSizeInBytes(value), - StringTypeNode => EmitStringSizeInBytes(value), - _ => throw new ArgumentOutOfRangeException(nameof(source.Type)) - }; - - _writer.Indented($"{destination} =l alloc8 {size}"); - EmitMemcpy(value, destination, size); - } - - return destination; - } - - // Utility to create QBE type names for function parameters and return types - private string FuncQBETypeName(TypeNode type) - { - if (type.IsSimpleType(out var simpleType, out var complexType)) - { - return simpleType.StorageSize switch - { - StorageSize.I64 or StorageSize.U64 => "l", - StorageSize.I32 or StorageSize.U32 => "w", - StorageSize.I16 => "sh", - StorageSize.I8 => "sb", - StorageSize.U16 => "uh", - StorageSize.U8 => "ub", - StorageSize.F64 => "d", - StorageSize.F32 => "s", - _ => throw new ArgumentOutOfRangeException() - }; - } - - if (complexType is StructTypeNode structType) - { - return StructTypeName(structType.Module, structType.Name); - } - - return "l"; - } - - private void EmitLocalFuncDefinition(LocalFuncNode funcDef) - { - _labelIndex = 0; - _tmpIndex = 0; - - _writer.Write("export function "); - - if (funcDef.Signature.ReturnType is not VoidTypeNode) - { - _writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' '); - } - - _writer.Write(LocalFuncName(_module.Name, funcDef.Name)); - - _writer.Write("("); - foreach (var parameter in funcDef.Signature.Parameters) - { - _writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}"); - } - - _writer.WriteLine(") {"); - _writer.WriteLine("@start"); - - EmitBlock(funcDef.Body); - - // Implicit return for void functions if no explicit return has been set - if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode) - { - _writer.Indented("ret"); - } - - _writer.WriteLine("}"); - } - - private void EmitStructDefinition(StructNode structDef) - { - var type = TypeResolver.ResolveStructType(_module.Name, structDef.Name, _moduleSignatures); - - // _writer.WriteLine($"export function {StructCtorName(_module.Name, structDef.Name)}() {{"); - // _writer.WriteLine("@start"); - // _writer.Indented($"%struct =l alloc8 {SizeOf(type)}"); - // // todo(nub31): Finish constructor - // _writer.Indented("ret %struct"); - // _writer.WriteLine("}"); - - foreach (var function in structDef.Functions) - { - _labelIndex = 0; - _tmpIndex = 0; - - _writer.NewLine(); - _writer.Write("export function "); - - if (function.Signature.ReturnType is not VoidTypeNode) - { - _writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' '); - } - - _writer.Write(StructFuncName(_module.Name, structDef.Name, function.Name)); - - _writer.Write("(l %this, "); - foreach (var parameter in function.Signature.Parameters) - { - _writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}, "); - } - - _writer.WriteLine(") {"); - _writer.WriteLine("@start"); - - EmitBlock(function.Body); - - // Implicit return for void functions if no explicit return has been set - if (function.Signature.ReturnType is VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode) - { - _writer.Indented("ret"); - } - - _writer.WriteLine("}"); - } - } - - private void EmitStructType(string module, StructTypeNode structType) - { - _writer.WriteLine($"type {StructTypeName(module, structType.Name)} = {{ "); - - foreach (var field in structType.Fields) - { - _writer.Indented($"{StructDefQBEType(field.Type)},"); - } - - _writer.WriteLine("}"); - return; - - string StructDefQBEType(TypeNode type) - { - if (type.IsSimpleType(out var simpleType, out var complexType)) - { - return simpleType.StorageSize switch - { - StorageSize.I64 or StorageSize.U64 => "l", - StorageSize.I32 or StorageSize.U32 => "w", - StorageSize.I16 or StorageSize.U16 => "h", - StorageSize.I8 or StorageSize.U8 => "b", - StorageSize.F64 => "d", - StorageSize.F32 => "s", - _ => throw new ArgumentOutOfRangeException() - }; - } - - if (complexType is StructTypeNode childStructType) - { - return StructTypeName(childStructType.Module, childStructType.Name); - } - - return "l"; - } - } - - private void EmitBlock(BlockNode block) - { - foreach (var statement in block.Statements) - { - if (_codeIsReachable) - { - EmitStatement(statement); - } - } - - _codeIsReachable = true; - } - - private void EmitStatement(StatementNode statement) - { - // var tokens = statement.Tokens.ToArray(); - // if (tokens.Length != 0) - // { - // _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}"); - // } - - switch (statement) - { - case AssignmentNode assignment: - EmitAssignment(assignment); - break; - case BreakNode: - EmitBreak(); - break; - case ContinueNode: - EmitContinue(); - break; - case IfNode ifStatement: - EmitIf(ifStatement); - break; - case ReturnNode @return: - EmitReturn(@return); - break; - case StatementExpressionNode statementExpression: - EmitExpression(statementExpression.Expression); - break; - case VariableDeclarationNode variableDeclaration: - EmitVariableDeclaration(variableDeclaration); - break; - case WhileNode whileStatement: - EmitWhile(whileStatement); - break; - default: - throw new ArgumentOutOfRangeException(nameof(statement)); - } - } - - private void EmitAssignment(AssignmentNode assignment) - { - EmitCopyInto(assignment.Value, EmitAddressOfLValue(assignment.Target)); - } - - private void EmitBreak() - { - _writer.Indented($"jmp {_breakLabels.Peek()}"); - _codeIsReachable = false; - } - - private void EmitContinue() - { - _writer.Indented($"jmp {_continueLabels.Peek()}"); - _codeIsReachable = false; - } - - private void EmitIf(IfNode ifStatement) - { - var trueLabel = LabelName(); - var falseLabel = LabelName(); - var endLabel = LabelName(); - - var result = EmitExpression(ifStatement.Condition); - _writer.Indented($"jnz {result}, {trueLabel}, {falseLabel}"); - _writer.WriteLine(trueLabel); - EmitBlock(ifStatement.Body); - _writer.Indented($"jmp {endLabel}"); - _writer.WriteLine(falseLabel); - if (ifStatement.Else.HasValue) - { - ifStatement.Else.Value.Match(EmitIf, EmitBlock); - } - - _writer.WriteLine(endLabel); - } - - private void EmitReturn(ReturnNode @return) - { - if (@return.Value.HasValue) - { - var result = EmitExpression(@return.Value.Value); - _writer.Indented($"ret {result}"); - } - else - { - _writer.Indented("ret"); - } - } - - private void EmitVariableDeclaration(VariableDeclarationNode variableDeclaration) - { - var name = $"%{variableDeclaration.Name}"; - _writer.Indented($"{name} =l alloc8 {SizeOf(variableDeclaration.Type)}"); - - if (variableDeclaration.Assignment.HasValue) - { - EmitCopyInto(variableDeclaration.Assignment.Value, name); - } - } - - private void EmitWhile(WhileNode whileStatement) - { - var conditionLabel = LabelName(); - var iterationLabel = LabelName(); - var endLabel = LabelName(); - - _breakLabels.Push(endLabel); - _continueLabels.Push(conditionLabel); - - _writer.Indented($"jmp {conditionLabel}"); - _writer.WriteLine(iterationLabel); - EmitBlock(whileStatement.Body); - _writer.WriteLine(conditionLabel); - var result = EmitExpression(whileStatement.Condition); - _writer.Indented($"jnz {result}, {iterationLabel}, {endLabel}"); - _writer.WriteLine(endLabel); - - _continueLabels.Pop(); - _breakLabels.Pop(); - } - - private string EmitExpression(ExpressionNode expression) - { - // var tokens = expression.Tokens.ToArray(); - // if (tokens.Length != 0) - // { - // _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}"); - // } - - return expression switch - { - ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), - StructInitializerNode structInitializer => EmitStructInitializer(structInitializer), - AddressOfNode addressOf => EmitAddressOf(addressOf), - DereferenceNode dereference => EmitDereference(dereference), - BinaryExpressionNode binary => EmitBinaryExpression(binary), - FuncCallNode funcCall => EmitFuncCall(funcCall), - InterfaceFuncCallNode interfaceFuncCall => EmitInterfaceFuncCall(interfaceFuncCall), - ConvertToInterfaceNode convertToInterface => EmitConvertToInterface(convertToInterface), - ConvertIntNode convertInt => EmitConvertInt(convertInt), - ConvertFloatNode convertFloat => EmitConvertFloat(convertFloat), - VariableIdentifierNode identifier => EmitVariableIdentifier(identifier), - FuncIdentifierNode funcIdentifier => EmitFuncIdentifier(funcIdentifier), - FuncParameterIdentifierNode funcParameterIdentifier => EmitParameterFuncIdentifier(funcParameterIdentifier), - LiteralNode literal => EmitLiteral(literal), - UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), - StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess), - StructFuncCallNode structFuncCall => EmitStructFuncCall(structFuncCall), - ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), - _ => throw new ArgumentOutOfRangeException(nameof(expression)) - }; - } - - private string EmitFuncIdentifier(FuncIdentifierNode localFuncIdent) - { - // todo(nub31): Support for extern funcs - return LocalFuncName(localFuncIdent.Module, localFuncIdent.Name); - } - - private string EmitVariableIdentifier(VariableIdentifierNode variableIdent) - { - var address = EmitAddressOfVariableIdent(variableIdent); - - return variableIdent.Type.IsSimpleType(out _, out _) - ? EmitLoad(variableIdent.Type, address) - : address; - } - - private string EmitParameterFuncIdentifier(FuncParameterIdentifierNode funcParameterIdent) - { - return "%" + funcParameterIdent.Name; - } - - private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) - { - // var address = EmitAddressOfArrayIndexAccess(arrayIndexAccess); - // if (arrayIndexAccess.Type is StructTypeNode) - // { - // return address; - // } - // - // return EmitLoad(arrayIndexAccess.Type, address); - throw new NotImplementedException(); - } - - private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer) - { - var capacity = EmitExpression(arrayInitializer.Capacity); - var elementSize = SizeOf(arrayInitializer.ElementType); - - var capacityInBytes = TmpName(); - _writer.Indented($"{capacityInBytes} =l mul {capacity}, {elementSize}"); - var totalSize = TmpName(); - _writer.Indented($"{totalSize} =l add {capacityInBytes}, 8"); - - var arrayPointer = TmpName(); - _writer.Indented($"{arrayPointer} =l alloc8 {totalSize}"); - _writer.Indented($"storel {capacity}, {arrayPointer}"); - - var dataPointer = TmpName(); - _writer.Indented($"{dataPointer} =l add {arrayPointer}, 8"); - EmitMemset(dataPointer, 0, capacityInBytes); - - return arrayPointer; - } - - private string EmitDereference(DereferenceNode dereference) - { - var address = EmitExpression(dereference.Expression); - if (dereference.Type is StructTypeNode) - { - return address; - } - - return EmitLoad(dereference.Type, address); - } - - private string EmitAddressOf(AddressOfNode addressOf) - { - return EmitAddressOfLValue(addressOf.LValue); - } - - private string EmitAddressOfLValue(LValueExpressionNode addressOf) - { - return addressOf switch - { - ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess), - StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess), - VariableIdentifierNode variableIdent => EmitAddressOfVariableIdent(variableIdent), - _ => throw new ArgumentOutOfRangeException(nameof(addressOf)) - }; - } - - private string EmitAddressOfArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) - { - var array = EmitExpression(arrayIndexAccess.Target); - var index = EmitExpression(arrayIndexAccess.Index); - - var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType; - - var offset = TmpName(); - _writer.Indented($"{offset} =l mul {index}, {SizeOf(elementType)}"); - _writer.Indented($"{offset} =l add {offset}, 8"); - _writer.Indented($"{offset} =l add {array}, {offset}"); - return offset; - } - - private string EmitAddressOfStructFieldAccess(StructFieldAccessNode structFieldAccess) - { - var target = EmitExpression(structFieldAccess.Target); - - var structType = TypeResolver.ResolveStructType(structFieldAccess.StructType.Module, structFieldAccess.StructType.Name, _moduleSignatures); - var offset = OffsetOf(structType, structFieldAccess.Field); - - var address = TmpName(); - _writer.Indented($"{address} =l add {target}, {offset}"); - return address; - } - - private string EmitAddressOfVariableIdent(VariableIdentifierNode variableIdent) - { - return "%" + variableIdent.Name; - } - - private string EmitBinaryExpression(BinaryExpressionNode binaryExpression) - { - var left = EmitExpression(binaryExpression.Left); - var right = EmitExpression(binaryExpression.Right); - - var outputName = TmpName(); - - var instruction = EmitBinaryInstructionForOperator(binaryExpression.Operator, binaryExpression.Left.Type); - - _writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); - return outputName; - } - - private static string EmitBinaryInstructionForOperator(BinaryOperator op, TypeNode type) - { - return op switch - { - BinaryOperator.RightShift => type switch - { - IntTypeNode { Signed: true } => "sar", - IntTypeNode { Signed: false } => "shr", - _ => throw new NotSupportedException($"Right shift not supported for type '{type}'") - }, - BinaryOperator.BitwiseAnd => "and", - BinaryOperator.BitwiseOr => "or", - BinaryOperator.BitwiseXor => "xor", - BinaryOperator.LeftShift => "shl", - BinaryOperator.Divide => type switch - { - IntTypeNode { Signed: true } => "div", - IntTypeNode { Signed: false } => "udiv", - FloatTypeNode => "div", - _ => throw new NotSupportedException($"Division not supported for type '{type}'") - }, - BinaryOperator.Modulo => type switch - { - IntTypeNode { Signed: true } => "rem", - IntTypeNode { Signed: false } => "urem", - _ => throw new NotSupportedException($"Modulo not supported for type '{type}'") - }, - BinaryOperator.Plus => "add", - BinaryOperator.Minus => "sub", - BinaryOperator.Multiply => "mul", - BinaryOperator.Equal => type switch - { - IntTypeNode intType => intType.Width switch - { - <= 32 => "ceqw", - 64 => "ceql", - _ => throw new ArgumentOutOfRangeException() - }, - FloatTypeNode floatType => floatType.Width switch - { - 32 => "ceqs", - 64 => "ceqd", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Equality comparison not supported for type '{type}'") - }, - BinaryOperator.NotEqual => type switch - { - IntTypeNode intType => intType.Width switch - { - <= 32 => "cnew", - 64 => "cnel", - _ => throw new ArgumentOutOfRangeException() - }, - FloatTypeNode floatType => floatType.Width switch - { - 32 => "cnes", - 64 => "cned", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Inequality comparison not supported for type '{type}'") - }, - BinaryOperator.LessThan => type switch - { - IntTypeNode { Signed: true } intType => intType.Width switch - { - <= 32 => "csltw", - 64 => "csltl", - _ => throw new ArgumentOutOfRangeException() - }, - IntTypeNode { Signed: false } intType => intType.Width switch - { - <= 32 => "cultw", - 64 => "cultl", - _ => throw new ArgumentOutOfRangeException() - }, - FloatTypeNode floatType => floatType.Width switch - { - 32 => "clts", - 64 => "cltd", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Less than comparison not supported for type '{type}'") - }, - BinaryOperator.LessThanOrEqual => type switch - { - IntTypeNode { Signed: true } intType => intType.Width switch - { - <= 32 => "cslew", - 64 => "cslel", - _ => throw new ArgumentOutOfRangeException() - }, - IntTypeNode { Signed: false } intType => intType.Width switch - { - <= 32 => "culew", - 64 => "culel", - _ => throw new ArgumentOutOfRangeException() - }, - FloatTypeNode floatType => floatType.Width switch - { - 32 => "cles", - 64 => "cled", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Less than or equal comparison not supported for type '{type}'") - }, - BinaryOperator.GreaterThan => type switch - { - IntTypeNode { Signed: true } intType => intType.Width switch - { - <= 32 => "csgtw", - 64 => "csgtl", - _ => throw new ArgumentOutOfRangeException() - }, - IntTypeNode { Signed: false } intType => intType.Width switch - { - <= 32 => "cugtw", - 64 => "cugtl", - _ => throw new ArgumentOutOfRangeException() - }, - FloatTypeNode floatType => floatType.Width switch - { - 32 => "cgts", - 64 => "cgtd", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Greater than comparison not supported for type '{type}'") - }, - BinaryOperator.GreaterThanOrEqual => type switch - { - IntTypeNode { Signed: true } intType => intType.Width switch - { - <= 32 => "csgew", - 64 => "csgel", - _ => throw new ArgumentOutOfRangeException() - }, - IntTypeNode { Signed: false } intType => intType.Width switch - { - <= 32 => "cugew", - 64 => "cugel", - _ => throw new ArgumentOutOfRangeException() - }, - FloatTypeNode floatType => floatType.Width switch - { - 32 => "cges", - 64 => "cged", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Greater than or equal comparison not supported for type '{type}'") - }, - // todo(nub31): Implement short circuiting - BinaryOperator.LogicalAnd => "and", - BinaryOperator.LogicalOr => "or", - _ => throw new ArgumentOutOfRangeException(nameof(op)) - }; - } - - private string EmitLiteral(LiteralNode literal) - { - switch (literal.Kind) - { - case LiteralKind.Integer: - { - if (literal.Type is FloatTypeNode { Width: 32 }) - { - var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.SingleToInt32Bits(value); - return bits.ToString(); - } - - if (literal.Type is FloatTypeNode { Width: 64 }) - { - var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.DoubleToInt64Bits(value); - return bits.ToString(); - } - - if (literal.Type is IntTypeNode) - { - return literal.Value; - } - - break; - } - case LiteralKind.Float: - { - if (literal.Type is IntTypeNode) - { - return literal.Value.Split(".").First(); - } - - if (literal.Type is FloatTypeNode { Width: 32 }) - { - var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.SingleToInt32Bits(value); - return bits.ToString(); - } - - if (literal.Type is FloatTypeNode { Width: 64 }) - { - var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.DoubleToInt64Bits(value); - return bits.ToString(); - } - - break; - } - case LiteralKind.String: - { - if (literal.Type is StringTypeNode) - { - var stringLiteral = new StringLiteral(literal.Value, StringName()); - _stringLiterals.Add(stringLiteral); - return stringLiteral.Name; - } - - if (literal.Type is CStringTypeNode) - { - var cStringLiteral = new CStringLiteral(literal.Value, CStringName()); - _cStringLiterals.Add(cStringLiteral); - return cStringLiteral.Name; - } - - break; - } - case LiteralKind.Bool: - { - if (literal.Type is BoolTypeNode) - { - return bool.Parse(literal.Value) ? "1" : "0"; - } - - break; - } - } - - throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}"); - } - - private string EmitStructInitializer(StructInitializerNode structInitializer) - { - var destination = TmpName(); - var size = SizeOf(structInitializer.StructType); - _writer.Indented($"{destination} =l alloc8 {size}"); - - foreach (var (field, value) in structInitializer.Initializers) - { - var offset = TmpName(); - _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structInitializer.StructType, field)}"); - EmitCopyInto(value, offset); - } - - return destination; - } - - private string EmitUnaryExpression(UnaryExpressionNode unaryExpression) - { - var operand = EmitExpression(unaryExpression.Operand); - var outputName = TmpName(); - - switch (unaryExpression.Operator) - { - case UnaryOperator.Negate: - { - switch (unaryExpression.Operand.Type) - { - case IntTypeNode { Signed: true, Width: 64 }: - _writer.Indented($"{outputName} =l neg {operand}"); - return outputName; - case IntTypeNode { Signed: true, Width: 8 or 16 or 32 }: - _writer.Indented($"{outputName} =w neg {operand}"); - return outputName; - case FloatTypeNode { Width: 64 }: - _writer.Indented($"{outputName} =d neg {operand}"); - return outputName; - case FloatTypeNode { Width: 32 }: - _writer.Indented($"{outputName} =s neg {operand}"); - return outputName; - } - - break; - } - case UnaryOperator.Invert: - { - switch (unaryExpression.Operand.Type) - { - case BoolTypeNode: - _writer.Indented($"{outputName} =w xor {operand}, 1"); - return outputName; - } - - break; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - - throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); - } - - private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccess) - { - var address = EmitAddressOfStructFieldAccess(structFieldAccess); - if (structFieldAccess.Type is StructTypeNode) - { - return address; - } - - return EmitLoad(structFieldAccess.Type, address); - } - - private string EmitStructFuncCall(StructFuncCallNode structFuncCall) - { - var func = StructFuncName(structFuncCall.StructType.Module, structFuncCall.StructType.Name, structFuncCall.Name); - - var thisParameter = EmitExpression(structFuncCall.StructExpression); - - List parameterStrings = [$"l {thisParameter}"]; - - foreach (var parameter in structFuncCall.Parameters) - { - var copy = EmitCopy(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); - } - - if (structFuncCall.Type is VoidTypeNode) - { - _writer.Indented($"call {func}({string.Join(", ", parameterStrings)})"); - return string.Empty; - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(structFuncCall.Type)} call {func}({string.Join(", ", parameterStrings)})"); - return outputName; - } - } - - private string EmitInterfaceFuncCall(InterfaceFuncCallNode interfaceFuncCall) - { - var target = EmitExpression(interfaceFuncCall.InterfaceExpression); - - var functionIndex = interfaceFuncCall.InterfaceType.Functions.ToList().FindIndex(x => x.Name == interfaceFuncCall.Name); - var offset = functionIndex * 8; - - var vtable = TmpName(); - _writer.Indented($"{vtable} =l loadl {target}"); - - var funcOffset = TmpName(); - _writer.Indented($"{funcOffset} =l add {vtable}, {offset}"); - - var func = TmpName(); - _writer.Indented($"{func} =l loadl {funcOffset}"); - - var data = TmpName(); - _writer.Indented($"{data} =l add {target}, 8"); - _writer.Indented($"{data} =l loadl {data}"); - - List parameterStrings = [$"l {data}"]; - - foreach (var parameter in interfaceFuncCall.Parameters) - { - var copy = EmitCopy(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); - } - - if (interfaceFuncCall.Type is VoidTypeNode) - { - _writer.Indented($"call {func}({string.Join(", ", parameterStrings)})"); - return string.Empty; - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(interfaceFuncCall.Type)} call {func}({string.Join(", ", parameterStrings)})"); - return outputName; - } - } - - private string EmitConvertToInterface(ConvertToInterfaceNode convertToInterface) - { - var implementation = EmitExpression(convertToInterface.Implementation); - - var vtableOffset = 0; - foreach (var interfaceImplementation in convertToInterface.StructType.InterfaceImplementations) - { - if (interfaceImplementation == convertToInterface.InterfaceType) - { - break; - } - - vtableOffset += interfaceImplementation.Functions.Count * 8; - } - - var destination = TmpName(); - _writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}"); - - var interfaceVtablePointer = TmpName(); - _writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(convertToInterface.StructType.Module, convertToInterface.StructType.Name)}, {vtableOffset}"); - _writer.Indented($"storel {interfaceVtablePointer}, {destination}"); - - var objectPointer = TmpName(); - _writer.Indented($"{objectPointer} =l add {destination}, 8"); - _writer.Indented($"storel {implementation}, {objectPointer}"); - - return destination; - } - - private string EmitConvertInt(ConvertIntNode convertInt) - { - var value = EmitExpression(convertInt.Value); - - if (convertInt.ValueType.Width >= convertInt.TargetType.Width) - { - return value; - } - - var method = convertInt.ValueType.Signed switch - { - true => convertInt.ValueType.Width switch - { - 8 => "extsb", - 16 => "extsh", - 32 => "extsw", - _ => throw new ArgumentOutOfRangeException() - }, - false => convertInt.ValueType.Width switch - { - 8 => "extub", - 16 => "extuh", - 32 => "extuw", - _ => throw new ArgumentOutOfRangeException() - } - }; - - var result = TmpName(); - _writer.Indented($"{result} {QBEAssign(convertInt.TargetType)} {method} {value}"); - return result; - } - - private string EmitConvertFloat(ConvertFloatNode convertFloat) - { - var value = EmitExpression(convertFloat.Value); - - if (convertFloat.ValueType.Width == convertFloat.TargetType.Width) - { - return value; - } - - var method = convertFloat.ValueType.Width switch - { - 32 => "exts", - 64 => "truncd", - _ => throw new ArgumentOutOfRangeException() - }; - - var result = TmpName(); - _writer.Indented($"{result} {QBEAssign(convertFloat.TargetType)} {method} {value}"); - return result; - } - - private string EmitFuncCall(FuncCallNode funcCall) - { - var funcPointer = EmitExpression(funcCall.Expression); - - var parameterStrings = new List(); - - foreach (var parameter in funcCall.Parameters) - { - var copy = EmitCopy(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); - } - - if (funcCall.Type is VoidTypeNode) - { - _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); - return string.Empty; - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return outputName; - } - } - - private static int SizeOf(TypeNode type) - { - return type switch - { - SimpleTypeNode simple => simple.StorageSize switch - { - StorageSize.Void => 0, - StorageSize.I8 or StorageSize.U8 => 1, - StorageSize.I16 or StorageSize.U16 => 2, - StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4, - StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8, - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}") - }, - CStringTypeNode => 8, - StringTypeNode => 8, - ArrayTypeNode => 8, - StructTypeNode structType => CalculateStructSize(structType), - InterfaceTypeNode => 16, - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}") - }; - } - - private static int CalculateStructSize(StructTypeNode structType) - { - var offset = 0; - - var fields = new List(structType.Fields.Count); - - foreach (var field in structType.Fields) - { - var fieldAlignment = AlignmentOf(field.Type); - offset = AlignTo(offset, fieldAlignment); - offset += SizeOf(field.Type); - } - - var structAlignment = CalculateStructAlignment(structType); - return AlignTo(offset, structAlignment); - } - - private static int AlignmentOf(TypeNode type) - { - return type switch - { - SimpleTypeNode simple => simple.StorageSize switch - { - StorageSize.Void => 1, - StorageSize.I8 or StorageSize.U8 => 1, - StorageSize.I16 or StorageSize.U16 => 2, - StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4, - StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8, - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}") - }, - CStringTypeNode => 8, - StringTypeNode => 8, - ArrayTypeNode => 8, - StructTypeNode structType => CalculateStructAlignment(structType), - InterfaceTypeNode => 8, - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}") - }; - } - - private static int CalculateStructAlignment(StructTypeNode structType) - { - var maxAlignment = 1; - - if (structType.InterfaceImplementations.Any()) - { - maxAlignment = Math.Max(maxAlignment, 8); - } - - foreach (var field in structType.Fields) - { - var fieldAlignment = AlignmentOf(field.Type); - maxAlignment = Math.Max(maxAlignment, fieldAlignment); - } - - return maxAlignment; - } - - private static int AlignTo(int offset, int alignment) - { - return (offset + alignment - 1) & ~(alignment - 1); - } - - private static int OffsetOf(StructTypeNode structDef, string member) - { - var offset = 0; - - foreach (var field in structDef.Fields) - { - if (field.Name == member) - { - return offset; - } - - var fieldAlignment = AlignmentOf(field.Type); - - offset = AlignTo(offset, fieldAlignment); - offset += SizeOf(field.Type); - } - - throw new UnreachableException($"Member '{member}' not found in struct"); - } - - #region Naming utilities - - private string TmpName() - { - return $"%t{++_tmpIndex}"; - } - - private string LabelName() - { - return $"@l{++_labelIndex}"; - } - - private string CStringName() - { - return $"$cstring{++_cStringLiteralIndex}"; - } - - private string StringName() - { - return $"$string{++_stringLiteralIndex}"; - } - - private string LocalFuncName(string module, string name) - { - return $"${module}.{name}"; - } - - private string ExternFuncName(ExternFuncNode funcDef) - { - return $"${funcDef.CallName}"; - } - - private string StructTypeName(string module, string name) - { - return $":{module}.{name}"; - } - - private string StructFuncName(string module, string structName, string funcName) - { - return $"${module}.{structName}_func.{funcName}"; - } - - private string StructCtorName(string module, string structName) - { - return $"${module}.{structName}_ctor"; - } - - private string StructVtableName(string module, string structName) - { - return $"${module}.{structName}_vtable"; - } - - #endregion -} - -public class StringLiteral(string value, string name) -{ - public string Value { get; } = value; - public string Name { get; } = name; -} - -public class CStringLiteral(string value, string name) -{ - public string Value { get; } = value; - public string Name { get; } = name; -} \ No newline at end of file diff --git a/src/compiler/NubLang/Generation/QBE/QBEWriter.cs b/src/compiler/NubLang/Generation/QBE/QBEWriter.cs deleted file mode 100644 index 2ff8668..0000000 --- a/src/compiler/NubLang/Generation/QBE/QBEWriter.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text; - -namespace NubLang.Generation.QBE; - -internal class QBEWriter -{ - private readonly StringBuilder _builder = new(); - - public void Indented(string value) - { - _builder.Append('\t'); - _builder.AppendLine(value); - } - - public void Comment(string comment) - { - _builder.AppendLine("# " + comment); - } - - public void WriteLine(string text) - { - _builder.AppendLine(text); - } - - public void Write(string text) - { - _builder.Append(text); - } - - public void NewLine() - { - _builder.AppendLine(); - } - - public override string ToString() - { - return _builder.ToString(); - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/NubLang.csproj b/src/compiler/NubLang/NubLang.csproj deleted file mode 100644 index b682a68..0000000 --- a/src/compiler/NubLang/NubLang.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - net9.0 - enable - enable - true - - - diff --git a/src/compiler/NubLang/Optional.cs b/src/compiler/NubLang/Optional.cs deleted file mode 100644 index a467b5b..0000000 --- a/src/compiler/NubLang/Optional.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace NubLang; - -public static class Optional -{ - public static Optional Empty() => new(); - - /// - /// Alias for creating an Optional which allows for implicit types - /// - public static Optional OfNullable(TValue? value) - { - return value ?? Optional.Empty(); - } - - /// - /// Converts a nullable type to an Optional - /// - public static Optional ToOptional(this TValue? value) - { - return OfNullable(value); - } -} - -public readonly struct Optional -{ - public static Optional Empty() => new(); - - public static Optional OfNullable(TValue? value) - { - return value ?? Empty(); - } - - public Optional() - { - Value = default; - HasValue = false; - } - - public Optional(TValue value) - { - Value = value; - HasValue = true; - } - - public TValue? Value { get; } - - [MemberNotNullWhen(true, nameof(Value))] - public bool HasValue { get; } - - - [MemberNotNullWhen(true, nameof(Value))] - public bool TryGetValue([NotNullWhen(true)] out TValue? value) - { - if (HasValue) - { - value = Value; - return true; - } - - value = default; - return false; - } - - public TValue GetValue() - { - return Value ?? throw new InvalidOperationException("Value is not set"); - } - - public static implicit operator Optional(TValue value) => new(value); - - public TValue Or(TValue other) - { - if (HasValue) - { - return Value; - } - - return other; - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/Parsing/Parser.cs b/src/compiler/NubLang/Parsing/Parser.cs deleted file mode 100644 index c6d84ff..0000000 --- a/src/compiler/NubLang/Parsing/Parser.cs +++ /dev/null @@ -1,856 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using NubLang.Diagnostics; -using NubLang.Parsing.Syntax; -using NubLang.Tokenization; - -namespace NubLang.Parsing; - -public sealed class Parser -{ - private readonly List _diagnostics = []; - private IReadOnlyList _tokens = []; - private int _tokenIndex; - private string _moduleName = string.Empty; - - private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null; - private bool HasToken => CurrentToken != null; - - public IReadOnlyList GetDiagnostics() - { - return _diagnostics; - } - - public SyntaxTree Parse(IReadOnlyList tokens) - { - _diagnostics.Clear(); - _tokens = tokens; - _tokenIndex = 0; - _moduleName = string.Empty; - - var metadata = ParseMetadata(); - var definitions = ParseDefinitions(); - - return new SyntaxTree(definitions, metadata); - } - - private SyntaxTreeMetadata ParseMetadata() - { - var imports = new List(); - - try - { - ExpectSymbol(Symbol.Module); - _moduleName = ExpectIdentifier().Value; - - while (TryExpectSymbol(Symbol.Import)) - { - imports.Add(ExpectIdentifier().Value); - } - } - catch (ParseException e) - { - _diagnostics.Add(e.Diagnostic); - while (HasToken) - { - if (CurrentToken is SymbolToken { Symbol: Symbol.Module or Symbol.Import }) - { - break; - } - - Next(); - } - } - - return new SyntaxTreeMetadata(_moduleName, imports); - } - - private List ParseDefinitions() - { - var definitions = new List(); - - while (HasToken) - { - try - { - var startIndex = _tokenIndex; - var keyword = ExpectSymbol(); - var definition = keyword.Symbol switch - { - Symbol.Extern => ParseExtern(startIndex), - Symbol.Func => ParseFunc(startIndex), - Symbol.Struct => ParseStruct(startIndex), - Symbol.Interface => ParseInterface(startIndex), - _ => throw new ParseException(Diagnostic - .Error($"Expected 'extern', 'func', 'struct' or 'interface' but found '{keyword.Symbol}'") - .WithHelp("Valid definition keywords are 'extern', 'func', 'struct' and 'interface'") - .At(keyword) - .Build()) - }; - - definitions.Add(definition); - } - catch (ParseException e) - { - _diagnostics.Add(e.Diagnostic); - while (HasToken) - { - if (CurrentToken is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct or Symbol.Interface }) - { - break; - } - - Next(); - } - } - } - - return definitions; - } - - private FuncSignatureSyntax ParseFuncSignature() - { - var startIndex = _tokenIndex; - List parameters = []; - - ExpectSymbol(Symbol.OpenParen); - - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseFuncParameter()); - - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseParen); - break; - } - } - - var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new VoidTypeSyntax([]); - - return new FuncSignatureSyntax(GetTokens(startIndex), parameters, returnType); - } - - private FuncParameterSyntax ParseFuncParameter() - { - var startIndex = _tokenIndex; - var name = ExpectIdentifier(); - ExpectSymbol(Symbol.Colon); - var type = ParseType(); - - return new FuncParameterSyntax(GetTokens(startIndex), name.Value, type); - } - - private DefinitionSyntax ParseExtern(int startIndex) - { - var keyword = ExpectSymbol(); - - return keyword.Symbol switch - { - Symbol.Func => ParseExternFunc(startIndex), - _ => throw new ParseException(Diagnostic - .Error($"Unexpected symbol {keyword.Symbol} after extern declaration") - .At(keyword) - .Build()) - }; - } - - private ExternFuncSyntax ParseExternFunc(int startIndex) - { - var name = ExpectIdentifier(); - - var callName = name.Value; - - if (TryExpectSymbol(Symbol.Calls)) - { - callName = ExpectIdentifier().Value; - } - - var signature = ParseFuncSignature(); - - return new ExternFuncSyntax(GetTokens(startIndex), name.Value, callName, signature); - } - - private FuncSyntax ParseFunc(int startIndex) - { - var name = ExpectIdentifier(); - var signature = ParseFuncSignature(); - var body = ParseBlock(); - - return new FuncSyntax(GetTokens(startIndex), name.Value, signature, body); - } - - private DefinitionSyntax ParseStruct(int startIndex) - { - var name = ExpectIdentifier(); - var interfaceImplementations = new List(); - - if (TryExpectSymbol(Symbol.Colon)) - { - do - { - var interfaceType = ParseType(); - interfaceImplementations.Add(interfaceType); - } while (TryExpectSymbol(Symbol.Comma)); - } - - ExpectSymbol(Symbol.OpenBrace); - - List fields = []; - List funcs = []; - - var fieldIndex = 0; - - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var memberStartIndex = _tokenIndex; - - if (TryExpectSymbol(Symbol.Func)) - { - var funcName = ExpectIdentifier().Value; - var funcSignature = ParseFuncSignature(); - var funcBody = ParseBlock(); - - funcs.Add(new StructFuncSyntax(GetTokens(memberStartIndex), funcName, funcSignature, funcBody)); - } - else - { - var fieldName = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Colon); - var fieldType = ParseType(); - - var fieldValue = Optional.Empty(); - - if (TryExpectSymbol(Symbol.Assign)) - { - fieldValue = ParseExpression(); - } - - fields.Add(new StructFieldSyntax(GetTokens(memberStartIndex), fieldIndex++, fieldName, fieldType, fieldValue)); - } - } - - return new StructSyntax(GetTokens(startIndex), name.Value, fields, funcs, interfaceImplementations); - } - - private InterfaceSyntax ParseInterface(int startIndex) - { - var name = ExpectIdentifier(); - - ExpectSymbol(Symbol.OpenBrace); - - List functions = []; - - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var funcStartIndex = _tokenIndex; - - ExpectSymbol(Symbol.Func); - - var funcName = ExpectIdentifier().Value; - var signature = ParseFuncSignature(); - - functions.Add(new InterfaceFuncSyntax(GetTokens(funcStartIndex), funcName, signature)); - } - - return new InterfaceSyntax(GetTokens(startIndex), name.Value, functions); - } - - private StatementSyntax ParseStatement() - { - if (CurrentToken is SymbolToken symbol) - { - switch (symbol.Symbol) - { - case Symbol.Return: - return ParseReturn(); - case Symbol.If: - return ParseIf(); - case Symbol.While: - return ParseWhile(); - case Symbol.Let: - return ParseVariableDeclaration(); - case Symbol.Break: - return ParseBreak(); - case Symbol.Continue: - return ParseContinue(); - } - } - - return ParseStatementExpression(); - } - - private StatementSyntax ParseStatementExpression() - { - var startIndex = _tokenIndex; - var expr = ParseExpression(); - - if (TryExpectSymbol(Symbol.Assign)) - { - var value = ParseExpression(); - return new AssignmentSyntax(GetTokens(startIndex), expr, value); - } - - return new StatementExpressionSyntax(GetTokens(startIndex), expr); - } - - private VariableDeclarationSyntax ParseVariableDeclaration() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.Let); - var name = ExpectIdentifier().Value; - - var explicitType = Optional.Empty(); - if (TryExpectSymbol(Symbol.Colon)) - { - explicitType = ParseType(); - } - - var assignment = Optional.Empty(); - if (TryExpectSymbol(Symbol.Assign)) - { - assignment = ParseExpression(); - } - - return new VariableDeclarationSyntax(GetTokens(startIndex), name, explicitType, assignment); - } - - private StatementSyntax ParseBreak() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.Break); - return new BreakSyntax(GetTokens(startIndex)); - } - - private StatementSyntax ParseContinue() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.Continue); - return new ContinueSyntax(GetTokens(startIndex)); - } - - private ReturnSyntax ParseReturn() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.Return); - - var value = Optional.Empty(); - - if (!TryExpectSymbol(Symbol.Semi)) - { - value = ParseExpression(); - } - - return new ReturnSyntax(GetTokens(startIndex), value); - } - - private IfSyntax ParseIf() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.If); - var condition = ParseExpression(); - var body = ParseBlock(); - - var elseStatement = Optional>.Empty(); - if (TryExpectSymbol(Symbol.Else)) - { - elseStatement = TryExpectSymbol(Symbol.If) - ? (Variant)ParseIf() - : (Variant)ParseBlock(); - } - - return new IfSyntax(GetTokens(startIndex), condition, body, elseStatement); - } - - private WhileSyntax ParseWhile() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.While); - var condition = ParseExpression(); - var body = ParseBlock(); - return new WhileSyntax(GetTokens(startIndex), condition, body); - } - - private ExpressionSyntax ParseExpression(int precedence = 0) - { - var startIndex = _tokenIndex; - var left = ParsePrimaryExpression(); - - while (CurrentToken is SymbolToken symbolToken && TryGetBinaryOperator(symbolToken.Symbol, out var op) && GetBinaryOperatorPrecedence(op.Value) >= precedence) - { - Next(); - var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1); - left = new BinaryExpressionSyntax(GetTokens(startIndex), left, op.Value, right); - } - - return left; - } - - private static int GetBinaryOperatorPrecedence(BinaryOperatorSyntax operatorSyntax) - { - return operatorSyntax switch - { - BinaryOperatorSyntax.Multiply => 10, - BinaryOperatorSyntax.Divide => 10, - BinaryOperatorSyntax.Modulo => 10, - - BinaryOperatorSyntax.Plus => 9, - BinaryOperatorSyntax.Minus => 9, - - BinaryOperatorSyntax.LeftShift => 8, - BinaryOperatorSyntax.RightShift => 8, - - BinaryOperatorSyntax.GreaterThan => 7, - BinaryOperatorSyntax.GreaterThanOrEqual => 7, - BinaryOperatorSyntax.LessThan => 7, - BinaryOperatorSyntax.LessThanOrEqual => 7, - - BinaryOperatorSyntax.Equal => 7, - BinaryOperatorSyntax.NotEqual => 7, - - BinaryOperatorSyntax.BitwiseAnd => 6, - BinaryOperatorSyntax.BitwiseXor => 5, - BinaryOperatorSyntax.BitwiseOr => 4, - - BinaryOperatorSyntax.LogicalAnd => 3, - BinaryOperatorSyntax.LogicalOr => 2, - - _ => throw new ArgumentOutOfRangeException(nameof(operatorSyntax), operatorSyntax, null) - }; - } - - private bool TryGetBinaryOperator(Symbol symbol, [NotNullWhen(true)] out BinaryOperatorSyntax? binaryExpressionOperator) - { - switch (symbol) - { - case Symbol.Equal: - binaryExpressionOperator = BinaryOperatorSyntax.Equal; - return true; - case Symbol.NotEqual: - binaryExpressionOperator = BinaryOperatorSyntax.NotEqual; - return true; - case Symbol.LessThan: - binaryExpressionOperator = BinaryOperatorSyntax.LessThan; - return true; - case Symbol.LessThanOrEqual: - binaryExpressionOperator = BinaryOperatorSyntax.LessThanOrEqual; - return true; - case Symbol.GreaterThan: - binaryExpressionOperator = BinaryOperatorSyntax.GreaterThan; - return true; - case Symbol.GreaterThanOrEqual: - binaryExpressionOperator = BinaryOperatorSyntax.GreaterThanOrEqual; - return true; - case Symbol.And: - binaryExpressionOperator = BinaryOperatorSyntax.LogicalAnd; - return true; - case Symbol.Or: - binaryExpressionOperator = BinaryOperatorSyntax.LogicalOr; - return true; - case Symbol.Plus: - binaryExpressionOperator = BinaryOperatorSyntax.Plus; - return true; - case Symbol.Minus: - binaryExpressionOperator = BinaryOperatorSyntax.Minus; - return true; - case Symbol.Star: - binaryExpressionOperator = BinaryOperatorSyntax.Multiply; - return true; - case Symbol.ForwardSlash: - binaryExpressionOperator = BinaryOperatorSyntax.Divide; - return true; - case Symbol.Percent: - binaryExpressionOperator = BinaryOperatorSyntax.Modulo; - return true; - case Symbol.LeftShift: - binaryExpressionOperator = BinaryOperatorSyntax.LeftShift; - return true; - case Symbol.RightShift: - binaryExpressionOperator = BinaryOperatorSyntax.RightShift; - return true; - case Symbol.Ampersand: - binaryExpressionOperator = BinaryOperatorSyntax.BitwiseAnd; - return true; - case Symbol.Pipe: - binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr; - return true; - case Symbol.Caret: - binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor; - return true; - default: - binaryExpressionOperator = null; - return false; - } - } - - private ExpressionSyntax ParsePrimaryExpression() - { - var startIndex = _tokenIndex; - var token = ExpectToken(); - var expr = token switch - { - LiteralToken literal => new LiteralSyntax(GetTokens(startIndex), literal.Value, literal.Kind), - IdentifierToken identifier => new IdentifierSyntax(GetTokens(startIndex), Optional.Empty(), identifier.Value), - SymbolToken symbolToken => symbolToken.Symbol switch - { - Symbol.OpenParen => ParseParenthesizedExpression(), - Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()), - Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()), - Symbol.OpenBracket => ParseArrayInitializer(startIndex), - Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), Optional.Empty(), ParseStructInitializerBody()), - Symbol.Struct => ParseStructInitializer(startIndex), - _ => throw new ParseException(Diagnostic - .Error($"Unexpected symbol '{symbolToken.Symbol}' in expression") - .WithHelp("Expected '(', '-', '!', '[' or '{'") - .At(symbolToken) - .Build()) - }, - _ => throw new ParseException(Diagnostic - .Error($"Unexpected token '{token.GetType().Name}' in expression") - .WithHelp("Expected literal, identifier, or parenthesized expression") - .At(token) - .Build()) - }; - - return ParsePostfixOperators(expr); - } - - private ExpressionSyntax ParseParenthesizedExpression() - { - var expression = ParseExpression(); - ExpectSymbol(Symbol.CloseParen); - return expression; - } - - private ExpressionSyntax ParsePostfixOperators(ExpressionSyntax expr) - { - var startIndex = _tokenIndex; - while (HasToken) - { - if (TryExpectSymbol(Symbol.Ampersand)) - { - expr = new AddressOfSyntax(GetTokens(startIndex), expr); - continue; - } - - if (TryExpectSymbol(Symbol.Caret)) - { - expr = new DereferenceSyntax(GetTokens(startIndex), expr); - continue; - } - - if (TryExpectSymbol(Symbol.Period)) - { - var member = ExpectIdentifier().Value; - if (TryExpectSymbol(Symbol.OpenParen)) - { - var parameters = new List(); - - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseExpression()); - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseParen); - break; - } - } - - expr = new DotFuncCallSyntax(GetTokens(startIndex), member, expr, parameters); - continue; - } - - expr = new StructFieldAccessSyntax(GetTokens(startIndex), expr, member); - continue; - } - - if (TryExpectSymbol(Symbol.OpenBracket)) - { - var index = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - expr = new ArrayIndexAccessSyntax(GetTokens(startIndex), expr, index); - continue; - } - - if (TryExpectSymbol(Symbol.OpenParen)) - { - var parameters = new List(); - - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseExpression()); - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseParen); - break; - } - } - - expr = new FuncCallSyntax(GetTokens(startIndex), expr, parameters); - continue; - } - - break; - } - - return expr; - } - - private ArrayInitializerSyntax ParseArrayInitializer(int startIndex) - { - var capacity = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - var type = ParseType(); - return new ArrayInitializerSyntax(GetTokens(startIndex), capacity, type); - } - - private StructInitializerSyntax ParseStructInitializer(int startIndex) - { - var type = Optional.Empty(); - if (!TryExpectSymbol(Symbol.OpenBrace)) - { - type = ParseType(); - ExpectSymbol(Symbol.OpenBrace); - } - - var initializers = ParseStructInitializerBody(); - - return new StructInitializerSyntax(GetTokens(startIndex), type, initializers); - } - - private Dictionary ParseStructInitializerBody() - { - Dictionary initializers = []; - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - initializers.Add(name, value); - } - - return initializers; - } - - private BlockSyntax ParseBlock() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.OpenBrace); - List statements = []; - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - try - { - statements.Add(ParseStatement()); - } - catch (ParseException ex) - { - _diagnostics.Add(ex.Diagnostic); - Next(); - } - } - - return new BlockSyntax(GetTokens(startIndex), statements); - } - - private TypeSyntax ParseType() - { - var startIndex = _tokenIndex; - if (TryExpectIdentifier(out var name)) - { - if (name.Value[0] == 'u' && int.TryParse(name.Value[1..], out var size)) - { - if (size is not 8 and not 16 and not 32 and not 64) - { - throw new ParseException(Diagnostic - .Error("Arbitrary uint size is not supported") - .WithHelp("Use u8, u16, u32 or u64") - .At(name) - .Build()); - } - - return new IntTypeSyntax(GetTokens(startIndex), false, size); - } - - if (name.Value[0] == 'i' && int.TryParse(name.Value[1..], out size)) - { - if (size is not 8 and not 16 and not 32 and not 64) - { - throw new ParseException(Diagnostic - .Error("Arbitrary int size is not supported") - .WithHelp("Use i8, i16, i32 or i64") - .At(name) - .Build()); - } - - return new IntTypeSyntax(GetTokens(startIndex), true, size); - } - - if (name.Value[0] == 'f' && int.TryParse(name.Value[1..], out size)) - { - if (size is not 32 and not 64) - { - throw new ParseException(Diagnostic - .Error("Arbitrary float size is not supported") - .WithHelp("Use f32 or f64") - .At(name) - .Build()); - } - - return new FloatTypeSyntax(GetTokens(startIndex), size); - } - - return name.Value switch - { - "void" => new VoidTypeSyntax(GetTokens(startIndex)), - "string" => new StringTypeSyntax(GetTokens(startIndex)), - "cstring" => new CStringTypeSyntax(GetTokens(startIndex)), - "bool" => new BoolTypeSyntax(GetTokens(startIndex)), - _ => new CustomTypeSyntax(GetTokens(startIndex), _moduleName, name.Value) - }; - } - - if (TryExpectSymbol(Symbol.Caret)) - { - var baseType = ParseType(); - return new PointerTypeSyntax(GetTokens(startIndex), baseType); - } - - if (TryExpectSymbol(Symbol.Func)) - { - ExpectSymbol(Symbol.OpenParen); - - List parameters = []; - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseType()); - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseParen); - break; - } - } - - var returnType = TryExpectSymbol(Symbol.Colon) - ? ParseType() - : new VoidTypeSyntax([]); - - return new FuncTypeSyntax(GetTokens(startIndex), parameters, returnType); - } - - if (TryExpectSymbol(Symbol.OpenBracket)) - { - ExpectSymbol(Symbol.CloseBracket); - var baseType = ParseType(); - return new ArrayTypeSyntax(GetTokens(startIndex), baseType); - } - - throw new ParseException(Diagnostic - .Error("Invalid type syntax") - .WithHelp("Expected type name, '^' for pointer, or '[]' for array") - .At(CurrentToken) - .Build()); - } - - private Token ExpectToken() - { - if (!HasToken) - { - throw new ParseException(Diagnostic - .Error("Unexpected end of file") - .WithHelp("Expected more tokens to complete the syntax") - .At(_tokens[^1]) - .Build()); - } - - var token = CurrentToken!; - Next(); - return token; - } - - private SymbolToken ExpectSymbol() - { - var token = ExpectToken(); - if (token is not SymbolToken symbol) - { - throw new ParseException(Diagnostic - .Error($"Expected symbol, but found {token.GetType().Name}") - .WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.") - .At(token) - .Build()); - } - - return symbol; - } - - private void ExpectSymbol(Symbol expectedSymbol) - { - var token = ExpectSymbol(); - if (token.Symbol != expectedSymbol) - { - throw new ParseException(Diagnostic - .Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'") - .WithHelp($"Insert '{expectedSymbol}' here") - .At(token) - .Build()); - } - } - - private bool TryExpectSymbol(Symbol symbol) - { - if (CurrentToken is SymbolToken symbolToken && symbolToken.Symbol == symbol) - { - Next(); - return true; - } - - return false; - } - - private bool TryExpectIdentifier([NotNullWhen(true)] out IdentifierToken? identifier) - { - if (CurrentToken is IdentifierToken identifierToken) - { - identifier = identifierToken; - Next(); - return true; - } - - identifier = null; - return false; - } - - private IdentifierToken ExpectIdentifier() - { - var token = ExpectToken(); - if (token is not IdentifierToken identifier) - { - throw new ParseException(Diagnostic - .Error($"Expected identifier, but found {token.GetType().Name}") - .WithHelp("Provide a valid identifier name here") - .At(token) - .Build()); - } - - return identifier; - } - - private void Next() - { - _tokenIndex++; - } - - private IEnumerable GetTokens(int tokenStartIndex) - { - return _tokens.Skip(tokenStartIndex).Take(_tokenIndex - tokenStartIndex); - } -} - -public class ParseException : Exception -{ - public Diagnostic Diagnostic { get; } - - public ParseException(Diagnostic diagnostic) : base(diagnostic.Message) - { - Diagnostic = diagnostic; - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs b/src/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs deleted file mode 100644 index 524a8ef..0000000 --- a/src/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs +++ /dev/null @@ -1,24 +0,0 @@ -using NubLang.Tokenization; - -namespace NubLang.Parsing.Syntax; - -// todo(nub31): Check export modifier instead of harcoding true -public abstract record DefinitionSyntax(IEnumerable Tokens, string Name, bool Exported = true) : SyntaxNode(Tokens); - -public record FuncParameterSyntax(IEnumerable Tokens, string Name, TypeSyntax Type) : SyntaxNode(Tokens); - -public record FuncSignatureSyntax(IEnumerable Tokens, IReadOnlyList Parameters, TypeSyntax ReturnType) : SyntaxNode(Tokens); - -public record FuncSyntax(IEnumerable Tokens, string Name, FuncSignatureSyntax Signature, BlockSyntax Body) : DefinitionSyntax(Tokens, Name); - -public record ExternFuncSyntax(IEnumerable Tokens, string Name, string CallName, FuncSignatureSyntax Signature) : DefinitionSyntax(Tokens, Name); - -public record StructFieldSyntax(IEnumerable Tokens, int Index, string Name, TypeSyntax Type, Optional Value) : SyntaxNode(Tokens); - -public record StructFuncSyntax(IEnumerable Tokens, string Name, FuncSignatureSyntax Signature, BlockSyntax Body) : SyntaxNode(Tokens); - -public record StructSyntax(IEnumerable Tokens, string Name, IReadOnlyList Fields, IReadOnlyList Functions, IReadOnlyList InterfaceImplementations) : DefinitionSyntax(Tokens, Name); - -public record InterfaceFuncSyntax(IEnumerable Tokens, string Name, FuncSignatureSyntax Signature) : SyntaxNode(Tokens); - -public record InterfaceSyntax(IEnumerable Tokens, string Name, IReadOnlyList Functions) : DefinitionSyntax(Tokens, Name); \ No newline at end of file diff --git a/src/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs b/src/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs deleted file mode 100644 index f5f4a30..0000000 --- a/src/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs +++ /dev/null @@ -1,57 +0,0 @@ -using NubLang.Tokenization; - -namespace NubLang.Parsing.Syntax; - -public enum UnaryOperatorSyntax -{ - Negate, - Invert -} - -public enum BinaryOperatorSyntax -{ - Equal, - NotEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual, - LogicalAnd, - LogicalOr, - Plus, - Minus, - Multiply, - Divide, - Modulo, - LeftShift, - RightShift, - BitwiseAnd, - BitwiseXor, - BitwiseOr, -} - -public abstract record ExpressionSyntax(IEnumerable Tokens) : SyntaxNode(Tokens); - -public record BinaryExpressionSyntax(IEnumerable Tokens, ExpressionSyntax Left, BinaryOperatorSyntax Operator, ExpressionSyntax Right) : ExpressionSyntax(Tokens); - -public record UnaryExpressionSyntax(IEnumerable Tokens, UnaryOperatorSyntax Operator, ExpressionSyntax Operand) : ExpressionSyntax(Tokens); - -public record FuncCallSyntax(IEnumerable Tokens, ExpressionSyntax Expression, IReadOnlyList Parameters) : ExpressionSyntax(Tokens); - -public record DotFuncCallSyntax(IEnumerable Tokens, string Name, ExpressionSyntax ThisParameter, IReadOnlyList Parameters) : ExpressionSyntax(Tokens); - -public record IdentifierSyntax(IEnumerable Tokens, Optional Module, string Name) : ExpressionSyntax(Tokens); - -public record ArrayInitializerSyntax(IEnumerable Tokens, ExpressionSyntax Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens); - -public record ArrayIndexAccessSyntax(IEnumerable Tokens, ExpressionSyntax Target, ExpressionSyntax Index) : ExpressionSyntax(Tokens); - -public record AddressOfSyntax(IEnumerable Tokens, ExpressionSyntax Expression) : ExpressionSyntax(Tokens); - -public record LiteralSyntax(IEnumerable Tokens, string Value, LiteralKind Kind) : ExpressionSyntax(Tokens); - -public record StructFieldAccessSyntax(IEnumerable Tokens, ExpressionSyntax Target, string Member) : ExpressionSyntax(Tokens); - -public record StructInitializerSyntax(IEnumerable Tokens, Optional StructType, Dictionary Initializers) : ExpressionSyntax(Tokens); - -public record DereferenceSyntax(IEnumerable Tokens, ExpressionSyntax Expression) : ExpressionSyntax(Tokens); \ No newline at end of file diff --git a/src/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs b/src/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs deleted file mode 100644 index acf1d8c..0000000 --- a/src/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs +++ /dev/null @@ -1,21 +0,0 @@ -using NubLang.Tokenization; - -namespace NubLang.Parsing.Syntax; - -public abstract record StatementSyntax(IEnumerable Tokens) : SyntaxNode(Tokens); - -public record StatementExpressionSyntax(IEnumerable Tokens, ExpressionSyntax Expression) : StatementSyntax(Tokens); - -public record ReturnSyntax(IEnumerable Tokens, Optional Value) : StatementSyntax(Tokens); - -public record AssignmentSyntax(IEnumerable Tokens, ExpressionSyntax Target, ExpressionSyntax Value) : StatementSyntax(Tokens); - -public record IfSyntax(IEnumerable Tokens, ExpressionSyntax Condition, BlockSyntax Body, Optional> Else) : StatementSyntax(Tokens); - -public record VariableDeclarationSyntax(IEnumerable Tokens, string Name, Optional ExplicitType, Optional Assignment) : StatementSyntax(Tokens); - -public record ContinueSyntax(IEnumerable Tokens) : StatementSyntax(Tokens); - -public record BreakSyntax(IEnumerable Tokens) : StatementSyntax(Tokens); - -public record WhileSyntax(IEnumerable Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); \ No newline at end of file diff --git a/src/compiler/NubLang/Parsing/Syntax/SyntaxNode.cs b/src/compiler/NubLang/Parsing/Syntax/SyntaxNode.cs deleted file mode 100644 index 7435d18..0000000 --- a/src/compiler/NubLang/Parsing/Syntax/SyntaxNode.cs +++ /dev/null @@ -1,11 +0,0 @@ -using NubLang.Tokenization; - -namespace NubLang.Parsing.Syntax; - -public abstract record SyntaxNode(IEnumerable Tokens); - -public record SyntaxTreeMetadata(string? ModuleName, IReadOnlyList Imports); - -public record SyntaxTree(IReadOnlyList Definitions, SyntaxTreeMetadata Metadata); - -public record BlockSyntax(IEnumerable Tokens, IReadOnlyList Statements) : SyntaxNode(Tokens); \ No newline at end of file diff --git a/src/compiler/NubLang/Parsing/Syntax/TypeSyntax.cs b/src/compiler/NubLang/Parsing/Syntax/TypeSyntax.cs deleted file mode 100644 index 29ff911..0000000 --- a/src/compiler/NubLang/Parsing/Syntax/TypeSyntax.cs +++ /dev/null @@ -1,25 +0,0 @@ -using NubLang.Tokenization; - -namespace NubLang.Parsing.Syntax; - -public abstract record TypeSyntax(IEnumerable Tokens) : SyntaxNode(Tokens); - -public record FuncTypeSyntax(IEnumerable Tokens, IReadOnlyList Parameters, TypeSyntax ReturnType) : TypeSyntax(Tokens); - -public record PointerTypeSyntax(IEnumerable Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); - -public record VoidTypeSyntax(IEnumerable Tokens) : TypeSyntax(Tokens); - -public record IntTypeSyntax(IEnumerable Tokens, bool Signed, int Width) : TypeSyntax(Tokens); - -public record FloatTypeSyntax(IEnumerable Tokens, int Width) : TypeSyntax(Tokens); - -public record BoolTypeSyntax(IEnumerable Tokens) : TypeSyntax(Tokens); - -public record StringTypeSyntax(IEnumerable Tokens) : TypeSyntax(Tokens); - -public record CStringTypeSyntax(IEnumerable Tokens) : TypeSyntax(Tokens); - -public record ArrayTypeSyntax(IEnumerable Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); - -public record CustomTypeSyntax(IEnumerable Tokens, string Module, string Name) : TypeSyntax(Tokens); \ No newline at end of file diff --git a/src/compiler/NubLang/Tokenization/Token.cs b/src/compiler/NubLang/Tokenization/Token.cs deleted file mode 100644 index f300e04..0000000 --- a/src/compiler/NubLang/Tokenization/Token.cs +++ /dev/null @@ -1,81 +0,0 @@ -using NubLang.Code; - -namespace NubLang.Tokenization; - -public abstract class Token(SourceFileSpan fileSpan) -{ - public SourceFileSpan FileSpan { get; } = fileSpan; -} - -public class IdentifierToken(SourceFileSpan fileSpan, string value) : Token(fileSpan) -{ - public string Value { get; } = value; -} - -public class LiteralToken(SourceFileSpan fileSpan, LiteralKind kind, string value) : Token(fileSpan) -{ - public LiteralKind Kind { get; } = kind; - public string Value { get; } = value; -} - -public enum LiteralKind -{ - Integer, - Float, - String, - Bool -} - -public class SymbolToken(SourceFileSpan fileSpan, Symbol symbol) : Token(fileSpan) -{ - public Symbol Symbol { get; } = symbol; -} - -public enum Symbol -{ - Func, - Return, - If, - Else, - While, - Break, - Continue, - Colon, - OpenParen, - CloseParen, - OpenBrace, - CloseBrace, - OpenBracket, - CloseBracket, - Comma, - Period, - Assign, - Bang, - Equal, - NotEqual, - LessThan, - LessThanOrEqual, - GreaterThan, - GreaterThanOrEqual, - Plus, - Minus, - Star, - ForwardSlash, - Struct, - Caret, - Ampersand, - Let, - Calls, - Interface, - For, - Extern, - Semi, - Percent, - LeftShift, - RightShift, - Pipe, - And, - Or, - Module, - Import, -} \ No newline at end of file diff --git a/src/compiler/NubLang/Tokenization/Tokenizer.cs b/src/compiler/NubLang/Tokenization/Tokenizer.cs deleted file mode 100644 index dda7393..0000000 --- a/src/compiler/NubLang/Tokenization/Tokenizer.cs +++ /dev/null @@ -1,264 +0,0 @@ -using NubLang.Code; -using NubLang.Diagnostics; - -namespace NubLang.Tokenization; - -public sealed class Tokenizer -{ - private static readonly Dictionary Keywords = new() - { - ["func"] = Symbol.Func, - ["if"] = Symbol.If, - ["else"] = Symbol.Else, - ["while"] = Symbol.While, - ["break"] = Symbol.Break, - ["continue"] = Symbol.Continue, - ["return"] = Symbol.Return, - ["struct"] = Symbol.Struct, - ["let"] = Symbol.Let, - ["calls"] = Symbol.Calls, - ["interface"] = Symbol.Interface, - ["for"] = Symbol.For, - ["extern"] = Symbol.Extern, - ["module"] = Symbol.Module, - }; - - private static readonly Dictionary Symbols = new() - { - [['=', '=']] = Symbol.Equal, - [['!', '=']] = Symbol.NotEqual, - [['<', '=']] = Symbol.LessThanOrEqual, - [['>', '=']] = Symbol.GreaterThanOrEqual, - [['<', '<']] = Symbol.LeftShift, - [['>', '>']] = Symbol.RightShift, - [['&', '&']] = Symbol.And, - [['|', '|']] = Symbol.Or, - [[':']] = Symbol.Colon, - [['(']] = Symbol.OpenParen, - [[')']] = Symbol.CloseParen, - [['{']] = Symbol.OpenBrace, - [['}']] = Symbol.CloseBrace, - [['[']] = Symbol.OpenBracket, - [[']']] = Symbol.CloseBracket, - [[',']] = Symbol.Comma, - [['.']] = Symbol.Period, - [['=']] = Symbol.Assign, - [['<']] = Symbol.LessThan, - [['>']] = Symbol.GreaterThan, - [['+']] = Symbol.Plus, - [['-']] = Symbol.Minus, - [['*']] = Symbol.Star, - [['/']] = Symbol.ForwardSlash, - [['!']] = Symbol.Bang, - [['^']] = Symbol.Caret, - [['&']] = Symbol.Ampersand, - [[';']] = Symbol.Semi, - [['%']] = Symbol.Percent, - [['|']] = Symbol.Pipe, - }; - - private static readonly (char[] Pattern, Symbol Symbol)[] OrderedSymbols = Symbols - .OrderByDescending(kvp => kvp.Key.Length) - .Select(kvp => (kvp.Key, kvp.Value)) - .ToArray(); - - private readonly SourceFile _sourceFile; - private readonly List _diagnostics = []; - private int _index; - - public Tokenizer(SourceFile sourceFile) - { - _sourceFile = sourceFile; - } - - public IReadOnlyList GetDiagnostics() => _diagnostics; - - public IEnumerable Tokenize() - { - _index = 0; - - while (Peek().TryGetValue(out var current)) - { - if (char.IsWhiteSpace(current)) - { - Next(); - continue; - } - - if (current == '/' && Peek(1).TryGetValue(out var nextChar) && nextChar == '/') - { - while (Peek().TryGetValue(out var ch) && ch != '\n') - { - Next(); - } - - continue; - } - - var tokenStartIndex = _index; - - if (char.IsLetter(current) || current == '_') - { - var buffer = string.Empty; - - while (Peek().TryGetValue(out var next) && (char.IsLetterOrDigit(next) || next == '_')) - { - buffer += next; - Next(); - } - - if (Keywords.TryGetValue(buffer, out var keywordSymbol)) - { - yield return new SymbolToken(GetSourceFileSpan(tokenStartIndex), keywordSymbol); - continue; - } - - if (buffer is "true" or "false") - { - yield return new LiteralToken(GetSourceFileSpan(tokenStartIndex), LiteralKind.Bool, buffer); - continue; - } - - yield return new IdentifierToken(GetSourceFileSpan(tokenStartIndex), buffer); - continue; - } - - if (char.IsDigit(current)) - { - var isFloat = false; - var buffer = string.Empty; - - while (Peek().TryGetValue(out var next)) - { - if (next == '.') - { - if (isFloat) - { - throw new Exception("More than one period found in float literal"); - } - - isFloat = true; - buffer += next; - Next(); - } - else if (char.IsDigit(next)) - { - buffer += next; - Next(); - } - else - { - break; - } - } - - yield return new LiteralToken(GetSourceFileSpan(tokenStartIndex), isFloat ? LiteralKind.Float : LiteralKind.Integer, buffer); - continue; - } - - if (current == '"') - { - Next(); - var buffer = string.Empty; - - while (true) - { - if (!Peek().TryGetValue(out var next)) - { - throw new Exception("Unclosed string literal"); - } - - if (next == '"') - { - Next(); - break; - } - - buffer += next; - Next(); - } - - yield return new LiteralToken(GetSourceFileSpan(tokenStartIndex), LiteralKind.String, buffer); - continue; - } - - var foundMatch = false; - foreach (var (pattern, symbol) in OrderedSymbols) - { - for (var i = 0; i < pattern.Length; i++) - { - var c = Peek(i); - if (!c.HasValue || c.Value != pattern[i]) break; - - if (i == pattern.Length - 1) - { - for (var j = 0; j <= i; j++) - { - Next(); - } - - yield return new SymbolToken(GetSourceFileSpan(tokenStartIndex), symbol); - foundMatch = true; - break; - } - } - - if (foundMatch) - { - break; - } - } - - if (foundMatch) - { - continue; - } - - _diagnostics.Add(Diagnostic.Error($"Unknown token '{current}'").At(GetSourceFileSpan(tokenStartIndex)).Build()); - Next(); - } - } - - private Optional Peek(int offset = 0) - { - if (_index + offset < _sourceFile.GetText().Length) - { - return _sourceFile.GetText()[_index + offset]; - } - - return Optional.Empty(); - } - - private void Next() - { - _index++; - } - - private SourceFileSpan GetSourceFileSpan(int tokenStartIndex) - { - var start = CalculateSourceLocation(tokenStartIndex); - var end = CalculateSourceLocation(_index); - return new SourceFileSpan(_sourceFile, new SourceSpan(start, end)); - } - - private SourceLocation CalculateSourceLocation(int index) - { - var line = 1; - var column = 1; - - for (var i = 0; i < index && i < _sourceFile.GetText().Length; i++) - { - if (_sourceFile.GetText()[i] == '\n') - { - line++; - column = 1; - } - else - { - column++; - } - } - - return new SourceLocation(line, column); - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Module.cs b/src/compiler/NubLang/TypeChecking/Module.cs deleted file mode 100644 index e522ca7..0000000 --- a/src/compiler/NubLang/TypeChecking/Module.cs +++ /dev/null @@ -1,179 +0,0 @@ -using NubLang.Parsing.Syntax; -using NubLang.TypeChecking.Node; - -namespace NubLang.TypeChecking; - -public class Module -{ - public static IReadOnlyList CollectFromSyntaxTrees(IReadOnlyList syntaxTrees) - { - var modules = new Dictionary(); - - foreach (var syntaxTree in syntaxTrees) - { - var name = syntaxTree.Metadata.ModuleName; - if (name == null) - { - continue; - } - - if (!modules.TryGetValue(name, out var module)) - { - module = new Module(name, syntaxTree.Metadata.Imports); - modules[name] = module; - } - - foreach (var definition in syntaxTree.Definitions) - { - module.AddDefinition(definition); - } - } - - return modules.Values.ToList(); - } - - private readonly List _definitions = []; - - public Module(string name, IReadOnlyList imports) - { - Name = name; - Imports = imports; - } - - public string Name { get; } - public IReadOnlyList Imports { get; } - - public IReadOnlyList Definitions => _definitions; - - private void AddDefinition(DefinitionSyntax syntax) - { - _definitions.Add(syntax); - } -} - -public class TypedModule -{ - public TypedModule(string name, IReadOnlyList definitions) - { - Name = name; - Definitions = definitions; - } - - public string Name { get; } - public IReadOnlyList Definitions { get; } -} - -public class ModuleSignature -{ - public static IReadOnlyDictionary CollectFromSyntaxTrees(IReadOnlyList syntaxTrees) - { - var modules = new Dictionary(); - - foreach (var syntaxTree in syntaxTrees) - { - var moduleName = syntaxTree.Metadata.ModuleName; - if (moduleName == null) - { - continue; - } - - if (!modules.TryGetValue(moduleName, out var module)) - { - module = new ModuleSignature(); - modules[moduleName] = module; - } - - foreach (var def in syntaxTree.Definitions) - { - if (def.Exported) - { - switch (def) - { - case ExternFuncSyntax externFuncDef: - { - var parameters = externFuncDef.Signature.Parameters.Select(p => TypeResolver.ResolveType(p.Type, modules)).ToList(); - var returnType = TypeResolver.ResolveType(externFuncDef.Signature.ReturnType, modules); - var type = new FuncTypeNode(parameters, returnType); - module._functions.Add(externFuncDef.Name, type); - break; - } - case FuncSyntax funcDef: - { - var parameters = funcDef.Signature.Parameters.Select(p => TypeResolver.ResolveType(p.Type, modules)).ToList(); - var returnType = TypeResolver.ResolveType(funcDef.Signature.ReturnType, modules); - var type = new FuncTypeNode(parameters, returnType); - module._functions.Add(funcDef.Name, type); - break; - } - case InterfaceSyntax interfaceDef: - { - var functions = new List(); - for (var i = 0; i < interfaceDef.Functions.Count; i++) - { - var function = interfaceDef.Functions[i]; - var parameters = function.Signature.Parameters.Select(p => TypeResolver.ResolveType(p.Type, modules)).ToList(); - var returnType = TypeResolver.ResolveType(function.Signature.ReturnType, modules); - functions.Add(new InterfaceTypeFunc(function.Name, new FuncTypeNode(parameters, returnType), i)); - } - - var type = new InterfaceTypeNode(moduleName, interfaceDef.Name, functions); - module._interfaces.Add(type); - break; - } - case StructSyntax structDef: - { - var fields = new List(); - foreach (var field in structDef.Fields) - { - fields.Add(new StructTypeField(field.Name, TypeResolver.ResolveType(field.Type, modules), field.Index, field.Value.HasValue)); - } - - var functions = new List(); - foreach (var function in structDef.Functions) - { - var parameters = function.Signature.Parameters.Select(p => TypeResolver.ResolveType(p.Type, modules)).ToList(); - var returnType = TypeResolver.ResolveType(function.Signature.ReturnType, modules); - functions.Add(new StructTypeFunc(function.Name, new FuncTypeNode(parameters, returnType))); - } - - var interfaceImplementations = new List(); - foreach (var interfaceImplementation in structDef.InterfaceImplementations) - { - if (interfaceImplementation is not CustomTypeSyntax customType) - { - throw new Exception("Interface implementation is not a custom type"); - } - - var resolvedType = TypeResolver.ResolveCustomType(customType.Module, customType.Name, modules); - if (resolvedType is not InterfaceTypeNode interfaceType) - { - throw new Exception("Interface implementation is not a interface"); - } - - interfaceImplementations.Add(interfaceType); - } - - var type = new StructTypeNode(moduleName, structDef.Name, fields, functions, interfaceImplementations); - module._structs.Add(type); - break; - } - default: - { - throw new ArgumentOutOfRangeException(nameof(def)); - } - } - } - } - } - - return modules; - } - - private readonly List _structs = []; - private readonly List _interfaces = []; - private readonly Dictionary _functions = []; - - public IReadOnlyList StructTypes => _structs; - public IReadOnlyList InterfaceTypes => _interfaces; - public IReadOnlyDictionary Functions => _functions; -} \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs b/src/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs deleted file mode 100644 index 3a42cd3..0000000 --- a/src/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace NubLang.TypeChecking.Node; - -public abstract record DefinitionNode : Node; - -public record FuncParameterNode(string Name, TypeNode Type) : Node; - -public record FuncSignatureNode(IReadOnlyList Parameters, TypeNode ReturnType) : Node; - -public record LocalFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : DefinitionNode; - -public record ExternFuncNode(string Name, string CallName, FuncSignatureNode Signature) : DefinitionNode; - -public record StructFieldNode(int Index, string Name, TypeNode Type, Optional Value) : Node; - -public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node; - -public record StructNode(string Name, IReadOnlyList Fields, IReadOnlyList Functions, IReadOnlyList InterfaceImplementations) : DefinitionNode; - -public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node; - -public record InterfaceNode(string Name, IReadOnlyList Functions) : DefinitionNode; \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs deleted file mode 100644 index df480cd..0000000 --- a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs +++ /dev/null @@ -1,72 +0,0 @@ -using NubLang.Tokenization; - -namespace NubLang.TypeChecking.Node; - -public enum UnaryOperator -{ - Negate, - Invert -} - -public enum BinaryOperator -{ - Equal, - NotEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual, - LogicalAnd, - LogicalOr, - Plus, - Minus, - Multiply, - Divide, - Modulo, - LeftShift, - RightShift, - BitwiseAnd, - BitwiseXor, - BitwiseOr -} - -public abstract record ExpressionNode(TypeNode Type) : Node; - -public abstract record LValueExpressionNode(TypeNode Type) : RValueExpressionNode(Type); -public abstract record RValueExpressionNode(TypeNode Type) : ExpressionNode(Type); - -public record BinaryExpressionNode(TypeNode Type, ExpressionNode Left, BinaryOperator Operator, ExpressionNode Right) : RValueExpressionNode(Type); - -public record UnaryExpressionNode(TypeNode Type, UnaryOperator Operator, ExpressionNode Operand) : RValueExpressionNode(Type); - -public record FuncCallNode(TypeNode Type, ExpressionNode Expression, IReadOnlyList Parameters) : RValueExpressionNode(Type); - -public record StructFuncCallNode(TypeNode Type, string Name, StructTypeNode StructType, ExpressionNode StructExpression, IReadOnlyList Parameters) : RValueExpressionNode(Type); - -public record InterfaceFuncCallNode(TypeNode Type, string Name, InterfaceTypeNode InterfaceType, ExpressionNode InterfaceExpression, IReadOnlyList Parameters) : RValueExpressionNode(Type); - -public record VariableIdentifierNode(TypeNode Type, string Name) : LValueExpressionNode(Type); - -public record FuncParameterIdentifierNode(TypeNode Type, string Name) : RValueExpressionNode(Type); - -public record FuncIdentifierNode(TypeNode Type, string Module, string Name) : RValueExpressionNode(Type); - -public record ArrayInitializerNode(TypeNode Type, ExpressionNode Capacity, TypeNode ElementType) : RValueExpressionNode(Type); - -public record ArrayIndexAccessNode(TypeNode Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Type); - -public record AddressOfNode(TypeNode Type, LValueExpressionNode LValue) : RValueExpressionNode(Type); - -public record LiteralNode(TypeNode Type, string Value, LiteralKind Kind) : RValueExpressionNode(Type); - -public record StructFieldAccessNode(TypeNode Type, StructTypeNode StructType, ExpressionNode Target, string Field) : LValueExpressionNode(Type); - -public record StructInitializerNode(StructTypeNode StructType, Dictionary Initializers) : RValueExpressionNode(StructType); - -public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : RValueExpressionNode(Type); - -public record ConvertToInterfaceNode(TypeNode Type, InterfaceTypeNode InterfaceType, StructTypeNode StructType, ExpressionNode Implementation) : RValueExpressionNode(Type); - -public record ConvertIntNode(TypeNode Type, ExpressionNode Value, IntTypeNode ValueType, IntTypeNode TargetType) : RValueExpressionNode(Type); - -public record ConvertFloatNode(TypeNode Type, ExpressionNode Value, FloatTypeNode ValueType, FloatTypeNode TargetType) : RValueExpressionNode(Type); diff --git a/src/compiler/NubLang/TypeChecking/Node/Node.cs b/src/compiler/NubLang/TypeChecking/Node/Node.cs deleted file mode 100644 index 4b75960..0000000 --- a/src/compiler/NubLang/TypeChecking/Node/Node.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace NubLang.TypeChecking.Node; - -public abstract record Node; - -public record BlockNode(IReadOnlyList Statements) : Node; \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Node/StatementNode.cs b/src/compiler/NubLang/TypeChecking/Node/StatementNode.cs deleted file mode 100644 index bd58462..0000000 --- a/src/compiler/NubLang/TypeChecking/Node/StatementNode.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace NubLang.TypeChecking.Node; - -public record StatementNode : Node; - -public record StatementExpressionNode(ExpressionNode Expression) : StatementNode; - -public record ReturnNode(Optional Value) : StatementNode; - -public record AssignmentNode(LValueExpressionNode Target, ExpressionNode Value) : StatementNode; - -public record IfNode(ExpressionNode Condition, BlockNode Body, Optional> Else) : StatementNode; - -public record VariableDeclarationNode(string Name, Optional Assignment, TypeNode Type) : StatementNode; - -public record ContinueNode : StatementNode; - -public record BreakNode : StatementNode; - -public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode; \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Node/TypeNode.cs b/src/compiler/NubLang/TypeChecking/Node/TypeNode.cs deleted file mode 100644 index 55d29aa..0000000 --- a/src/compiler/NubLang/TypeChecking/Node/TypeNode.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace NubLang.TypeChecking.Node; - -public abstract class TypeNode : IEquatable -{ - public bool IsSimpleType([NotNullWhen(true)] out SimpleTypeNode? simpleType, [NotNullWhen(false)] out ComplexTypeNode? complexType) - { - if (this is SimpleTypeNode st) - { - complexType = null; - simpleType = st; - return true; - } - - if (this is ComplexTypeNode ct) - { - complexType = ct; - simpleType = null; - return false; - } - - throw new ArgumentException($"Type {this} is not a simple type nor a complex type"); - } - - public override bool Equals(object? obj) => obj is TypeNode other && Equals(other); - - public abstract bool Equals(TypeNode? other); - public abstract override int GetHashCode(); - public abstract override string ToString(); - - public static bool operator ==(TypeNode? left, TypeNode? right) => Equals(left, right); - public static bool operator !=(TypeNode? left, TypeNode? right) => !Equals(left, right); -} - -public enum StorageSize -{ - Void, - I8, - I16, - I32, - I64, - U8, - U16, - U32, - U64, - F32, - F64 -} - -public abstract class SimpleTypeNode : TypeNode -{ - public abstract StorageSize StorageSize { get; } -} - -#region Simple types - -public class IntTypeNode(bool signed, int width) : SimpleTypeNode -{ - public bool Signed { get; } = signed; - public int Width { get; } = width; - - public override StorageSize StorageSize => Signed switch - { - true => Width switch - { - 8 => StorageSize.I8, - 16 => StorageSize.I16, - 32 => StorageSize.I32, - 64 => StorageSize.I64, - _ => throw new ArgumentOutOfRangeException(nameof(Width)) - }, - false => Width switch - { - 8 => StorageSize.U8, - 16 => StorageSize.U16, - 32 => StorageSize.U32, - 64 => StorageSize.U64, - _ => throw new ArgumentOutOfRangeException(nameof(Width)) - } - }; - - public override string ToString() => $"{(Signed ? "i" : "u")}{Width}"; - public override bool Equals(TypeNode? other) => other is IntTypeNode @int && @int.Width == Width && @int.Signed == Signed; - public override int GetHashCode() => HashCode.Combine(typeof(IntTypeNode), Signed, Width); -} - -public class FloatTypeNode(int width) : SimpleTypeNode -{ - public int Width { get; } = width; - - public override StorageSize StorageSize => Width switch - { - 32 => StorageSize.F32, - 64 => StorageSize.F64, - _ => throw new ArgumentOutOfRangeException(nameof(Width)) - }; - - public override string ToString() => $"f{Width}"; - public override bool Equals(TypeNode? other) => other is FloatTypeNode @int && @int.Width == Width; - public override int GetHashCode() => HashCode.Combine(typeof(FloatTypeNode), Width); -} - -public class BoolTypeNode : SimpleTypeNode -{ - public override StorageSize StorageSize => StorageSize.U8; - - public override string ToString() => "bool"; - public override bool Equals(TypeNode? other) => other is BoolTypeNode; - public override int GetHashCode() => HashCode.Combine(typeof(BoolTypeNode)); -} - -public class FuncTypeNode(IReadOnlyList parameters, TypeNode returnType) : SimpleTypeNode -{ - public IReadOnlyList Parameters { get; } = parameters; - public TypeNode ReturnType { get; } = returnType; - - public override StorageSize StorageSize => StorageSize.U64; - - public override string ToString() => $"func({string.Join(", ", Parameters)}): {ReturnType}"; - public override bool Equals(TypeNode? other) => other is FuncTypeNode func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters); - - public override int GetHashCode() - { - var hash = new HashCode(); - hash.Add(typeof(FuncTypeNode)); - hash.Add(ReturnType); - foreach (var param in Parameters) - { - hash.Add(param); - } - - return hash.ToHashCode(); - } -} - -public class PointerTypeNode(TypeNode baseType) : SimpleTypeNode -{ - public TypeNode BaseType { get; } = baseType; - - public override StorageSize StorageSize => StorageSize.U64; - - public override string ToString() => "^" + BaseType; - public override bool Equals(TypeNode? other) => other is PointerTypeNode pointer && BaseType.Equals(pointer.BaseType); - public override int GetHashCode() => HashCode.Combine(typeof(PointerTypeNode), BaseType); -} - -public class VoidTypeNode : SimpleTypeNode -{ - public override StorageSize StorageSize => StorageSize.Void; - - public override string ToString() => "void"; - public override bool Equals(TypeNode? other) => other is VoidTypeNode; - public override int GetHashCode() => HashCode.Combine(typeof(VoidTypeNode)); -} - -#endregion - -public abstract class ComplexTypeNode : TypeNode; - -#region Complex types - -public class CStringTypeNode : ComplexTypeNode -{ - public override string ToString() => "cstring"; - public override bool Equals(TypeNode? other) => other is CStringTypeNode; - public override int GetHashCode() => HashCode.Combine(typeof(CStringTypeNode)); -} - -public class StringTypeNode : ComplexTypeNode -{ - public override string ToString() => "string"; - public override bool Equals(TypeNode? other) => other is StringTypeNode; - public override int GetHashCode() => HashCode.Combine(typeof(StringTypeNode)); -} - -public class StructTypeField(string name, TypeNode type, int index, bool hasDefaultValue) -{ - public string Name { get; } = name; - public TypeNode Type { get; } = type; - public int Index { get; } = index; - public bool HasDefaultValue { get; } = hasDefaultValue; -} - -public class StructTypeFunc(string name, FuncTypeNode type) -{ - public string Name { get; } = name; - public FuncTypeNode Type { get; } = type; -} - -public class StructTypeNode(string module, string name, IReadOnlyList fields, IReadOnlyList functions, IReadOnlyList interfaceImplementations) : ComplexTypeNode -{ - public string Module { get; } = module; - public string Name { get; } = name; - public IReadOnlyList Fields { get; set; } = fields; - public IReadOnlyList Functions { get; set; } = functions; - public IReadOnlyList InterfaceImplementations { get; set; } = interfaceImplementations; - - public override string ToString() => Name; - public override bool Equals(TypeNode? other) => other is StructTypeNode structType && Name == structType.Name && Module == structType.Module; - public override int GetHashCode() => HashCode.Combine(typeof(StructTypeNode), Name); -} - -public class InterfaceTypeFunc(string name, FuncTypeNode type, int index) -{ - public string Name { get; } = name; - public FuncTypeNode Type { get; } = type; - public int Index { get; } = index; -} - -public class InterfaceTypeNode(string module, string name, IReadOnlyList functions) : ComplexTypeNode -{ - public string Module { get; } = module; - public string Name { get; } = name; - public IReadOnlyList Functions { get; set; } = functions; - - public override string ToString() => Name; - public override bool Equals(TypeNode? other) => other is InterfaceTypeNode interfaceType && Name == interfaceType.Name && Module == interfaceType.Module; - public override int GetHashCode() => HashCode.Combine(typeof(InterfaceTypeNode), Name); -} - -public class ArrayTypeNode(TypeNode elementType) : ComplexTypeNode -{ - public TypeNode ElementType { get; } = elementType; - - public override string ToString() => "[]" + ElementType; - - public override bool Equals(TypeNode? other) => other is ArrayTypeNode array && ElementType.Equals(array.ElementType); - public override int GetHashCode() => HashCode.Combine(typeof(ArrayTypeNode), ElementType); -} - -#endregion \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/TypeChecker.cs b/src/compiler/NubLang/TypeChecking/TypeChecker.cs deleted file mode 100644 index d636b43..0000000 --- a/src/compiler/NubLang/TypeChecking/TypeChecker.cs +++ /dev/null @@ -1,457 +0,0 @@ -using NubLang.Diagnostics; -using NubLang.Parsing.Syntax; -using NubLang.Tokenization; -using NubLang.TypeChecking.Node; - -namespace NubLang.TypeChecking; - -public sealed class TypeChecker -{ - private readonly Module _currentModule; - private readonly IReadOnlyDictionary _moduleSignatures; - - private readonly Stack _scopes = []; - private readonly Stack _funcReturnTypes = []; - private readonly List _diagnostics = []; - - private Scope Scope => _scopes.Peek(); - - public TypeChecker(Module currentModule, IReadOnlyDictionary moduleSignatures) - { - _currentModule = currentModule; - _moduleSignatures = moduleSignatures.Where(x => currentModule.Imports.Contains(x.Key) || _currentModule.Name == x.Key).ToDictionary(); - } - - public IReadOnlyList GetDiagnostics() => _diagnostics; - - public TypedModule CheckModule() - { - _diagnostics.Clear(); - _scopes.Clear(); - - var definitions = new List(); - - foreach (var definition in _currentModule.Definitions) - { - try - { - definitions.Add(CheckDefinition(definition)); - } - catch (TypeCheckerException e) - { - _diagnostics.Add(e.Diagnostic); - } - } - - return new TypedModule(_currentModule.Name, definitions); - } - - private DefinitionNode CheckDefinition(DefinitionSyntax node) - { - return node switch - { - ExternFuncSyntax definition => CheckExternFuncDefinition(definition), - InterfaceSyntax definition => CheckInterfaceDefinition(definition), - FuncSyntax definition => CheckLocalFuncDefinition(definition), - StructSyntax definition => CheckStructDefinition(definition), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - } - - private InterfaceNode CheckInterfaceDefinition(InterfaceSyntax node) - { - throw new NotImplementedException(); - } - - private StructNode CheckStructDefinition(StructSyntax node) - { - var fields = new List(); - foreach (var field in node.Fields) - { - var value = Optional.Empty(); - if (field.Value.HasValue) - { - value = CheckExpression(field.Value.Value); - } - - fields.Add(new StructFieldNode(field.Index, field.Name, ResolveType(field.Type), value)); - } - - var functions = new List(); - foreach (var function in node.Functions) - { - var scope = new Scope(); - // todo(nub31): Add this parameter - foreach (var parameter in function.Signature.Parameters) - { - scope.Declare(new Identifier(parameter.Name, ResolveType(parameter.Type), IdentifierKind.FunctionParameter)); - } - - _funcReturnTypes.Push(ResolveType(function.Signature.ReturnType)); - var body = CheckBlock(function.Body, scope); - _funcReturnTypes.Pop(); - functions.Add(new StructFuncNode(function.Name, CheckFuncSignature(function.Signature), body)); - } - - var interfaceImplementations = new List(); - foreach (var interfaceImplementation in node.InterfaceImplementations) - { - var type = ResolveType(interfaceImplementation); - if (type is not InterfaceTypeNode interfaceType) - { - _diagnostics.Add(Diagnostic.Error($"Struct {node.Name} cannot implement non-struct type {interfaceImplementation}").At(interfaceImplementation).Build()); - continue; - } - - interfaceImplementations.Add(interfaceType); - } - - return new StructNode(node.Name, fields, functions, interfaceImplementations); - } - - private ExternFuncNode CheckExternFuncDefinition(ExternFuncSyntax node) - { - return new ExternFuncNode(node.Name, node.CallName, CheckFuncSignature(node.Signature)); - } - - private LocalFuncNode CheckLocalFuncDefinition(FuncSyntax node) - { - var scope = new Scope(); - foreach (var parameter in node.Signature.Parameters) - { - scope.Declare(new Identifier(parameter.Name, ResolveType(parameter.Type), IdentifierKind.FunctionParameter)); - } - - _funcReturnTypes.Push(ResolveType(node.Signature.ReturnType)); - var body = CheckBlock(node.Body, scope); - _funcReturnTypes.Pop(); - return new LocalFuncNode(node.Name, CheckFuncSignature(node.Signature), body); - } - - private StatementNode CheckStatement(StatementSyntax node) - { - return node switch - { - AssignmentSyntax statement => CheckAssignment(statement), - BreakSyntax => new BreakNode(), - ContinueSyntax => new ContinueNode(), - IfSyntax statement => CheckIf(statement), - ReturnSyntax statement => CheckReturn(statement), - StatementExpressionSyntax statement => CheckStatementExpression(statement), - VariableDeclarationSyntax statement => CheckVariableDeclaration(statement), - WhileSyntax statement => CheckWhile(statement), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - } - - private StatementNode CheckAssignment(AssignmentSyntax statement) - { - throw new NotImplementedException(); - } - - private IfNode CheckIf(IfSyntax statement) - { - throw new NotImplementedException(); - } - - private ReturnNode CheckReturn(ReturnSyntax statement) - { - var value = Optional.Empty(); - - if (statement.Value.HasValue) - { - value = CheckExpression(statement.Value.Value, _funcReturnTypes.Peek()); - } - - return new ReturnNode(value); - } - - private StatementExpressionNode CheckStatementExpression(StatementExpressionSyntax statement) - { - return new StatementExpressionNode(CheckExpression(statement.Expression)); - } - - private VariableDeclarationNode CheckVariableDeclaration(VariableDeclarationSyntax statement) - { - TypeNode? type = null; - ExpressionNode? assignmentNode = null; - - if (statement.ExplicitType.TryGetValue(out var explicitType)) - { - type = ResolveType(explicitType); - } - - if (statement.Assignment.TryGetValue(out var assignment)) - { - assignmentNode = CheckExpression(assignment, type); - type ??= assignmentNode.Type; - } - - if (type == null) - { - throw new TypeCheckerException(Diagnostic.Error($"Cannot infer type of variable {statement.Name}").At(statement).Build()); - } - - Scope.Declare(new Identifier(statement.Name, type, IdentifierKind.Variable)); - - return new VariableDeclarationNode(statement.Name, Optional.OfNullable(assignmentNode), type); - } - - private WhileNode CheckWhile(WhileSyntax statement) - { - throw new NotImplementedException(); - } - - private FuncSignatureNode CheckFuncSignature(FuncSignatureSyntax statement) - { - var parameters = new List(); - foreach (var parameter in statement.Parameters) - { - parameters.Add(new FuncParameterNode(parameter.Name, ResolveType(parameter.Type))); - } - - return new FuncSignatureNode(parameters, ResolveType(statement.ReturnType)); - } - - private ExpressionNode CheckExpression(ExpressionSyntax node, TypeNode? expectedType = null) - { - var result = node switch - { - AddressOfSyntax expression => CheckAddressOf(expression), - ArrayIndexAccessSyntax expression => CheckArrayIndexAccess(expression), - ArrayInitializerSyntax expression => CheckArrayInitializer(expression), - BinaryExpressionSyntax expression => CheckBinaryExpression(expression), - DereferenceSyntax expression => CheckDereference(expression), - DotFuncCallSyntax expression => CheckDotFuncCall(expression), - FuncCallSyntax expression => CheckFuncCall(expression), - IdentifierSyntax expression => CheckIdentifier(expression), - LiteralSyntax expression => CheckLiteral(expression, expectedType), - StructFieldAccessSyntax expression => CheckStructFieldAccess(expression), - StructInitializerSyntax expression => CheckStructInitializer(expression, expectedType), - UnaryExpressionSyntax expression => CheckUnaryExpression(expression), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - - if (expectedType == null || result.Type == expectedType) - { - return result; - } - - if (result.Type is StructTypeNode structType && expectedType is InterfaceTypeNode interfaceType) - { - return new ConvertToInterfaceNode(interfaceType, interfaceType, structType, result); - } - - if (result.Type is IntTypeNode sourceIntType && expectedType is IntTypeNode targetIntType) - { - if (sourceIntType.Signed == targetIntType.Signed && sourceIntType.Width < targetIntType.Width) - { - return new ConvertIntNode(targetIntType, result, sourceIntType, targetIntType); - } - } - - if (result.Type is FloatTypeNode sourceFloatType && expectedType is FloatTypeNode targetFloatType) - { - if (sourceFloatType.Width < targetFloatType.Width) - { - return new ConvertFloatNode(targetFloatType, result, sourceFloatType, targetFloatType); - } - } - - throw new TypeCheckerException(Diagnostic.Error($"Cannot convert {result.Type} to {expectedType}").At(node).Build()); - } - - private AddressOfNode CheckAddressOf(AddressOfSyntax expression) - { - throw new NotImplementedException(); - } - - private ArrayIndexAccessNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression) - { - throw new NotImplementedException(); - } - - private ArrayInitializerNode CheckArrayInitializer(ArrayInitializerSyntax expression) - { - throw new NotImplementedException(); - } - - private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression) - { - throw new NotImplementedException(); - } - - private DereferenceNode CheckDereference(DereferenceSyntax expression) - { - throw new NotImplementedException(); - } - - private FuncCallNode CheckFuncCall(FuncCallSyntax expression) - { - var accessor = CheckExpression(expression.Expression); - if (accessor.Type is not FuncTypeNode funcType) - { - throw new TypeCheckerException(Diagnostic.Error($"Cannot call non-function type {accessor.Type}").At(expression.Expression).Build()); - } - - if (expression.Parameters.Count != funcType.Parameters.Count) - { - throw new TypeCheckerException(Diagnostic.Error($"Function {funcType} expects {funcType.Parameters} but got {expression.Parameters.Count} parameters").At(expression.Expression).Build()); - } - - var parameters = new List(); - for (var i = 0; i < expression.Parameters.Count; i++) - { - var parameter = expression.Parameters[i]; - var expectedType = funcType.Parameters[i]; - - var parameterExpression = CheckExpression(parameter, expectedType); - if (parameterExpression.Type != expectedType) - { - throw new Exception($"Parameter {i + 1} does not match the type {expectedType} for function {funcType}"); - } - - parameters.Add(parameterExpression); - } - - return new FuncCallNode(funcType.ReturnType, accessor, parameters); - } - - private ExpressionNode CheckDotFuncCall(DotFuncCallSyntax expression) - { - throw new NotImplementedException(); - } - - private ExpressionNode CheckIdentifier(IdentifierSyntax expression) - { - // If the identifier does not have a module specified, first check if a local variable or function parameter with that identifier exists - if (!expression.Module.TryGetValue(out var moduleName)) - { - var scopeIdent = Scope.Lookup(expression.Name); - if (scopeIdent != null) - { - switch (scopeIdent.Kind) - { - case IdentifierKind.Variable: - { - return new VariableIdentifierNode(scopeIdent.Type, expression.Name); - } - case IdentifierKind.FunctionParameter: - { - return new FuncParameterIdentifierNode(scopeIdent.Type, expression.Name); - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - } - } - - moduleName ??= _currentModule.Name; - if (_moduleSignatures.TryGetValue(moduleName, out var module)) - { - if (module.Functions.TryGetValue(expression.Name, out var function)) - { - return new FuncIdentifierNode(function, moduleName, expression.Name); - } - } - - throw new TypeCheckerException(Diagnostic.Error($"Identifier {expression.Name} not found").At(expression).Build()); - } - - private LiteralNode CheckLiteral(LiteralSyntax expression, TypeNode? expectedType) - { - // todo(nub31): Check if the types can actually be represented as another one. For example, an int should be passed when a string is expected - var type = expectedType ?? expression.Kind switch - { - LiteralKind.Integer => new IntTypeNode(true, 64), - LiteralKind.Float => new FloatTypeNode(64), - LiteralKind.String => new StringTypeNode(), - LiteralKind.Bool => new BoolTypeNode(), - _ => throw new ArgumentOutOfRangeException() - }; - - return new LiteralNode(type, expression.Value, expression.Kind); - } - - private StructFieldAccessNode CheckStructFieldAccess(StructFieldAccessSyntax expression) - { - throw new NotImplementedException(); - } - - private StructInitializerNode CheckStructInitializer(StructInitializerSyntax expression, TypeNode? expectedType) - { - throw new NotImplementedException(); - } - - private UnaryExpressionNode CheckUnaryExpression(UnaryExpressionSyntax expression) - { - throw new NotImplementedException(); - } - - private BlockNode CheckBlock(BlockSyntax node, Scope? scope = null) - { - var statements = new List(); - - _scopes.Push(scope ?? Scope.SubScope()); - - foreach (var statement in node.Statements) - { - statements.Add(CheckStatement(statement)); - } - - _scopes.Pop(); - - return new BlockNode(statements); - } - - private TypeNode ResolveType(TypeSyntax fieldType) - { - return TypeResolver.ResolveType(fieldType, _moduleSignatures); - } -} - -public enum IdentifierKind -{ - Variable, - FunctionParameter -} - -public record Identifier(string Name, TypeNode Type, IdentifierKind Kind); - -public class Scope(Scope? parent = null) -{ - private readonly List _variables = []; - - public Identifier? Lookup(string name) - { - var variable = _variables.FirstOrDefault(x => x.Name == name); - if (variable != null) - { - return variable; - } - - return parent?.Lookup(name); - } - - public void Declare(Identifier identifier) - { - _variables.Add(identifier); - } - - public Scope SubScope() - { - return new Scope(this); - } -} - -public class TypeCheckerException : Exception -{ - public Diagnostic Diagnostic { get; } - - public TypeCheckerException(Diagnostic diagnostic) : base(diagnostic.Message) - { - Diagnostic = diagnostic; - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/TypeResolver.cs b/src/compiler/NubLang/TypeResolver.cs deleted file mode 100644 index 972dec2..0000000 --- a/src/compiler/NubLang/TypeResolver.cs +++ /dev/null @@ -1,80 +0,0 @@ -using NubLang.Parsing.Syntax; -using NubLang.TypeChecking; -using NubLang.TypeChecking.Node; - -namespace NubLang; - -public static class TypeResolver -{ - public static TypeNode ResolveType(TypeSyntax type, IReadOnlyDictionary modules) - { - return type switch - { - BoolTypeSyntax => new BoolTypeNode(), - CStringTypeSyntax => new CStringTypeNode(), - IntTypeSyntax i => new IntTypeNode(i.Signed, i.Width), - CustomTypeSyntax c => ResolveCustomType(c.Module, c.Name, modules), - FloatTypeSyntax f => new FloatTypeNode(f.Width), - FuncTypeSyntax func => new FuncTypeNode(func.Parameters.Select(x => ResolveType(x, modules)).ToList(), ResolveType(func.ReturnType, modules)), - ArrayTypeSyntax arr => new ArrayTypeNode(ResolveType(arr.BaseType, modules)), - PointerTypeSyntax ptr => new PointerTypeNode(ResolveType(ptr.BaseType, modules)), - StringTypeSyntax => new StringTypeNode(), - VoidTypeSyntax => new VoidTypeNode(), - _ => throw new NotSupportedException($"Unknown type syntax: {type}") - }; - } - - public static TypeNode ResolveCustomType(string moduleName, string typeName, IReadOnlyDictionary modules) - { - if (!modules.TryGetValue(moduleName, out var module)) - { - throw new Exception("Module not found: " + moduleName); - } - - var structType = module.StructTypes.FirstOrDefault(x => x.Name == typeName); - if (structType != null) - { - return structType; - } - - var interfaceType = module.InterfaceTypes.FirstOrDefault(x => x.Name == typeName); - if (interfaceType != null) - { - return interfaceType; - } - - throw new Exception($"Type {typeName} not found in module {moduleName}"); - } - - public static StructTypeNode ResolveStructType(string moduleName, string structName, IReadOnlyDictionary modules) - { - if (!modules.TryGetValue(moduleName, out var module)) - { - throw new Exception("Module not found: " + moduleName); - } - - var structType = module.StructTypes.FirstOrDefault(x => x.Name == structName); - if (structType != null) - { - return structType; - } - - throw new Exception($"Struct type {structName} not found in module {moduleName}"); - } - - public static InterfaceTypeNode ResolveInterfaceType(string moduleName, string structName, IReadOnlyDictionary modules) - { - if (!modules.TryGetValue(moduleName, out var module)) - { - throw new Exception("Module not found: " + moduleName); - } - - var structType = module.InterfaceTypes.FirstOrDefault(x => x.Name == structName); - if (structType != null) - { - return structType; - } - - throw new Exception($"Interface type {structName} not found in module {moduleName}"); - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/Variant.cs b/src/compiler/NubLang/Variant.cs deleted file mode 100644 index c9898b8..0000000 --- a/src/compiler/NubLang/Variant.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace NubLang; - -public readonly struct Variant where T1 : notnull where T2 : notnull -{ - public Variant() - { - throw new InvalidOperationException("Variant must be initialized with a value"); - } - - public Variant(T1 value) - { - _value = value; - } - - public Variant(T2 value) - { - _value = value; - } - - private readonly object _value; - - public void Match(Action on1, Action on2) - { - switch (_value) - { - case T1 v1: - on1(v1); - break; - case T2 v2: - on2(v2); - break; - default: - throw new InvalidCastException(); - } - } - - public T Match(Func on1, Func on2) - { - return _value switch - { - T1 v1 => on1(v1), - T2 v2 => on2(v2), - _ => throw new InvalidCastException() - }; - } - - public bool IsCase1([NotNullWhen(true)] out T1? value) - { - if (_value is T1 converted) - { - value = converted; - return true; - } - - value = default; - return false; - } - - public bool IsCase2([NotNullWhen(true)] out T2? value) - { - if (_value is T2 converted) - { - value = converted; - return true; - } - - value = default; - return false; - } - - public static implicit operator Variant(T1 value) => new(value); - public static implicit operator Variant(T2 value) => new(value); -} \ No newline at end of file diff --git a/src/runtime/.clang-format b/src/runtime/.clang-format deleted file mode 100644 index 277b2b7..0000000 --- a/src/runtime/.clang-format +++ /dev/null @@ -1,17 +0,0 @@ -IndentWidth: 4 - -# Pointer formatting -DerivePointerAlignment: false -PointerAlignment: Left - -# Function formatting -AllowShortFunctionsOnASingleLine: None - -# Control how short statements are placed -AllowShortBlocksOnASingleLine: Never -AllowShortIfStatementsOnASingleLine: Never -AllowShortLoopsOnASingleLine: false - -SeparateDefinitionBlocks: Always - -BreakBeforeBraces: Stroustrup diff --git a/src/runtime/.gitignore b/src/runtime/.gitignore deleted file mode 100644 index 378eac2..0000000 --- a/src/runtime/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/src/runtime/makefile b/src/runtime/makefile deleted file mode 100644 index ef774d8..0000000 --- a/src/runtime/makefile +++ /dev/null @@ -1,14 +0,0 @@ -CC ?= x86_64-linux-gnu-gcc -TARGET ?= x64 - -CFLAGS = -Wall -Werror -Wextra -g - -libruntime: build/runtime.o - $(CC) $(CFLAGS) -c targets/$(TARGET).s -o build/$(TARGET).o - -build/runtime.o: runtime/runtime.c - mkdir -p build - $(CC) $(CFLAGS) -c runtime/runtime.c -o build/runtime.o - -clean: - rm -r build diff --git a/src/runtime/runtime/runtime.c b/src/runtime/runtime/runtime.c deleted file mode 100644 index 3a819e9..0000000 --- a/src/runtime/runtime/runtime.c +++ /dev/null @@ -1,47 +0,0 @@ -#include - -typedef struct nub_string { - uint64_t size; - uint8_t buffer[]; -} nub_string_t; - -uint64_t nub_cstring_length(const char* string) -{ - uint64_t len = 0; - while (string[len] != '\0') { - len++; - } - return len; -} - -uint64_t nub_string_length(const nub_string_t* string) -{ - uint64_t count = 0; - uint64_t i = 0; - - while (i < string->size) { - uint8_t byte = string->buffer[i]; - - if ((byte & 0b11000000) != 0b10000000) { - count++; - } - - i++; - } - - return count; -} - -void nub_memcpy(uint8_t* source, uint8_t* destination, uint64_t length) -{ - for (uint64_t i = 0; i < length; i++) { - destination[i] = source[i]; - } -} - -void nub_memset(uint8_t* destination, uint8_t value, uint64_t count) -{ - for (uint64_t i = 0; i < count; i++) { - destination[i] = value; - } -} diff --git a/src/runtime/targets/x64.s b/src/runtime/targets/x64.s deleted file mode 100644 index f55a1d0..0000000 --- a/src/runtime/targets/x64.s +++ /dev/null @@ -1,10 +0,0 @@ -.intel_syntax noprefix - -.text -.globl _start -_start: - mov rdi, rsp - call main - mov rdi, rax - mov rax, 60 - syscall diff --git a/src/vscode/.gitignore b/src/vscode/.gitignore deleted file mode 100644 index c5e82d7..0000000 --- a/src/vscode/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bin \ No newline at end of file diff --git a/src/vscode/language-configuration.json b/src/vscode/language-configuration.json deleted file mode 100644 index 538ffc1..0000000 --- a/src/vscode/language-configuration.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "comments": { - "lineComment": "//", - "blockComment": ["/*", "*/"] - }, - "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] - ], - "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] - ], - "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] - ], - "indentationRules": { - "increaseIndentPattern": "^((?!\\/\\*).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$", - "decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\}\\]\\)].*$" - } -} diff --git a/src/vscode/makefile b/src/vscode/makefile deleted file mode 100644 index f2b1655..0000000 --- a/src/vscode/makefile +++ /dev/null @@ -1,6 +0,0 @@ -vscode: - mkdir -p bin - npx vsce package -o bin/nub-lang.vsix - -clean: - rm -r bin \ No newline at end of file diff --git a/src/vscode/package.json b/src/vscode/package.json deleted file mode 100644 index a988628..0000000 --- a/src/vscode/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "nub-lang", - "displayName": "Nub Language Support", - "description": "Syntax highlighting for Nub programming language", - "version": "0.0.0", - "publisher": "nub31", - "repository": { - "type": "git", - "url": "https://git.oliste.no/nub31/nub-lang" - }, - "license": "MIT", - "engines": { - "vscode": "^1.74.0" - }, - "categories": [ - "Programming Languages" - ], - "contributes": { - "languages": [ - { - "id": "nub", - "aliases": [ - "Nub", - "nub" - ], - "extensions": [ - ".nub" - ], - "configuration": "./language-configuration.json" - } - ], - "grammars": [ - { - "language": "nub", - "scopeName": "source.nub", - "path": "./syntaxes/nub.tmLanguage.json" - } - ] - } -} diff --git a/src/vscode/syntaxes/nub.tmLanguage.json b/src/vscode/syntaxes/nub.tmLanguage.json deleted file mode 100644 index 4a4780c..0000000 --- a/src/vscode/syntaxes/nub.tmLanguage.json +++ /dev/null @@ -1,300 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", - "name": "Nub", - "scopeName": "source.nub", - "patterns": [ - { - "include": "#comments" - }, - { - "include": "#keywords" - }, - { - "include": "#modifiers" - }, - { - "include": "#types" - }, - { - "include": "#strings" - }, - { - "include": "#numbers" - }, - { - "include": "#operators" - }, - { - "include": "#function-definition" - }, - { - "include": "#struct-definition" - }, - { - "include": "#function-call" - }, - { - "include": "#identifiers" - } - ], - "repository": { - "comments": { - "patterns": [ - { - "name": "comment.line.double-slash.nub", - "begin": "//", - "end": "$" - }, - { - "name": "comment.block.nub", - "begin": "/\\*", - "end": "\\*/" - } - ] - }, - "keywords": { - "patterns": [ - { - "name": "keyword.control.nub", - "match": "\\b(if|else|while|break|continue|return|let)\\b" - }, - { - "name": "keyword.other.nub", - "match": "\\b(namespace|func|struct|interface)\\b" - } - ] - }, - "modifiers": { - "patterns": [ - { - "name": "storage.modifier.nub", - "match": "\\b(export|extern|calls)\\b" - } - ] - }, - "types": { - "patterns": [ - { - "include": "#function-type" - }, - { - "name": "storage.type.primitive.nub", - "match": "\\b(i8|i16|i32|i64|u8|u16|u32|u64|f32|f64|bool|string|cstring|void|any)\\b" - }, - { - "name": "storage.type.array.nub", - "match": "\\[\\]" - }, - { - "name": "storage.type.pointer.nub", - "match": "\\^" - } - ] - }, - "function-type": { - "patterns": [ - { - "begin": "\\b(func)\\s*\\(", - "beginCaptures": { - "1": { - "name": "storage.type.function.nub" - } - }, - "end": "(?<=\\))(?:\\s*:\\s*([^\\s,;{}()]+))?", - "endCaptures": { - "1": { - "name": "storage.type.nub" - } - }, - "patterns": [ - { - "include": "#function-type-parameters" - } - ] - } - ] - }, - "function-type-parameters": { - "patterns": [ - { - "match": "\\.\\.\\.", - "name": "keyword.operator.variadic.nub" - }, - { - "include": "#types" - }, - { - "match": ",", - "name": "punctuation.separator.nub" - } - ] - }, - "strings": { - "patterns": [ - { - "name": "string.quoted.double.nub", - "begin": "\"", - "end": "\"", - "patterns": [ - { - "name": "constant.character.escape.nub", - "match": "\\\\(n|t|r|\\\\|\"|')" - }, - { - "name": "constant.character.escape.nub", - "match": "\\\\[0-7]{1,3}" - }, - { - "name": "constant.character.escape.nub", - "match": "\\\\x[0-9A-Fa-f]{1,2}" - } - ] - }, - { - "name": "string.quoted.single.nub", - "begin": "'", - "end": "'", - "patterns": [ - { - "name": "constant.character.escape.nub", - "match": "\\\\(n|t|r|\\\\|\"|')" - } - ] - } - ] - }, - "numbers": { - "patterns": [ - { - "name": "constant.numeric.float.nub", - "match": "\\b\\d+\\.\\d*([eE][+-]?\\d+)?[fF]?\\b" - }, - { - "name": "constant.numeric.integer.decimal.nub", - "match": "\\b\\d+\\b" - }, - { - "name": "constant.numeric.integer.hexadecimal.nub", - "match": "\\b0[xX][0-9A-Fa-f]+\\b" - }, - { - "name": "constant.numeric.integer.binary.nub", - "match": "\\b0[bB][01]+\\b" - } - ] - }, - "operators": { - "patterns": [ - { - "name": "keyword.operator.assignment.nub", - "match": "=" - }, - { - "name": "keyword.operator.comparison.nub", - "match": "(==|!=|<=|>=|<|>)" - }, - { - "name": "keyword.operator.arithmetic.nub", - "match": "(\\+|\\-|\\*|/)" - }, - { - "name": "keyword.operator.logical.nub", - "match": "(&&|\\|\\||!)" - }, - { - "name": "keyword.operator.address.nub", - "match": "&" - }, - { - "name": "keyword.operator.dereference.nub", - "match": "\\^" - }, - { - "name": "keyword.operator.member-access.nub", - "match": "\\." - } - ] - }, - "function-definition": { - "patterns": [ - { - "begin": "\\b(global\\s+|extern\\s+)?(func)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(", - "beginCaptures": { - "1": { - "name": "storage.modifier.nub" - }, - "2": { - "name": "keyword.other.nub" - }, - "3": { - "name": "entity.name.function.nub" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#function-parameters" - } - ] - } - ] - }, - "struct-definition": { - "patterns": [ - { - "match": "\\b(struct)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\b", - "captures": { - "1": { - "name": "keyword.other.nub" - }, - "2": { - "name": "entity.name.type.struct.nub" - } - } - } - ] - }, - "function-parameters": { - "patterns": [ - { - "match": "\\.\\.\\.", - "name": "keyword.operator.variadic.nub" - }, - { - "match": "([a-zA-Z_][a-zA-Z0-9_]*)\\s*:\\s*", - "captures": { - "1": { - "name": "variable.parameter.nub" - } - } - }, - { - "include": "#types" - }, - { - "include": "#identifiers" - } - ] - }, - "function-call": { - "patterns": [ - { - "match": "([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=\\()", - "captures": { - "1": { - "name": "entity.name.function.call.nub" - } - } - } - ] - }, - "identifiers": { - "patterns": [ - { - "name": "variable.other.nub", - "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b" - } - ] - } - } -} \ No newline at end of file