diff --git a/run.sh b/run.sh index 8ee553e..cb2a272 100755 --- a/run.sh +++ b/run.sh @@ -1,4 +1,4 @@ #!/bin/bash set -e -dotnet run --project src/compilation/CLI/CLI.csproj example/main.nub example/c.nub +dotnet run --project src/compiler/CLI/CLI.csproj example/main.nub example/c.nub ./bin/out \ No newline at end of file diff --git a/src/compilation/.gitignore b/src/compilation/.gitignore deleted file mode 100644 index c6cc67a..0000000 --- a/src/compilation/.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/compilation/.idea/.idea.Compiler/.idea/.gitignore b/src/compilation/.idea/.idea.Compiler/.idea/.gitignore deleted file mode 100644 index cda1cf4..0000000 --- a/src/compilation/.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/compilation/.idea/.idea.Compiler/.idea/.name b/src/compilation/.idea/.idea.Compiler/.idea/.name deleted file mode 100644 index b92b7a3..0000000 --- a/src/compilation/.idea/.idea.Compiler/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -Compiler \ No newline at end of file diff --git a/src/compilation/.idea/.idea.Compiler/.idea/encodings.xml b/src/compilation/.idea/.idea.Compiler/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/src/compilation/.idea/.idea.Compiler/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/compilation/.idea/.idea.Compiler/.idea/indexLayout.xml b/src/compilation/.idea/.idea.Compiler/.idea/indexLayout.xml deleted file mode 100644 index 2135b43..0000000 --- a/src/compilation/.idea/.idea.Compiler/.idea/indexLayout.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Runtime - - - - - \ No newline at end of file diff --git a/src/compilation/.idea/.idea.Compiler/.idea/vcs.xml b/src/compilation/.idea/.idea.Compiler/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/src/compilation/.idea/.idea.Compiler/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/compilation/CLI/CLI.csproj b/src/compilation/CLI/CLI.csproj deleted file mode 100644 index c8dff8b..0000000 --- a/src/compilation/CLI/CLI.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - nub - Exe - net9.0 - enable - enable - true - - - - - - - - - - - - - - - - diff --git a/src/compilation/CLI/GCC.cs b/src/compilation/CLI/GCC.cs deleted file mode 100644 index 445814c..0000000 --- a/src/compilation/CLI/GCC.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Diagnostics; - -namespace CLI; - -public static class GCC -{ - public static async Task Assemble(string asmPath, string objPath) - { - using var gccProcess = new Process(); - gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-g", "-c", asmPath, "-o", objPath]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - gccProcess.Start(); - await gccProcess.WaitForExitAsync(); - - var gccErrors = await gccProcess.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(gccErrors)) - { - await Console.Error.WriteLineAsync("gcc error when assembling:\n" + gccErrors); - } - - return gccProcess.ExitCode == 0; - } - - public static async Task Link(List objectFiles, string outputPath) - { - using var gccProcess = new Process(); - gccProcess.StartInfo = new ProcessStartInfo("gcc", ["-g", "-nostartfiles", "-o", outputPath, ..objectFiles]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - gccProcess.Start(); - await gccProcess.WaitForExitAsync(); - - var gccErrors = await gccProcess.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(gccErrors)) - { - await Console.Error.WriteLineAsync("gcc error when linking:\n" + gccErrors); - } - - return gccProcess.ExitCode == 0; - } -} diff --git a/src/compilation/CLI/Program.cs b/src/compilation/CLI/Program.cs deleted file mode 100644 index f62895f..0000000 --- a/src/compilation/CLI/Program.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System.Diagnostics; -using System.Reflection; -using CLI; -using Generation.QBE; -using Syntax; -using Syntax.Diagnostics; -using Syntax.Parsing; -using Syntax.Tokenization; -using Syntax.Typing; -using Binder = Syntax.Typing.Binder; - -const string BIN_DIR = "bin"; -const string BIN_INT_DIR = "bin-int"; - -if (Directory.Exists(BIN_DIR)) -{ - Directory.Delete(BIN_DIR, true); -} - -if (Directory.Exists(BIN_INT_DIR)) -{ - Directory.Delete(BIN_INT_DIR, true); -} - -Directory.CreateDirectory(BIN_DIR); -Directory.CreateDirectory(BIN_INT_DIR); - -var diagnostics = new List(); -var syntaxTrees = new List(); - -foreach (var file in args) -{ - if (!File.Exists(file)) - { - Console.Error.WriteLine($"File '{file}' does not exist"); - return 1; - } -} - -foreach (var file in args) -{ - var content = File.ReadAllText(file); - var sourceText = new SourceText(file, content); - - var tokenizeResult = Tokenizer.Tokenize(sourceText, out var tokenizerDiagnostics); - diagnostics.AddRange(tokenizerDiagnostics); - - var syntaxTree = Parser.ParseFile(tokenizeResult, file, out var parseDiagnostics); - diagnostics.AddRange(parseDiagnostics); - - if (syntaxTree != null) - { - syntaxTrees.Add(syntaxTree); - } - else - { - throw new Exception(); - } -} - -var definitionTable = new DefinitionTable(syntaxTrees); - -var boundSyntaxTrees = new List(); - -foreach (var syntaxTree in syntaxTrees) -{ - boundSyntaxTrees.Add(Binder.Bind(syntaxTree, definitionTable)); -} - -var boundDefinitionTable = new BoundDefinitionTable(boundSyntaxTrees); - -foreach (var diagnostic in diagnostics) -{ - Console.Error.WriteLine(diagnostic.FormatANSI()); -} - -if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)) -{ - return 1; -} - -var objectFiles = new List(); - -foreach (var boundSyntaxTree in boundSyntaxTrees) -{ - var relativeFilePath = Path.GetRelativePath(Environment.CurrentDirectory, boundSyntaxTree.FilePath); - var outputPath = Path.Combine(BIN_INT_DIR, "program", relativeFilePath); - - var outputDirectory = Path.GetDirectoryName(outputPath); - Debug.Assert(!string.IsNullOrWhiteSpace(outputDirectory)); - Directory.CreateDirectory(outputDirectory); - - var ssa = QBEGenerator.Emit(boundSyntaxTree, boundDefinitionTable); - var ssaPath = Path.ChangeExtension(outputPath, "ssa"); - File.WriteAllText(ssaPath, ssa); - - var asm = await QBE.Invoke(ssa); - if (asm == null) - { - return 1; - } - - var asmPath = Path.ChangeExtension(outputPath, "s"); - await File.WriteAllTextAsync(asmPath, asm); - - var objPath = Path.ChangeExtension(outputPath, "o"); - var asmSuccess = await GCC.Assemble(asmPath, objPath); - if (!asmSuccess) - { - return 1; - } - - objectFiles.Add(objPath); -} - -var assembly = Assembly.GetExecutingAssembly(); -var runtimeResources = assembly - .GetManifestResourceNames() - .Where(name => name.EndsWith(".s", StringComparison.OrdinalIgnoreCase)); - -foreach (var resourceName in runtimeResources) -{ - await using var stream = assembly.GetManifestResourceStream(resourceName); - if (stream == null) - { - Console.Error.WriteLine($"Could not load embedded resource {resourceName}"); - return 1; - } - - using var reader = new StreamReader(stream); - var asm = await reader.ReadToEndAsync(); - - var outputDirectory = Path.Combine(BIN_INT_DIR, "runtime"); - Directory.CreateDirectory(outputDirectory); - - var fileName = resourceName.Split('.').Reverse().Skip(1).First(); - - var asmPath = Path.Combine(outputDirectory, fileName + ".s"); - await File.WriteAllTextAsync(asmPath, asm); - - var objPath = Path.Combine(outputDirectory, fileName + ".o"); - var asmSuccess = await GCC.Assemble(asmPath, objPath); - if (!asmSuccess) - { - return 1; - } - - objectFiles.Add(objPath); -} - -var linkSuccess = await GCC.Link(objectFiles, Path.Combine(BIN_DIR, "out")); -return linkSuccess ? 0 : 1; \ No newline at end of file diff --git a/src/compilation/CLI/QBE.cs b/src/compilation/CLI/QBE.cs deleted file mode 100644 index c592e12..0000000 --- a/src/compilation/CLI/QBE.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Diagnostics; - -namespace CLI; - -public static class QBE -{ - public static async Task Invoke(string ssa) - { - using var qbeProcess = new Process(); - qbeProcess.StartInfo = new ProcessStartInfo - { - FileName = "qbe", - UseShellExecute = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - qbeProcess.Start(); - - await qbeProcess.StandardInput.WriteAsync(ssa); - qbeProcess.StandardInput.Close(); - - await qbeProcess.WaitForExitAsync(); - - var qbeErrors = await qbeProcess.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(qbeErrors)) - { - await Console.Error.WriteLineAsync("qbe error:\n" + qbeErrors); - } - - var asm = await qbeProcess.StandardOutput.ReadToEndAsync(); - - return qbeProcess.ExitCode == 0 ? asm : null; - } -} diff --git a/src/compilation/CLI/Runtime/entry.s b/src/compilation/CLI/Runtime/entry.s deleted file mode 100644 index 5ea2a6a..0000000 --- a/src/compilation/CLI/Runtime/entry.s +++ /dev/null @@ -1,12 +0,0 @@ -.intel_syntax noprefix - -.equ SYS_EXIT, 60 - -.text -.globl _start -_start: - mov rdi, rsp - call main - mov rdi, rax - mov rax, SYS_EXIT - syscall diff --git a/src/compilation/CLI/Runtime/nub_cstring.s b/src/compilation/CLI/Runtime/nub_cstring.s deleted file mode 100644 index 8acef4f..0000000 --- a/src/compilation/CLI/Runtime/nub_cstring.s +++ /dev/null @@ -1,17 +0,0 @@ -.intel_syntax noprefix - -.text -.globl nub_cstring_length -# func nub_cstring_length(string: cstring): u64 -nub_cstring_length: - xor rax, rax - -count_loop: - cmp byte ptr [rdi + rax], 0 - je done - - inc rax - jmp count_loop - -done: - ret diff --git a/src/compilation/CLI/Runtime/nub_mem.s b/src/compilation/CLI/Runtime/nub_mem.s deleted file mode 100644 index 27cf71c..0000000 --- a/src/compilation/CLI/Runtime/nub_mem.s +++ /dev/null @@ -1,27 +0,0 @@ -.intel_syntax noprefix - -.text -.globl nub_memcpy -# func nub_memcpy(destination: ^u8, source: ^u8, count: u64): ^u8 -nub_memcpy: - mov rcx, rdx - rep movsb - ret - -.text -.globl nub_memset -# func nub_memset(destination: ^u8, value: i8, count: u64): ^u8 -nub_memset: - push rdi - mov rcx, rdx - mov al, sil - test rcx, rcx - jz memset_done -memset_loop: - mov BYTE PTR [rdi], al - inc rdi - dec rcx - jnz memset_loop -memset_done: - pop rax - ret diff --git a/src/compilation/CLI/Runtime/nub_panic.s b/src/compilation/CLI/Runtime/nub_panic.s deleted file mode 100644 index 51332e2..0000000 --- a/src/compilation/CLI/Runtime/nub_panic.s +++ /dev/null @@ -1,47 +0,0 @@ -.intel_syntax noprefix - -.equ NUB_PANIC_ERROR_CODE, 101 - -.equ SYS_WRITE, 1 -.equ SYS_EXIT, 60 - -.equ FD_STDIN, 0 -.equ FD_STDOUT, 1 -.equ FD_STDERR, 2 - -.data -.align 8 -array_oob_msg: - .ascii "Index is out of bounds of array\n" - -.data -.align 8 -oom_msg: - .ascii "Out of memory\n" - -.text -.globl nub_panic -nub_panic: - mov rax, SYS_EXIT - mov rdi, NUB_PANIC_ERROR_CODE - syscall - -.text -.globl nub_panic_array_oob -nub_panic_array_oob: - mov rax, SYS_WRITE - mov rdi, FD_STDERR - lea rsi, [rip + array_oob_msg] - mov rdx, 32 - syscall - call nub_panic - -.text -.globl nub_panic_oom -nub_panic_oom: - mov rax, SYS_WRITE - mov rdi, FD_STDERR - lea rsi, [rip + oom_msg] - mov rdx, 14 - syscall - call nub_panic diff --git a/src/compilation/CLI/Runtime/nub_string.s b/src/compilation/CLI/Runtime/nub_string.s deleted file mode 100644 index dccbd5d..0000000 --- a/src/compilation/CLI/Runtime/nub_string.s +++ /dev/null @@ -1,32 +0,0 @@ -.intel_syntax noprefix - -.text -.globl nub_string_length -# func nub_string_length(string: string): u64 -nub_string_length: - mov rsi, [rdi] # Length of string in bytes - - add rdi, 8 # Start of bytes - xor rax, rax # Character count - xor rdx, rdx # Current byte position - - test rsi, rsi - jz _done - -_count_loop: - cmp rdx, rsi - jge _done - - mov dl, [rdi + rdx] - and dl, 0b11000000 - cmp dl, 0b10000000 - je _skip_byte - - inc rax - -_skip_byte: - inc rdx - jmp _count_loop - -_done: - ret diff --git a/src/compilation/Common/Common.csproj b/src/compilation/Common/Common.csproj deleted file mode 100644 index b682a68..0000000 --- a/src/compilation/Common/Common.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - net9.0 - enable - enable - true - - - diff --git a/src/compilation/Common/Optional.cs b/src/compilation/Common/Optional.cs deleted file mode 100644 index 5cd9f29..0000000 --- a/src/compilation/Common/Optional.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Common; - -public readonly struct Optional -{ - public static Optional Empty() => new(); - - /// - /// Alias for creating an Optional<TValue> which allows for implicit types - /// - /// - /// - /// - public static Optional OfNullable(TValue? value) - { - return value ?? Optional.Empty(); - } -} - -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/compilation/Common/Variant.cs b/src/compilation/Common/Variant.cs deleted file mode 100644 index 541b111..0000000 --- a/src/compilation/Common/Variant.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Common; - -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 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/compilation/Compiler.sln b/src/compilation/Compiler.sln deleted file mode 100644 index 702f726..0000000 --- a/src/compilation/Compiler.sln +++ /dev/null @@ -1,34 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Syntax", "Syntax\Syntax.csproj", "{5047E21F-590D-4CB3-AFF3-064316485009}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI", "CLI\CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generation", "Generation\Generation.csproj", "{F903F1B9-69A6-4522-B483-81A4B072C8B1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{91ECE034-32D4-48E6-A905-5F95DB95A3D4}" -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 - {F903F1B9-69A6-4522-B483-81A4B072C8B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F903F1B9-69A6-4522-B483-81A4B072C8B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F903F1B9-69A6-4522-B483-81A4B072C8B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F903F1B9-69A6-4522-B483-81A4B072C8B1}.Release|Any CPU.Build.0 = Release|Any CPU - {91ECE034-32D4-48E6-A905-5F95DB95A3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {91ECE034-32D4-48E6-A905-5F95DB95A3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {91ECE034-32D4-48E6-A905-5F95DB95A3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {91ECE034-32D4-48E6-A905-5F95DB95A3D4}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/src/compilation/Generation/Generation.csproj b/src/compilation/Generation/Generation.csproj deleted file mode 100644 index fbe4e73..0000000 --- a/src/compilation/Generation/Generation.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - net9.0 - enable - enable - true - - - - - - - - diff --git a/src/compilation/Generation/QBE/QBEGenerator.cs b/src/compilation/Generation/QBE/QBEGenerator.cs deleted file mode 100644 index d8850c6..0000000 --- a/src/compilation/Generation/QBE/QBEGenerator.cs +++ /dev/null @@ -1,1433 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Text; -using Syntax; -using Syntax.Parsing.Node; -using Syntax.Tokenization; -using Syntax.Typing; -using Syntax.Typing.BoundNode; - -namespace Generation.QBE; - -public static class QBEGenerator -{ - private static BoundSyntaxTree _syntaxTree = null!; - private static BoundDefinitionTable _definitionTable = null!; - - private static StringBuilder _builder = new(); - private static List _cStringLiterals = []; - private static List _stringLiterals = []; - private static Stack _breakLabels = []; - private static Stack _continueLabels = []; - private static Queue<(BoundAnonymousFuncNode Func, string Name)> _anonymousFunctions = []; - private static Stack _variables = []; - private static Stack _variableScopes = []; - private static int _variableIndex; - private static int _labelIndex; - private static int _anonymousFuncIndex; - private static int _cStringLiteralIndex; - private static int _stringLiteralIndex; - private static bool _codeIsReachable = true; - - public static string Emit(BoundSyntaxTree syntaxTree, BoundDefinitionTable definitionTable) - { - _syntaxTree = syntaxTree; - _definitionTable = definitionTable; - - _builder = new StringBuilder(); - _cStringLiterals = []; - _stringLiterals = []; - _breakLabels = []; - _continueLabels = []; - _anonymousFunctions = []; - _variables = []; - _variableScopes = []; - _variableIndex = 0; - _labelIndex = 0; - _anonymousFuncIndex = 0; - _cStringLiteralIndex = 0; - _stringLiteralIndex = 0; - _codeIsReachable = true; - - foreach (var structDef in _definitionTable.GetStructs()) - { - EmitStructDefinition(structDef); - _builder.AppendLine(); - } - - foreach (var funcDef in _syntaxTree.Definitions.OfType()) - { - EmitFuncDefinition(FuncName(funcDef), funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); - _builder.AppendLine(); - } - - while (_anonymousFunctions.TryDequeue(out var anon)) - { - EmitFuncDefinition(anon.Name, anon.Func.Parameters, anon.Func.ReturnType, anon.Func.Body, false); - _builder.AppendLine(); - } - - foreach (var cStringLiteral in _cStringLiterals) - { - _builder.AppendLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}"); - } - - foreach (var stringLiteral in _stringLiterals) - { - var bytes = Encoding.UTF8.GetBytes(stringLiteral.Value).Select(b => $"b {b}"); - _builder.AppendLine($"data {stringLiteral.Name} = {{ l {stringLiteral.Value.Length}, {string.Join(", ", bytes)} }}"); - } - - return _builder.ToString(); - } - - private static string VarName() - { - return $"%v{++_variableIndex}"; - } - - private static string LabelName() - { - return $"@l{++_labelIndex}"; - } - - private static string CStringName() - { - return $"$cstring{++_cStringLiteralIndex}"; - } - - private static string StringName() - { - return $"$string{++_stringLiteralIndex}"; - } - - private static string FuncName(BoundFuncDefinition funcDef) - { - return funcDef switch - { - BoundExternFuncDefinitionNode externFuncDefinition => $"${externFuncDefinition.CallName}", - BoundLocalFuncDefinitionNode localFuncDefinition => localFuncDefinition.Exported - ? $"${localFuncDefinition.Name}" - : $"${localFuncDefinition.Namespace}_{localFuncDefinition.Name}", - _ => throw new ArgumentOutOfRangeException(nameof(funcDef)) - }; - } - - private static string StructName(BoundStructDefinitionNode structDef) - { - return $":{structDef.Namespace}_{structDef.Name}"; - } - - private static string QBEStore(NubType type) - { - return type switch - { - NubArrayType => "storel", - NubPointerType => "storel", - NubPrimitiveType primitiveType => primitiveType.Kind switch - { - PrimitiveTypeKind.I64 => "storel", - PrimitiveTypeKind.I32 => "storew", - PrimitiveTypeKind.I16 => "storeh", - PrimitiveTypeKind.I8 => "storeb", - PrimitiveTypeKind.U64 => "storel", - PrimitiveTypeKind.U32 => "storew", - PrimitiveTypeKind.U16 => "storeh", - PrimitiveTypeKind.U8 => "storeb", - PrimitiveTypeKind.F64 => "stored", - PrimitiveTypeKind.F32 => "stores", - PrimitiveTypeKind.Bool => "storew", - _ => throw new ArgumentOutOfRangeException() - }, - NubStructType => "storel", - NubFixedArrayType => "storel", - NubFuncType => "storel", - NubCStringType => "storel", - NubStringType => "storel", - _ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions") - }; - } - - private static string QBELoad(NubType type) - { - return type switch - { - NubArrayType => "loadl", - NubPointerType => "loadl", - NubPrimitiveType primitiveType => primitiveType.Kind switch - { - PrimitiveTypeKind.I64 => "loadl", - PrimitiveTypeKind.I32 => "loadw", - PrimitiveTypeKind.I16 => "loadsh", - PrimitiveTypeKind.I8 => "loadsb", - PrimitiveTypeKind.U64 => "loadl", - PrimitiveTypeKind.U32 => "loadw", - PrimitiveTypeKind.U16 => "loaduh", - PrimitiveTypeKind.U8 => "loadub", - PrimitiveTypeKind.F64 => "loadd", - PrimitiveTypeKind.F32 => "loads", - PrimitiveTypeKind.Bool => "loadw", - _ => throw new ArgumentOutOfRangeException() - }, - NubStructType => "loadl", - NubFixedArrayType => "loadl", - NubFuncType => "loadl", - NubCStringType => "loadl", - NubStringType => "loadl", - _ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions") - }; - } - - private static string QBEAssign(NubType type) - { - return type switch - { - NubArrayType => "=l", - NubPointerType => "=l", - NubPrimitiveType primitiveType => primitiveType.Kind switch - { - PrimitiveTypeKind.I64 => "=l", - PrimitiveTypeKind.I32 => "=w", - PrimitiveTypeKind.I16 => "=w", - PrimitiveTypeKind.I8 => "=w", - PrimitiveTypeKind.U64 => "=l", - PrimitiveTypeKind.U32 => "=w", - PrimitiveTypeKind.U16 => "=w", - PrimitiveTypeKind.U8 => "=w", - PrimitiveTypeKind.F64 => "=d", - PrimitiveTypeKind.F32 => "=s", - PrimitiveTypeKind.Bool => "=w", - _ => throw new ArgumentOutOfRangeException() - }, - NubStructType => "=l", - NubFixedArrayType => "=l", - NubFuncType => "=l", - NubCStringType => "=l", - NubStringType => "=l", - _ => throw new NotSupportedException($"'{type}' type cannot be used in variables") - }; - } - - private static int AlignmentOf(NubType type) - { - switch (type) - { - case NubPrimitiveType primitiveType: - { - return primitiveType.Kind switch - { - PrimitiveTypeKind.I64 or PrimitiveTypeKind.U64 or PrimitiveTypeKind.F64 => 8, - PrimitiveTypeKind.I32 or PrimitiveTypeKind.U32 or PrimitiveTypeKind.F32 or PrimitiveTypeKind.Bool => 4, - PrimitiveTypeKind.I16 or PrimitiveTypeKind.U16 => 2, - PrimitiveTypeKind.I8 or PrimitiveTypeKind.U8 => 1, - _ => throw new ArgumentOutOfRangeException() - }; - } - case NubStructType nubStructType: - { - var definition = _definitionTable.LookupStruct(nubStructType.Namespace, nubStructType.Name).GetValue(); - return definition.Fields.Max(f => AlignmentOf(f.Type)); - } - case NubPointerType: - case NubArrayType: - case NubCStringType: - case NubStringType: - case NubFuncType: - { - return 8; - } - case NubFixedArrayType nubFixedArrayType: - { - return AlignmentOf(nubFixedArrayType.ElementType); - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - } - - private static int AlignTo(int offset, int alignment) - { - return (offset + alignment - 1) & ~(alignment - 1); - } - - private static int SizeOf(NubType type) - { - switch (type) - { - case NubPrimitiveType primitiveType: - { - return primitiveType.Kind switch - { - PrimitiveTypeKind.I64 or PrimitiveTypeKind.U64 or PrimitiveTypeKind.F64 => 8, - PrimitiveTypeKind.I32 or PrimitiveTypeKind.U32 or PrimitiveTypeKind.F32 or PrimitiveTypeKind.Bool => 4, - PrimitiveTypeKind.I16 or PrimitiveTypeKind.U16 => 2, - PrimitiveTypeKind.I8 or PrimitiveTypeKind.U8 => 1, - _ => throw new ArgumentOutOfRangeException() - }; - } - case NubStructType nubStructType: - { - var definition = _definitionTable.LookupStruct(nubStructType.Namespace, nubStructType.Name).GetValue(); - - var size = 0; - var maxAlignment = 1; - - foreach (var field in definition.Fields) - { - var fieldAlignment = AlignmentOf(field.Type); - maxAlignment = Math.Max(maxAlignment, fieldAlignment); - - size = AlignTo(size, fieldAlignment); - size += SizeOf(field.Type); - } - - size = AlignTo(size, maxAlignment); - - return size; - } - case NubPointerType: - case NubArrayType: - case NubCStringType: - case NubStringType: - case NubFuncType: - { - return 8; - } - case NubFixedArrayType nubFixedArrayType: - { - return SizeOf(nubFixedArrayType.ElementType) * nubFixedArrayType.Capacity + 8; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - } - - private static int OffsetOf(BoundStructDefinitionNode structDefinition, string member) - { - var offset = 0; - - foreach (var field in structDefinition.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"); - } - - private static bool IsPointerType(NubType type) - { - if (type.IsVoid) - { - throw new InvalidOperationException($"{nameof(IsPointerType)} should not be called on void types"); - } - - return type is NubStructType or NubFixedArrayType; - } - - private static void EmitFuncDefinition(string name, List parameters, NubType returnType, BoundBlockNode body, bool exported) - { - _variables.Clear(); - _variableScopes.Clear(); - - if (exported) - { - _builder.Append("export "); - } - - _builder.Append("function "); - if (returnType is not NubVoidType) - { - _builder.Append(returnType switch - { - NubArrayType => "l", - NubPointerType => "l", - NubPrimitiveType primitiveType => primitiveType.Kind switch - { - PrimitiveTypeKind.I64 => "l", - PrimitiveTypeKind.I32 => "w", - PrimitiveTypeKind.I16 => "sh", - PrimitiveTypeKind.I8 => "sb", - PrimitiveTypeKind.U64 => "l", - PrimitiveTypeKind.U32 => "w", - PrimitiveTypeKind.U16 => "uh", - PrimitiveTypeKind.U8 => "ub", - PrimitiveTypeKind.F64 => "d", - PrimitiveTypeKind.F32 => "s", - PrimitiveTypeKind.Bool => "w", - _ => throw new ArgumentOutOfRangeException() - }, - NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()), - NubFixedArrayType => "l", - NubFuncType => "l", - NubCStringType => "l", - NubStringType => "l", - _ => throw new NotSupportedException($"'{returnType}' type cannot be used as a function return type") - }); - _builder.Append(' '); - } - - _builder.Append(name); - - var parameterStrings = parameters.Select(parameter => - { - var qbeType = parameter.Type switch - { - NubArrayType => "l", - NubPointerType => "l", - NubPrimitiveType primitiveType => primitiveType.Kind switch - { - PrimitiveTypeKind.I64 => "l", - PrimitiveTypeKind.I32 => "w", - PrimitiveTypeKind.I16 => "sh", - PrimitiveTypeKind.I8 => "sb", - PrimitiveTypeKind.U64 => "l", - PrimitiveTypeKind.U32 => "w", - PrimitiveTypeKind.U16 => "uh", - PrimitiveTypeKind.U8 => "ub", - PrimitiveTypeKind.F64 => "d", - PrimitiveTypeKind.F32 => "s", - PrimitiveTypeKind.Bool => "w", - _ => throw new ArgumentOutOfRangeException() - }, - NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()), - NubFixedArrayType => "l", - NubFuncType => "l", - NubCStringType => "l", - NubStringType => "l", - _ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type") - }; - - return $"{qbeType} %{parameter.Name}"; - }); - - _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); - _builder.AppendLine("@start"); - - List parameterVars = []; - - foreach (var parameter in parameters) - { - var parameterName = "%" + parameter.Name; - - if (parameter.Type is NubPrimitiveType primitiveType) - { - switch (primitiveType.Kind) - { - case PrimitiveTypeKind.I16: - parameterName = VarName(); - _builder.AppendLine($" {parameterName} =w extsh %{parameter.Name}"); - break; - case PrimitiveTypeKind.I8: - parameterName = VarName(); - _builder.AppendLine($" {parameterName} =w extsb %{parameter.Name}"); - break; - case PrimitiveTypeKind.U16: - parameterName = VarName(); - _builder.AppendLine($" {parameterName} =w extuh %{parameter.Name}"); - break; - case PrimitiveTypeKind.U8: - parameterName = VarName(); - _builder.AppendLine($" {parameterName} =w extub %{parameter.Name}"); - break; - } - } - - parameterVars.Add(new Variable(parameter.Name, new Val(parameterName, parameter.Type, ValKind.Immediate))); - } - - EmitBlock(body, parameterVars); - - if (body.Statements.LastOrDefault() is not BoundReturnNode) - { - if (returnType is NubVoidType) - { - _builder.AppendLine(" ret"); - } - } - - _builder.AppendLine("}"); - } - - private static void EmitStructDefinition(BoundStructDefinitionNode structDefinition) - { - _builder.Append($"type {StructName(structDefinition)} = {{ "); - foreach (var structDefinitionField in structDefinition.Fields) - { - var qbeType = structDefinitionField.Type switch - { - NubArrayType => "l", - NubPointerType => "l", - NubPrimitiveType primitiveType => primitiveType.Kind switch - { - PrimitiveTypeKind.I64 => "l", - PrimitiveTypeKind.I32 => "w", - PrimitiveTypeKind.I16 => "h", - PrimitiveTypeKind.I8 => "b", - PrimitiveTypeKind.U64 => "l", - PrimitiveTypeKind.U32 => "w", - PrimitiveTypeKind.U16 => "h", - PrimitiveTypeKind.U8 => "b", - PrimitiveTypeKind.F64 => "d", - PrimitiveTypeKind.F32 => "s", - PrimitiveTypeKind.Bool => "w", - _ => throw new ArgumentOutOfRangeException() - }, - NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()), - NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}", - NubFuncType => "l", - NubCStringType => "l", - NubStringType => "l", - _ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs") - }; - _builder.Append(qbeType + ", "); - } - - _builder.AppendLine("}"); - } - - private static void EmitStatement(BoundStatementNode statement) - { - switch (statement) - { - case BoundArrayIndexAssignmentNode arrayIndexAssignment: - EmitArrayIndexAssignment(arrayIndexAssignment); - break; - case BoundBreakNode: - EmitBreak(); - break; - case BoundContinueNode: - EmitContinue(); - break; - case BoundDereferenceAssignmentNode dereferenceAssignment: - EmitDereferenceAssignment(dereferenceAssignment); - break; - case BoundIfNode ifStatement: - EmitIf(ifStatement); - break; - case BoundMemberAssignmentNode memberAssignment: - EmitMemberAssignment(memberAssignment); - break; - case BoundReturnNode @return: - EmitReturn(@return); - break; - case BoundStatementExpressionNode statementExpression: - EmitExpression(statementExpression.Expression); - break; - case BoundVariableDeclarationNode variableDeclaration: - EmitVariableDeclaration(variableDeclaration); - break; - case BoundVariableAssignmentNode variableAssignment: - EmitVariableAssignment(variableAssignment); - break; - case BoundWhileNode whileStatement: - EmitWhile(whileStatement); - break; - default: - throw new ArgumentOutOfRangeException(nameof(statement)); - } - } - - private static void EmitArrayIndexAssignment(BoundArrayIndexAssignmentNode arrayIndexAssignment) - { - var array = EmitUnwrap(EmitExpression(arrayIndexAssignment.ArrayIndexAccess.Array)); - var index = EmitUnwrap(EmitExpression(arrayIndexAssignment.ArrayIndexAccess.Index)); - EmitArrayBoundsCheck(array, index); - var value = EmitUnwrap(EmitExpression(arrayIndexAssignment.Value)); - - switch (arrayIndexAssignment.ArrayIndexAccess.Array.Type) - { - case NubArrayType arrayType: - { - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l mul {index}, {SizeOf(arrayType.ElementType)}"); - _builder.AppendLine($" {pointer} =l add {pointer}, 8"); - _builder.AppendLine($" {pointer} =l add {array}, {pointer}"); - - EmitCopy(arrayType.ElementType, value, pointer); - break; - } - case NubFixedArrayType fixedArrayType: - { - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l mul {index}, {SizeOf(fixedArrayType.ElementType)}"); - _builder.AppendLine($" {pointer} =l add {pointer}, 8"); - _builder.AppendLine($" {pointer} =l add {array}, {pointer}"); - - EmitCopy(fixedArrayType.ElementType, value, pointer); - break; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - } - - private static void EmitBlock(BoundBlockNode block, List? variables = null) - { - _variableScopes.Push(_variables.Count); - if (variables != null) - { - foreach (var variable in variables) - { - _variables.Push(variable); - } - } - - foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) - { - EmitStatement(statement); - } - - var count = _variableScopes.Pop(); - while (_variableScopes.Count > count) - { - _variableScopes.Pop(); - } - - _codeIsReachable = true; - } - - private static void EmitBreak() - { - _builder.AppendLine($" jmp {_breakLabels.Peek()}"); - _codeIsReachable = false; - } - - private static void EmitContinue() - { - _builder.AppendLine($" jmp {_continueLabels.Peek()}"); - _codeIsReachable = false; - } - - private static void EmitDereferenceAssignment(BoundDereferenceAssignmentNode dereferenceAssignment) - { - var pointer = EmitUnwrap(EmitExpression(dereferenceAssignment.Dereference.Expression)); - var value = EmitUnwrap(EmitExpression(dereferenceAssignment.Value)); - EmitCopy(dereferenceAssignment.Value.Type, value, pointer); - } - - private static void EmitIf(BoundIfNode ifStatement) - { - var trueLabel = LabelName(); - var falseLabel = LabelName(); - var endLabel = LabelName(); - - var result = EmitUnwrap(EmitExpression(ifStatement.Condition)); - _builder.AppendLine($" jnz {result}, {trueLabel}, {falseLabel}"); - _builder.AppendLine(trueLabel); - EmitBlock(ifStatement.Body); - _builder.AppendLine($" jmp {endLabel}"); - _builder.AppendLine(falseLabel); - if (ifStatement.Else.HasValue) - { - ifStatement.Else.Value.Match - ( - elseIfNode => EmitIf(elseIfNode), - elseNode => EmitBlock(elseNode) - ); - } - - _builder.AppendLine(endLabel); - } - - private static void EmitMemberAssignment(BoundMemberAssignmentNode memberAssignment) - { - var structType = memberAssignment.MemberAccess.Expression.Type as NubStructType; - Debug.Assert(structType != null); - - var structDefinition = _definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue(); - var offset = OffsetOf(structDefinition, memberAssignment.MemberAccess.Member); - - var item = EmitUnwrap(EmitExpression(memberAssignment.MemberAccess.Expression)); - var pointer = VarName(); - - _builder.AppendLine($" {pointer} =l add {item}, {offset}"); - - var value = EmitUnwrap(EmitExpression(memberAssignment.Value)); - - EmitCopy(memberAssignment.Value.Type, value, pointer); - } - - private static void EmitReturn(BoundReturnNode @return) - { - if (@return.Value.HasValue) - { - var result = EmitUnwrap(EmitExpression(@return.Value.Value)); - _builder.AppendLine($" ret {result}"); - } - else - { - _builder.AppendLine(" ret"); - } - } - - private static void EmitVariableDeclaration(BoundVariableDeclarationNode variableDeclaration) - { - var tmp = VarName(); - _builder.AppendLine($" {tmp} =l alloc8 {SizeOf(variableDeclaration.Type)}"); - _variables.Push(new Variable(variableDeclaration.Name, new Val(tmp, variableDeclaration.Type, ValKind.Pointer))); - } - - private static void EmitVariableAssignment(BoundVariableAssignmentNode variableAssignment) - { - var value = EmitUnwrap(EmitExpression(variableAssignment.Value)); - var variable = _variables.Single(x => x.Name == variableAssignment.Identifier.Name); - EmitCopy(variableAssignment.Value.Type, value, variable.Val.Name); - } - - private static void EmitWhile(BoundWhileNode whileStatement) - { - var conditionLabel = LabelName(); - var iterationLabel = LabelName(); - var endLabel = LabelName(); - - _breakLabels.Push(endLabel); - _continueLabels.Push(conditionLabel); - - _builder.AppendLine($" jmp {conditionLabel}"); - _builder.AppendLine(iterationLabel); - EmitBlock(whileStatement.Body); - _builder.AppendLine(conditionLabel); - var result = EmitUnwrap(EmitExpression(whileStatement.Condition)); - _builder.AppendLine($" jnz {result}, {iterationLabel}, {endLabel}"); - _builder.AppendLine(endLabel); - - _continueLabels.Pop(); - _breakLabels.Pop(); - } - - private static Val EmitExpression(BoundExpressionNode expression) - { - return expression switch - { - BoundAddressOfNode addressOf => EmitAddressOf(addressOf), - BoundAnonymousFuncNode anonymousFunc => EmitAnonymousFunc(anonymousFunc), - BoundArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), - BoundArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), - BoundBinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), - BoundDereferenceNode dereference => EmitDereference(dereference), - BoundFixedArrayInitializerNode fixedArrayInitializer => EmitFixedArrayInitializer(fixedArrayInitializer), - BoundFuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), - BoundIdentifierNode identifier => EmitIdentifier(identifier), - BoundLiteralNode literal => EmitLiteral(literal), - BoundStructInitializerNode structInitializer => EmitStructInitializer(structInitializer), - BoundUnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), - BoundMemberAccessNode memberAccess => EmitMemberAccess(memberAccess), - _ => throw new ArgumentOutOfRangeException(nameof(expression)) - }; - } - - private static Val EmitAnonymousFunc(BoundAnonymousFuncNode anonymousFunc) - { - var name = $"$anon_func{++_anonymousFuncIndex}"; - _anonymousFunctions.Enqueue((anonymousFunc, name)); - return new Val(name, anonymousFunc.Type, ValKind.Func); - } - - private static string EmitArrayIndexPointer(BoundArrayIndexAccessNode arrayIndexAccess) - { - var array = EmitUnwrap(EmitExpression(arrayIndexAccess.Array)); - var index = EmitUnwrap(EmitExpression(arrayIndexAccess.Index)); - EmitArrayBoundsCheck(array, index); - - var elementType = arrayIndexAccess.Array.Type switch - { - NubArrayType arrayType => arrayType.ElementType, - NubFixedArrayType fixedArrayType => fixedArrayType.ElementType, - _ => throw new ArgumentOutOfRangeException() - }; - - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l mul {index}, {SizeOf(elementType)}"); - _builder.AppendLine($" {pointer} =l add {pointer}, 8"); - _builder.AppendLine($" {pointer} =l add {array}, {pointer}"); - return pointer; - } - - private static Val EmitArrayIndexAccess(BoundArrayIndexAccessNode arrayIndexAccess) - { - var pointer = EmitArrayIndexPointer(arrayIndexAccess); - var outputName = VarName(); - _builder.AppendLine($" {outputName} {QBEAssign(arrayIndexAccess.Type)} {QBELoad(arrayIndexAccess.Type)} {pointer}"); - return new Val(outputName, arrayIndexAccess.Type, ValKind.Immediate); - } - - private static void EmitArrayBoundsCheck(string array, string index) - { - var count = VarName(); - _builder.AppendLine($" {count} =l loadl {array}"); - - var isNegative = VarName(); - _builder.AppendLine($" {isNegative} =w csltl {index}, 0"); - - var isOob = VarName(); - _builder.AppendLine($" {isOob} =w csgel {index}, {count}"); - - var anyOob = VarName(); - _builder.AppendLine($" {anyOob} =w or {isNegative}, {isOob}"); - - var oobLabel = LabelName(); - var notOobLabel = LabelName(); - _builder.AppendLine($" jnz {anyOob}, {oobLabel}, {notOobLabel}"); - - _builder.AppendLine(oobLabel); - _builder.AppendLine($" call $nub_panic_array_oob()"); - - _builder.AppendLine(notOobLabel); - } - - private static Val EmitArrayInitializer(BoundArrayInitializerNode arrayInitializer) - { - var capacity = EmitUnwrap(EmitExpression(arrayInitializer.Capacity)); - var elementSize = SizeOf(arrayInitializer.ElementType); - - var capacityInBytes = VarName(); - _builder.AppendLine($" {capacityInBytes} =l mul {capacity}, {elementSize}"); - var totalSize = VarName(); - _builder.AppendLine($" {totalSize} =l add {capacityInBytes}, 8"); - - var arrayPointer = VarName(); - _builder.AppendLine($" {arrayPointer} =l alloc8 {totalSize}"); - _builder.AppendLine($" storel {capacity}, {arrayPointer}"); - - var dataPointer = VarName(); - _builder.AppendLine($" {dataPointer} =l add {arrayPointer}, 8"); - _builder.AppendLine($" call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); - - return new Val(arrayPointer, arrayInitializer.Type, ValKind.Immediate); - } - - private static Val EmitDereference(BoundDereferenceNode dereference) - { - var result = EmitUnwrap(EmitExpression(dereference.Expression)); - var outputName = VarName(); - _builder.AppendLine($" {outputName} {QBEAssign(dereference.Type)} {QBELoad(dereference.Type)} {result}"); - return new Val(outputName, dereference.Type, ValKind.Immediate); - } - - private static Val EmitAddressOf(BoundAddressOfNode addressOf) - { - switch (addressOf.Expression) - { - case BoundArrayIndexAccessNode arrayIndexAccess: - { - var pointer = EmitArrayIndexPointer(arrayIndexAccess); - return new Val(pointer, addressOf.Type, ValKind.Immediate); - } - case BoundDereferenceNode dereference: - { - return EmitExpression(dereference.Expression); - } - case BoundIdentifierNode identifier: - { - if (identifier.Namespace.HasValue) - { - throw new NotSupportedException("There is nothing to address in another namespace"); - } - - return _variables.Single(x => x.Name == identifier.Name).Val; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - } - - private static Val EmitBinaryExpression(BoundBinaryExpressionNode binaryExpression) - { - var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); - var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); - var outputName = VarName(); - var output = new Val(outputName, binaryExpression.Type, ValKind.Immediate); - - switch (binaryExpression.Operator) - { - case BinaryExpressionOperator.Equal: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =w ceql {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w ceqw {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) - { - _builder.AppendLine($" {outputName} =w ceqw {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.NotEqual: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =w cnel {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w cnew {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) - { - _builder.AppendLine($" {outputName} =w cnew {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.GreaterThan: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =w csgtl {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w csgtw {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) - { - _builder.AppendLine($" {outputName} =w csgtw {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.GreaterThanOrEqual: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =w csgel {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w csgew {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) - { - _builder.AppendLine($" {outputName} =w csgew {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.LessThan: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =w csltl {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w csltw {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) - { - _builder.AppendLine($" {outputName} =w csltw {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.LessThanOrEqual: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =w cslel {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w cslew {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool)) - { - _builder.AppendLine($" {outputName} =w cslew {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.Plus: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =l add {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w add {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.Minus: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =l sub {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w sub {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.Multiply: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =l mul {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w mul {left}, {right}"); - return output; - } - - break; - } - case BinaryExpressionOperator.Divide: - { - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64)) - { - _builder.AppendLine($" {outputName} =l div {left}, {right}"); - return output; - } - - if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32)) - { - _builder.AppendLine($" {outputName} =w div {left}, {right}"); - return output; - } - - break; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - - throw new NotSupportedException($"Binary operator {binaryExpression.Operator} for types {binaryExpression.Left.Type} and {binaryExpression.Right.Type} not supported"); - } - - private static Val EmitIdentifier(BoundIdentifierNode identifier) - { - if (_definitionTable.LookupFunc(identifier.Namespace.Or(_syntaxTree.Namespace), identifier.Name).TryGetValue(out var func)) - { - return new Val(FuncName(func), identifier.Type, ValKind.Func); - } - - if (!identifier.Namespace.HasValue) - { - return _variables.Single(v => v.Name == identifier.Name).Val; - } - - throw new UnreachableException(); - } - - private static Val EmitLiteral(BoundLiteralNode literal) - { - switch (literal.Kind) - { - case LiteralKind.Integer: - { - if (literal.Type.IsFloat32) - { - var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture); - var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Immediate); - } - - if (literal.Type.IsFloat64) - { - var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture); - var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Immediate); - } - - if (literal.Type.IsInteger) - { - return new Val(literal.Literal, literal.Type, ValKind.Immediate); - } - break; - } - case LiteralKind.Float: - { - if (literal.Type.IsInteger) - { - return new Val(literal.Literal.Split(".").First(), literal.Type, ValKind.Immediate); - } - - if (literal.Type.IsFloat32) - { - var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture); - var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Immediate); - } - - if (literal.Type.IsFloat64) - { - var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture); - var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Immediate); - } - break; - } - case LiteralKind.String: - { - if (literal.Type.IsString) - { - var stringLiteral = new StringLiteral(literal.Literal, StringName()); - _stringLiterals.Add(stringLiteral); - return new Val(stringLiteral.Name, literal.Type, ValKind.Immediate); - } - - if (literal.Type.IsCString) - { - var cStringLiteral = new CStringLiteral(literal.Literal, CStringName()); - _cStringLiterals.Add(cStringLiteral); - return new Val(cStringLiteral.Name, literal.Type, ValKind.Immediate); - } - break; - } - case LiteralKind.Bool: - { - if (literal.Type.IsBool) - { - return new Val(bool.Parse(literal.Literal) ? "1" : "0", literal.Type, ValKind.Immediate); - } - break; - } - } - - throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}"); - } - - private static Val EmitStructInitializer(BoundStructInitializerNode structInitializer) - { - var structDefinition = _definitionTable.LookupStruct(structInitializer.StructType.Namespace, structInitializer.StructType.Name).GetValue(); - - var output = VarName(); - var size = SizeOf(structInitializer.StructType); - _builder.AppendLine($" {output} =l alloc8 {size}"); - - foreach (var field in structDefinition.Fields) - { - var offset = OffsetOf(structDefinition, field.Name); - - if (structInitializer.Initializers.TryGetValue(field.Name, out var fieldValue)) - { - var value = EmitUnwrap(EmitExpression(fieldValue)); - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l add {output}, {offset}"); - EmitCopy(field.Type, value, pointer); - } - else if (field.Value.HasValue) - { - var value = EmitUnwrap(EmitExpression(field.Value.Value)); - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l add {output}, {offset}"); - EmitCopy(field.Type, value, pointer); - } - else - { - Debug.Assert(false); - } - } - - return new Val(output, structInitializer.StructType, ValKind.Immediate); - } - - private static Val EmitUnaryExpression(BoundUnaryExpressionNode unaryExpression) - { - var operand = EmitUnwrap(EmitExpression(unaryExpression.Operand)); - var outputName = VarName(); - - switch (unaryExpression.Operator) - { - case UnaryExpressionOperator.Negate: - { - switch (unaryExpression.Operand.Type) - { - case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }: - _builder.AppendLine($" {outputName} =l neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Immediate); - case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }: - _builder.AppendLine($" {outputName} =w neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Immediate); - case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }: - _builder.AppendLine($" {outputName} =d neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Immediate); - case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }: - _builder.AppendLine($" {outputName} =s neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Immediate); - } - - break; - } - case UnaryExpressionOperator.Invert: - { - switch (unaryExpression.Operand.Type) - { - case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }: - _builder.AppendLine($" {outputName} =w xor {operand}, 1"); - return new Val(outputName, unaryExpression.Type, ValKind.Immediate); - } - - break; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - - throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); - } - - private static Val EmitMemberAccess(BoundMemberAccessNode memberAccess) - { - var item = EmitUnwrap(EmitExpression(memberAccess.Expression)); - var output = VarName(); - - switch (memberAccess.Expression.Type) - { - case NubArrayType: - { - if (memberAccess.Member == "count") - { - _builder.AppendLine($" {output} =l loadl {item}"); - break; - } - - throw new UnreachableException(); - } - case NubStringType: - { - _builder.AppendLine($" {output} =l call $nub_string_length(l {item})"); - break; - } - case NubCStringType: - { - _builder.AppendLine($" {output} =l call $nub_cstring_length(l {item})"); - break; - } - case NubStructType structType: - { - var structDefinition = _definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue(); - var offset = OffsetOf(structDefinition, memberAccess.Member); - - var offsetName = VarName(); - _builder.AppendLine($" {offsetName} =l add {item}, {offset}"); - _builder.AppendLine($" {output} {QBEAssign(memberAccess.Type)} {QBELoad(memberAccess.Type)} {item}"); - break; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - - return new Val(output, memberAccess.Type, ValKind.Immediate); - } - - private static Val EmitFixedArrayInitializer(BoundFixedArrayInitializerNode fixedArrayInitializer) - { - var totalSize = SizeOf(fixedArrayInitializer.Type); - var outputName = VarName(); - _builder.AppendLine($" {outputName} =l alloc8 {totalSize}"); - - _builder.AppendLine($" storel {fixedArrayInitializer.Capacity}, {outputName}"); - - var dataPtr = VarName(); - _builder.AppendLine($" {dataPtr} =l add {outputName}, 8"); - - var dataSize = totalSize - 8; - _builder.AppendLine($" call $nub_memset(l {dataPtr}, w 0, l {dataSize})"); - - return new Val(outputName, fixedArrayInitializer.Type, ValKind.Immediate); - } - - private static Val EmitFuncCall(BoundFuncCallNode funcCall) - { - var funcType = (NubFuncType)funcCall.Expression.Type; - - var parameterStrings = new List(); - - for (var i = 0; i < funcCall.Parameters.Count; i++) - { - var parameter = funcCall.Parameters[i]; - var result = EmitUnwrap(EmitExpression(parameter)); - - var qbeType = parameter.Type switch - { - NubArrayType => "l", - NubPointerType => "l", - NubPrimitiveType pointerType => pointerType.Kind switch - { - PrimitiveTypeKind.I64 => "l", - PrimitiveTypeKind.I32 => "w", - PrimitiveTypeKind.I16 => "sh", - PrimitiveTypeKind.I8 => "sb", - PrimitiveTypeKind.U64 => "l", - PrimitiveTypeKind.U32 => "w", - PrimitiveTypeKind.U16 => "uh", - PrimitiveTypeKind.U8 => "ub", - PrimitiveTypeKind.F64 => "d", - PrimitiveTypeKind.F32 => "s", - PrimitiveTypeKind.Bool => "w", - _ => throw new ArgumentOutOfRangeException() - }, - NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()), - NubFixedArrayType => "l", - NubFuncType => "l", - NubCStringType => "l", - NubStringType => "l", - _ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls") - }; - parameterStrings.Add($"{qbeType} {result}"); - } - - var funcPointer = EmitUnwrap(EmitExpression(funcCall.Expression)); - - if (funcType.ReturnType is not NubVoidType) - { - var outputName = VarName(); - _builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, funcCall.Type, ValKind.Immediate); - } - else - { - _builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, funcCall.Type, ValKind.Immediate); - } - } - - private static void EmitCopy(NubType type, string sourcePtr, string destinationPtr) - { - if (SizeOf(type) > 8) - { - _builder.AppendLine($" blit {sourcePtr}, {destinationPtr}, {SizeOf(type)}"); - } - else - { - _builder.AppendLine($" {QBEStore(type)} {sourcePtr}, {destinationPtr}"); - } - } - - private static string EmitUnwrap(Val val) - { - if (val.Type.IsVoid) - { - throw new InvalidOperationException("Cannot unwrap temporary of void type"); - } - - switch (val.Kind) - { - case ValKind.Func: - case ValKind.Immediate: - return val.Name; - case ValKind.Pointer: - if (IsPointerType(val.Type)) - { - return val.Name; - } - else - { - var result = VarName(); - _builder.AppendLine($" {result} {QBEAssign(val.Type)} {QBELoad(val.Type)} {val.Name}"); - return result; - } - default: - throw new ArgumentOutOfRangeException(); - } - } -} - -internal class StringLiteral(string value, string name) -{ - public string Value { get; } = value; - public string Name { get; } = name; -} - -internal class CStringLiteral(string value, string name) -{ - public string Value { get; } = value; - public string Name { get; } = name; -} - -internal class Variable(string name, Val val) -{ - public string Name { get; } = name; - public Val Val { get; } = val; -} - -internal class Val(string name, NubType type, ValKind kind) -{ - public string Name { get; } = name; - public NubType Type { get; } = type; - public ValKind Kind { get; } = kind; - - public override string ToString() - { - throw new InvalidOperationException(); - } -} - -internal enum ValKind -{ - Func, - Pointer, - Immediate -} \ No newline at end of file diff --git a/src/compilation/Syntax/DefinitionTable.cs b/src/compilation/Syntax/DefinitionTable.cs deleted file mode 100644 index 4df8f31..0000000 --- a/src/compilation/Syntax/DefinitionTable.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Common; -using Syntax.Parsing; -using Syntax.Parsing.Node; -using Syntax.Typing; -using Syntax.Typing.BoundNode; - -namespace Syntax; - -public class DefinitionTable -{ - private readonly IEnumerable _syntaxTrees; - - public DefinitionTable(IEnumerable syntaxTrees) - { - _syntaxTrees = syntaxTrees; - } - - public Optional LookupFunc(string @namespace, string name) - { - var definition = _syntaxTrees - .Where(c => c.Namespace == @namespace) - .SelectMany(c => c.Definitions) - .OfType() - .SingleOrDefault(f => f.Name == name); - - return Optional.OfNullable(definition); - } - - public Optional LookupStruct(string @namespace, string name) - { - var definition = _syntaxTrees - .Where(c => c.Namespace == @namespace) - .SelectMany(c => c.Definitions) - .OfType() - .SingleOrDefault(f => f.Name == name); - - return Optional.OfNullable(definition); - } - - public IEnumerable GetStructs() - { - return _syntaxTrees - .SelectMany(c => c.Definitions) - .OfType(); - } -} - -public class BoundDefinitionTable -{ - private readonly IEnumerable _syntaxTrees; - - public BoundDefinitionTable(IEnumerable syntaxTrees) - { - _syntaxTrees = syntaxTrees; - } - - public Optional LookupFunc(string @namespace, string name) - { - var definition = _syntaxTrees - .Where(c => c.Namespace == @namespace) - .SelectMany(c => c.Definitions) - .OfType() - .SingleOrDefault(f => f.Name == name); - - return Optional.OfNullable(definition); - } - - public Optional LookupStruct(string @namespace, string name) - { - var definition = _syntaxTrees - .Where(c => c.Namespace == @namespace) - .SelectMany(c => c.Definitions) - .OfType() - .SingleOrDefault(f => f.Name == name); - - return Optional.OfNullable(definition); - } - - public IEnumerable GetStructs() - { - return _syntaxTrees - .SelectMany(c => c.Definitions) - .OfType(); - } -} \ No newline at end of file diff --git a/src/compilation/Syntax/Diagnostics/ConsoleColors.cs b/src/compilation/Syntax/Diagnostics/ConsoleColors.cs deleted file mode 100644 index 887a2fa..0000000 --- a/src/compilation/Syntax/Diagnostics/ConsoleColors.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System.Text; -using Syntax.Tokenization; - -namespace Syntax.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; - } - - private static string GetTokenColor(Token token) - { - switch (token) - { - case DocumentationToken: - return Faint; - case IdentifierToken: - return White; - case LiteralToken literal: - return literal.Kind switch - { - LiteralKind.String => Green, - LiteralKind.Integer or LiteralKind.Float => BrightBlue, - LiteralKind.Bool => Blue, - _ => White - }; - case ModifierToken: - return White; - case SymbolToken symbol: - switch (symbol.Symbol) - { - case Symbol.If: - case Symbol.Else: - case Symbol.While: - case Symbol.Break: - case Symbol.Continue: - case Symbol.Return: - return Magenta; - case Symbol.Func: - case Symbol.Struct: - case Symbol.Namespace: - case Symbol.Let: - case Symbol.Alloc: - return 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 White; - case Symbol.Semicolon: - case Symbol.Colon: - case Symbol.Comma: - case Symbol.Period: - case Symbol.DoubleColon: - return Faint; - case Symbol.OpenParen: - case Symbol.CloseParen: - case Symbol.OpenBrace: - case Symbol.CloseBrace: - case Symbol.OpenBracket: - case Symbol.CloseBracket: - return Yellow; - default: - return White; - } - default: - return White; - } - } - - public static string ColorizeSource(string source) - { - var sourceText = new SourceText(string.Empty, source); - var tokens = Tokenizer.Tokenize(sourceText, out _); - var result = new StringBuilder(); - var lastCharIndex = 0; - - foreach (var token in tokens) - { - var tokenStartIndex = GetCharacterIndex(sourceText, token.Span.Start); - var tokenEndIndex = GetCharacterIndex(sourceText, token.Span.End); - - if (tokenStartIndex > lastCharIndex) - { - var between = sourceText.Content.Substring(lastCharIndex, tokenStartIndex - lastCharIndex); - result.Append(between); - } - - var tokenText = sourceText.Content.Substring(tokenStartIndex, tokenEndIndex - tokenStartIndex); - - result.Append(Colorize(tokenText, GetTokenColor(token))); - lastCharIndex = tokenEndIndex; - } - - if (lastCharIndex < sourceText.Content.Length) - { - var remaining = sourceText.Content[lastCharIndex..]; - result.Append(Colorize(remaining, Faint)); - } - - return result.ToString(); - } - - private static int GetCharacterIndex(SourceText sourceText, SourceLocation location) - { - var lines = sourceText.Content.Split('\n'); - var index = 0; - - for (var i = 0; i < location.Line - 1 && i < lines.Length; i++) - { - index += lines[i].Length + 1; - } - - index += location.Column - 1; - - return Math.Min(index, sourceText.Content.Length); - } -} \ No newline at end of file diff --git a/src/compilation/Syntax/Diagnostics/Diagnostic.cs b/src/compilation/Syntax/Diagnostics/Diagnostic.cs deleted file mode 100644 index 4ba1ee3..0000000 --- a/src/compilation/Syntax/Diagnostics/Diagnostic.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System.Text; -using Syntax.Parsing; -using Syntax.Parsing.Node; -using Syntax.Tokenization; - -namespace Syntax.Diagnostics; - -public class Diagnostic -{ - public class DiagnosticBuilder - { - private readonly DiagnosticSeverity _severity; - private readonly string _message; - private string? _help; - private SourceSpan? _sourceSpan; - - public DiagnosticBuilder(DiagnosticSeverity severity, string message) - { - _severity = severity; - _message = message; - } - - public DiagnosticBuilder At(Token token) - { - _sourceSpan = token.Span; - return this; - } - - public DiagnosticBuilder At(Node node) - { - _sourceSpan = SourceSpan.Merge(node.Tokens.Select(t => t.Span)); - return this; - } - - public DiagnosticBuilder At(SourceSpan span) - { - _sourceSpan = span; - return this; - } - - public DiagnosticBuilder WithHelp(string help) - { - _help = help; - return this; - } - - public Diagnostic Build() => new(_severity, _message, _sourceSpan, _help); - } - - 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 SourceSpan? Span { get; } - public string? Help { get; } - - private Diagnostic(DiagnosticSeverity severity, string message, SourceSpan? span, string? help) - { - Severity = severity; - Message = message; - Span = span; - Help = help; - } - - public string FormatANSI() - { - var sb = new StringBuilder(); - - var severityText = GetSeverityText(Severity); - sb.Append(severityText); - - if (Span.HasValue) - { - var locationText = $" at {Span.Value.Text.Path}:{Span}"; - sb.Append(ConsoleColors.Colorize(locationText, ConsoleColors.Faint)); - } - - sb.Append(": "); - sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite)); - - if (Span.HasValue) - { - sb.AppendLine(); - AppendSourceContext(sb, Span.Value, Severity); - } - - if (!string.IsNullOrEmpty(Help)) - { - sb.AppendLine(); - sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan)); - } - - return sb.ToString(); - } - - private static string GetSeverityText(DiagnosticSeverity severity) - { - return 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), - _ => throw new ArgumentOutOfRangeException(nameof(severity), severity, "Unknown diagnostic severity") - }; - } - - private static void AppendSourceContext(StringBuilder sb, SourceSpan span, DiagnosticSeverity severity) - { - var lines = span.Text.Content.Split('\n'); - var startLine = span.Start.Line; - var endLine = span.End.Line; - - const int contextLines = 3; - - var lineNumWidth = Math.Min(endLine + contextLines, lines.Length).ToString().Length; - - var contextStart = Math.Max(1, startLine - contextLines); - var contextEnd = Math.Min(lines.Length, endLine + contextLines); - - var contextWidth = 0; - for (var i = contextStart; i <= contextEnd; i++) - { - if (lines[i - 1].Length > contextWidth) - { - contextWidth = lines[i - 1].Length; - } - } - - sb.AppendLine(ConsoleColors.Colorize('╭' + new string('─', lineNumWidth + 2) + '┬' + new string('─', contextWidth + 2) + '╮', ConsoleColors.Faint)); - - for (var lineNum = contextStart; lineNum < startLine; lineNum++) - { - AppendContextLine(sb, lineNum, lines[lineNum - 1], lineNumWidth, contextWidth); - } - - for (var lineNum = startLine; lineNum <= endLine; lineNum++) - { - AppendContextLine(sb, lineNum, lines[lineNum - 1], lineNumWidth, contextWidth); - AppendErrorIndicators(sb, span, lineNum, lines[lineNum - 1], lineNumWidth, contextWidth, severity); - } - - for (var lineNum = endLine + 1; lineNum <= contextEnd; lineNum++) - { - AppendContextLine(sb, lineNum, lines[lineNum - 1], lineNumWidth, contextWidth); - } - - sb.Append(ConsoleColors.Colorize('╰' + new string('─', lineNumWidth + 2) + '┴' + new string('─', contextWidth + 2) + '╯', ConsoleColors.Faint)); - } - - private static void AppendContextLine(StringBuilder sb, int lineNum, string line, int lineNumWidth, int contextWidth) - { - sb.Append(ConsoleColors.Colorize('│' + " ", ConsoleColors.Faint)); - var lineNumStr = lineNum.ToString().PadLeft(lineNumWidth); - sb.Append(ConsoleColors.Colorize(lineNumStr, ConsoleColors.Faint)); - sb.Append(ConsoleColors.Colorize(" │ ", ConsoleColors.Faint)); - sb.Append(ConsoleColors.ColorizeSource(line.PadRight(contextWidth))); - sb.Append(ConsoleColors.Colorize(" " + '│', ConsoleColors.Faint)); - sb.AppendLine(); - } - - private static void AppendErrorIndicators(StringBuilder sb, SourceSpan span, int lineNum, string line, int lineNumWidth, int contextWidth, DiagnosticSeverity severity) - { - var color = severity switch - { - DiagnosticSeverity.Info => ConsoleColors.Blue, - DiagnosticSeverity.Warning => ConsoleColors.Yellow, - DiagnosticSeverity.Error => ConsoleColors.Red, - _ => throw new ArgumentOutOfRangeException(nameof(severity), severity, null) - }; - - sb.Append(ConsoleColors.Colorize('│' + " ", ConsoleColors.Faint)); - sb.Append(new string(' ', lineNumWidth)); - sb.Append(ConsoleColors.Colorize(" │ ", ConsoleColors.Faint)); - var indicators = GetIndicatorsForLine(span, lineNum, line); - sb.Append(ConsoleColors.Colorize(indicators.PadRight(contextWidth), color)); - sb.Append(ConsoleColors.Colorize(" " + '│', ConsoleColors.Faint)); - sb.AppendLine(); - } - - private static string GetIndicatorsForLine(SourceSpan span, int lineNum, string line) - { - const char indicator = '^'; - - if (lineNum == span.Start.Line && lineNum == span.End.Line) - { - var startCol = Math.Max(0, span.Start.Column - 1); - var endCol = Math.Min(line.Length, span.End.Column - 1); - var length = Math.Max(1, endCol - startCol); - - return new string(' ', startCol) + new string(indicator, length); - } - - if (lineNum == span.Start.Line) - { - var startCol = Math.Max(0, span.Start.Column - 1); - return new string(' ', startCol) + new string(indicator, Math.Max(0, line.Length - startCol)); - } - - if (lineNum == span.End.Line) - { - var endCol = Math.Min(line.Length, span.End.Column - 1); - return new string(indicator, Math.Max(0, endCol)); - } - - return new string(indicator, line.Length); - } -} - -public enum DiagnosticSeverity -{ - Info, - Warning, - Error -} \ No newline at end of file diff --git a/src/compilation/Syntax/Parsing/Node/Definition.cs b/src/compilation/Syntax/Parsing/Node/Definition.cs deleted file mode 100644 index aec8f54..0000000 --- a/src/compilation/Syntax/Parsing/Node/Definition.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Common; -using Syntax.Tokenization; -using Syntax.Typing; - -namespace Syntax.Parsing.Node; - -public abstract record DefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace) : Node(Tokens); - -public record FuncParameter(string Name, NubType Type); -public abstract record FuncDefinition(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Parameters, NubType ReturnType) : DefinitionNode(Tokens, Documentation, Namespace); -public record LocalFuncDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Parameters, BlockNode Body, NubType ReturnType, bool Exported) : FuncDefinition(Tokens, Documentation, Namespace, Name, Parameters, ReturnType); -public record ExternFuncDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, string CallName, List Parameters, NubType ReturnType) : FuncDefinition(Tokens, Documentation, Namespace, Name, Parameters, ReturnType); - -public record StructField(string Name, NubType Type, Optional Value); -public record StructDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Fields) : DefinitionNode(Tokens, Documentation, Namespace); diff --git a/src/compilation/Syntax/Parsing/Node/Expression.cs b/src/compilation/Syntax/Parsing/Node/Expression.cs deleted file mode 100644 index a71316b..0000000 --- a/src/compilation/Syntax/Parsing/Node/Expression.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Common; -using Syntax.Tokenization; -using Syntax.Typing; - -namespace Syntax.Parsing.Node; - -public abstract record ExpressionNode(IEnumerable Tokens) : Node(Tokens); -public abstract record LValueNode(IEnumerable Tokens) : ExpressionNode(Tokens); - -public record BinaryExpressionNode(IEnumerable Tokens, ExpressionNode Left, BinaryExpressionOperator Operator, ExpressionNode Right) : ExpressionNode(Tokens); - -public enum BinaryExpressionOperator -{ - Equal, - NotEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual, - Plus, - Minus, - Multiply, - Divide -} - -public record UnaryExpressionNode(IEnumerable Tokens, UnaryExpressionOperator Operator, ExpressionNode Operand) : ExpressionNode(Tokens); - -public enum UnaryExpressionOperator -{ - Negate, - Invert -} - -public record FuncCallNode(IEnumerable Tokens, ExpressionNode Expression, List Parameters) : ExpressionNode(Tokens); -public record IdentifierNode(IEnumerable Tokens, Optional Namespace, string Name) : LValueNode(Tokens); -public record ArrayInitializerNode(IEnumerable Tokens, ExpressionNode Capacity, NubType ElementType) : ExpressionNode(Tokens); -public record ArrayIndexAccessNode(IEnumerable Tokens, ExpressionNode Array, ExpressionNode Index) : LValueNode(Tokens); -public record AnonymousFuncNode(IEnumerable Tokens, List Parameters, BlockNode Body, NubType ReturnType) : ExpressionNode(Tokens); -public record AddressOfNode(IEnumerable Tokens, LValueNode Expression) : ExpressionNode(Tokens); -public record FixedArrayInitializerNode(IEnumerable Tokens, NubType ElementType, int Capacity) : ExpressionNode(Tokens); -public record LiteralNode(IEnumerable Tokens, string Literal, LiteralKind Kind) : ExpressionNode(Tokens); -public record MemberAccessNode(IEnumerable Tokens, ExpressionNode Expression, string Member) : ExpressionNode(Tokens); -public record StructInitializerNode(IEnumerable Tokens, NubStructType StructType, Dictionary Initializers) : ExpressionNode(Tokens); -public record DereferenceNode(IEnumerable Tokens, ExpressionNode Expression) : LValueNode(Tokens); diff --git a/src/compilation/Syntax/Parsing/Node/Node.cs b/src/compilation/Syntax/Parsing/Node/Node.cs deleted file mode 100644 index 5d4357e..0000000 --- a/src/compilation/Syntax/Parsing/Node/Node.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Syntax.Tokenization; - -namespace Syntax.Parsing.Node; - -public abstract record Node(IEnumerable Tokens); -public record BlockNode(IEnumerable Tokens, List Statements) : Node(Tokens); diff --git a/src/compilation/Syntax/Parsing/Node/Statement.cs b/src/compilation/Syntax/Parsing/Node/Statement.cs deleted file mode 100644 index db506de..0000000 --- a/src/compilation/Syntax/Parsing/Node/Statement.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Common; -using Syntax.Tokenization; -using Syntax.Typing; - -namespace Syntax.Parsing.Node; - -public record StatementNode(IEnumerable Tokens) : Node(Tokens); -public record StatementExpressionNode(IEnumerable Tokens, ExpressionNode Expression) : StatementNode(Tokens); -public record ReturnNode(IEnumerable Tokens, Optional Value) : StatementNode(Tokens); -public record MemberAssignmentNode(IEnumerable Tokens, MemberAccessNode MemberAccess, ExpressionNode Value) : StatementNode(Tokens); -public record IfNode(IEnumerable Tokens, ExpressionNode Condition, BlockNode Body, Optional> Else) : StatementNode(Tokens); -public record DereferenceAssignmentNode(IEnumerable Tokens, DereferenceNode Dereference, ExpressionNode Value) : StatementNode(Tokens); -public record VariableAssignmentNode(IEnumerable Tokens, IdentifierNode Identifier, ExpressionNode Value) : StatementNode(Tokens); -public record VariableDeclarationNode(IEnumerable Tokens, string Name, NubType Type) : StatementNode(Tokens); -public record ContinueNode(IEnumerable Tokens) : StatementNode(Tokens); -public record BreakNode(IEnumerable Tokens) : StatementNode(Tokens); -public record ArrayIndexAssignmentNode(IEnumerable Tokens, ArrayIndexAccessNode ArrayIndexAccess, ExpressionNode Value) : StatementNode(Tokens); -public record WhileNode(IEnumerable Tokens, ExpressionNode Condition, BlockNode Body) : StatementNode(Tokens); diff --git a/src/compilation/Syntax/Parsing/Parser.cs b/src/compilation/Syntax/Parsing/Parser.cs deleted file mode 100644 index 546310f..0000000 --- a/src/compilation/Syntax/Parsing/Parser.cs +++ /dev/null @@ -1,939 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using Common; -using Syntax.Diagnostics; -using Syntax.Parsing.Node; -using Syntax.Tokenization; -using Syntax.Typing; - -namespace Syntax.Parsing; - -public static class Parser -{ - private static string _namespace = null!; - private static List _diagnostics = []; - private static IEnumerable _tokens = []; - private static int _index; - - public static SyntaxTree? ParseFile(IEnumerable tokens, string filePath, out IEnumerable diagnostics) - { - _tokens = tokens; - _namespace = null!; - _diagnostics = []; - _index = 0; - - try - { - ExpectSymbol(Symbol.Namespace); - var @namespace = ExpectIdentifier(); - _namespace = @namespace.Value; - } - catch (ParseException ex) - { - _diagnostics.Add(ex.Diagnostic); - diagnostics = _diagnostics; - return null; - } - - try - { - List definitions = []; - - while (Peek().HasValue) - { - var definition = ParseDefinition(); - definitions.Add(definition); - } - - diagnostics = _diagnostics; - return new SyntaxTree(filePath, _namespace, definitions); - } - catch (ParseException ex) - { - _diagnostics.Add(ex.Diagnostic); - RecoverToNextDefinition(); - } - - diagnostics = _diagnostics; - return null; - } - - private static DefinitionNode ParseDefinition() - { - var startIndex = _index; - List modifiers = []; - - List documentationParts = []; - while (_index < _tokens.Count() && _tokens.ElementAt(_index) is DocumentationToken commentToken) - { - documentationParts.Add(commentToken.Documentation); - _index++; - } - - var documentation = documentationParts.Count == 0 ? null : string.Join('\n', documentationParts); - - while (TryExpectModifier(out var modifier)) - { - modifiers.Add(modifier); - } - - var keyword = ExpectSymbol(); - return keyword.Symbol switch - { - Symbol.Func => ParseFuncDefinition(startIndex, modifiers, Optional.OfNullable(documentation)), - Symbol.Struct => ParseStruct(startIndex, modifiers, Optional.OfNullable(documentation)), - _ => throw new ParseException(Diagnostic - .Error($"Expected 'func' or 'struct', but found '{keyword.Symbol}'") - .WithHelp("Valid definition keywords are 'func' and 'struct'") - .At(keyword) - .Build()) - }; - } - - private static DefinitionNode ParseFuncDefinition(int startIndex, List modifiers, Optional documentation) - { - var name = ExpectIdentifier(); - List parameters = []; - - ExpectSymbol(Symbol.OpenParen); - - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseFuncParameter()); - - if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var token) && token is not SymbolToken { Symbol: Symbol.CloseParen }) - { - _diagnostics.Add(Diagnostic - .Warning("Missing comma between function parameters") - .WithHelp("Add a ',' to separate parameters") - .At(token) - .Build()); - } - } - - var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType(); - - var isExtern = modifiers.RemoveAll(x => x.Modifier == Modifier.Extern) > 0; - if (isExtern) - { - if (modifiers.Count != 0) - { - throw new ParseException(Diagnostic - .Error($"Invalid modifier for extern function: {modifiers[0].Modifier}") - .WithHelp($"Extern functions cannot use the '{modifiers[0].Modifier}' modifier") - .At(modifiers[0]) - .Build()); - } - - var callName = name.Value; - - if (TryExpectSymbol(Symbol.Calls)) - { - callName = ExpectIdentifier().Value; - } - - return new ExternFuncDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name.Value, callName, parameters, returnType); - } - - var body = ParseBlock(); - var exported = modifiers.RemoveAll(x => x.Modifier == Modifier.Export) > 0; - - if (modifiers.Count != 0) - { - throw new ParseException(Diagnostic - .Error($"Invalid modifiers for function: {modifiers[0].Modifier}") - .WithHelp($"Functions cannot use the '{modifiers[0].Modifier}' modifier") - .At(modifiers[0]) - .Build()); - } - - return new LocalFuncDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name.Value, parameters, body, returnType, exported); - } - - private static StructDefinitionNode ParseStruct(int startIndex, List _, Optional documentation) - { - var name = ExpectIdentifier().Value; - - ExpectSymbol(Symbol.OpenBrace); - - List variables = []; - - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var variableName = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Colon); - var variableType = ParseType(); - - var variableValue = Optional.Empty(); - - if (TryExpectSymbol(Symbol.Assign)) - { - variableValue = ParseExpression(); - } - - variables.Add(new StructField(variableName, variableType, variableValue)); - } - - return new StructDefinitionNode(GetTokensForNode(startIndex), documentation, _namespace, name, variables); - } - - private static FuncParameter ParseFuncParameter() - { - var name = ExpectIdentifier(); - ExpectSymbol(Symbol.Colon); - var type = ParseType(); - - return new FuncParameter(name.Value, type); - } - - private static StatementNode ParseStatement() - { - var startIndex = _index; - if (!Peek().TryGetValue(out var token)) - { - throw new ParseException(Diagnostic - .Error("Unexpected end of file while parsing statement") - .At(_tokens.Last()) - .Build()); - } - - if (token is SymbolToken symbol) - { - switch (symbol.Symbol) - { - case Symbol.Return: - return ParseReturn(startIndex); - case Symbol.If: - return ParseIf(startIndex); - case Symbol.While: - return ParseWhile(startIndex); - case Symbol.Let: - return ParseVariableDeclaration(startIndex); - case Symbol.Break: - return ParseBreak(startIndex); - case Symbol.Continue: - return ParseContinue(startIndex); - } - } - - return ParseStatementExpression(startIndex); - } - - private static StatementNode ParseStatementExpression(int startIndex) - { - var expr = ParseExpression(); - - if (Peek().TryGetValue(out var token)) - { - if (token is SymbolToken symbol) - { - switch (symbol.Symbol) - { - case Symbol.Assign: - { - switch (expr) - { - case MemberAccessNode memberAccess: - { - Next(); - var value = ParseExpression(); - return new MemberAssignmentNode(GetTokensForNode(startIndex), memberAccess, value); - } - case ArrayIndexAccessNode arrayIndexAccess: - { - Next(); - var value = ParseExpression(); - return new ArrayIndexAssignmentNode(GetTokensForNode(startIndex), arrayIndexAccess, value); - } - case IdentifierNode identifier: - { - Next(); - var value = ParseExpression(); - return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier, value); - } - case DereferenceNode dereference: - { - Next(); - var value = ParseExpression(); - return new DereferenceAssignmentNode(GetTokensForNode(startIndex), dereference, value); - } - } - - break; - } - } - } - } - - return new StatementExpressionNode(GetTokensForNode(startIndex), expr); - } - - private static VariableDeclarationNode ParseVariableDeclaration(int startIndex) - { - ExpectSymbol(Symbol.Let); - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Colon); - var type = ParseType(); - - return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type); - } - - private static StatementNode ParseBreak(int startIndex) - { - ExpectSymbol(Symbol.Break); - Next(); - return new BreakNode(GetTokensForNode(startIndex)); - } - - private static StatementNode ParseContinue(int startIndex) - { - ExpectSymbol(Symbol.Continue); - return new ContinueNode(GetTokensForNode(startIndex)); - } - - private static ReturnNode ParseReturn(int startIndex) - { - ExpectSymbol(Symbol.Return); - var value = Optional.Empty(); - if (!TryExpectSymbol(Symbol.Semicolon)) - { - value = ParseExpression(); - } - - return new ReturnNode(GetTokensForNode(startIndex), value); - } - - private static IfNode ParseIf(int startIndex) - { - ExpectSymbol(Symbol.If); - var condition = ParseExpression(); - var body = ParseBlock(); - - var elseStatement = Optional>.Empty(); - if (TryExpectSymbol(Symbol.Else)) - { - var newStartIndex = _index; - elseStatement = TryExpectSymbol(Symbol.If) - ? (Variant)ParseIf(newStartIndex) - : (Variant)ParseBlock(); - } - - return new IfNode(GetTokensForNode(startIndex), condition, body, elseStatement); - } - - private static WhileNode ParseWhile(int startIndex) - { - ExpectSymbol(Symbol.While); - var condition = ParseExpression(); - var body = ParseBlock(); - return new WhileNode(GetTokensForNode(startIndex), condition, body); - } - - private static ExpressionNode ParseExpression(int precedence = 0) - { - var startIndex = _index; - var left = ParsePrimaryExpression(); - - while (true) - { - var token = Peek(); - if (!token.HasValue || token.Value is not SymbolToken symbolToken || !TryGetBinaryOperator(symbolToken.Symbol, out var op) || - GetBinaryOperatorPrecedence(op.Value) < precedence) - { - break; - } - - Next(); - var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1); - - left = new BinaryExpressionNode(GetTokensForNode(startIndex), left, op.Value, right); - } - - return left; - } - - private static int GetBinaryOperatorPrecedence(BinaryExpressionOperator binaryExpressionOperator) - { - return binaryExpressionOperator switch - { - BinaryExpressionOperator.Multiply => 3, - BinaryExpressionOperator.Divide => 3, - BinaryExpressionOperator.Plus => 2, - BinaryExpressionOperator.Minus => 2, - BinaryExpressionOperator.GreaterThan => 1, - BinaryExpressionOperator.GreaterThanOrEqual => 1, - BinaryExpressionOperator.LessThan => 1, - BinaryExpressionOperator.LessThanOrEqual => 1, - BinaryExpressionOperator.Equal => 0, - BinaryExpressionOperator.NotEqual => 0, - _ => throw new ArgumentOutOfRangeException(nameof(binaryExpressionOperator), binaryExpressionOperator, null) - }; - } - - private static bool TryGetBinaryOperator(Symbol symbol, [NotNullWhen(true)] out BinaryExpressionOperator? binaryExpressionOperator) - { - switch (symbol) - { - case Symbol.Equal: - binaryExpressionOperator = BinaryExpressionOperator.Equal; - return true; - case Symbol.NotEqual: - binaryExpressionOperator = BinaryExpressionOperator.NotEqual; - return true; - case Symbol.LessThan: - binaryExpressionOperator = BinaryExpressionOperator.LessThan; - return true; - case Symbol.LessThanOrEqual: - binaryExpressionOperator = BinaryExpressionOperator.LessThanOrEqual; - return true; - case Symbol.GreaterThan: - binaryExpressionOperator = BinaryExpressionOperator.GreaterThan; - return true; - case Symbol.GreaterThanOrEqual: - binaryExpressionOperator = BinaryExpressionOperator.GreaterThanOrEqual; - return true; - case Symbol.Plus: - binaryExpressionOperator = BinaryExpressionOperator.Plus; - return true; - case Symbol.Minus: - binaryExpressionOperator = BinaryExpressionOperator.Minus; - return true; - case Symbol.Star: - binaryExpressionOperator = BinaryExpressionOperator.Multiply; - return true; - case Symbol.ForwardSlash: - binaryExpressionOperator = BinaryExpressionOperator.Divide; - return true; - default: - binaryExpressionOperator = null; - return false; - } - } - - private static ExpressionNode ParsePrimaryExpression() - { - var startIndex = _index; - ExpressionNode expr; - - var token = ExpectToken(); - switch (token) - { - case LiteralToken literal: - { - expr = new LiteralNode(GetTokensForNode(startIndex), literal.Value, literal.Kind); - break; - } - case IdentifierToken identifier: - { - var @namespace = Optional.Empty(); - var name = identifier.Value; - if (TryExpectSymbol(Symbol.DoubleColon)) - { - @namespace = identifier.Value; - name = ExpectIdentifier().Value; - } - - expr = new IdentifierNode(GetTokensForNode(startIndex), @namespace, name); - break; - } - case SymbolToken symbolToken: - { - switch (symbolToken.Symbol) - { - case Symbol.Func: - { - List parameters = []; - ExpectSymbol(Symbol.OpenParen); - while (!TryExpectSymbol(Symbol.CloseParen)) - { - var parameter = ParseFuncParameter(); - parameters.Add(parameter); - if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) - { - _diagnostics.Add(Diagnostic - .Warning("Missing comma between function arguments") - .WithHelp("Add a ',' to separate arguments") - .At(nextToken) - .Build()); - } - } - - var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType(); - - var body = ParseBlock(); - - expr = new AnonymousFuncNode(GetTokensForNode(startIndex), parameters, body, returnType); - break; - } - case Symbol.OpenParen: - { - var expression = ParseExpression(); - ExpectSymbol(Symbol.CloseParen); - expr = expression; - break; - } - case Symbol.Ampersand: - { - var expression = ParsePrimaryExpression(); - if (expression is not LValueNode lValue) - { - throw new ParseException(Diagnostic - .Error("& symbol can only be used on lvalues") - .At(expression) - .Build()); - } - - expr = new AddressOfNode(GetTokensForNode(startIndex), lValue); - break; - } - case Symbol.Minus: - { - var expression = ParsePrimaryExpression(); - expr = new UnaryExpressionNode(GetTokensForNode(startIndex), UnaryExpressionOperator.Negate, expression); - break; - } - case Symbol.Bang: - { - var expression = ParsePrimaryExpression(); - expr = new UnaryExpressionNode(GetTokensForNode(startIndex), UnaryExpressionOperator.Invert, expression); - break; - } - case Symbol.OpenBracket: - { - if (Peek().TryGetValue(out var capacityToken) && capacityToken is LiteralToken { Kind: LiteralKind.Integer } literalToken) - { - var capacity = int.Parse(literalToken.Value); - Next(); - ExpectSymbol(Symbol.CloseBracket); - var elementType = ParseType(); - - if (capacity > 0) - { - expr = new FixedArrayInitializerNode(GetTokensForNode(startIndex), elementType, capacity); - } - else - { - throw new ParseException(Diagnostic - .Error("Fixed array size must be a positive integer") - .WithHelp("Use a positive integer literal for the array size") - .At(literalToken) - .Build()); - } - } - else - { - var capacity = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - var type = ParseType(); - - expr = new ArrayInitializerNode(GetTokensForNode(startIndex), capacity, type); - } - - break; - } - case Symbol.Alloc: - { - var type = ParseType(); - Dictionary initializers = []; - ExpectSymbol(Symbol.OpenBrace); - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - initializers.Add(name, value); - } - - if (type is not NubStructType structType) - { - throw new ParseException(Diagnostic - .Error($"Cannot alloc type '{type}'") - .At(symbolToken) - .Build()); - } - - expr = new StructInitializerNode(GetTokensForNode(startIndex), structType, initializers); - break; - } - default: - { - throw new ParseException(Diagnostic - .Error($"Unexpected symbol '{symbolToken.Symbol}' in expression") - .WithHelp("Expected literal, identifier, or '(' to start expression") - .At(symbolToken) - .Build()); - } - } - - break; - } - default: - { - throw new ParseException(Diagnostic - .Error($"Unexpected token '{token.GetType().Name}' in expression") - .WithHelp("Expected literal, identifier, or parenthesized expression") - .At(token) - .Build()); - } - } - - return ParsePostfixOperators(startIndex, expr); - } - - private static ExpressionNode ParsePostfixOperators(int startIndex, ExpressionNode expr) - { - while (true) - { - if (TryExpectSymbol(Symbol.Caret)) - { - expr = new DereferenceNode(GetTokensForNode(startIndex), expr); - continue; - } - - if (TryExpectSymbol(Symbol.Period)) - { - var structMember = ExpectIdentifier().Value; - expr = new MemberAccessNode(GetTokensForNode(startIndex), expr, structMember); - continue; - } - - if (TryExpectSymbol(Symbol.OpenBracket)) - { - var index = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - expr = new ArrayIndexAccessNode(GetTokensForNode(startIndex), expr, index); - continue; - } - - if (TryExpectSymbol(Symbol.OpenParen)) - { - var parameters = new List(); - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseExpression()); - if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) - { - _diagnostics.Add(Diagnostic - .Warning("Missing comma between function arguments") - .WithHelp("Add a ',' to separate arguments") - .At(nextToken) - .Build()); - } - } - - expr = new FuncCallNode(GetTokensForNode(startIndex), expr, parameters); - continue; - } - - break; - } - - return expr; - } - - private static BlockNode ParseBlock() - { - var startIndex = _index; - ExpectSymbol(Symbol.OpenBrace); - List statements = []; - while (Peek().HasValue && !TryExpectSymbol(Symbol.CloseBrace)) - { - try - { - statements.Add(ParseStatement()); - } - catch (ParseException ex) - { - _diagnostics.Add(ex.Diagnostic); - RecoverToNextStatement(); - } - } - - return new BlockNode(GetTokensForNode(startIndex), statements); - } - - private static NubType ParseType() - { - if (TryExpectIdentifier(out var name)) - { - if (name.Value == "any") - { - return new NubAnyType(); - } - - if (name.Value == "void") - { - return new NubVoidType(); - } - - if (name.Value == "string") - { - return new NubStringType(); - } - - if (name.Value == "cstring") - { - return new NubCStringType(); - } - - if (NubPrimitiveType.TryParse(name.Value, out var primitiveTypeKind)) - { - return new NubPrimitiveType(primitiveTypeKind.Value); - } - - var @namespace = _namespace; - if (TryExpectSymbol(Symbol.DoubleColon)) - { - @namespace = ExpectIdentifier().Value; - } - - if (@namespace == null) - { - throw new ParseException(Diagnostic - .Error($"Struct '{name.Value}' does not belong to a namespace") - .WithHelp("Make sure you have specified a namespace at the top of the file") - .At(name) - .Build()); - } - - return new NubStructType(@namespace , name.Value); - } - - if (TryExpectSymbol(Symbol.Caret)) - { - var baseType = ParseType(); - return new NubPointerType(baseType); - } - - if (TryExpectSymbol(Symbol.Func)) - { - ExpectSymbol(Symbol.OpenParen); - List parameters = []; - while (!TryExpectSymbol(Symbol.CloseParen)) - { - var parameter = ParseType(); - parameters.Add(parameter); - if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) - { - _diagnostics.Add(Diagnostic - .Warning("Missing comma between func type arguments") - .WithHelp("Add a ',' to separate arguments") - .At(nextToken) - .Build()); - } - } - - var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType(); - - return new NubFuncType(returnType, parameters); - } - - if (TryExpectSymbol(Symbol.OpenBracket)) - { - if (Peek().TryGetValue(out var token) && token is LiteralToken { Kind: LiteralKind.Integer, Value: var sizeValue }) - { - Next(); - ExpectSymbol(Symbol.CloseBracket); - var baseType = ParseType(); - - var size = int.Parse(sizeValue); - - if (size > 0) - { - return new NubFixedArrayType(baseType, size); - } - else - { - throw new UnreachableException(); - } - } - else - { - ExpectSymbol(Symbol.CloseBracket); - var baseType = ParseType(); - return new NubArrayType(baseType); - } - } - - if (!Peek().TryGetValue(out var peekToken)) - { - throw new ParseException(Diagnostic - .Error("Unexpected end of file while parsing type") - .WithHelp("Expected a type name") - .At(_tokens.Last()) - .Build()); - } - - throw new ParseException(Diagnostic - .Error("Invalid type Syntax") - .WithHelp("Expected type name, '^' for pointer, or '[]' for array") - .At(peekToken) - .Build()); -} - - private static Token ExpectToken() - { - if (!Peek().TryGetValue(out var token)) - { - throw new ParseException(Diagnostic - .Error("Unexpected end of file") - .WithHelp("Expected more tokens to complete the Syntax") - .At(_tokens.Last()) - .Build()); - } - - Next(); - return token; - } - - private static 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 static 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 static bool TryExpectSymbol(Symbol symbol) - { - if (Peek() is { Value: SymbolToken symbolToken } && symbolToken.Symbol == symbol) - { - Next(); - return true; - } - - return false; - } - - private static bool TryExpectModifier([NotNullWhen(true)] out ModifierToken? modifier) - { - if (Peek() is { Value: ModifierToken modifierToken }) - { - modifier = modifierToken; - Next(); - return true; - } - - modifier = null; - return false; - } - - private static bool TryExpectIdentifier([NotNullWhen(true)] out IdentifierToken? identifier) - { - if (Peek() is { Value: IdentifierToken identifierToken }) - { - identifier = identifierToken; - Next(); - return true; - } - - identifier = null; - return false; - } - - private static 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 static void RecoverToNextDefinition() - { - while (Peek().HasValue) - { - var token = Peek().Value; - if (token is SymbolToken { Symbol: Symbol.Func or Symbol.Struct } or ModifierToken) - { - break; - } - - Next(); - } - } - - private static void RecoverToNextStatement() - { - while (Peek().TryGetValue(out var token)) - { - if (token is SymbolToken { Symbol: Symbol.CloseBrace } or IdentifierToken or SymbolToken - { - Symbol: Symbol.Return or Symbol.If or Symbol.While or Symbol.Let or Symbol.Break or Symbol.Continue - }) - { - break; - } - - Next(); - } - } - - private static Optional Peek(int offset = 0) - { - var peekIndex = _index + offset; - while (peekIndex < _tokens.Count() && _tokens.ElementAt(peekIndex) is DocumentationToken) - { - peekIndex++; - } - - if (peekIndex < _tokens.Count()) - { - return _tokens.ElementAt(peekIndex); - } - - return Optional.Empty(); - } - - private static void Next() - { - while (_index < _tokens.Count() && _tokens.ElementAt(_index) is DocumentationToken) - { - _index++; - } - - _index++; - } - - private static IEnumerable GetTokensForNode(int startIndex) - { - return _tokens.Skip(startIndex).Take(Math.Min(_index, _tokens.Count() - 1) - startIndex); - } -} - -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/compilation/Syntax/Parsing/SyntaxTree.cs b/src/compilation/Syntax/Parsing/SyntaxTree.cs deleted file mode 100644 index 914a9ed..0000000 --- a/src/compilation/Syntax/Parsing/SyntaxTree.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Syntax.Parsing.Node; - -namespace Syntax.Parsing; - -public record SyntaxTree(string FilePath, string Namespace, List Definitions); diff --git a/src/compilation/Syntax/Source.cs b/src/compilation/Syntax/Source.cs deleted file mode 100644 index bfeb6af..0000000 --- a/src/compilation/Syntax/Source.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Syntax; - -/// -/// Represents a location in source code with line and column information. -/// Lines and columns are 1-based to match typical editor conventions. -/// -public readonly struct SourceLocation : IEquatable, IComparable -{ - public SourceLocation(int line, int column) - { - if (line < 1) - { - throw new ArgumentOutOfRangeException(nameof(line), "Line must be >= 1"); - } - - if (column < 1) - { - throw new ArgumentOutOfRangeException(nameof(column), "Column must be >= 1"); - } - - Line = line; - Column = column; - } - - public int Line { get; } - public int Column { get; } - - public int CompareTo(SourceLocation other) - { - var lineComparison = Line.CompareTo(other.Line); - if (lineComparison == 0) - { - return Column.CompareTo(other.Column); - } - - return lineComparison; - } - - public override string ToString() - { - return $"{Line}:{Column}"; - } - - public bool Equals(SourceLocation other) - { - return Line == other.Line && Column == other.Column; - } - - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is SourceLocation other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Line, Column); - } - - public static bool operator ==(SourceLocation left, SourceLocation right) => left.Equals(right); - public static bool operator !=(SourceLocation left, SourceLocation right) => !left.Equals(right); - public static bool operator <(SourceLocation left, SourceLocation right) => left.CompareTo(right) < 0; - public static bool operator >(SourceLocation left, SourceLocation right) => left.CompareTo(right) > 0; - public static bool operator <=(SourceLocation left, SourceLocation right) => left.CompareTo(right) <= 0; - public static bool operator >=(SourceLocation left, SourceLocation right) => left.CompareTo(right) >= 0; -} - -/// -/// Represents source text with a name (typically filename) and content. -/// Equality is based on both name and content for better semantics. -/// -public struct SourceText : IEquatable -{ - private int _lines = -1; - - public SourceText(string path, string content) - { - Path = path ?? throw new ArgumentNullException(nameof(path)); - Content = content ?? throw new ArgumentNullException(nameof(content)); - } - - public string Path { get; } - public string Content { get; } - - public int LineCount() - { - if (_lines == -1) - { - _lines = Content.Split('\n').Length + 1; - } - - return _lines; - } - - /// - /// Gets a specific line from the source text (1-based). - /// - public string GetLine(int lineNumber) - { - if (lineNumber < 1) - { - throw new ArgumentOutOfRangeException(nameof(lineNumber)); - } - - var lines = Content.Split('\n'); - return lineNumber <= lines.Length ? lines[lineNumber - 1] : string.Empty; - } - - public bool Equals(SourceText other) - { - return Path == other.Path && Content == other.Content; - } - - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is SourceText other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Path, Content); - } - - public override string ToString() - { - return Path; - } - - public static bool operator ==(SourceText left, SourceText right) => left.Equals(right); - public static bool operator !=(SourceText left, SourceText right) => !left.Equals(right); -} - -/// -/// Represents a span of source code from a start to end location within a source text. -/// -public readonly struct SourceSpan : IEquatable -{ - public SourceSpan(SourceText text, SourceLocation start, SourceLocation end) - { - if (start > end) - { - throw new ArgumentException("Start location cannot be after end location"); - } - - if (end.Line > text.LineCount() || end.Line == text.LineCount() && end.Column > text.GetLine(text.LineCount()).Length + 1) - { - throw new ArgumentException("End location cannot be after text end location"); - } - - Text = text; - Start = start; - End = end; - } - - public SourceText Text { get; } - public SourceLocation Start { get; } - public SourceLocation End { get; } - - /// - /// Gets whether this span represents a single point (start == end). - /// - public bool IsEmpty => Start == End; - - /// - /// Gets whether this span is contained within a single line. - /// - public bool IsSingleLine => Start.Line == End.Line; - - /// - /// Gets the text content covered by this span. - /// - public string GetText() - { - if (IsEmpty) - { - return string.Empty; - } - - var lines = Text.Content.Split('\n'); - - if (IsSingleLine) - { - var line = lines[Start.Line - 1]; - var startCol = Math.Min(Start.Column - 1, line.Length); - var endCol = Math.Min(End.Column - 1, line.Length); - return line.Substring(startCol, Math.Max(0, endCol - startCol)); - } - - var result = new List(); - for (var i = Start.Line - 1; i < Math.Min(End.Line, lines.Length); i++) - { - var line = lines[i]; - if (i == Start.Line - 1) - { - result.Add(line[Math.Min(Start.Column - 1, line.Length)..]); - } - else if (i == End.Line - 1) - { - result.Add(line[..Math.Min(End.Column - 1, line.Length)]); - } - else - { - result.Add(line); - } - } - - return string.Join("\n", result); - } - - /// - /// Merges multiple source spans from the same file into a single span. - /// The result spans from the earliest start to the latest end. - /// - public static SourceSpan Merge(IEnumerable spans) - { - var spanArray = spans.ToArray(); - if (spanArray.Length == 0) - { - throw new ArgumentException("Cannot merge empty collection of spans", nameof(spans)); - } - - var firstText = spanArray[0].Text; - if (spanArray.Any(s => !s.Text.Equals(firstText))) - { - throw new ArgumentException("All spans must be from the same source text", nameof(spans)); - } - - var minStart = spanArray.Min(s => s.Start); - var maxEnd = spanArray.Max(s => s.End); - - return new SourceSpan(firstText, minStart, maxEnd); - } - - public override string ToString() - { - if (IsEmpty) - { - return $"{Start}"; - } - - if (IsSingleLine) - { - return Start.Column == End.Column ? $"{Start}" : $"{Start.Line}:{Start.Column}-{End.Column}"; - } - - return $"{Start}-{End}"; - } - - public bool Equals(SourceSpan other) - { - return Text.Equals(other.Text) && Start.Equals(other.Start) && End.Equals(other.End); - } - - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is SourceSpan other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Text, Start, End); - } - - public static bool operator ==(SourceSpan left, SourceSpan right) - { - return left.Equals(right); - } - - public static bool operator !=(SourceSpan left, SourceSpan right) - { - return !left.Equals(right); - } -} \ No newline at end of file diff --git a/src/compilation/Syntax/Syntax.csproj b/src/compilation/Syntax/Syntax.csproj deleted file mode 100644 index 31addee..0000000 --- a/src/compilation/Syntax/Syntax.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net9.0 - enable - enable - true - - - - - - - diff --git a/src/compilation/Syntax/Tokenization/DocumentationToken.cs b/src/compilation/Syntax/Tokenization/DocumentationToken.cs deleted file mode 100644 index 54a0e51..0000000 --- a/src/compilation/Syntax/Tokenization/DocumentationToken.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Syntax.Tokenization; - -public class DocumentationToken(SourceSpan span, string documentation) : Token(span) -{ - public string Documentation { get; } = documentation; -} \ No newline at end of file diff --git a/src/compilation/Syntax/Tokenization/IdentifierToken.cs b/src/compilation/Syntax/Tokenization/IdentifierToken.cs deleted file mode 100644 index 7d8c9e7..0000000 --- a/src/compilation/Syntax/Tokenization/IdentifierToken.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Syntax.Tokenization; - -public class IdentifierToken(SourceSpan span, string value) : Token(span) -{ - public string Value { get; } = value; -} \ No newline at end of file diff --git a/src/compilation/Syntax/Tokenization/LiteralToken.cs b/src/compilation/Syntax/Tokenization/LiteralToken.cs deleted file mode 100644 index f16671e..0000000 --- a/src/compilation/Syntax/Tokenization/LiteralToken.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Syntax.Tokenization; - -public class LiteralToken(SourceSpan span, LiteralKind kind, string value) : Token(span) -{ - public LiteralKind Kind { get; } = kind; - public string Value { get; } = value; -} - -public enum LiteralKind -{ - Integer, - Float, - String, - Bool -} \ No newline at end of file diff --git a/src/compilation/Syntax/Tokenization/ModifierToken.cs b/src/compilation/Syntax/Tokenization/ModifierToken.cs deleted file mode 100644 index 5312667..0000000 --- a/src/compilation/Syntax/Tokenization/ModifierToken.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Syntax.Tokenization; - -public class ModifierToken(SourceSpan span, Modifier modifier) : Token(span) -{ - public Modifier Modifier { get; } = modifier; -} - -public enum Modifier -{ - Extern, - Export -} \ No newline at end of file diff --git a/src/compilation/Syntax/Tokenization/SymbolToken.cs b/src/compilation/Syntax/Tokenization/SymbolToken.cs deleted file mode 100644 index af31ca0..0000000 --- a/src/compilation/Syntax/Tokenization/SymbolToken.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Syntax.Tokenization; - -public class SymbolToken(SourceSpan span, Symbol symbol) : Token(span) -{ - public Symbol Symbol { get; } = symbol; -} - -public enum Symbol -{ - Func, - Return, - If, - Else, - While, - Break, - Continue, - Semicolon, - Colon, - OpenParen, - CloseParen, - OpenBrace, - CloseBrace, - OpenBracket, - CloseBracket, - Comma, - Period, - Assign, - Bang, - Equal, - NotEqual, - LessThan, - LessThanOrEqual, - GreaterThan, - GreaterThanOrEqual, - Plus, - Minus, - Star, - ForwardSlash, - Struct, - Caret, - Ampersand, - DoubleColon, - Namespace, - Let, - Alloc, - Calls -} \ No newline at end of file diff --git a/src/compilation/Syntax/Tokenization/Token.cs b/src/compilation/Syntax/Tokenization/Token.cs deleted file mode 100644 index 89f3f47..0000000 --- a/src/compilation/Syntax/Tokenization/Token.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Syntax.Tokenization; - -public abstract class Token(SourceSpan span) -{ - public SourceSpan Span { get; } = span; -} \ No newline at end of file diff --git a/src/compilation/Syntax/Tokenization/Tokenizer.cs b/src/compilation/Syntax/Tokenization/Tokenizer.cs deleted file mode 100644 index 5a3784d..0000000 --- a/src/compilation/Syntax/Tokenization/Tokenizer.cs +++ /dev/null @@ -1,283 +0,0 @@ -using Common; -using Syntax.Diagnostics; - -namespace Syntax.Tokenization; - -public static class Tokenizer -{ - private static readonly Dictionary Keywords = new() - { - ["namespace"] = Symbol.Namespace, - ["func"] = Symbol.Func, - ["if"] = Symbol.If, - ["else"] = Symbol.Else, - ["while"] = Symbol.While, - ["break"] = Symbol.Break, - ["continue"] = Symbol.Continue, - ["return"] = Symbol.Return, - ["alloc"] = Symbol.Alloc, - ["struct"] = Symbol.Struct, - ["let"] = Symbol.Let, - ["calls"] = Symbol.Calls, - }; - - private static readonly Dictionary Modifiers = new() - { - ["export"] = Modifier.Export, - ["extern"] = Modifier.Extern, - }; - - private static readonly Dictionary Chians = new() - { - [['=', '=']] = Symbol.Equal, - [['!', '=']] = Symbol.NotEqual, - [['<', '=']] = Symbol.LessThanOrEqual, - [['>', '=']] = Symbol.GreaterThanOrEqual, - [[':', ':']] = Symbol.DoubleColon, - }; - - private static readonly Dictionary Chars = new() - { - [';'] = Symbol.Semicolon, - [':'] = 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, - }; - - private static SourceText _sourceText; - private static int _index; - - public static IEnumerable Tokenize(SourceText sourceText, out IEnumerable diagnostics) - { - _sourceText = sourceText; - _index = 0; - - List tokens = []; - while (ParseToken().TryGetValue(out var token)) - { - tokens.Add(token); - } - - // TODO: Implement diagnostics - diagnostics = []; - return tokens; - } - - private static void ConsumeWhitespace() - { - while (Peek().TryGetValue(out var character) && char.IsWhiteSpace(character)) - { - Next(); - } - } - - private static Optional ParseToken() - { - ConsumeWhitespace(); - var startIndex = _index; - - if (!Peek().TryGetValue(out var current)) - { - return Optional.Empty(); - } - - if (current == '/' && Peek(1).TryGetValue(out var nextChar) && nextChar == '/') - { - Next(); - Next(); - - if (Peek().TryGetValue(out var thirdChar) && thirdChar == '/') - { - Next(); - var buffer = string.Empty; - while (Peek().TryGetValue(out var character) && character != '\n') - { - buffer += character; - Next(); - } - - Next(); - return new DocumentationToken(CreateSpan(startIndex), buffer); - } - - while (Peek().TryGetValue(out var character) && character != '\n') - { - Next(); - } - - Next(); - return ParseToken(); - } - - 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)) - { - return new SymbolToken(CreateSpan(startIndex), keywordSymbol); - } - - if (Modifiers.TryGetValue(buffer, out var modifer)) - { - return new ModifierToken(CreateSpan(startIndex), modifer); - } - - if (buffer is "true" or "false") - { - return new LiteralToken(CreateSpan(startIndex), LiteralKind.Bool, buffer); - } - - return new IdentifierToken(CreateSpan(startIndex), buffer); - } - - 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; - } - } - - return new LiteralToken(CreateSpan(startIndex), isFloat ? LiteralKind.Float : LiteralKind.Integer, buffer); - } - - // TODO: Revisit this - foreach (var chain in Chians) - { - if (current != chain.Key[0]) continue; - - for (var i = 1; i < chain.Key.Length; i++) - { - var c = Peek(i); - if (!c.HasValue || c.Value != chain.Key[i]) break; - - if (i == chain.Key.Length - 1) - { - for (var j = 0; j <= i; j++) - { - Next(); - } - - return new SymbolToken(CreateSpan(startIndex), chain.Value); - } - } - } - - if (Chars.TryGetValue(current, out var charSymbol)) - { - Next(); - return new SymbolToken(CreateSpan(startIndex), charSymbol); - } - - 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(); - } - - return new LiteralToken(CreateSpan(startIndex), LiteralKind.String, buffer); - } - - throw new Exception($"Unknown character {current}"); - } - - private static SourceLocation CreateLocation(int index) - { - var line = 1; - var column = 1; - for (var i = 0; i < Math.Min(index, _sourceText.Content.Length - 1); i++) - { - if (_sourceText.Content[i] == '\n') - { - column = 1; - line += 1; - } - else - { - column += 1; - } - } - - return new SourceLocation(line, column); - } - - private static SourceSpan CreateSpan(int startIndex) - { - return new SourceSpan(_sourceText, CreateLocation(startIndex), CreateLocation(_index)); - } - - private static Optional Peek(int offset = 0) - { - if (_index + offset < _sourceText.Content.Length) - { - return _sourceText.Content[_index + offset]; - } - - return Optional.Empty(); - } - - private static void Next() - { - _index++; - } -} \ No newline at end of file diff --git a/src/compilation/Syntax/Typing/Binder.cs b/src/compilation/Syntax/Typing/Binder.cs deleted file mode 100644 index 306d40f..0000000 --- a/src/compilation/Syntax/Typing/Binder.cs +++ /dev/null @@ -1,463 +0,0 @@ -using Common; -using Syntax.Parsing; -using Syntax.Parsing.Node; -using Syntax.Tokenization; -using Syntax.Typing.BoundNode; -using UnaryExpressionNode = Syntax.Parsing.Node.UnaryExpressionNode; - -namespace Syntax.Typing; - -// TODO: Currently anonymous function does not get a new scope -public static class Binder -{ - private static SyntaxTree _syntaxTree = null!; - private static DefinitionTable _definitionTable = null!; - - private static Dictionary _variables = new(); - private static NubType? _funcReturnType; - - public static BoundSyntaxTree Bind(SyntaxTree syntaxTree, DefinitionTable definitionTable) - { - _syntaxTree = syntaxTree; - _definitionTable = definitionTable; - - _variables = []; - _funcReturnType = null; - - var definitions = new List(); - - foreach (var definition in syntaxTree.Definitions) - { - definitions.Add(BindDefinition(definition)); - } - - return new BoundSyntaxTree(syntaxTree.FilePath, syntaxTree.Namespace, definitions); - } - - private static BoundDefinitionNode BindDefinition(DefinitionNode node) - { - return node switch - { - ExternFuncDefinitionNode definition => BindExternFuncDefinition(definition), - LocalFuncDefinitionNode definition => BindLocalFuncDefinition(definition), - StructDefinitionNode definition => BindStruct(definition), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - } - - private static BoundStructDefinitionNode BindStruct(StructDefinitionNode node) - { - var defOpt = _definitionTable.LookupStruct(node.Namespace, node.Name); - if (!defOpt.TryGetValue(out var definition)) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - var structFields = new List(); - - foreach (var structField in node.Fields) - { - var value = Optional.Empty(); - - if (structField.Value.HasValue) - { - var definitionField = definition.Fields.FirstOrDefault(f => f.Name == structField.Name); - if (definitionField == null) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - value = BindExpression(structField.Value.Value, definitionField.Type); - } - - structFields.Add(new BoundStructField(structField.Name, structField.Type, value)); - } - - return new BoundStructDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Name, structFields); - } - - private static BoundExternFuncDefinitionNode BindExternFuncDefinition(ExternFuncDefinitionNode node) - { - var parameters = new List(); - - foreach (var parameter in node.Parameters) - { - parameters.Add(new BoundFuncParameter(parameter.Name, parameter.Type)); - } - - return new BoundExternFuncDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Name, node.CallName, parameters, node.ReturnType); - } - - private static BoundLocalFuncDefinitionNode BindLocalFuncDefinition(LocalFuncDefinitionNode node) - { - _variables.Clear(); - _funcReturnType = node.ReturnType; - - var parameters = new List(); - - foreach (var parameter in node.Parameters) - { - parameters.Add(new BoundFuncParameter(parameter.Name, parameter.Type)); - _variables[parameter.Name] = parameter.Type; - } - - var body = BindBlock(node.Body); - - return new BoundLocalFuncDefinitionNode(node.Tokens, node.Documentation, node.Namespace, node.Name, parameters, body, node.ReturnType, node.Exported); - } - - private static BoundBlockNode BindBlock(BlockNode node) - { - var statements = new List(); - - foreach (var statement in node.Statements) - { - statements.Add(BindStatement(statement)); - } - - return new BoundBlockNode(node.Tokens, statements); - } - - private static BoundStatementNode BindStatement(StatementNode node) - { - return node switch - { - ArrayIndexAssignmentNode statement => BindArrayIndex(statement), - BreakNode statement => BindBreak(statement), - ContinueNode statement => BindContinue(statement), - DereferenceAssignmentNode statement => BindDereferenceAssignment(statement), - IfNode statement => BindIf(statement), - MemberAssignmentNode statement => BindMemberAssignment(statement), - ReturnNode statement => BindReturn(statement), - StatementExpressionNode statement => BindStatementExpression(statement), - VariableAssignmentNode statement => BindVariableAssignment(statement), - VariableDeclarationNode statement => BindVariableDeclaration(statement), - WhileNode statement => BindWhile(statement), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - } - - private static BoundArrayIndexAssignmentNode BindArrayIndex(ArrayIndexAssignmentNode statement) - { - var boundArrayIndex = BindArrayIndexAccess(statement.ArrayIndexAccess); - var elementType = ((NubArrayType)boundArrayIndex.Type).ElementType; - return new BoundArrayIndexAssignmentNode(statement.Tokens, boundArrayIndex, BindExpression(statement.Value, elementType)); - } - - private static BoundBreakNode BindBreak(BreakNode statement) - { - return new BoundBreakNode(statement.Tokens); - } - - private static BoundContinueNode BindContinue(ContinueNode statement) - { - return new BoundContinueNode(statement.Tokens); - } - - private static BoundDereferenceAssignmentNode BindDereferenceAssignment(DereferenceAssignmentNode statement) - { - var boundDereference = BindDereference(statement.Dereference); - var dereferenceBaseType = ((NubPointerType)boundDereference.Type).BaseType; - return new BoundDereferenceAssignmentNode(statement.Tokens, boundDereference, BindExpression(statement.Value, dereferenceBaseType)); - } - - private static BoundIfNode BindIf(IfNode statement) - { - var elseStatement = Optional.Empty>(); - - if (statement.Else.HasValue) - { - elseStatement = statement.Else.Value.Match> - ( - elseIf => BindIf(elseIf), - @else => BindBlock(@else) - ); - } - - return new BoundIfNode(statement.Tokens, BindExpression(statement.Condition, NubPrimitiveType.Bool), BindBlock(statement.Body), elseStatement); - } - - private static BoundMemberAssignmentNode BindMemberAssignment(MemberAssignmentNode statement) - { - var boundMemberAccess = BindMemberAccess(statement.MemberAccess); - var elementType = ((NubArrayType)boundMemberAccess.Type).ElementType; - return new BoundMemberAssignmentNode(statement.Tokens, boundMemberAccess, BindExpression(statement.Value, elementType)); - } - - private static BoundReturnNode BindReturn(ReturnNode statement) - { - var value = Optional.Empty(); - - if (statement.Value.HasValue) - { - value = BindExpression(statement.Value.Value, _funcReturnType); - } - - return new BoundReturnNode(statement.Tokens, value); - } - - private static BoundStatementExpressionNode BindStatementExpression(StatementExpressionNode statement) - { - return new BoundStatementExpressionNode(statement.Tokens, BindExpression(statement.Expression)); - } - - private static BoundVariableAssignmentNode BindVariableAssignment(VariableAssignmentNode statement) - { - var variableType = _variables[statement.Identifier.Name]; - return new BoundVariableAssignmentNode(statement.Tokens, BindIdentifier(statement.Identifier), BindExpression(statement.Value, variableType)); - } - - private static BoundVariableDeclarationNode BindVariableDeclaration(VariableDeclarationNode statement) - { - _variables[statement.Name] = statement.Type; - return new BoundVariableDeclarationNode(statement.Tokens, statement.Name, statement.Type); - } - - private static BoundWhileNode BindWhile(WhileNode statement) - { - return new BoundWhileNode(statement.Tokens, BindExpression(statement.Condition, NubPrimitiveType.Bool), BindBlock(statement.Body)); - } - - private static BoundExpressionNode BindExpression(ExpressionNode node, NubType? expectedType = null) - { - return node switch - { - AddressOfNode expression => BindAddressOf(expression), - AnonymousFuncNode expression => BindAnonymousFunc(expression), - ArrayIndexAccessNode expression => BindArrayIndexAccess(expression), - ArrayInitializerNode expression => BindArrayInitializer(expression), - BinaryExpressionNode expression => BindBinaryExpression(expression), - DereferenceNode expression => BindDereference(expression), - FixedArrayInitializerNode expression => BindFixedArrayInitializer(expression), - FuncCallNode expression => BindFuncCall(expression), - IdentifierNode expression => BindIdentifier(expression), - LiteralNode expression => BindLiteral(expression, expectedType), - MemberAccessNode expression => BindMemberAccess(expression), - StructInitializerNode expression => BindStructInitializer(expression), - UnaryExpressionNode expression => BindUnaryExpression(expression), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - } - - private static BoundAddressOfNode BindAddressOf(AddressOfNode expression) - { - var inner = (BoundLValueNode)BindExpression(expression.Expression); - return new BoundAddressOfNode(expression.Tokens, new NubPointerType(inner.Type), inner); - } - - private static BoundAnonymousFuncNode BindAnonymousFunc(AnonymousFuncNode expression) - { - var parameters = new List(); - var parameterTypes = new List(); - - foreach (var parameter in expression.Parameters) - { - var boundParameter = new BoundFuncParameter(parameter.Name, parameter.Type); - parameters.Add(boundParameter); - parameterTypes.Add(boundParameter.Type); - } - - var body = BindBlock(expression.Body); - - return new BoundAnonymousFuncNode(expression.Tokens, new NubFuncType(expression.ReturnType, parameterTypes), parameters, body, expression.ReturnType); - } - - private static BoundArrayIndexAccessNode BindArrayIndexAccess(ArrayIndexAccessNode expression) - { - var boundArray = BindExpression(expression.Array); - var elementType = ((NubArrayType)boundArray.Type).ElementType; - return new BoundArrayIndexAccessNode(expression.Tokens, elementType, boundArray, BindExpression(expression.Index, NubPrimitiveType.U64)); - } - - private static BoundArrayInitializerNode BindArrayInitializer(ArrayInitializerNode expression) - { - return new BoundArrayInitializerNode(expression.Tokens, new NubArrayType(expression.ElementType), BindExpression(expression.Capacity, NubPrimitiveType.U64), - expression.ElementType); - } - - private static BoundBinaryExpressionNode BindBinaryExpression(BinaryExpressionNode expression) - { - var boundLeft = BindExpression(expression.Left); - var boundRight = BindExpression(expression.Right, boundLeft.Type); - return new BoundBinaryExpressionNode(expression.Tokens, boundLeft.Type, boundLeft, expression.Operator, boundRight); - } - - private static BoundDereferenceNode BindDereference(DereferenceNode expression) - { - var boundExpression = BindExpression(expression.Expression); - var dereferencedType = ((NubPointerType)boundExpression.Type).BaseType; - return new BoundDereferenceNode(expression.Tokens, dereferencedType, boundExpression); - } - - private static BoundFixedArrayInitializerNode BindFixedArrayInitializer(FixedArrayInitializerNode expression) - { - return new BoundFixedArrayInitializerNode(expression.Tokens, new NubArrayType(expression.ElementType), expression.ElementType, expression.Capacity); - } - - private static BoundFuncCallNode BindFuncCall(FuncCallNode expression) - { - var boundExpression = BindExpression(expression.Expression); - - var funcType = (NubFuncType)boundExpression.Type; - var returnType = ((NubFuncType)boundExpression.Type).ReturnType; - - var parameters = new List(); - - foreach (var (i, parameter) in expression.Parameters.Index()) - { - if (i >= funcType.Parameters.Count) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - var expectedType = funcType.Parameters[i]; - - parameters.Add(BindExpression(parameter, expectedType)); - } - - return new BoundFuncCallNode(expression.Tokens, returnType, boundExpression, parameters); - } - - private static BoundIdentifierNode BindIdentifier(IdentifierNode expression) - { - NubType? type = null; - - var definition = _definitionTable.LookupFunc(expression.Namespace.Or(_syntaxTree.Namespace), expression.Name); - if (definition.HasValue) - { - type = new NubFuncType(definition.Value.ReturnType, definition.Value.Parameters.Select(p => p.Type).ToList()); - } - - if (type == null && !expression.Namespace.HasValue) - { - type = _variables[expression.Name]; - } - - if (type == null) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - return new BoundIdentifierNode(expression.Tokens, type, expression.Namespace, expression.Name); - } - - private static BoundLiteralNode BindLiteral(LiteralNode expression, NubType? expectedType = null) - { - var type = expectedType ?? expression.Kind switch - { - LiteralKind.Integer => NubPrimitiveType.I64, - LiteralKind.Float => NubPrimitiveType.F64, - LiteralKind.String => new NubStringType(), - LiteralKind.Bool => NubPrimitiveType.Bool, - _ => throw new ArgumentOutOfRangeException() - }; - - return new BoundLiteralNode(expression.Tokens, type, expression.Literal, expression.Kind); - } - - private static BoundMemberAccessNode BindMemberAccess(MemberAccessNode expression) - { - var boundExpression = BindExpression(expression.Expression); - - NubType? type = null; - - switch (boundExpression.Type) - { - case NubArrayType: - case NubStringType: - case NubCStringType: - { - if (expression.Member == "count") - { - type = NubPrimitiveType.U64; - } - - break; - } - case NubStructType structType: - { - var defOpt = _definitionTable.LookupStruct(structType.Namespace, structType.Name); - if (!defOpt.TryGetValue(out var definition)) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - var field = definition.Fields.FirstOrDefault(f => f.Name == expression.Member); - if (field == null) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - type = field.Type; - break; - } - } - - if (type == null) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - return new BoundMemberAccessNode(expression.Tokens, type, boundExpression, expression.Member); - } - - private static BoundStructInitializerNode BindStructInitializer(StructInitializerNode expression) - { - var defOpt = _definitionTable.LookupStruct(expression.StructType.Namespace, expression.StructType.Name); - if (!defOpt.TryGetValue(out var definition)) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - var initializers = new Dictionary(); - - foreach (var (member, initializer) in expression.Initializers) - { - var definitionField = definition.Fields.FirstOrDefault(x => x.Name == member); - if (definitionField == null) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - initializers[member] = BindExpression(initializer, definitionField.Type); - } - - return new BoundStructInitializerNode(expression.Tokens, expression.StructType, expression.StructType, initializers); - } - - private static BoundUnaryExpressionNode BindUnaryExpression(UnaryExpressionNode expression) - { - var boundOperand = BindExpression(expression.Operand); - - NubType? type = null; - - switch (expression.Operator) - { - case UnaryExpressionOperator.Negate: - { - boundOperand = BindExpression(expression.Operand, NubPrimitiveType.I64); - - if (boundOperand.Type.IsNumber) - { - type = boundOperand.Type; - } - - break; - } - case UnaryExpressionOperator.Invert: - { - boundOperand = BindExpression(expression.Operand, NubPrimitiveType.Bool); - - type = new NubPrimitiveType(PrimitiveTypeKind.Bool); - break; - } - } - - if (type == null) - { - throw new NotImplementedException("Diagnostics not implemented"); - } - - return new BoundUnaryExpressionNode(expression.Tokens, type, expression.Operator, boundOperand); - } -} \ No newline at end of file diff --git a/src/compilation/Syntax/Typing/BoundNode/Definition.cs b/src/compilation/Syntax/Typing/BoundNode/Definition.cs deleted file mode 100644 index fcfa22c..0000000 --- a/src/compilation/Syntax/Typing/BoundNode/Definition.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Common; -using Syntax.Tokenization; - -namespace Syntax.Typing.BoundNode; - -public abstract record BoundDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace) : BoundNode(Tokens); - -public record BoundFuncParameter(string Name, NubType Type); -public abstract record BoundFuncDefinition(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Parameters, NubType ReturnType) : BoundDefinitionNode(Tokens, Documentation, Namespace); -public record BoundLocalFuncDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Parameters, BoundBlockNode Body, NubType ReturnType, bool Exported) : BoundFuncDefinition(Tokens, Documentation, Namespace, Name, Parameters, ReturnType); -public record BoundExternFuncDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, string CallName, List Parameters, NubType ReturnType) : BoundFuncDefinition(Tokens, Documentation, Namespace, Name, Parameters, ReturnType); - -public record BoundStructField(string Name, NubType Type, Optional Value); -public record BoundStructDefinitionNode(IEnumerable Tokens, Optional Documentation, string Namespace, string Name, List Fields) : BoundDefinitionNode(Tokens, Documentation, Namespace); diff --git a/src/compilation/Syntax/Typing/BoundNode/Expression.cs b/src/compilation/Syntax/Typing/BoundNode/Expression.cs deleted file mode 100644 index f6f204f..0000000 --- a/src/compilation/Syntax/Typing/BoundNode/Expression.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Common; -using Syntax.Parsing.Node; -using Syntax.Tokenization; - -namespace Syntax.Typing.BoundNode; - -public abstract record BoundExpressionNode(IEnumerable Tokens, NubType Type) : BoundNode(Tokens); -public abstract record BoundLValueNode(IEnumerable Tokens, NubType Type) : BoundExpressionNode(Tokens, Type); -public record BoundBinaryExpressionNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Left, BinaryExpressionOperator Operator, BoundExpressionNode Right) : BoundExpressionNode(Tokens, Type); -public record BoundUnaryExpressionNode(IEnumerable Tokens, NubType Type, UnaryExpressionOperator Operator, BoundExpressionNode Operand) : BoundExpressionNode(Tokens, Type); -public record BoundFuncCallNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Expression, List Parameters) : BoundExpressionNode(Tokens, Type); -public record BoundIdentifierNode(IEnumerable Tokens, NubType Type, Optional Namespace, string Name) : BoundLValueNode(Tokens, Type); -public record BoundArrayInitializerNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Capacity, NubType ElementType) : BoundExpressionNode(Tokens, Type); -public record BoundArrayIndexAccessNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Array, BoundExpressionNode Index) : BoundLValueNode(Tokens, Type); -public record BoundAnonymousFuncNode(IEnumerable Tokens, NubType Type, List Parameters, BoundBlockNode Body, NubType ReturnType) : BoundExpressionNode(Tokens, Type); -public record BoundAddressOfNode(IEnumerable Tokens, NubType Type, BoundLValueNode Expression) : BoundExpressionNode(Tokens, Type); -public record BoundFixedArrayInitializerNode(IEnumerable Tokens, NubType Type, NubType ElementType, int Capacity) : BoundExpressionNode(Tokens, Type); -public record BoundLiteralNode(IEnumerable Tokens, NubType Type, string Literal, LiteralKind Kind) : BoundExpressionNode(Tokens, Type); -public record BoundMemberAccessNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Expression, string Member) : BoundExpressionNode(Tokens, Type); -public record BoundStructInitializerNode(IEnumerable Tokens, NubType Type, NubStructType StructType, Dictionary Initializers) : BoundExpressionNode(Tokens, Type); -public record BoundDereferenceNode(IEnumerable Tokens, NubType Type, BoundExpressionNode Expression) : BoundLValueNode(Tokens, Type); diff --git a/src/compilation/Syntax/Typing/BoundNode/Node.cs b/src/compilation/Syntax/Typing/BoundNode/Node.cs deleted file mode 100644 index 6030c31..0000000 --- a/src/compilation/Syntax/Typing/BoundNode/Node.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Syntax.Tokenization; - -namespace Syntax.Typing.BoundNode; - -public abstract record BoundNode(IEnumerable Tokens); -public record BoundBlockNode(IEnumerable Tokens, List Statements) : BoundNode(Tokens); diff --git a/src/compilation/Syntax/Typing/BoundNode/Statement.cs b/src/compilation/Syntax/Typing/BoundNode/Statement.cs deleted file mode 100644 index 151626a..0000000 --- a/src/compilation/Syntax/Typing/BoundNode/Statement.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Common; -using Syntax.Tokenization; - -namespace Syntax.Typing.BoundNode; - -public record BoundStatementNode(IEnumerable Tokens) : BoundNode(Tokens); -public record BoundStatementExpressionNode(IEnumerable Tokens, BoundExpressionNode Expression) : BoundStatementNode(Tokens); -public record BoundReturnNode(IEnumerable Tokens, Optional Value) : BoundStatementNode(Tokens); -public record BoundMemberAssignmentNode(IEnumerable Tokens, BoundMemberAccessNode MemberAccess, BoundExpressionNode Value) : BoundStatementNode(Tokens); -public record BoundIfNode(IEnumerable Tokens, BoundExpressionNode Condition, BoundBlockNode Body, Optional> Else) : BoundStatementNode(Tokens); -public record BoundDereferenceAssignmentNode(IEnumerable Tokens, BoundDereferenceNode Dereference, BoundExpressionNode Value) : BoundStatementNode(Tokens); -public record BoundVariableAssignmentNode(IEnumerable Tokens, BoundIdentifierNode Identifier, BoundExpressionNode Value) : BoundStatementNode(Tokens); -public record BoundVariableDeclarationNode(IEnumerable Tokens, string Name, NubType Type) : BoundStatementNode(Tokens); -public record BoundContinueNode(IEnumerable Tokens) : BoundStatementNode(Tokens); -public record BoundBreakNode(IEnumerable Tokens) : BoundStatementNode(Tokens); -public record BoundArrayIndexAssignmentNode(IEnumerable Tokens, BoundArrayIndexAccessNode ArrayIndexAccess, BoundExpressionNode Value) : BoundStatementNode(Tokens); -public record BoundWhileNode(IEnumerable Tokens, BoundExpressionNode Condition, BoundBlockNode Body) : BoundStatementNode(Tokens); diff --git a/src/compilation/Syntax/Typing/BoundSyntaxTree.cs b/src/compilation/Syntax/Typing/BoundSyntaxTree.cs deleted file mode 100644 index 0dd20b5..0000000 --- a/src/compilation/Syntax/Typing/BoundSyntaxTree.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Syntax.Typing.BoundNode; - -namespace Syntax.Typing; - -public record BoundSyntaxTree(string FilePath, string Namespace, List Definitions); diff --git a/src/compilation/Syntax/Typing/NubType.cs b/src/compilation/Syntax/Typing/NubType.cs deleted file mode 100644 index 1c89f69..0000000 --- a/src/compilation/Syntax/Typing/NubType.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Syntax.Typing; - -public abstract class NubType -{ - public bool IsInteger => this is NubPrimitiveType - { - Kind: PrimitiveTypeKind.I8 - or PrimitiveTypeKind.I16 - or PrimitiveTypeKind.I32 - or PrimitiveTypeKind.I64 - or PrimitiveTypeKind.U8 - or PrimitiveTypeKind.U16 - or PrimitiveTypeKind.U32 - or PrimitiveTypeKind.U64 - }; - - public bool IsFloat32 => this is NubPrimitiveType - { - Kind: PrimitiveTypeKind.F32 - }; - - public bool IsFloat64 => this is NubPrimitiveType - { - Kind: PrimitiveTypeKind.F64 - }; - - public bool IsNumber => IsFloat32 || IsFloat64 || IsInteger; - - public bool IsVoid => this is NubVoidType; - - public bool IsString => this is NubStringType; - - public bool IsCString => this is NubCStringType; - - public bool IsBool => this is NubPrimitiveType - { - Kind: PrimitiveTypeKind.Bool - }; - - public abstract override bool Equals(object? obj); - public abstract override int GetHashCode(); - public abstract override string ToString(); -} - -public class NubCStringType : NubType -{ - public override bool Equals(object? obj) - { - return obj is NubCStringType; - } - - public override int GetHashCode() - { - return "cstring".GetHashCode(); - } - - public override string ToString() - { - return "cstring"; - } -} - -public class NubStringType : NubType -{ - public override bool Equals(object? obj) - { - return obj is NubStringType; - } - - public override int GetHashCode() - { - return "string".GetHashCode(); - } - - public override string ToString() - { - return "string"; - } -} - -public class NubFuncType(NubType returnType, List parameters) : NubType -{ - public NubType ReturnType { get; } = returnType; - public List Parameters { get; } = parameters; - - public override bool Equals(object? obj) - { - return obj is NubFuncType other && other.ReturnType.Equals(ReturnType) && other.Parameters.SequenceEqual(Parameters); - } - - public override int GetHashCode() - { - return HashCode.Combine(ReturnType, Parameters); - } - - public override string ToString() - { - return $"func({string.Join(", ", Parameters)}): {ReturnType}"; - } -} - -public class NubStructType(string @namespace, string name) : NubType -{ - public string Namespace { get; } = @namespace; - public string Name { get; } = name; - - public override bool Equals(object? obj) - { - return obj is NubStructType other && other.Namespace == Namespace && other.Name == Name; - } - - public override int GetHashCode() - { - return HashCode.Combine(Namespace, Name); - } - - public override string ToString() - { - return $"{Namespace}::{Name}"; - } -} - -public class NubPointerType(NubType baseType) : NubType -{ - public NubType BaseType { get; } = baseType; - - public override bool Equals(object? obj) - { - return obj is NubPointerType other && BaseType.Equals(other.BaseType); - } - - public override int GetHashCode() - { - return HashCode.Combine(BaseType); - } - - public override string ToString() - { - return "^" + BaseType; - } -} - -public class NubArrayType(NubType elementType) : NubType -{ - public NubType ElementType { get; } = elementType; - - public override bool Equals(object? obj) - { - if (obj is NubArrayType other) - { - return ElementType.Equals(other.ElementType); - } - return false; - } - - public override int GetHashCode() - { - return HashCode.Combine(ElementType); - } - - public override string ToString() - { - return "[]" + ElementType; - } -} - -public class NubFixedArrayType(NubType elementType, int capacity) : NubType -{ - public NubType ElementType { get; } = elementType; - public int Capacity { get; } = capacity; - - public override string ToString() => $"[{Capacity}]{ElementType}"; - - public override bool Equals(object? obj) - { - return obj is NubFixedArrayType other && ElementType.Equals(other.ElementType) && Capacity == other.Capacity; - } - - public override int GetHashCode() - { - return HashCode.Combine(ElementType, Capacity); - } -} - -public class NubAnyType : NubType -{ - public override string ToString() => "any"; - - public override bool Equals(object? obj) - { - return obj is NubAnyType; - } - - public override int GetHashCode() - { - return "any".GetHashCode(); - } -} - -public class NubVoidType : NubType -{ - public override string ToString() => "void"; - - public override bool Equals(object? obj) - { - return obj is NubVoidType; - } - - public override int GetHashCode() - { - return GetType().GetHashCode(); - } -} - -public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType -{ - public PrimitiveTypeKind Kind { get; } = kind; - - public static NubPrimitiveType I64 => new(PrimitiveTypeKind.I64); - public static NubPrimitiveType I32 => new(PrimitiveTypeKind.I32); - public static NubPrimitiveType I16 => new(PrimitiveTypeKind.I16); - public static NubPrimitiveType I8 => new(PrimitiveTypeKind.I8); - - public static NubPrimitiveType U64 => new(PrimitiveTypeKind.U64); - public static NubPrimitiveType U32 => new(PrimitiveTypeKind.U32); - public static NubPrimitiveType U16 => new(PrimitiveTypeKind.U16); - public static NubPrimitiveType U8 => new(PrimitiveTypeKind.U8); - - public static NubPrimitiveType F64 => new(PrimitiveTypeKind.F64); - public static NubPrimitiveType F32 => new(PrimitiveTypeKind.F32); - - public static NubPrimitiveType Bool => new(PrimitiveTypeKind.Bool); - - public static bool TryParse(string s, [NotNullWhen(true)] out PrimitiveTypeKind? kind) - { - kind = s switch - { - "i64" => PrimitiveTypeKind.I64, - "i32" => PrimitiveTypeKind.I32, - "i16" => PrimitiveTypeKind.I16, - "i8" => PrimitiveTypeKind.I8, - "u64" => PrimitiveTypeKind.U64, - "u32" => PrimitiveTypeKind.U32, - "u16" => PrimitiveTypeKind.U16, - "u8" => PrimitiveTypeKind.U8, - "f64" => PrimitiveTypeKind.F64, - "f32" => PrimitiveTypeKind.F32, - "bool" => PrimitiveTypeKind.Bool, - _ => null - }; - - return kind != null; - } - - public override bool Equals(object? obj) - { - return obj is NubPrimitiveType other && other.Kind == Kind; - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind); - } - - public override string ToString() - { - return Kind switch - { - PrimitiveTypeKind.I8 => "i8", - PrimitiveTypeKind.I16 => "i16", - PrimitiveTypeKind.I32 => "i32", - PrimitiveTypeKind.I64 => "i64", - - PrimitiveTypeKind.U8 => "u8", - PrimitiveTypeKind.U16 => "u16", - PrimitiveTypeKind.U32 => "u32", - PrimitiveTypeKind.U64 => "u64", - - PrimitiveTypeKind.F32 => "f32", - PrimitiveTypeKind.F64 => "f64", - - PrimitiveTypeKind.Bool => "bool", - _ => throw new ArgumentOutOfRangeException(nameof(kind), Kind, null) - }; - } -} - -public enum PrimitiveTypeKind -{ - I64, - I32, - I16, - I8, - U64, - U32, - U16, - U8, - F64, - F32, - Bool -} \ No newline at end of file diff --git a/src/compilation/Syntax/Typing/TypeChecker.cs b/src/compilation/Syntax/Typing/TypeChecker.cs deleted file mode 100644 index e935a2b..0000000 --- a/src/compilation/Syntax/Typing/TypeChecker.cs +++ /dev/null @@ -1,633 +0,0 @@ -// using System.Diagnostics; -// using Syntax.Diagnostics; -// using Syntax.Parsing; -// using Syntax.Parsing.Node; -// using Syntax.Tokenization; -// -// namespace Syntax.Typing; -// -// public static class TypeChecker -// { -// private static SyntaxTree _syntaxTree = null!; -// private static DefinitionTable _definitionTable = null!; -// -// private static Dictionary _variables = new(); -// private static List _diagnostics = []; -// private static NubType? _currentFunctionReturnType; -// private static Queue _anonymousFunctions = []; -// -// public static void Check(SyntaxTree syntaxTree, DefinitionTable definitionTable, out IEnumerable diagnostics) -// { -// _syntaxTree = syntaxTree; -// _definitionTable = definitionTable; -// -// _variables = new Dictionary(); -// _diagnostics = []; -// _currentFunctionReturnType = null; -// _anonymousFunctions = []; -// -// foreach (var structDef in syntaxTree.Definitions.OfType()) -// { -// CheckStructDef(structDef); -// } -// -// foreach (var funcDef in syntaxTree.Definitions.OfType()) -// { -// CheckFuncDef(funcDef.Parameters, funcDef.Body, funcDef.ReturnType); -// } -// -// while (_anonymousFunctions.TryDequeue(out var func)) -// { -// CheckFuncDef(func.Parameters, func.Body, func.ReturnType); -// -// } -// -// diagnostics = _diagnostics; -// } -// -// private static void CheckStructDef(StructDefinitionNode structDef) -// { -// var fields = new Dictionary(); -// foreach (var field in structDef.Fields) -// { -// if (fields.ContainsKey(field.Name)) -// { -// ReportError($"Duplicate field '{field.Name}' in struct '{structDef.Name}'", structDef); -// continue; -// } -// -// if (field.Value.HasValue) -// { -// var fieldType = CheckExpression(field.Value.Value, field.Type); -// if (fieldType != null && !fieldType.Equals(field.Type)) -// { -// ReportError("Default field initializer does not match the defined type", field.Value.Value); -// } -// } -// -// fields[field.Name] = field.Type; -// } -// } -// -// private static void CheckFuncDef(List parameters, BlockNode body, NubType returnType) -// { -// _variables.Clear(); -// _currentFunctionReturnType = returnType; -// -// foreach (var param in parameters) -// { -// _variables[param.Name] = param.Type; -// } -// -// CheckBlock(body); -// } -// -// private static void CheckBlock(BlockNode block) -// { -// foreach (var statement in block.Statements) -// { -// CheckStatement(statement); -// } -// } -// -// private static void CheckStatement(StatementNode statement) -// { -// switch (statement) -// { -// case ArrayIndexAssignmentNode arrayIndexAssignment: -// CheckArrayIndexAssignment(arrayIndexAssignment); -// break; -// case VariableAssignmentNode variableAssignment: -// CheckVariableAssignment(variableAssignment); -// break; -// case VariableDeclarationNode variableDeclaration: -// CheckVariableVariableDeclaration(variableDeclaration); -// break; -// case IfNode ifNode: -// CheckIf(ifNode); -// break; -// case MemberAssignmentNode memberAssignment: -// CheckMemberAssignment(memberAssignment); -// break; -// case WhileNode whileNode: -// CheckWhile(whileNode); -// break; -// case ReturnNode returnNode: -// CheckReturn(returnNode); -// break; -// case StatementExpressionNode statementExpression: -// CheckExpression(statementExpression.Expression); -// break; -// case BreakNode: -// case ContinueNode: -// break; -// case DereferenceAssignmentNode dereferenceAssignment: -// CheckDereferenceAssignment(dereferenceAssignment); -// break; -// default: -// ReportError($"Unsupported statement type: {statement.GetType().Name}", statement); -// break; -// } -// } -// -// private static void CheckMemberAssignment(MemberAssignmentNode memberAssignment) -// { -// var memberType = CheckExpression(memberAssignment.MemberAccess); -// if (memberType == null) return; -// var valueType = CheckExpression(memberAssignment.Value, memberType); -// if (valueType == null) return; -// -// if (!NubType.IsCompatibleWith(memberType, valueType)) -// { -// ReportError($"'{valueType}' is not assignable to member of type '{memberType}'", memberAssignment); -// } -// } -// -// private static void CheckArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) -// { -// var itemType = CheckExpression(arrayIndexAssignment.ArrayIndexAccess); -// if (itemType == null) return; -// var valueType = CheckExpression(arrayIndexAssignment.Value, itemType); -// if (valueType == null) return; -// -// if (!NubType.IsCompatibleWith(itemType, valueType)) -// { -// ReportError($"'{valueType}' is not assignable to array of type '{itemType}'", arrayIndexAssignment); -// } -// } -// -// private static void CheckVariableAssignment(VariableAssignmentNode variableAssignment) -// { -// if (!_variables.TryGetValue(variableAssignment.Identifier.Name, out var variable)) -// { -// ReportError($"Variable '{variableAssignment.Identifier}' is not declared", variableAssignment); -// return; -// } -// -// var valueType = CheckExpression(variableAssignment.Value, variable); -// if (valueType == null) return; -// -// if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, variable)) -// { -// ReportError($"Cannot assign expression of type '{variableAssignment.Value.Type}' to variable '{variableAssignment.Identifier}' with type '{variable}'", variableAssignment); -// } -// } -// -// private static void CheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration) -// { -// if (_variables.TryGetValue(variableDeclaration.Name, out var variable)) -// { -// ReportError($"Cannot redeclare variable '{variable}'", variableDeclaration); -// } -// -// _variables[variableDeclaration.Name] = variableDeclaration.Type; -// } -// -// private static NubType? CheckDereference(DereferenceNode dereference) -// { -// var exprType = CheckExpression(dereference.Expression); -// if (exprType == null) return null; -// -// if (exprType is not NubPointerType nubPointerType) -// { -// ReportError($"Cannot dereference a non-pointer type {exprType}", dereference); -// return null; -// } -// -// return nubPointerType.BaseType; -// } -// -// private static NubType CheckFixedInitializerArray(FixedArrayInitializerNode fixedArrayInitializer) -// { -// return new NubFixedArrayType(fixedArrayInitializer.ElementType, fixedArrayInitializer.Capacity); -// } -// -// private static NubType? CheckFuncCall(FuncCallNode funcCall) -// { -// var identType = CheckExpression(funcCall.Expression); -// if (identType is not NubFuncType funcType) -// { -// ReportError("Cannot call function on non-function type", funcCall); -// return null; -// } -// -// if (funcCall.Parameters.Count != funcType.Parameters.Count) -// { -// ReportError($"{funcType} expects {funcType.Parameters.Count} arguments, but was called with {funcType.Parameters.Count} arguments", funcCall); -// return null; -// } -// -// for (var i = 0; i < funcCall.Parameters.Count; i++) -// { -// var parameter = funcCall.Parameters[i]; -// var parameterType = CheckExpression(parameter); -// if (parameterType == null) return null; -// -// if (!NubType.IsCompatibleWith(parameterType, funcType.Parameters[i])) -// { -// ReportError($"'{parameterType}' does not match expected type {funcType.Parameters[i]}", funcCall); -// return null; -// } -// } -// -// return funcType.ReturnType; -// } -// -// private static void CheckIf(IfNode ifNode) -// { -// var conditionType = CheckExpression(ifNode.Condition, NubPrimitiveType.Bool); -// if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool)) -// { -// ReportError($"If condition must be a boolean expression, got '{conditionType}'", ifNode.Condition); -// } -// -// CheckBlock(ifNode.Body); -// -// if (ifNode.Else.HasValue) -// { -// var elseValue = ifNode.Else.Value; -// elseValue.Match(CheckIf, CheckBlock); -// } -// } -// -// private static void CheckWhile(WhileNode whileNode) -// { -// var conditionType = CheckExpression(whileNode.Condition, NubPrimitiveType.Bool); -// if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool)) -// { -// ReportError($"While condition must be a boolean expression, got '{conditionType}'", whileNode.Condition); -// } -// -// CheckBlock(whileNode.Body); -// } -// -// private static void CheckReturn(ReturnNode returnNode) -// { -// if (returnNode.Value.HasValue) -// { -// var returnType = CheckExpression(returnNode.Value.Value, _currentFunctionReturnType); -// if (returnType == null) return; -// -// if (_currentFunctionReturnType == null) -// { -// ReportError("Cannot return a value from a function with no return type", returnNode.Value.Value); -// return; -// } -// -// if (!NubType.IsCompatibleWith(returnType, _currentFunctionReturnType)) -// { -// ReportError($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'", returnNode.Value.Value); -// } -// } -// else if (_currentFunctionReturnType != null) -// { -// ReportError($"Function must return a value of type '{_currentFunctionReturnType}'", returnNode); -// } -// } -// -// private static void CheckDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment) -// { -// var dereferenceType = CheckExpression(dereferenceAssignment.Dereference); -// if (dereferenceType == null) return; -// var valueType = CheckExpression(dereferenceAssignment.Value, dereferenceType); -// if (valueType == null) return; -// -// if (!NubType.IsCompatibleWith(dereferenceType, valueType)) -// { -// ReportError($"'{valueType}' is not assignable to type '{dereferenceType}'", dereferenceAssignment); -// } -// } -// -// private static NubType? CheckExpression(ExpressionNode expression, NubType? expectedType = null) -// { -// var resultType = expression switch -// { -// AddressOfNode addressOf => CheckAddressOf(addressOf), -// AnonymousFuncNode anonymousFunc => CheckAnonymousFunc(anonymousFunc), -// ArrayIndexAccessNode arrayIndex => CheckArrayIndex(arrayIndex), -// ArrayInitializerNode arrayInitializer => CheckArrayInitializer(arrayInitializer), -// LiteralNode literal => CheckLiteral(literal, expectedType), -// IdentifierNode identifier => CheckIdentifier(identifier), -// BinaryExpressionNode binaryExpr => CheckBinaryExpression(binaryExpr), -// DereferenceNode dereference => CheckDereference(dereference), -// FixedArrayInitializerNode fixedArray => CheckFixedInitializerArray(fixedArray), -// FuncCallNode funcCallExpr => CheckFuncCall(funcCallExpr), -// StructInitializerNode structInit => CheckStructInitializer(structInit), -// UnaryExpressionNode unaryExpression => CheckUnaryExpression(unaryExpression), -// MemberAccessNode memberAccess => CheckMemberAccess(memberAccess), -// _ => throw new UnreachableException() -// }; -// -// if (resultType != null) -// { -// expression.Type = resultType; -// } -// -// return resultType; -// } -// -// private static NubType CheckAnonymousFunc(AnonymousFuncNode anonymousFunc) -// { -// _anonymousFunctions.Enqueue(anonymousFunc); -// return new NubFuncType(anonymousFunc.ReturnType, anonymousFunc.Parameters.Select(p => p.Type).ToList()); -// } -// -// private static NubType? CheckLiteral(LiteralNode literal, NubType? expectedType = null) -// { -// if (expectedType != null) -// { -// if (expectedType.IsNumber && literal.Kind is not LiteralKind.Integer and not LiteralKind.Float) -// { -// ReportError("Expression expects a numeric literal", literal); -// return null; -// } -// -// if (expectedType.IsInteger && literal.Kind == LiteralKind.Float) -// { -// if (literal.Kind == LiteralKind.Float) -// { -// ReportWarning("Float used in integer context. Everything after the '.' will be ignored", literal); -// } -// } -// -// return expectedType; -// } -// -// return literal.Kind switch -// { -// LiteralKind.Integer => NubPrimitiveType.I64, -// LiteralKind.Float => NubPrimitiveType.F64, -// LiteralKind.String => new NubCStringType(), -// LiteralKind.Bool => NubPrimitiveType.Bool, -// _ => throw new ArgumentOutOfRangeException() -// }; -// } -// -// private static NubType? CheckArrayIndex(ArrayIndexAccessNode arrayIndexAccess) -// { -// var expressionType = CheckExpression(arrayIndexAccess.Array); -// if (expressionType == null) return null; -// var indexType = CheckExpression(arrayIndexAccess.Index, NubPrimitiveType.U64); -// if (indexType is { IsInteger: false }) -// { -// ReportError("Array index type must be a number", arrayIndexAccess.Index); -// } -// -// if (expressionType is NubArrayType arrayType) -// { -// return arrayType.ElementType; -// } -// -// if (expressionType is NubFixedArrayType fixedArrayType) -// { -// return fixedArrayType.ElementType; -// } -// -// ReportError($"Cannot access index of non-array type {expressionType}", arrayIndexAccess.Array); -// return null; -// } -// -// private static NubType CheckArrayInitializer(ArrayInitializerNode arrayInitializer) -// { -// var capacityType = CheckExpression(arrayInitializer.Capacity, NubPrimitiveType.U64); -// if (capacityType is { IsInteger: false }) -// { -// ReportError("Array capacity type must be an integer", arrayInitializer.Capacity); -// } -// -// return new NubArrayType(arrayInitializer.ElementType); -// } -// -// private static NubType? CheckIdentifier(IdentifierNode identifier) -// { -// var definition = _definitionTable.LookupFunc(identifier.Namespace.Or(_syntaxTree.Namespace), identifier.Name); -// if (definition.HasValue) -// { -// return new NubFuncType(definition.Value.ReturnType, definition.Value.Parameters.Select(p => p.Type).ToList()); -// } -// -// if (!identifier.Namespace.HasValue) -// { -// return _variables[identifier.Name]; -// } -// -// ReportError($"Identifier '{identifier}' not found", identifier); -// return null; -// } -// -// private static NubType? CheckAddressOf(AddressOfNode addressOf) -// { -// var exprType = CheckExpression(addressOf.Expression); -// if (exprType == null) return null; -// -// return new NubPointerType(exprType); -// } -// -// private static NubType? CheckBinaryExpression(BinaryExpressionNode binaryExpr) -// { -// var leftType = CheckExpression(binaryExpr.Left); -// var rightType = CheckExpression(binaryExpr.Right); -// -// if (leftType == null || rightType == null) return null; -// -// if (!leftType.Equals(rightType)) -// { -// ReportError($"Left '{leftType}' and right '{rightType}' side of the binary expression must be the same type", binaryExpr); -// return null; -// } -// -// switch (binaryExpr.Operator) -// { -// case BinaryExpressionOperator.Equal: -// case BinaryExpressionOperator.NotEqual: -// return NubPrimitiveType.Bool; -// case BinaryExpressionOperator.GreaterThan: -// case BinaryExpressionOperator.GreaterThanOrEqual: -// case BinaryExpressionOperator.LessThan: -// case BinaryExpressionOperator.LessThanOrEqual: -// if (!IsNumeric(leftType)) -// { -// ReportError($"Comparison operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr); -// return null; -// } -// -// return NubPrimitiveType.Bool; -// case BinaryExpressionOperator.Plus: -// case BinaryExpressionOperator.Minus: -// case BinaryExpressionOperator.Multiply: -// case BinaryExpressionOperator.Divide: -// if (!IsNumeric(leftType)) -// { -// ReportError($"Arithmetic operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr); -// return null; -// } -// -// return leftType; -// default: -// ReportError($"Unsupported binary operator: {binaryExpr.Operator}", binaryExpr); -// return null; -// } -// } -// -// private static NubType? CheckStructInitializer(StructInitializerNode structInit) -// { -// var initialized = new HashSet(); -// -// var defOpt = _definitionTable.LookupStruct(structInit.StructType.Namespace, structInit.StructType.Name); -// if (!defOpt.TryGetValue(out var definition)) -// { -// ReportError($"Struct type '{structInit.StructType.Name}' is not defined", structInit); -// return null; -// } -// -// foreach (var initializer in structInit.Initializers) -// { -// var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key); -// if (definitionField == null) -// { -// ReportError($"Field '{initializer.Key}' does not exist in struct '{structInit.StructType.Name}'", initializer.Value); -// continue; -// } -// -// var initializerType = CheckExpression(initializer.Value, definitionField.Type); -// if (initializerType != null && !NubType.IsCompatibleWith(initializerType, definitionField.Type)) -// { -// ReportError($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'", initializer.Value); -// } -// -// initialized.Add(initializer.Key); -// } -// -// foreach (var field in definition.Fields.Where(f => f.Value.HasValue)) -// { -// initialized.Add(field.Name); -// } -// -// foreach (var field in definition.Fields) -// { -// if (!initialized.Contains(field.Name)) -// { -// ReportError($"Struct field '{field.Name}' is not initialized on type '{structInit.StructType.Name}'", structInit); -// } -// } -// -// return structInit.StructType; -// } -// -// private static NubType? CheckUnaryExpression(UnaryExpressionNode unaryExpression) -// { -// var operandType = CheckExpression(unaryExpression.Operand); -// if (operandType == null) return null; -// -// switch (unaryExpression.Operator) -// { -// case UnaryExpressionOperator.Negate: -// { -// if (operandType.Equals(NubPrimitiveType.I8) || -// operandType.Equals(NubPrimitiveType.I16) || -// operandType.Equals(NubPrimitiveType.I32) || -// operandType.Equals(NubPrimitiveType.I64) || -// operandType.Equals(NubPrimitiveType.F32) || -// operandType.Equals(NubPrimitiveType.F64)) -// { -// return operandType; -// } -// -// ReportError($"Cannot negate non-numeric type {operandType}", unaryExpression.Operand); -// return null; -// } -// case UnaryExpressionOperator.Invert: -// { -// if (!operandType.Equals(NubPrimitiveType.Bool)) -// { -// ReportError($"Cannot invert non-boolean type {operandType}", unaryExpression.Operand); -// return null; -// } -// -// return operandType; -// } -// default: -// { -// ReportError($"Unsupported unary operator: {unaryExpression.Operator}", unaryExpression); -// return null; -// } -// } -// } -// -// private static NubType? CheckMemberAccess(MemberAccessNode memberAccess) -// { -// var expressionType = CheckExpression(memberAccess.Expression); -// if (expressionType == null) return null; -// -// switch (expressionType) -// { -// case NubArrayType: -// { -// if (memberAccess.Member == "count") -// { -// return NubPrimitiveType.I64; -// } -// -// break; -// } -// case NubStructType structType: -// { -// var defOpt = _definitionTable.LookupStruct(structType.Namespace, structType.Name); -// if (!defOpt.TryGetValue(out var definition)) -// { -// ReportError($"Struct type '{structType.Name}' is not defined", memberAccess); -// return null; -// } -// -// var field = definition.Fields.FirstOrDefault(f => f.Name == memberAccess.Member); -// if (field == null) -// { -// ReportError($"Field '{memberAccess.Member}' does not exist in struct '{structType.Name}'", memberAccess); -// return null; -// } -// -// return field.Type; -// } -// } -// -// ReportError($"Cannot access member '{memberAccess.Member}' on type '{expressionType}'", memberAccess); -// return null; -// } -// -// private static void ReportError(string message, Node node) -// { -// var diagnostic = Diagnostic.Error(message).At(node).Build(); -// _diagnostics.Add(diagnostic); -// } -// -// private static void ReportWarning(string message, Node node) -// { -// var diagnostic = Diagnostic.Warning(message).At(node).Build(); -// _diagnostics.Add(diagnostic); -// } -// -// private static bool IsNumeric(NubType type) -// { -// if (type is not NubPrimitiveType primitiveType) -// { -// return false; -// } -// -// switch (primitiveType.Kind) -// { -// case PrimitiveTypeKind.I8: -// case PrimitiveTypeKind.I16: -// case PrimitiveTypeKind.I32: -// case PrimitiveTypeKind.I64: -// case PrimitiveTypeKind.U8: -// case PrimitiveTypeKind.U16: -// case PrimitiveTypeKind.U32: -// case PrimitiveTypeKind.U64: -// case PrimitiveTypeKind.F32: -// case PrimitiveTypeKind.F64: -// return true; -// default: -// return false; -// } -// } -// } \ No newline at end of file diff --git a/src/runtime/entry.s b/src/runtime/entry.s deleted file mode 100644 index 5ea2a6a..0000000 --- a/src/runtime/entry.s +++ /dev/null @@ -1,12 +0,0 @@ -.intel_syntax noprefix - -.equ SYS_EXIT, 60 - -.text -.globl _start -_start: - mov rdi, rsp - call main - mov rdi, rax - mov rax, SYS_EXIT - syscall diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index e2a4d24..1173ff4 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -1,17 +1,49 @@ #include +#include +#include +#include +#include +#include +#include #define NUB_PANIC_ERROR_CODE 101 -uint64_t nub_cstring_length() { return 0; } +uint64_t nub_cstring_length(char* string) +{ + return strlen(string); +} -uint64_t nub_string_length() {} +uint64_t nub_string_length() +{ + return 0; +} -void nub_memcpy() {} +void nub_memcpy(void* source, void* destination, uint64_t length) +{ + memcpy(destination, source, length); +} -void nub_memset() {} +void nub_memset(void* destination, int value, uint64_t count) +{ + memset(destination, value, count); +} -void nub_panic() {} +void nub_panic_array_oob() +{ + puts("Array out of bounds"); + exit(NUB_PANIC_ERROR_CODE); +} -void nub_panic_array_oob() {} +void nub_panic_oom() +{ + puts("Out of memory"); + exit(NUB_PANIC_ERROR_CODE); +} -void nub_panic_oom() {} \ No newline at end of file +extern int64_t main(); + +void _start() +{ + int64_t exit_code = main(); + exit(exit_code); +} \ No newline at end of file