diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 0000000..4274638
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,3 @@
+string formatting
+Dynamic arrays
+C-style arrays
\ No newline at end of file
diff --git a/bindings/generate.py b/bindings/generate.py
deleted file mode 100755
index f2ce3c2..0000000
--- a/bindings/generate.py
+++ /dev/null
@@ -1,166 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import os
-import clang.cindex
-from clang.cindex import CursorKind, TypeKind, Type
-
-
-def map_type(clang_type: Type):
- canonical = clang_type.get_canonical()
- kind = canonical.kind
- spelling = (
- canonical.spelling.replace("const ", "")
- .replace("volatile ", "")
- .replace("restrict ", "")
- .replace("struct ", "")
- .replace("union ", "")
- )
-
- if kind == TypeKind.POINTER:
- pointee = canonical.get_pointee()
- if pointee.kind == TypeKind.RECORD:
- decl = pointee.get_declaration()
- if not decl.is_definition():
- return "^void"
-
- if pointee.kind == TypeKind.FUNCTIONPROTO:
- arg_types = []
-
- for arg in pointee.get_canonical().argument_types():
- arg_types.append(map_type(arg))
-
- mapped_return = map_type(pointee.get_canonical().get_result())
- args_str = ", ".join(arg_types)
-
- return f"func({args_str}): {mapped_return}"
-
- return f"^{map_type(pointee)}"
-
- if kind == TypeKind.CONSTANTARRAY:
- element_type = canonical.get_array_element_type()
- size = canonical.get_array_size()
- return f"[{size}]{map_type(element_type)}"
-
- if kind == TypeKind.INCOMPLETEARRAY:
- element_type = canonical.get_array_element_type()
- return f"[?]{map_type(element_type)}"
-
- if kind == TypeKind.FUNCTIONPROTO or kind == TypeKind.FUNCTIONNOPROTO:
- arg_types = []
-
- for arg in canonical.argument_types():
- arg_types.append(map_type(arg))
-
- mapped_return = map_type(canonical.get_result())
- args_str = ", ".join(arg_types)
-
- return f"func({args_str}): {mapped_return}"
-
- if kind == TypeKind.VOID:
- return "void"
-
- if kind == TypeKind.BOOL:
- return "bool"
-
- if kind in [TypeKind.CHAR_S, TypeKind.SCHAR]:
- return "i8"
- if kind == TypeKind.CHAR_U or kind == TypeKind.UCHAR:
- return "u8"
- if kind == TypeKind.SHORT:
- return "i16"
- if kind == TypeKind.USHORT:
- return "u16"
- if kind == TypeKind.INT:
- return "i32"
- if kind == TypeKind.UINT:
- return "u32"
- if kind in [TypeKind.LONG, TypeKind.LONGLONG]:
- return "i64"
- if kind in [TypeKind.ULONG, TypeKind.ULONGLONG]:
- return "u64"
-
- if kind == TypeKind.FLOAT:
- return "f32"
- if kind == TypeKind.DOUBLE or kind == TypeKind.LONGDOUBLE:
- return "f64"
-
- if kind == TypeKind.RECORD:
- return spelling
-
- raise Exception(f"Unresolved type: {spelling}")
-
-
-if len(sys.argv) != 2:
- print("Usage: python3 generate.py [path to header]", file=sys.stderr)
- sys.exit(1)
-
-filename = sys.argv[1]
-
-index = clang.cindex.Index.create()
-
-tu = index.parse(filename, ["-x", "c", "-std=c23", "-I/usr/include"])
-
-if tu.diagnostics:
- for diag in tu.diagnostics:
- if diag.severity >= clang.cindex.Diagnostic.Error:
- print(f"Error: {diag.spelling}", file=sys.stderr)
-
-print(f'module "{os.path.basename(filename).split(".")[0]}"')
-print()
-
-seen_structs = []
-
-for cursor in tu.cursor.walk_preorder():
- if cursor.location.file and cursor.location.file.name != filename:
- continue
-
- if cursor.kind == CursorKind.FUNCTION_DECL:
- name = cursor.spelling
- return_type = map_type(cursor.result_type)
-
- params = []
- for arg in cursor.get_arguments():
- param_name = arg.spelling
- param_type = map_type(arg.type)
- params.append(f"{param_name}: {param_type}")
-
- params_str = ", ".join(params)
-
- print(f'export extern "{name}" func {name}({params_str}): {return_type}')
-
- elif cursor.kind == CursorKind.STRUCT_DECL:
- if cursor.get_usr() in seen_structs:
- continue
-
- seen_structs.append(cursor.get_usr())
-
- if cursor.is_definition():
- name = cursor.spelling
- print(f"export struct {name}")
- print("{")
- for field in cursor.get_children():
- if field.kind == CursorKind.FIELD_DECL:
- field_name = field.spelling
- field_type = map_type(field.type)
- print(f" {field_name}: {field_type}")
- else:
- raise Exception(
- f"Unsupported child of struct: {field.spelling}: {field.kind}"
- )
- print("}")
-
- elif cursor.kind == CursorKind.ENUM_DECL:
- name = cursor.spelling
- print(f"export enum {name} : u32")
- print("{")
- for field in cursor.get_children():
- if field.kind == CursorKind.ENUM_CONSTANT_DECL:
- field_name = field.spelling
- field_value = field.enum_value
- print(f" {field_name} = {field_value}")
- else:
- raise Exception(
- f"Unsupported child of enum: {field.spelling}: {field.kind}"
- )
- print("}")
diff --git a/compiler/.gitignore b/compiler/.gitignore
index c6cc67a..0418d79 100644
--- a/compiler/.gitignore
+++ b/compiler/.gitignore
@@ -1,34 +1,4 @@
-# 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
+.idea
+bin
+obj
+.build
\ No newline at end of file
diff --git a/compiler/.idea/.idea.Compiler/.idea/.gitignore b/compiler/.idea/.idea.Compiler/.idea/.gitignore
deleted file mode 100644
index cda1cf4..0000000
--- a/compiler/.idea/.idea.Compiler/.idea/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Rider ignored files
-/modules.xml
-/projectSettingsUpdater.xml
-/contentModel.xml
-/.idea.Compiler.iml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/compiler/.idea/.idea.Compiler/.idea/.name b/compiler/.idea/.idea.Compiler/.idea/.name
deleted file mode 100644
index b92b7a3..0000000
--- a/compiler/.idea/.idea.Compiler/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-Compiler
\ No newline at end of file
diff --git a/compiler/.idea/.idea.Compiler/.idea/encodings.xml b/compiler/.idea/.idea.Compiler/.idea/encodings.xml
deleted file mode 100644
index df87cf9..0000000
--- a/compiler/.idea/.idea.Compiler/.idea/encodings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml b/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml
deleted file mode 100644
index 2135b43..0000000
--- a/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
- Runtime
-
-
-
-
-
\ No newline at end of file
diff --git a/compiler/.idea/.idea.Compiler/.idea/vcs.xml b/compiler/.idea/.idea.Compiler/.idea/vcs.xml
deleted file mode 100644
index 6c0b863..0000000
--- a/compiler/.idea/.idea.Compiler/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/compiler/NubLang/NubLang.csproj b/compiler/Compiler.csproj
similarity index 82%
rename from compiler/NubLang/NubLang.csproj
rename to compiler/Compiler.csproj
index b682a68..85b4959 100644
--- a/compiler/NubLang/NubLang.csproj
+++ b/compiler/Compiler.csproj
@@ -1,10 +1,10 @@
+ Exe
net9.0
enable
enable
- true
diff --git a/compiler/Compiler.sln b/compiler/Compiler.sln
deleted file mode 100644
index 480156a..0000000
--- a/compiler/Compiler.sln
+++ /dev/null
@@ -1,28 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang", "NubLang\NubLang.csproj", "{5047E21F-590D-4CB3-AFF3-064316485009}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.LSP", "NubLang.LSP\NubLang.LSP.csproj", "{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}"
-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
- {07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/compiler/Diagnostic.cs b/compiler/Diagnostic.cs
new file mode 100644
index 0000000..1d4b46e
--- /dev/null
+++ b/compiler/Diagnostic.cs
@@ -0,0 +1,173 @@
+namespace Compiler;
+
+public class Diagnostic
+{
+ public static Builder Info(string message) => new Builder(DiagnosticSeverity.Info, message);
+ public static Builder Warning(string message) => new Builder(DiagnosticSeverity.Warning, message);
+ public static Builder Error(string message) => new Builder(DiagnosticSeverity.Error, message);
+
+ private Diagnostic(DiagnosticSeverity severity, string message, string? help, FileInfo? file)
+ {
+ Severity = severity;
+ Message = message;
+ Help = help;
+ File = file;
+ }
+
+ public class FileInfo(string file, int line, int column, int length)
+ {
+ public string File { get; } = file;
+ public int Line { get; } = line;
+ public int Column { get; } = column;
+ public int Length { get; } = length;
+ }
+
+ public DiagnosticSeverity Severity { get; }
+ public string Message { get; }
+ public string? Help { get; }
+ public FileInfo? File { get; }
+
+ public enum DiagnosticSeverity
+ {
+ Info,
+ Warning,
+ Error,
+ }
+
+ public class Builder(DiagnosticSeverity severity, string message)
+ {
+ private FileInfo? file;
+ private string? help;
+
+ public Builder At(string fileName, int line, int column, int length)
+ {
+ file = new FileInfo(fileName, line, column, length);
+ return this;
+ }
+
+ public Builder At(string fileName, Token? token)
+ {
+ if (token != null)
+ {
+ At(fileName, token.Line, token.Column, token.Length);
+ }
+
+ return this;
+ }
+
+ public Builder At(string fileName, Node? node)
+ {
+ if (node != null && node.Tokens.Count != 0)
+ {
+ // todo(nub31): Calculate length based on last token
+ At(fileName, node.Tokens[0]);
+ }
+
+ return this;
+ }
+
+ public Builder At(string fileName, TypedNode? node)
+ {
+ if (node != null && node.Tokens.Count != 0)
+ {
+ // todo(nub31): Calculate length based on last token
+ At(fileName, node.Tokens[0]);
+ }
+
+ return this;
+ }
+
+ public Builder WithHelp(string helpMessage)
+ {
+ help = helpMessage;
+ return this;
+ }
+
+ public Diagnostic Build()
+ {
+ return new Diagnostic(severity, message, help, file);
+ }
+ }
+}
+
+public class CompileException(Diagnostic diagnostic) : Exception
+{
+ public Diagnostic Diagnostic { get; } = diagnostic;
+}
+
+public static class DiagnosticFormatter
+{
+ public static void Print(Diagnostic diagnostic, TextWriter writer)
+ {
+ var (label, color) = diagnostic.Severity switch
+ {
+ Diagnostic.DiagnosticSeverity.Info => ("info", Ansi.Cyan),
+ Diagnostic.DiagnosticSeverity.Warning => ("warning", Ansi.Yellow),
+ Diagnostic.DiagnosticSeverity.Error => ("error", Ansi.Red),
+ _ => ("unknown", Ansi.Reset),
+ };
+
+ writer.Write(color);
+ writer.Write(label);
+ writer.Write(Ansi.Reset);
+ writer.Write(": ");
+ writer.WriteLine(diagnostic.Message);
+
+ if (diagnostic.File is null)
+ return;
+
+ var file = diagnostic.File;
+ var lineNumberWidth = diagnostic.File.Line.ToString().Length;
+
+ writer.WriteLine($" {new string(' ', lineNumberWidth)}{file.File}:{file.Line}:{file.Column}");
+ writer.WriteLine($"{new string(' ', lineNumberWidth)} | ");
+
+ var sourceLine = TryReadLine(file.File, file.Line);
+ if (sourceLine != null)
+ {
+ writer.Write($"{file.Line.ToString().PadLeft(lineNumberWidth)} | ");
+ writer.WriteLine(sourceLine);
+
+ writer.Write(new string(' ', lineNumberWidth));
+ writer.Write(" | ");
+ writer.Write(new string(' ', file.Column - 1));
+ writer.Write(color);
+ writer.Write(new string('^', Math.Max(1, file.Length)));
+ writer.WriteLine(Ansi.Reset);
+ }
+
+ writer.WriteLine($"{new string(' ', lineNumberWidth)} |");
+
+ if (!string.IsNullOrWhiteSpace(diagnostic.Help))
+ {
+ writer.WriteLine($" = help: {diagnostic.Help}");
+ }
+ }
+
+ private static string? TryReadLine(string file, int line)
+ {
+ try
+ {
+ using var reader = new StreamReader(file);
+ for (var i = 1; i < line; i++)
+ {
+ if (reader.ReadLine() == null)
+ return null;
+ }
+
+ return reader.ReadLine();
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static class Ansi
+ {
+ public const string Reset = "\e[0m";
+ public const string Red = "\e[31m";
+ public const string Yellow = "\e[33m";
+ public const string Cyan = "\e[36m";
+ }
+}
\ No newline at end of file
diff --git a/compiler/Generator.cs b/compiler/Generator.cs
new file mode 100644
index 0000000..61cd584
--- /dev/null
+++ b/compiler/Generator.cs
@@ -0,0 +1,1136 @@
+using System.Diagnostics;
+using System.Text;
+
+namespace Compiler;
+
+public class Generator
+{
+ public static string Emit(List functions, ModuleGraph moduleGraph, string? entryPoint)
+ {
+ return new Generator(functions, moduleGraph, entryPoint).Emit();
+ }
+
+ private Generator(List functions, ModuleGraph moduleGraph, string? entryPoint)
+ {
+ this.functions = functions;
+ this.moduleGraph = moduleGraph;
+ this.entryPoint = entryPoint;
+ }
+
+ const string NUB_H_CONTENTS =
+ """
+ #include
+ #include
+ #include
+ #include
+
+ static inline void *nub_alloc(size_t size)
+ {
+ void *mem = malloc(size);
+ if (mem == NULL)
+ {
+ puts("Out of memory");
+ exit(1);
+ }
+
+ return mem;
+ }
+
+ typedef struct
+ {
+ char *data;
+ size_t length;
+ size_t ref;
+ uint32_t flags;
+ } string;
+
+ typedef string *string_ptr;
+
+ #define FLAG_STRING_LITERAL 1
+
+ static inline void string_rc_inc(string_ptr str)
+ {
+ if (str == NULL)
+ return;
+
+ if (str->flags & FLAG_STRING_LITERAL)
+ return;
+
+ str->ref += 1;
+ }
+
+ static inline void string_rc_dec(string_ptr str)
+ {
+ if (str == NULL)
+ return;
+
+ if (str->flags & FLAG_STRING_LITERAL)
+ return;
+
+ if (str->ref == 0)
+ return;
+
+ str->ref -= 1;
+
+ if (str->ref == 0)
+ {
+ free(str->data);
+ free(str);
+ }
+ }
+
+ static inline string_ptr string_concat(string_ptr left, string_ptr right)
+ {
+ size_t new_length = left->length + right->length;
+
+ string_ptr result = (string_ptr)nub_alloc(sizeof(string));
+ result->data = (char*)nub_alloc(new_length + 1);
+
+ memcpy(result->data, left->data, left->length);
+ memcpy(result->data + left->length, right->data, right->length);
+
+ result->data[new_length] = '\0';
+ result->length = new_length;
+ result->ref = 1;
+ result->flags = 0;
+
+ return result;
+ }
+
+ static inline string_ptr string_from_cstr(char *cstr)
+ {
+ size_t len = strlen(cstr);
+
+ string_ptr result = (string_ptr)nub_alloc(sizeof(string));
+ result->data = (char*)nub_alloc(len + 1);
+
+ memcpy(result->data, cstr, len + 1);
+
+ result->length = len;
+ result->ref = 1;
+ result->flags = 0;
+
+ return result;
+ }
+
+ #define da_append(xs, x) \
+ do \
+ { \
+ if ((xs)->count >= (xs)->capacity) \
+ { \
+ (xs)->capacity *= 2; \
+ (xs)->items = realloc((xs)->items, (xs)->capacity * sizeof(*(xs)->items)); \
+ } \
+ (xs)->items[(xs)->count++] = (x); \
+ } while (0)
+
+ """;
+
+ private readonly List functions;
+ private readonly ModuleGraph moduleGraph;
+ private readonly string? entryPoint;
+ private IndentedTextWriter writer = new();
+ private readonly Dictionary referencedStringLiterals = [];
+ private readonly HashSet emittedTypes = [];
+ private readonly Stack scopes = new();
+ private int tmpNameIndex = 0;
+
+ private string Emit()
+ {
+ var outPath = ".build";
+ var fileName = "out.c";
+
+ if (entryPoint != null)
+ {
+ writer.WriteLine("int main(int argc, char *argv[])");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ writer.WriteLine($"return {entryPoint}();");
+ }
+ writer.WriteLine("}");
+ writer.WriteLine();
+ }
+
+ foreach (var function in functions)
+ {
+ if (!moduleGraph.TryResolveIdentifier(function.Module, function.Name.Ident, true, out var info))
+ throw new UnreachableException($"Module graph does not have info about the function {function.Module}::{function.Name.Ident}. This should have been caught earlier");
+
+ if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported)
+ writer.Write("static ");
+
+ var parameters = function.Parameters.Select(x => $"{TypeName(x.Type)} {x.Name.Ident}");
+
+ writer.WriteLine($"{TypeName(function.ReturnType)} {info.MangledName}({string.Join(", ", parameters)})");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ PushScope();
+ EmitStatement(function.Body);
+ PopScope();
+ }
+ writer.WriteLine("}");
+ writer.WriteLine();
+ }
+
+ var definitions = writer.ToString();
+
+ writer = new IndentedTextWriter();
+
+ foreach (var module in moduleGraph.GetModules())
+ {
+ foreach (var (name, info) in module.GetIdentifiers())
+ {
+ if (info.Source == Module.DefinitionSource.Internal || info.Exported)
+ {
+ if (info.Source == Module.DefinitionSource.Imported || info.Extern)
+ writer.Write("extern ");
+ else if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported)
+ writer.Write("static ");
+
+ if (info.Type is NubTypeFunc fn)
+ writer.WriteLine($"{TypeName(fn.ReturnType)} {info.MangledName}({string.Join(", ", fn.Parameters.Select(TypeName))});");
+ else
+ writer.WriteLine($"{TypeName(info.Type)} {info.MangledName};");
+
+ writer.WriteLine();
+ }
+ }
+ }
+
+ foreach (var (name, value) in referencedStringLiterals)
+ {
+ writer.WriteLine
+ (
+ $$"""
+ static string {{name}} = (string){
+ .data = "{{value}}",
+ .length = {{Encoding.UTF8.GetByteCount(value)}},
+ .ref = 0,
+ .flags = FLAG_STRING_LITERAL
+ };
+
+ """
+ );
+ }
+
+ var declarations = writer.ToString();
+
+ writer = new IndentedTextWriter();
+
+
+ while (emittedTypes.Count != typeNames.Count)
+ {
+ var nextTypes = typeNames.Keys.ToArray();
+ foreach (var type in nextTypes)
+ {
+ EmitTypeDefinitionIfNotEmitted(type);
+ }
+ }
+
+ var types = writer.ToString();
+
+ var sb = new StringBuilder();
+
+ sb.AppendLine("#include \"nub.h\"");
+ sb.AppendLine();
+ sb.AppendLine(types);
+ sb.AppendLine(declarations);
+ sb.AppendLine(definitions);
+
+ Directory.CreateDirectory(outPath);
+
+ var filePath = Path.Combine(outPath, fileName);
+
+ File.WriteAllText(Path.Combine(outPath, "nub.h"), NUB_H_CONTENTS);
+ File.WriteAllText(filePath, sb.ToString());
+
+ return filePath;
+ }
+
+ private void EmitTypeDefinitionIfNotEmitted(NubType type)
+ {
+ if (emittedTypes.Contains(type))
+ return;
+
+ emittedTypes.Add(type);
+
+ var name = TypeName(type);
+
+ switch (type)
+ {
+ case NubTypeStruct structType:
+ {
+ if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo)
+ throw new UnreachableException();
+
+ foreach (var field in structInfo.Fields)
+ EmitTypeDefinitionIfNotEmitted(field.Type);
+
+ if (structInfo.Packed)
+ writer.Write("__attribute__((__packed__)) ");
+
+ writer.WriteLine("typedef struct");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ foreach (var field in structInfo.Fields)
+ {
+ writer.WriteLine($"{TypeName(field.Type)} {field.Name};");
+ }
+ }
+ writer.WriteLine($"}} {name};");
+ writer.WriteLine();
+
+ break;
+ }
+ case NubTypeAnonymousStruct anonymousStructType:
+ {
+ foreach (var field in anonymousStructType.Fields)
+ EmitTypeDefinitionIfNotEmitted(field.Type);
+
+ writer.WriteLine("typedef struct");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ foreach (var field in anonymousStructType.Fields)
+ {
+ writer.WriteLine($"{TypeName(field.Type)} {field.Name};");
+ }
+ }
+ writer.WriteLine($"}} {name};");
+ writer.WriteLine();
+
+ break;
+ }
+ case NubTypeEnum enumType:
+ {
+ if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo)
+ throw new UnreachableException();
+
+ foreach (var variant in enumInfo.Variants)
+ {
+ if (variant.Type is not null)
+ {
+ EmitTypeDefinitionIfNotEmitted(variant.Type);
+ }
+ }
+
+ writer.WriteLine("typedef struct");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ writer.WriteLine("uint32_t tag;");
+ writer.WriteLine("union");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ foreach (var variant in enumInfo.Variants)
+ {
+ if (variant.Type is not null)
+ {
+ writer.WriteLine($"{TypeName(variant.Type)} {variant.Name};");
+ }
+ }
+ }
+ writer.WriteLine("};");
+ }
+ writer.WriteLine($"}} {name};");
+ writer.WriteLine();
+
+ break;
+ }
+ case NubTypeEnumVariant variantType:
+ {
+ EmitTypeDefinitionIfNotEmitted(variantType.EnumType);
+ break;
+ }
+ case NubTypePointer pointerType:
+ {
+ EmitTypeDefinitionIfNotEmitted(pointerType.To);
+ writer.WriteLine($"typedef {TypeName(pointerType.To)} *{name};");
+ break;
+ }
+ case NubTypeFunc funcType:
+ {
+ EmitTypeDefinitionIfNotEmitted(funcType.ReturnType);
+ foreach (var parameterType in funcType.Parameters)
+ EmitTypeDefinitionIfNotEmitted(parameterType);
+
+ writer.WriteLine($"typedef {TypeName(funcType.ReturnType)} (*)({string.Join(", ", funcType.Parameters.Select(TypeName))}) {name};");
+
+ break;
+ }
+ case NubTypeArray arrayType:
+ {
+ EmitTypeDefinitionIfNotEmitted(arrayType.ElementType);
+
+ var backingName = Tmp();
+
+ writer.WriteLine("typedef struct");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ writer.WriteLine("size_t count;");
+ writer.WriteLine("size_t capacity;");
+ writer.WriteLine($"{TypeName(arrayType.ElementType)} *items;");
+ writer.WriteLine("uint32_t ref;");
+ }
+ writer.WriteLine($"}} {backingName};");
+ writer.WriteLine();
+
+ writer.WriteLine($"typedef {backingName} *{name};");
+ writer.WriteLine();
+
+ writer.WriteLine($"static inline void {name}_rc_inc({name} array)");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ writer.WriteLine("if (array == NULL) return;");
+ writer.WriteLine("array->ref += 1;");
+ }
+ writer.WriteLine("}");
+ writer.WriteLine();
+
+ writer.WriteLine($"static inline void {name}_rc_dec({name} array)");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ writer.WriteLine("if (array == NULL) return;");
+ writer.WriteLine("if (array->ref == 0) return;");
+ writer.WriteLine();
+ writer.WriteLine("array->ref -= 1;");
+ writer.WriteLine();
+ writer.WriteLine("if (array->ref == 0)");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ writer.WriteLine("for (size_t i = 0; i < array->count; ++i)");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ EmitCopyDestructor("array->items[i]", arrayType.ElementType);
+ }
+ writer.WriteLine("}");
+ writer.WriteLine();
+ writer.WriteLine("free(array->items);");
+ writer.WriteLine("free(array);");
+ }
+ writer.WriteLine("}");
+ }
+ writer.WriteLine("}");
+ writer.WriteLine();
+
+ writer.WriteLine($"static inline {name} {name}_make()");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ writer.WriteLine($"{name} array = ({name})nub_alloc(sizeof({backingName}));");
+ writer.WriteLine("array->ref = 1;");
+ writer.WriteLine("array->count = 0;");
+ writer.WriteLine("array->capacity = 10;");
+ writer.WriteLine($"array->items = ({TypeName(arrayType.ElementType)}*)nub_alloc(sizeof({TypeName(arrayType.ElementType)}) * array->capacity);");
+ writer.WriteLine("return array;");
+ }
+ writer.WriteLine("}");
+ writer.WriteLine();
+
+ break;
+ }
+ }
+ }
+
+ private void EmitStatement(TypedNodeStatement node)
+ {
+ if (scopes.Peek().Unreachable)
+ return;
+
+ switch (node)
+ {
+ case TypedNodeStatementBlock statement:
+ EmitStatementBlock(statement);
+ break;
+ case TypedNodeStatementFuncCall statement:
+ EmitStatementFuncCall(statement);
+ break;
+ case TypedNodeStatementReturn statement:
+ EmitStatementReturn(statement);
+ break;
+ case TypedNodeStatementVariableDeclaration statement:
+ EmitStatementVariableDeclaration(statement);
+ break;
+ case TypedNodeStatementAssignment statement:
+ EmitStatementAssignment(statement);
+ break;
+ case TypedNodeStatementIf statement:
+ EmitStatementIf(statement);
+ break;
+ case TypedNodeStatementWhile statement:
+ EmitStatementWhile(statement);
+ break;
+ case TypedNodeStatementFor statement:
+ EmitStatementFor(statement);
+ break;
+ case TypedNodeStatementMatch statement:
+ EmitStatementMatch(statement);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(node), node, null);
+ }
+ }
+
+ private void EmitStatementBlock(TypedNodeStatementBlock node)
+ {
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ PushScope();
+ foreach (var statement in node.Statements)
+ EmitStatement(statement);
+ PopScope();
+ }
+ writer.WriteLine("}");
+ }
+
+ private void EmitStatementFuncCall(TypedNodeStatementFuncCall node)
+ {
+ var name = EmitExpression(node.Target);
+ var parameterValues = node.Parameters.Select(EmitExpression).ToList();
+ writer.WriteLine($"{name}({string.Join(", ", parameterValues)});");
+ }
+
+ private void EmitStatementReturn(TypedNodeStatementReturn statement)
+ {
+ if (statement.Value != null)
+ {
+ var value = EmitExpression(statement.Value);
+ EmitCleanupAllScopes();
+ writer.WriteLine($"return {value};");
+ }
+ else
+ {
+ EmitCleanupAllScopes();
+ writer.WriteLine($"return;");
+ }
+
+ scopes.Peek().Unreachable = true;
+ }
+
+ private void EmitStatementVariableDeclaration(TypedNodeStatementVariableDeclaration statement)
+ {
+ var value = EmitExpression(statement.Value);
+ EmitCopyConstructor(value, statement.Value.Type);
+ writer.WriteLine($"{TypeName(statement.Type)} {statement.Name.Ident} = {value};");
+ scopes.Peek().DeconstructableNames.Add((statement.Name.Ident, statement.Type));
+ }
+
+ private void EmitStatementAssignment(TypedNodeStatementAssignment statement)
+ {
+ var target = EmitExpression(statement.Target);
+ EmitCopyDestructor(target, statement.Target.Type);
+ var value = EmitExpression(statement.Value);
+ EmitCopyConstructor(value, statement.Value.Type);
+ writer.WriteLine($"{target} = {value};");
+ }
+
+ private void EmitStatementIf(TypedNodeStatementIf statement)
+ {
+ var condition = EmitExpression(statement.Condition);
+ writer.WriteLine($"if ({condition})");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ PushScope();
+ EmitStatement(statement.ThenBlock);
+ PopScope();
+ }
+ writer.WriteLine("}");
+
+ if (statement.ElseBlock != null)
+ {
+ writer.Write("else");
+ if (statement.ElseBlock is TypedNodeStatementIf)
+ writer.Write(" ");
+ else
+ writer.WriteLine();
+
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ PushScope();
+ EmitStatement(statement.ElseBlock);
+ PopScope();
+ }
+ writer.WriteLine("}");
+ }
+ }
+
+ private void EmitStatementWhile(TypedNodeStatementWhile statement)
+ {
+ var condition = EmitExpression(statement.Condition);
+ writer.WriteLine($"while ({condition})");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ PushScope();
+ EmitStatement(statement.Body);
+ PopScope();
+ }
+ writer.WriteLine("}");
+ }
+
+ private void EmitStatementFor(TypedNodeStatementFor statement)
+ {
+ var index = Tmp();
+ var array = EmitExpression(statement.Array);
+ writer.WriteLine($"for (size_t {index} = 0; {index} < {array}->count; ++{index})");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ var arrayType = (NubTypeArray)statement.Array.Type;
+ writer.WriteLine($"{TypeName(arrayType.ElementType)} {statement.VariableName.Ident} = {array}->items[{index}];");
+ EmitStatement(statement.Body);
+ }
+ writer.WriteLine("}");
+ }
+
+ private void EmitStatementMatch(TypedNodeStatementMatch statement)
+ {
+ var target = EmitExpression(statement.Target);
+ var enumType = (NubTypeEnum)statement.Target.Type;
+
+ if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info))
+ throw new UnreachableException();
+
+ var enumInfo = (Module.TypeInfoEnum)info;
+
+ writer.WriteLine($"switch ({target}.tag)");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ foreach (var @case in statement.Cases)
+ {
+ var variantInfo = enumInfo.Variants.First(x => x.Name == @case.Variant.Ident);
+ var tag = enumInfo.Variants.ToList().FindIndex(x => x.Name == @case.Variant.Ident);
+
+ writer.WriteLine($"case {tag}:");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ PushScope();
+ if (@case.VariableName != null)
+ {
+ Debug.Assert(variantInfo.Type is not null);
+ writer.WriteLine($"{TypeName(variantInfo.Type)} {@case.VariableName.Ident} = {target}.{@case.Variant.Ident};");
+ }
+
+ EmitStatement(@case.Body);
+ PopScope();
+ writer.WriteLine("break;");
+ }
+ writer.WriteLine("}");
+ }
+ }
+ writer.WriteLine("}");
+ }
+
+ private string EmitExpression(TypedNodeExpression node)
+ {
+ return node switch
+ {
+ TypedNodeExpressionBinary expression => EmitExpressionBinary(expression),
+ TypedNodeExpressionUnary expression => EmitExpressionUnary(expression),
+ TypedNodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false",
+ TypedNodeExpressionIntLiteral expression => expression.Value.Value.ToString(),
+ TypedNodeExpressionStringLiteral expression => EmitExpressionStringLiteral(expression),
+ TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
+ TypedNodeExpressionEnumLiteral expression => EmitExpressionEnumLiteral(expression),
+ TypedNodeExpressionArrayLiteral expression => EmitNodeExpressionArrayLiteral(expression),
+ TypedNodeExpressionStringConstructor expression => EmitExpressionStringConstructor(expression),
+ TypedNodeExpressionStructMemberAccess expression => EmitExpressionMemberAccess(expression),
+ TypedNodeExpressionStringLength expression => EmitExpressionStringLength(expression),
+ TypedNodeExpressionStringPointer expression => EmitExpressionStringPointer(expression),
+ TypedNodeExpressionArrayCount expression => EmitExpressionArrayCount(expression),
+ TypedNodeExpressionArrayPointer expression => EmitExpressionArrayPointer(expression),
+ TypedNodeExpressionLocalIdent expression => expression.Name,
+ TypedNodeExpressionGlobalIdent expression => EmitNodeExpressionGlobalIdent(expression),
+ TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression),
+ _ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
+ };
+ }
+
+ private string EmitExpressionBinary(TypedNodeExpressionBinary expression)
+ {
+ var left = EmitExpression(expression.Left);
+ var right = EmitExpression(expression.Right);
+
+ var name = Tmp();
+
+ if (expression.Operation == TypedNodeExpressionBinary.Op.Add && expression.Left.Type is NubTypeString && expression.Right.Type is NubTypeString)
+ {
+ scopes.Peek().DeconstructableNames.Add((name, expression.Type));
+ writer.WriteLine($"{TypeName(NubTypeString.Instance)} {name} = string_concat({left}, {right});");
+ return name;
+ }
+
+ var op = expression.Operation switch
+ {
+ TypedNodeExpressionBinary.Op.Add => $"({left} + {right})",
+ TypedNodeExpressionBinary.Op.Subtract => $"({left} - {right})",
+ TypedNodeExpressionBinary.Op.Multiply => $"({left} * {right})",
+ TypedNodeExpressionBinary.Op.Divide => $"({left} / {right})",
+ TypedNodeExpressionBinary.Op.Modulo => $"({left} % {right})",
+ TypedNodeExpressionBinary.Op.Equal => $"({left} == {right})",
+ TypedNodeExpressionBinary.Op.NotEqual => $"({left} != {right})",
+ TypedNodeExpressionBinary.Op.LessThan => $"({left} < {right})",
+ TypedNodeExpressionBinary.Op.LessThanOrEqual => $"({left} <= {right})",
+ TypedNodeExpressionBinary.Op.GreaterThan => $"({left} > {right})",
+ TypedNodeExpressionBinary.Op.GreaterThanOrEqual => $"({left} >= {right})",
+ TypedNodeExpressionBinary.Op.LeftShift => $"({left} << {right})",
+ TypedNodeExpressionBinary.Op.RightShift => $"({left} >> {right})",
+ TypedNodeExpressionBinary.Op.LogicalAnd => $"({left} && {right})",
+ TypedNodeExpressionBinary.Op.LogicalOr => $"({left} || {right})",
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ writer.WriteLine($"{TypeName(expression.Type)} {name} = {op};");
+
+ return name;
+ }
+
+ private string EmitExpressionUnary(TypedNodeExpressionUnary expression)
+ {
+ var target = EmitExpression(expression.Target);
+
+ var name = Tmp();
+
+ var op = expression.Operation switch
+ {
+ TypedNodeExpressionUnary.Op.Negate => $"(-{target})",
+ TypedNodeExpressionUnary.Op.Invert => $"(!{target})",
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ writer.WriteLine($"{TypeName(expression.Type)} {name} = {op};");
+
+ return name;
+ }
+
+ private string EmitExpressionStringLiteral(TypedNodeExpressionStringLiteral expression)
+ {
+ var name = Tmp();
+ referencedStringLiterals.Add(name, expression.Value.Value);
+ return $"(&{name})";
+ }
+
+ private string EmitExpressionStructLiteral(TypedNodeExpressionStructLiteral expression)
+ {
+ var name = Tmp();
+ scopes.Peek().DeconstructableNames.Add((name, expression.Type));
+
+ var initializerValues = new Dictionary();
+
+ foreach (var initializer in expression.Initializers)
+ {
+ var value = EmitExpression(initializer.Value);
+ EmitCopyConstructor(value, initializer.Value.Type);
+ initializerValues[initializer.Name.Ident] = value;
+ }
+
+ var initializerStrings = initializerValues.Select(x => $".{x.Key} = {x.Value}");
+
+ writer.WriteLine($"{TypeName(expression.Type)} {name} = ({TypeName(expression.Type)}){{ {string.Join(", ", initializerStrings)} }};");
+
+ return name;
+ }
+
+ private string EmitExpressionEnumLiteral(TypedNodeExpressionEnumLiteral expression)
+ {
+ var name = Tmp();
+ scopes.Peek().DeconstructableNames.Add((name, expression.Type));
+
+ var enumVariantType = (NubTypeEnumVariant)expression.Type;
+
+ if (!moduleGraph.TryResolveType(enumVariantType.EnumType.Module, enumVariantType.EnumType.Name, true, out var info))
+ throw new UnreachableException();
+
+ var enumInfo = (Module.TypeInfoEnum)info;
+ var tag = enumInfo.Variants.ToList().FindIndex(x => x.Name == enumVariantType.Variant);
+
+ string? value = null;
+ if (expression.Value != null)
+ {
+ value = EmitExpression(expression.Value);
+ EmitCopyConstructor(value, expression.Value.Type);
+ }
+
+ writer.Write($"{TypeName(expression.Type)} {name} = ({TypeName(expression.Type)}){{ .tag = {tag}");
+
+ if (value != null)
+ writer.WriteLine($", .{enumVariantType.Variant} = {value} }};");
+ else
+ writer.WriteLine(" };");
+
+ return name;
+ }
+
+ private string EmitNodeExpressionArrayLiteral(TypedNodeExpressionArrayLiteral expression)
+ {
+ var name = Tmp();
+ scopes.Peek().DeconstructableNames.Add((name, expression.Type));
+
+ writer.WriteLine($"{TypeName(expression.Type)} {name} = {TypeName(expression.Type)}_make();");
+
+ foreach (var value in expression.Values)
+ {
+ var valueName = EmitExpression(value);
+ writer.WriteLine($"da_append({name}, {valueName});");
+ }
+
+ return name;
+ }
+
+ private string EmitExpressionStringConstructor(TypedNodeExpressionStringConstructor expression)
+ {
+ var name = Tmp();
+ scopes.Peek().DeconstructableNames.Add((name, expression.Type));
+ var value = EmitExpression(expression.Value);
+ writer.WriteLine($"{TypeName(expression.Type)} {name} = string_from_cstr({value});");
+ return name;
+ }
+
+ private string EmitExpressionMemberAccess(TypedNodeExpressionStructMemberAccess expression)
+ {
+ var target = EmitExpression(expression.Target);
+ return $"{target}.{expression.Name.Ident}";
+ }
+
+ private string EmitExpressionStringLength(TypedNodeExpressionStringLength expression)
+ {
+ var target = EmitExpression(expression.Target);
+ return $"{target}->length";
+ }
+
+ private string EmitExpressionStringPointer(TypedNodeExpressionStringPointer expression)
+ {
+ var target = EmitExpression(expression.Target);
+ return $"{target}->data";
+ }
+
+ private string EmitExpressionArrayCount(TypedNodeExpressionArrayCount expression)
+ {
+ var target = EmitExpression(expression.Target);
+ return $"{target}->count";
+ }
+
+ private string EmitExpressionArrayPointer(TypedNodeExpressionArrayPointer expression)
+ {
+ var target = EmitExpression(expression.Target);
+ return $"{target}->items";
+ }
+
+ private string EmitNodeExpressionGlobalIdent(TypedNodeExpressionGlobalIdent expression)
+ {
+ if (!moduleGraph.TryResolveIdentifier(expression.Module, expression.Name, true, out var info))
+ throw new UnreachableException($"Module graph does not have info about identifier {expression.Module}::{expression.Name}. This should have been caught earlier");
+
+ return info.MangledName;
+ }
+
+ private string EmitExpressionFuncCall(TypedNodeExpressionFuncCall expression)
+ {
+ var name = EmitExpression(expression.Target);
+ var parameterValues = expression.Parameters.Select(EmitExpression).ToList();
+
+ var tmp = Tmp();
+ writer.WriteLine($"{TypeName(expression.Type)} {tmp} = {name}({string.Join(", ", parameterValues)});");
+ return tmp;
+ }
+
+ private readonly Dictionary typeNames = [];
+
+ private string TypeName(NubType type)
+ {
+ if (!typeNames.TryGetValue(type, out var name))
+ {
+ name = type switch
+ {
+ NubTypeVoid => "void",
+ NubTypeBool => "bool",
+ NubTypeStruct => Tmp(),
+ NubTypeAnonymousStruct => Tmp(),
+ NubTypeEnum => Tmp(),
+ NubTypeEnumVariant t => TypeName(t.EnumType),
+ NubTypeSInt t => $"int{t.Width}_t",
+ NubTypeUInt t => $"uint{t.Width}_t",
+ NubTypePointer => Tmp(),
+ NubTypeString => "string_ptr",
+ NubTypeChar => "char",
+ NubTypeFunc => Tmp(),
+ NubTypeArray => Tmp(),
+ _ => throw new NotImplementedException(),
+ };
+
+ typeNames[type] = name;
+ }
+
+ return name;
+ }
+
+ private string Tmp()
+ {
+ return $"_tmp{tmpNameIndex++}";
+ }
+
+ private void EmitCleanupAllScopes()
+ {
+ foreach (var scope in scopes.Reverse())
+ {
+ for (int i = scope.DeconstructableNames.Count - 1; i >= 0; i--)
+ {
+ var (name, type) = scope.DeconstructableNames[i];
+ EmitCopyDestructor(name, type);
+ }
+ }
+ }
+
+ private void EmitCleanupCurrentScope(Scope scope)
+ {
+ for (int i = scope.DeconstructableNames.Count - 1; i >= 0; i--)
+ {
+ var (name, type) = scope.DeconstructableNames[i];
+ EmitCopyDestructor(name, type);
+ }
+ }
+
+ private void EmitCopyConstructor(string value, NubType type)
+ {
+ switch (type)
+ {
+ case NubTypeArray arrayType:
+ {
+ writer.WriteLine($"{TypeName(type)}_rc_inc({value});");
+ break;
+ }
+ case NubTypeString:
+ {
+ writer.WriteLine($"string_rc_inc({value});");
+ break;
+ }
+ case NubTypeStruct structType:
+ {
+ if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo)
+ throw new UnreachableException();
+
+ foreach (var field in structInfo.Fields)
+ {
+ EmitCopyConstructor($"{value}.{field.Name}", field.Type);
+ }
+ break;
+ }
+ case NubTypeAnonymousStruct anonymousStructType:
+ {
+ foreach (var field in anonymousStructType.Fields)
+ {
+ EmitCopyConstructor($"{value}.{field.Name}", field.Type);
+ }
+ break;
+ }
+ case NubTypeEnum enumType:
+ {
+ if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo)
+ throw new UnreachableException();
+
+ writer.WriteLine($"switch ({value}.tag)");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ for (int i = 0; i < enumInfo.Variants.Count; i++)
+ {
+ Module.TypeInfoEnum.Variant variant = enumInfo.Variants[i];
+
+ if (variant.Type is not null)
+ {
+ writer.WriteLine($"case {i}:");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ EmitCopyConstructor($"{value}.{variant.Name}", variant.Type);
+ writer.WriteLine("break;");
+ }
+ writer.WriteLine("}");
+ }
+ }
+ }
+ writer.WriteLine("}");
+ break;
+ }
+ case NubTypeEnumVariant enumVariantType:
+ {
+ if (!moduleGraph.TryResolveType(enumVariantType.EnumType.Module, enumVariantType.EnumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo)
+ throw new UnreachableException();
+
+ var variant = enumInfo.Variants.First(x => x.Name == enumVariantType.Variant);
+ if (variant.Type is not null)
+ EmitCopyConstructor($"{value}.{variant.Name}", variant.Type);
+
+ break;
+ }
+ }
+ }
+
+ private void EmitCopyDestructor(string value, NubType type)
+ {
+ switch (type)
+ {
+ case NubTypeArray arrayType:
+ {
+ writer.WriteLine($"{TypeName(type)}_rc_dec({value});");
+ break;
+ }
+ case NubTypeString:
+ {
+ writer.WriteLine($"string_rc_dec({value});");
+ break;
+ }
+ case NubTypeStruct structType:
+ {
+ if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo)
+ throw new UnreachableException();
+
+ foreach (var field in structInfo.Fields)
+ {
+ EmitCopyDestructor($"{value}.{field.Name}", field.Type);
+ }
+ break;
+ }
+ case NubTypeAnonymousStruct anonymousStructType:
+ {
+ foreach (var field in anonymousStructType.Fields)
+ {
+ EmitCopyDestructor($"{value}.{field.Name}", field.Type);
+ }
+ break;
+ }
+ case NubTypeEnum enumType:
+ {
+ if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo)
+ throw new UnreachableException();
+
+ writer.WriteLine($"switch ({value}.tag)");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ for (int i = 0; i < enumInfo.Variants.Count; i++)
+ {
+ var variant = enumInfo.Variants[i];
+ if (variant.Type is not null)
+ {
+ writer.WriteLine($"case {i}:");
+ writer.WriteLine("{");
+ using (writer.Indent())
+ {
+ EmitCopyDestructor($"{value}.{variant.Name}", variant.Type);
+ writer.WriteLine("break;");
+ }
+ writer.WriteLine("}");
+ }
+ }
+ }
+ writer.WriteLine("}");
+ break;
+ }
+ case NubTypeEnumVariant enumVariantType:
+ {
+ if (!moduleGraph.TryResolveType(enumVariantType.EnumType.Module, enumVariantType.EnumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo)
+ throw new UnreachableException();
+
+ var variant = enumInfo.Variants.First(x => x.Name == enumVariantType.Variant);
+ if (variant.Type is not null)
+ EmitCopyDestructor($"{value}.{variant.Name}", variant.Type);
+
+ break;
+ }
+ }
+ }
+
+ private void PushScope()
+ {
+ scopes.Push(new Scope());
+ }
+
+ private void PopScope()
+ {
+ var scope = scopes.Pop();
+ if (!scope.Unreachable)
+ EmitCleanupCurrentScope(scope);
+ }
+
+ private class Scope
+ {
+ public List<(string Name, NubType Type)> DeconstructableNames { get; } = [];
+ public bool Unreachable { get; set; }
+ }
+}
+
+internal class IndentedTextWriter
+{
+ private readonly StringBuilder builder = new();
+ private int indentLevel;
+
+ public IDisposable Indent()
+ {
+ indentLevel++;
+ return new IndentScope(this);
+ }
+
+ public void WriteLine(string text)
+ {
+ WriteIndent();
+ builder.AppendLine(text);
+ }
+
+ public void Write(string text)
+ {
+ WriteIndent();
+ builder.Append(text);
+ }
+
+ public void WriteLine()
+ {
+ builder.AppendLine();
+ }
+
+ public override string ToString()
+ {
+ return builder.ToString();
+ }
+
+ private void WriteIndent()
+ {
+ if (builder.Length > 0)
+ {
+ var lastChar = builder[^1];
+ if (lastChar != '\n' && lastChar != '\r')
+ return;
+ }
+
+ for (var i = 0; i < indentLevel; i++)
+ {
+ builder.Append(" ");
+ }
+ }
+
+ private class IndentScope(IndentedTextWriter writer) : IDisposable
+ {
+ private bool disposed;
+
+ public void Dispose()
+ {
+ if (disposed) return;
+ writer.indentLevel--;
+ disposed = true;
+ }
+ }
+}
diff --git a/compiler/ModuleGraph.cs b/compiler/ModuleGraph.cs
new file mode 100644
index 0000000..d09013d
--- /dev/null
+++ b/compiler/ModuleGraph.cs
@@ -0,0 +1,446 @@
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Compiler;
+
+public class ModuleGraph
+{
+ public static Builder CreateBuilder() => new();
+
+ private ModuleGraph(Dictionary modules)
+ {
+ this.modules = modules;
+ }
+
+ private readonly Dictionary modules;
+
+ public List GetModules()
+ {
+ return modules.Values.ToList();
+ }
+
+ public bool TryResolveIdentifier(string moduleName, string identifierName, bool searchPrivate, [NotNullWhen(true)] out Module.IdentifierInfo? info)
+ {
+ if (!TryResolveModule(moduleName, out var module))
+ {
+ info = null;
+ return false;
+ }
+
+ if (!module.TryResolveIdentifier(identifierName, searchPrivate, out info))
+ return false;
+
+ return true;
+ }
+
+ public bool TryResolveType(string moduleName, string typeName, bool searchPrivate, [NotNullWhen(true)] out Module.TypeInfo? info)
+ {
+ if (!TryResolveModule(moduleName, out var module))
+ {
+ info = null;
+ return false;
+ }
+
+ if (!module.TryResolveType(typeName, searchPrivate, out info))
+ return false;
+
+ return true;
+ }
+
+ public bool TryResolveModule(string moduleName, [NotNullWhen(true)] out Module? module)
+ {
+ module = modules.GetValueOrDefault(moduleName);
+ return module != null;
+ }
+
+ public class Builder
+ {
+ private readonly List asts = [];
+ private readonly List manifests = [];
+
+ public void AddAst(Ast ast)
+ {
+ asts.Add(ast);
+ }
+
+ public void AddManifest(Manifest manifest)
+ {
+ manifests.Add(manifest);
+ }
+
+ public ModuleGraph? Build(out List diagnostics)
+ {
+ diagnostics = [];
+
+ var modules = new Dictionary();
+
+ foreach (var manifest in manifests)
+ {
+ foreach (var (moduleName, manifestModule) in manifest.Modules)
+ {
+ var module = GetOrCreateModule(moduleName);
+
+ foreach (var (name, type) in manifestModule.Types)
+ {
+ switch (type)
+ {
+ case Manifest.Module.TypeInfoStruct s:
+ {
+ var info = new Module.TypeInfoStruct(Module.DefinitionSource.Imported, s.Exported, s.Packed);
+ var fields = s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList();
+ info.SetFields(fields);
+ module.AddType(name, info);
+ break;
+ }
+ case Manifest.Module.TypeInfoEnum e:
+ {
+ var info = new Module.TypeInfoEnum(Module.DefinitionSource.Imported, e.Exported);
+ var variants = e.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList();
+ info.SetVariants(variants);
+ module.AddType(name, info);
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(type));
+ }
+ }
+
+ foreach (var (name, identifier) in manifestModule.Identifiers)
+ {
+ module.AddIdentifier(name, new Module.IdentifierInfo(Module.DefinitionSource.Imported, identifier.Exported, identifier.Extern, identifier.Type, identifier.MangledName));
+ }
+ }
+ }
+
+ foreach (var ast in asts)
+ {
+ var module = GetOrCreateModule(ast.ModuleName.Ident);
+
+ foreach (var structDef in ast.Definitions.OfType())
+ {
+ module.AddType(structDef.Name.Ident, new Module.TypeInfoStruct(Module.DefinitionSource.Internal, structDef.Exported, structDef.Packed));
+ }
+
+ foreach (var enumDef in ast.Definitions.OfType())
+ {
+ module.AddType(enumDef.Name.Ident, new Module.TypeInfoEnum(Module.DefinitionSource.Internal, enumDef.Exported));
+ }
+ }
+
+ foreach (var ast in asts)
+ {
+ var module = GetOrCreateModule(ast.ModuleName.Ident);
+
+ foreach (var structDef in ast.Definitions.OfType())
+ {
+ if (!module.TryResolveType(structDef.Name.Ident, true, out var typeInfo))
+ throw new UnreachableException($"{nameof(typeInfo)} should always be registered");
+
+ if (typeInfo is Module.TypeInfoStruct structType)
+ {
+ var fields = structDef.Fields.Select(f => new Module.TypeInfoStruct.Field(f.Name.Ident, ResolveType(f.Type, module.Name))).ToList();
+ structType.SetFields(fields);
+ }
+ }
+
+ foreach (var enumDef in ast.Definitions.OfType())
+ {
+ if (!module.TryResolveType(enumDef.Name.Ident, true, out var typeInfo))
+ throw new UnreachableException($"{nameof(typeInfo)} should always be registered");
+
+ if (typeInfo is Module.TypeInfoEnum enumType)
+ {
+ var variants = enumDef.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name.Ident, v.Type == null ? null : ResolveType(v.Type, module.Name))).ToList();
+ enumType.SetVariants(variants);
+ }
+ }
+ }
+
+ foreach (var ast in asts)
+ {
+ var module = GetOrCreateModule(ast.ModuleName.Ident);
+
+ foreach (var funcDef in ast.Definitions.OfType())
+ {
+ var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList();
+ var returnType = funcDef.ReturnType == null ? NubTypeVoid.Instance : ResolveType(funcDef.ReturnType, module.Name);
+ var funcType = NubTypeFunc.Get(parameters, returnType);
+ var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, funcDef.Exported, false, funcType, NameMangler.Mangle(module.Name, funcDef.Name.Ident, funcType));
+ module.AddIdentifier(funcDef.Name.Ident, info);
+ }
+
+ foreach (var funcDef in ast.Definitions.OfType())
+ {
+ var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList();
+ var returnType = funcDef.ReturnType == null ? NubTypeVoid.Instance : ResolveType(funcDef.ReturnType, module.Name);
+ var funcType = NubTypeFunc.Get(parameters, returnType);
+ var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, funcDef.Exported, true, funcType, funcDef.Name.Ident);
+ module.AddIdentifier(funcDef.Name.Ident, info);
+ }
+
+ foreach (var globalVariable in ast.Definitions.OfType())
+ {
+ var type = ResolveType(globalVariable.Type, module.Name);
+ var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, globalVariable.Exported, false, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type));
+ module.AddIdentifier(globalVariable.Name.Ident, info);
+ }
+
+ foreach (var globalVariable in ast.Definitions.OfType())
+ {
+ var type = ResolveType(globalVariable.Type, module.Name);
+ var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, globalVariable.Exported, true, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type));
+ module.AddIdentifier(globalVariable.Name.Ident, info);
+ }
+ }
+
+ if (diagnostics.Any(x => x.Severity == Diagnostic.DiagnosticSeverity.Error))
+ return null;
+
+ return new ModuleGraph(modules);
+
+ NubType ResolveType(NodeType node, string currentModule)
+ {
+ return node switch
+ {
+ NodeTypeBool => NubTypeBool.Instance,
+ NodeTypeNamed type => ResolveNamedType(type, currentModule),
+ NodeTypeAnonymousStruct type => NubTypeAnonymousStruct.Get(type.Fields.Select(x => new NubTypeAnonymousStruct.Field(x.Name.Ident, ResolveType(x.Type, currentModule))).ToList()),
+ NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(x => ResolveType(x, currentModule)).ToList(), ResolveType(type.ReturnType, currentModule)),
+ NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To, currentModule)),
+ NodeTypeSInt type => NubTypeSInt.Get(type.Width),
+ NodeTypeUInt type => NubTypeUInt.Get(type.Width),
+ NodeTypeString => NubTypeString.Instance,
+ NodeTypeChar => NubTypeChar.Instance,
+ NodeTypeVoid => NubTypeVoid.Instance,
+ NodeTypeArray type => NubTypeArray.Get(ResolveType(type.ElementType, currentModule)),
+ _ => throw new ArgumentOutOfRangeException(nameof(node))
+ };
+ }
+
+ NubType ResolveNamedType(NodeTypeNamed type, string currentModule)
+ {
+ return type.Sections.Count switch
+ {
+ 3 => ResolveThreePartType(type.Sections[0], type.Sections[1], type.Sections[2], currentModule),
+ 2 => ResolveTwoPartType(type.Sections[0], type.Sections[1], currentModule),
+ 1 => ResolveOnePartType(type.Sections[0], currentModule),
+ _ => throw BasicError("Invalid type name")
+ };
+ }
+
+ NubType ResolveThreePartType(TokenIdent first, TokenIdent second, TokenIdent third, string currentModule)
+ {
+ var module = ResolveModule(first);
+ if (!module.TryResolveType(second.Ident, currentModule == module.Name, out var typeInfo))
+ throw BasicError($"Named type '{module.Name}::{second.Ident}' not found");
+
+ if (typeInfo is not Module.TypeInfoEnum enumInfo)
+ throw BasicError($"'{module.Name}::{second.Ident}' is not an enum");
+
+ var variant = enumInfo.Variants.FirstOrDefault(v => v.Name == third.Ident);
+ if (variant == null)
+ throw BasicError($"Enum '{module.Name}::{second.Ident}' does not have a variant named '{third.Ident}'");
+
+ return NubTypeEnumVariant.Get(NubTypeEnum.Get(module.Name, second.Ident), third.Ident);
+ }
+
+ NubType ResolveTwoPartType(TokenIdent first, TokenIdent second, string currentModule)
+ {
+ if (TryResolveEnumVariant(currentModule, first.Ident, second.Ident, out var variant))
+ return variant;
+
+ var module = ResolveModule(first);
+ if (!module.TryResolveType(second.Ident, currentModule == module.Name, out var typeInfo))
+ throw BasicError($"Named type '{module.Name}::{second.Ident}' not found");
+
+ return typeInfo switch
+ {
+ Module.TypeInfoStruct => NubTypeStruct.Get(module.Name, second.Ident),
+ Module.TypeInfoEnum => NubTypeEnum.Get(module.Name, second.Ident),
+ _ => throw new ArgumentOutOfRangeException(nameof(typeInfo))
+ };
+ }
+
+ NubType ResolveOnePartType(TokenIdent name, string currentModule)
+ {
+ if (!modules.TryGetValue(currentModule, out var module))
+ throw BasicError($"Module '{currentModule}' not found");
+
+ if (!module.TryResolveType(name.Ident, true, out var typeInfo))
+ throw BasicError($"Named type '{module.Name}::{name.Ident}' not found");
+
+ return typeInfo switch
+ {
+ Module.TypeInfoStruct => NubTypeStruct.Get(module.Name, name.Ident),
+ Module.TypeInfoEnum => NubTypeEnum.Get(module.Name, name.Ident),
+ _ => throw new ArgumentOutOfRangeException(nameof(typeInfo))
+ };
+ }
+
+ Module ResolveModule(TokenIdent name)
+ {
+ if (!modules.TryGetValue(name.Ident, out var module))
+ throw BasicError($"Module '{name.Ident}' not found");
+
+ return module;
+ }
+
+ bool TryResolveEnumVariant(string moduleName, string enumName, string variantName, [NotNullWhen(true)] out NubType? result)
+ {
+ result = null;
+
+ if (!modules.TryGetValue(moduleName, out var module))
+ return false;
+
+ if (!module.TryResolveType(enumName, true, out var typeInfo))
+ return false;
+
+ if (typeInfo is not Module.TypeInfoEnum enumInfo)
+ return false;
+
+ var variant = enumInfo.Variants.FirstOrDefault(v => v.Name == variantName);
+ if (variant == null)
+ return false;
+
+ result = NubTypeEnumVariant.Get(
+ NubTypeEnum.Get(moduleName, enumName),
+ variantName);
+
+ return true;
+ }
+
+ Exception BasicError(string message)
+ {
+ return new CompileException(Diagnostic.Error(message).Build());
+ }
+
+ Module GetOrCreateModule(string name)
+ {
+ if (!modules.TryGetValue(name, out var module))
+ {
+ module = new Module(name);
+ modules.Add(name, module);
+ }
+
+ return module;
+ }
+ }
+ }
+}
+
+public class Module(string name)
+{
+ public string Name { get; } = name;
+
+ private Dictionary types = new();
+ private Dictionary identifiers = new();
+
+ public IReadOnlyDictionary GetTypes() => types;
+ public IReadOnlyDictionary GetIdentifiers() => identifiers;
+
+ public bool TryResolveType(string name, bool searchPrivate, [NotNullWhen(true)] out TypeInfo? customType)
+ {
+ var info = types.GetValueOrDefault(name);
+ if (info == null)
+ {
+ customType = null;
+ return false;
+ }
+
+ if (searchPrivate || info.Source == DefinitionSource.Internal)
+ {
+ customType = info;
+ return true;
+ }
+
+ customType = null;
+ return false;
+ }
+
+ public bool TryResolveIdentifier(string name, bool searchPrivate, [NotNullWhen(true)] out IdentifierInfo? identifierType)
+ {
+ var info = identifiers.GetValueOrDefault(name);
+ if (info == null)
+ {
+ identifierType = null;
+ return false;
+ }
+
+ if (searchPrivate || info.Source == DefinitionSource.Internal)
+ {
+ identifierType = info;
+ return true;
+ }
+
+ identifierType = null;
+ return false;
+ }
+
+ public void AddType(string name, TypeInfo info)
+ {
+ types.Add(name, info);
+ }
+
+ public void AddIdentifier(string name, IdentifierInfo info)
+ {
+ identifiers.Add(name, info);
+ }
+
+ public enum DefinitionSource
+ {
+ Internal,
+ Imported,
+ }
+
+ public class IdentifierInfo(DefinitionSource source, bool exported, bool @extern, NubType type, string mangledName)
+ {
+ public DefinitionSource Source { get; } = source;
+ public bool Exported { get; } = exported;
+ public bool Extern { get; } = @extern;
+ public NubType Type { get; } = type;
+ public string MangledName { get; } = mangledName;
+ }
+
+ public abstract class TypeInfo(DefinitionSource source, bool exported)
+ {
+ public DefinitionSource Source { get; } = source;
+ public bool Exported { get; } = exported;
+ }
+
+ public class TypeInfoStruct(DefinitionSource source, bool exported, bool packed) : TypeInfo(source, exported)
+ {
+ private IReadOnlyList? fields;
+
+ public bool Packed { get; } = packed;
+ public IReadOnlyList Fields => fields ?? throw new InvalidOperationException("Fields has not been set yet");
+
+ public void SetFields(IReadOnlyList fields)
+ {
+ this.fields = fields;
+ }
+
+ public class Field(string name, NubType type)
+ {
+ public string Name { get; } = name;
+ public NubType Type { get; } = type;
+ }
+ }
+
+ public class TypeInfoEnum(DefinitionSource source, bool exported) : TypeInfo(source, exported)
+ {
+ private IReadOnlyList? variants;
+
+ public IReadOnlyList Variants => variants ?? throw new InvalidOperationException("Fields has not been set yet");
+
+ public void SetVariants(IReadOnlyList variants)
+ {
+ this.variants = variants;
+ }
+
+ public class Variant(string name, NubType? type)
+ {
+ public string Name { get; } = name;
+ public NubType? Type { get; } = type;
+ }
+ }
+}
diff --git a/compiler/NubLang.CLI/NubLang.CLI.csproj b/compiler/NubLang.CLI/NubLang.CLI.csproj
deleted file mode 100644
index 0550d0f..0000000
--- a/compiler/NubLang.CLI/NubLang.CLI.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- nubc
- Exe
- net9.0
- enable
- enable
- true
-
-
-
-
-
-
-
diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs
deleted file mode 100644
index fb51798..0000000
--- a/compiler/NubLang.CLI/Program.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using System.Diagnostics;
-using NubLang.Ast;
-using NubLang.Diagnostics;
-using NubLang.Generation;
-using NubLang.Syntax;
-
-var diagnostics = new List();
-var syntaxTrees = new List();
-
-foreach (var file in args)
-{
- var tokenizer = new Tokenizer(file, File.ReadAllText(file));
- tokenizer.Tokenize();
- diagnostics.AddRange(tokenizer.Diagnostics);
-
- var parser = new Parser();
- var syntaxTree = parser.Parse(tokenizer.Tokens);
- diagnostics.AddRange(parser.Diagnostics);
-
- syntaxTrees.Add(syntaxTree);
-}
-
-var modules = Module.Collect(syntaxTrees);
-var compilationUnits = new List();
-
-for (var i = 0; i < args.Length; i++)
-{
- var typeChecker = new TypeChecker(syntaxTrees[i], modules);
- var compilationUnit = typeChecker.Check();
-
- compilationUnits.Add(compilationUnit);
- diagnostics.AddRange(typeChecker.Diagnostics);
-}
-
-foreach (var diagnostic in diagnostics)
-{
- Console.Error.WriteLine(diagnostic.FormatANSI());
-}
-
-if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error))
-{
- return 1;
-}
-
-var cPaths = new List();
-
-Directory.CreateDirectory(".build");
-
-for (var i = 0; i < args.Length; i++)
-{
- var file = args[i];
- var compilationUnit = compilationUnits[i];
-
- var generator = new Generator(compilationUnit);
- var directory = Path.GetDirectoryName(file);
- if (!string.IsNullOrWhiteSpace(directory))
- {
- Directory.CreateDirectory(Path.Combine(".build", directory));
- }
-
- var path = Path.Combine(".build", Path.ChangeExtension(file, "c"));
- File.WriteAllText(path, generator.Emit());
- cPaths.Add(path);
-}
-
-var objectPaths = new List();
-
-foreach (var cPath in cPaths)
-{
- var objectPath = Path.ChangeExtension(cPath, "o");
- using var compileProcess = Process.Start("clang", [
- "-ffreestanding", "-std=c23",
- "-g", "-c",
- "-o", objectPath,
- cPath,
- ]);
-
- compileProcess.WaitForExit();
-
- if (compileProcess.ExitCode != 0)
- {
- Console.Error.WriteLine($"clang failed with exit code {compileProcess.ExitCode}");
- return 1;
- }
-
- objectPaths.Add(objectPath);
-}
-
-Console.Out.WriteLine(string.Join(' ', objectPaths));
-
-return 0;
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/AstExtensions.cs b/compiler/NubLang.LSP/AstExtensions.cs
deleted file mode 100644
index e0b2477..0000000
--- a/compiler/NubLang.LSP/AstExtensions.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using NubLang.Ast;
-using NubLang.Syntax;
-using OmniSharp.Extensions.LanguageServer.Protocol.Models;
-using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
-
-namespace NubLang.LSP;
-
-public static class AstExtensions
-{
- public static Location ToLocation(this Node node)
- {
- if (node.Tokens.Count == 0)
- {
- return new Location();
- }
-
- return new Location
- {
- Uri = node.Tokens.First().Span.FilePath,
- Range = new Range(node.Tokens.First().Span.Start.Line - 1, node.Tokens.First().Span.Start.Column - 1, node.Tokens.Last().Span.End.Line - 1, node.Tokens.Last().Span.End.Column - 1)
- };
- }
-
- public static bool ContainsPosition(this Node node, int line, int character)
- {
- if (node.Tokens.Count == 0)
- {
- return false;
- }
-
- var start = node.Tokens.First().Span.Start;
- var end = node.Tokens.Last().Span.End;
-
- var startLine = start.Line - 1;
- var startChar = start.Column - 1;
- var endLine = end.Line - 1;
- var endChar = end.Column - 1;
-
- if (line < startLine || line > endLine) return false;
-
- if (line > startLine && line < endLine) return true;
-
- if (startLine == endLine)
- {
- return character >= startChar && character <= endChar;
- }
-
- if (line == startLine)
- {
- return character >= startChar;
- }
-
- if (line == endLine)
- {
- return character <= endChar;
- }
-
- return false;
- }
-
- public static FuncNode? FunctionAtPosition(this CompilationUnit compilationUnit, int line, int character)
- {
- return compilationUnit
- .Functions
- .FirstOrDefault(x => x.ContainsPosition(line, character));
- }
-
- public static Node? DeepestNodeAtPosition(this CompilationUnit compilationUnit, int line, int character)
- {
- return compilationUnit.Functions
- .SelectMany(x => x.DescendantsAndSelf())
- .Where(n => n.ContainsPosition(line, character))
- .OrderBy(n => n.Tokens.First().Span.Start.Line)
- .ThenBy(n => n.Tokens.First().Span.Start.Column)
- .LastOrDefault();
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/CompletionHandler.cs b/compiler/NubLang.LSP/CompletionHandler.cs
deleted file mode 100644
index 8155328..0000000
--- a/compiler/NubLang.LSP/CompletionHandler.cs
+++ /dev/null
@@ -1,180 +0,0 @@
-using NubLang.Ast;
-using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
-using OmniSharp.Extensions.LanguageServer.Protocol.Document;
-using OmniSharp.Extensions.LanguageServer.Protocol.Models;
-
-namespace NubLang.LSP;
-
-internal class CompletionHandler(WorkspaceManager workspaceManager) : CompletionHandlerBase
-{
- private readonly CompletionItem[] _definitionSnippets =
- [
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "func",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "func ${1:name}(${2:params})\n{\n $0\n}",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "struct",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "struct ${1:name}\n{\n $0\n}",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "module",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "module \"$0\"",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "import",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "import \"$0\"",
- }
- ];
-
- private readonly CompletionItem[] _statementSnippets =
- [
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "let",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "let ${1:name} = $0",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "if",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "if ${1:condition}\n{\n $0\n}",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "else if",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "else if ${1:condition}\n{\n $0\n}",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "else",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "else\n{\n $0\n}",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "while",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "while ${1:condition}\n{\n $0\n}",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "for",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "for ${1:name}, ${2:index} in ${3:array}\n{\n $0\n}",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "return",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "return $0",
- },
- new()
- {
- Kind = CompletionItemKind.Keyword,
- Label = "defer",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = "defer $0",
- }
- ];
-
- protected override CompletionRegistrationOptions CreateRegistrationOptions(CompletionCapability capability, ClientCapabilities clientCapabilities)
- {
- return new CompletionRegistrationOptions();
- }
-
- public override Task Handle(CompletionParams request, CancellationToken cancellationToken)
- {
- return Task.FromResult(HandleSync(request, cancellationToken));
- }
-
- private CompletionList HandleSync(CompletionParams request, CancellationToken cancellationToken)
- {
- var completions = new List();
- var position = request.Position;
-
- var uri = request.TextDocument.Uri;
- var compilationUnit = workspaceManager.GetCompilationUnit(uri);
- if (compilationUnit != null)
- {
- var function = compilationUnit.Functions.FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(position.Line, position.Character));
- if (function != null)
- {
- completions.AddRange(_statementSnippets);
-
- foreach (var prototype in compilationUnit.ImportedFunctions)
- {
- var parameterStrings = new List();
- foreach (var (index, parameter) in prototype.Parameters.Index())
- {
- parameterStrings.AddRange($"${{{index + 1}:{parameter.Name}}}");
- }
-
- completions.Add(new CompletionItem
- {
- Kind = CompletionItemKind.Function,
- Label = $"{prototype.Module}::{prototype.Name}",
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = $"{prototype.Module}::{prototype.Name}({string.Join(", ", parameterStrings)})",
- });
- }
-
- foreach (var parameter in function.Prototype.Parameters)
- {
- completions.Add(new CompletionItem
- {
- Kind = CompletionItemKind.Variable,
- Label = parameter.Name,
- InsertText = parameter.Name,
- });
- }
-
- var variables = function.Body!
- .Descendants()
- .OfType();
-
- foreach (var variable in variables)
- {
- completions.Add(new CompletionItem
- {
- Kind = CompletionItemKind.Variable,
- Label = variable.Name,
- InsertText = variable.Name,
- });
- }
- }
- else
- {
- completions.AddRange(_definitionSnippets);
- }
- }
-
- return new CompletionList(completions, false);
- }
-
- public override Task Handle(CompletionItem request, CancellationToken cancellationToken)
- {
- return Task.FromResult(new CompletionItem());
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/DefinitionHandler.cs b/compiler/NubLang.LSP/DefinitionHandler.cs
deleted file mode 100644
index d354e9d..0000000
--- a/compiler/NubLang.LSP/DefinitionHandler.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using NubLang.Ast;
-using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
-using OmniSharp.Extensions.LanguageServer.Protocol.Document;
-using OmniSharp.Extensions.LanguageServer.Protocol.Models;
-
-namespace NubLang.LSP;
-
-internal class DefinitionHandler(WorkspaceManager workspaceManager) : DefinitionHandlerBase
-{
- protected override DefinitionRegistrationOptions CreateRegistrationOptions(DefinitionCapability capability, ClientCapabilities clientCapabilities)
- {
- return new DefinitionRegistrationOptions();
- }
-
- public override Task Handle(DefinitionParams request, CancellationToken cancellationToken)
- {
- return Task.FromResult(HandleSync(request, cancellationToken));
- }
-
- private LocationOrLocationLinks? HandleSync(DefinitionParams request, CancellationToken cancellationToken)
- {
- var uri = request.TextDocument.Uri;
- var compilationUnit = workspaceManager.GetCompilationUnit(uri);
- if (compilationUnit == null)
- {
- return null;
- }
-
- var line = request.Position.Line;
- var character = request.Position.Character;
-
- var node = compilationUnit.DeepestNodeAtPosition(line, character);
-
- switch (node)
- {
- case VariableIdentifierNode variableIdentifierNode:
- {
- var function = compilationUnit.FunctionAtPosition(line, character);
-
- var parameter = function?.Prototype.Parameters.FirstOrDefault(x => x.Name == variableIdentifierNode.Name);
- if (parameter != null)
- {
- return new LocationOrLocationLinks(parameter.ToLocation());
- }
-
- var variable = function?.Body?
- .Descendants()
- .OfType()
- .FirstOrDefault(x => x.Name == variableIdentifierNode.Name);
-
- if (variable != null)
- {
- return new LocationOrLocationLinks(variable.ToLocation());
- }
-
- return null;
- }
- case FuncIdentifierNode funcIdentifierNode:
- {
- var prototype = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name);
- if (prototype != null)
- {
- return new LocationOrLocationLinks(prototype.ToLocation());
- }
-
- return null;
- }
- default:
- {
- return null;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/DiagnosticsPublisher.cs b/compiler/NubLang.LSP/DiagnosticsPublisher.cs
deleted file mode 100644
index 7364396..0000000
--- a/compiler/NubLang.LSP/DiagnosticsPublisher.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using OmniSharp.Extensions.LanguageServer.Protocol;
-using OmniSharp.Extensions.LanguageServer.Protocol.Document;
-using OmniSharp.Extensions.LanguageServer.Protocol.Models;
-using OmniSharp.Extensions.LanguageServer.Protocol.Server;
-using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
-
-namespace NubLang.LSP;
-
-public class DiagnosticsPublisher
-{
- private readonly ILanguageServerFacade _server;
-
- public DiagnosticsPublisher(ILanguageServerFacade server)
- {
- _server = server;
- }
-
- public void Publish(DocumentUri uri, IEnumerable diagnostics)
- {
- _server.TextDocument.PublishDiagnostics(new PublishDiagnosticsParams
- {
- Uri = uri,
- Diagnostics = new Container(diagnostics.Select(MapDiagnostic))
- });
- }
-
- private static Diagnostic MapDiagnostic(Diagnostics.Diagnostic nubDiagnostic)
- {
- return new Diagnostic
- {
- Severity = nubDiagnostic.Severity switch
- {
- Diagnostics.DiagnosticSeverity.Info => DiagnosticSeverity.Information,
- Diagnostics.DiagnosticSeverity.Warning => DiagnosticSeverity.Warning,
- Diagnostics.DiagnosticSeverity.Error => DiagnosticSeverity.Error,
- _ => null
- },
- Message = $"{nubDiagnostic.Message}\n{(nubDiagnostic.Help == null ? "" : $"help: {nubDiagnostic.Help}")}",
- Range = nubDiagnostic.Span.HasValue
- ? new Range(nubDiagnostic.Span.Value.Start.Line - 1, nubDiagnostic.Span.Value.Start.Column - 1, nubDiagnostic.Span.Value.End.Line - 1, nubDiagnostic.Span.Value.End.Column - 1)
- : new Range(),
- };
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/HoverHandler.cs b/compiler/NubLang.LSP/HoverHandler.cs
deleted file mode 100644
index f90e6c0..0000000
--- a/compiler/NubLang.LSP/HoverHandler.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-using System.Globalization;
-using NubLang.Ast;
-using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
-using OmniSharp.Extensions.LanguageServer.Protocol.Document;
-using OmniSharp.Extensions.LanguageServer.Protocol.Models;
-
-namespace NubLang.LSP;
-
-internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBase
-{
- protected override HoverRegistrationOptions CreateRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities)
- {
- return new HoverRegistrationOptions
- {
- DocumentSelector = TextDocumentSelector.ForLanguage("nub")
- };
- }
-
- public override Task Handle(HoverParams request, CancellationToken cancellationToken)
- {
- return Task.FromResult(HandleSync(request, cancellationToken));
- }
-
- private Hover? HandleSync(HoverParams request, CancellationToken cancellationToken)
- {
- var compilationUnit = workspaceManager.GetCompilationUnit(request.TextDocument.Uri);
- if (compilationUnit == null)
- {
- return null;
- }
-
- var line = request.Position.Line;
- var character = request.Position.Character;
-
- var hoveredNode = compilationUnit.DeepestNodeAtPosition(line, character);
-
- if (hoveredNode == null)
- {
- return null;
- }
-
- var message = CreateMessage(hoveredNode, compilationUnit);
- if (message == null)
- {
- return null;
- }
-
- return new Hover
- {
- Contents = new MarkedStringsOrMarkupContent(new MarkupContent
- {
- Value = message,
- Kind = MarkupKind.Markdown,
- })
- };
- }
-
- private static string? CreateMessage(Node hoveredNode, CompilationUnit compilationUnit)
- {
- return hoveredNode switch
- {
- FuncNode funcNode => CreateFuncPrototypeMessage(funcNode.Prototype),
- FuncPrototypeNode funcPrototypeNode => CreateFuncPrototypeMessage(funcPrototypeNode),
- FuncIdentifierNode funcIdentifierNode => CreateFuncIdentifierMessage(funcIdentifierNode, compilationUnit),
- FuncParameterNode funcParameterNode => CreateTypeNameMessage("Function parameter", funcParameterNode.Name, funcParameterNode.Type),
- VariableIdentifierNode variableIdentifierNode => CreateTypeNameMessage("Variable", variableIdentifierNode.Name, variableIdentifierNode.Type),
- VariableDeclarationNode variableDeclarationNode => CreateTypeNameMessage("Variable declaration", variableDeclarationNode.Name, variableDeclarationNode.Type),
- StructFieldAccessNode structFieldAccessNode => CreateTypeNameMessage("Struct field", $"{structFieldAccessNode.Target.Type}.{structFieldAccessNode.Field}", structFieldAccessNode.Type),
- CStringLiteralNode cStringLiteralNode => CreateLiteralMessage(cStringLiteralNode.Type, '"' + cStringLiteralNode.Value + '"'),
- StringLiteralNode stringLiteralNode => CreateLiteralMessage(stringLiteralNode.Type, '"' + stringLiteralNode.Value + '"'),
- BoolLiteralNode boolLiteralNode => CreateLiteralMessage(boolLiteralNode.Type, boolLiteralNode.Value.ToString()),
- Float32LiteralNode float32LiteralNode => CreateLiteralMessage(float32LiteralNode.Type, float32LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
- Float64LiteralNode float64LiteralNode => CreateLiteralMessage(float64LiteralNode.Type, float64LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
- I8LiteralNode i8LiteralNode => CreateLiteralMessage(i8LiteralNode.Type, i8LiteralNode.Value.ToString()),
- I16LiteralNode i16LiteralNode => CreateLiteralMessage(i16LiteralNode.Type, i16LiteralNode.Value.ToString()),
- I32LiteralNode i32LiteralNode => CreateLiteralMessage(i32LiteralNode.Type, i32LiteralNode.Value.ToString()),
- I64LiteralNode i64LiteralNode => CreateLiteralMessage(i64LiteralNode.Type, i64LiteralNode.Value.ToString()),
- U8LiteralNode u8LiteralNode => CreateLiteralMessage(u8LiteralNode.Type, u8LiteralNode.Value.ToString()),
- U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()),
- U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()),
- U64LiteralNode u64LiteralNode => CreateLiteralMessage(u64LiteralNode.Type, u64LiteralNode.Value.ToString()),
- // Expressions can have a generic fallback showing the resulting type
- ExpressionNode expressionNode => $"""
- **Expression** `{expressionNode.GetType().Name}`
- ```nub
- {expressionNode.Type}
- ```
- """,
- BlockNode => null,
- _ => hoveredNode.GetType().Name
- };
- }
-
- private static string CreateLiteralMessage(NubType type, string value)
- {
- return $"""
- **Literal** `{type}`
- ```nub
- {value}: {type}
- ```
- """;
- }
-
- private static string CreateTypeNameMessage(string description, string name, NubType type)
- {
- return $"""
- **{description}** `{name}`
- ```nub
- {name}: {type}
- ```
- """;
- }
-
- private static string CreateFuncIdentifierMessage(FuncIdentifierNode funcIdentifierNode, CompilationUnit compilationUnit)
- {
- var func = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name);
- if (func == null)
- {
- return $"""
- **Function** `{funcIdentifierNode.Module}::{funcIdentifierNode.Name}`
- ```nub
- // Declaration not found
- ```
- """;
- }
-
- return CreateFuncPrototypeMessage(func);
- }
-
- private static string CreateFuncPrototypeMessage(FuncPrototypeNode funcPrototypeNode)
- {
- var parameterText = string.Join(", ", funcPrototypeNode.Parameters.Select(x => $"{x.Name}: {x.Type}"));
- var externText = funcPrototypeNode.ExternSymbol != null ? $"extern \"{funcPrototypeNode.ExternSymbol}\" " : "";
-
- return $"""
- **Function** `{funcPrototypeNode.Module}::{funcPrototypeNode.Name}`
- ```nub
- {externText}func {funcPrototypeNode.Module}::{funcPrototypeNode.Name}({parameterText}): {funcPrototypeNode.ReturnType}
- ```
- """;
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/NubLang.LSP.csproj b/compiler/NubLang.LSP/NubLang.LSP.csproj
deleted file mode 100644
index 720f388..0000000
--- a/compiler/NubLang.LSP/NubLang.LSP.csproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- nublsp
- Exe
- net9.0
- enable
- enable
- true
- true
-
-
-
-
-
-
-
-
-
-
-
diff --git a/compiler/NubLang.LSP/Program.cs b/compiler/NubLang.LSP/Program.cs
deleted file mode 100644
index 095630f..0000000
--- a/compiler/NubLang.LSP/Program.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using NubLang.LSP;
-using OmniSharp.Extensions.LanguageServer.Server;
-
-var server = await LanguageServer.From(options => options
- .WithInput(Console.OpenStandardInput())
- .WithOutput(Console.OpenStandardOutput())
- .WithServices(services =>
- {
- services.AddSingleton();
- services.AddSingleton();
- })
- .ConfigureLogging(x => x
- .AddLanguageProtocolLogging()
- .SetMinimumLevel(LogLevel.Debug))
- .WithHandler()
- .WithHandler()
- .WithHandler()
- .WithHandler()
- .OnInitialize((server, request, ct) =>
- {
- var workspaceManager = server.GetRequiredService();
-
- if (request.RootPath != null)
- {
- workspaceManager.Init(request.RootPath);
- }
-
- return Task.CompletedTask;
- })
-);
-
-await server.WaitForExit;
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/TextDocumentSyncHandler.cs b/compiler/NubLang.LSP/TextDocumentSyncHandler.cs
deleted file mode 100644
index e29aa44..0000000
--- a/compiler/NubLang.LSP/TextDocumentSyncHandler.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediatR;
-using OmniSharp.Extensions.LanguageServer.Protocol;
-using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
-using OmniSharp.Extensions.LanguageServer.Protocol.Document;
-using OmniSharp.Extensions.LanguageServer.Protocol.Models;
-
-namespace NubLang.LSP;
-
-internal class TextDocumentSyncHandler(WorkspaceManager workspaceManager) : TextDocumentSyncHandlerBase
-{
- public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri)
- {
- return new TextDocumentAttributes(uri, "nub");
- }
-
- public override Task Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken)
- {
- workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
- return Unit.Task;
- }
-
- public override Task Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken)
- {
- workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
- return Unit.Task;
- }
-
- public override Task Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken)
- {
- workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
- return Unit.Task;
- }
-
- public override Task Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken)
- {
- workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
- return Unit.Task;
- }
-
- protected override TextDocumentSyncRegistrationOptions CreateRegistrationOptions(TextSynchronizationCapability capability, ClientCapabilities clientCapabilities)
- {
- return new TextDocumentSyncRegistrationOptions();
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang.LSP/WorkspaceManager.cs b/compiler/NubLang.LSP/WorkspaceManager.cs
deleted file mode 100644
index a897e13..0000000
--- a/compiler/NubLang.LSP/WorkspaceManager.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using NubLang.Ast;
-using NubLang.Syntax;
-using OmniSharp.Extensions.LanguageServer.Protocol;
-
-namespace NubLang.LSP;
-
-public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
-{
- private readonly Dictionary _syntaxTrees = new();
- private readonly Dictionary _compilationUnits = new();
-
- public void Init(string rootPath)
- {
- var files = Directory.GetFiles(rootPath, "*.nub", SearchOption.AllDirectories);
- foreach (var path in files)
- {
- var text = File.ReadAllText(path);
- var tokenizer = new Tokenizer(path, text);
-
- tokenizer.Tokenize();
- diagnosticsPublisher.Publish(path, tokenizer.Diagnostics);
-
- var parser = new Parser();
- var parseResult = parser.Parse(tokenizer.Tokens);
- diagnosticsPublisher.Publish(path, parser.Diagnostics);
-
- _syntaxTrees[path] = parseResult;
- }
-
- foreach (var (fsPath, syntaxTree) in _syntaxTrees)
- {
- var modules = Module.Collect(_syntaxTrees.Select(x => x.Value).ToList());
-
- var typeChecker = new TypeChecker(syntaxTree, modules);
- var result = typeChecker.Check();
- diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
- _compilationUnits[fsPath] = result;
- }
- }
-
- public void UpdateFile(DocumentUri path)
- {
- var fsPath = path.GetFileSystemPath();
-
- var text = File.ReadAllText(fsPath);
- var tokenizer = new Tokenizer(fsPath, text);
- tokenizer.Tokenize();
- diagnosticsPublisher.Publish(path, tokenizer.Diagnostics);
-
- var parser = new Parser();
- var syntaxTree = parser.Parse(tokenizer.Tokens);
- diagnosticsPublisher.Publish(path, parser.Diagnostics);
- _syntaxTrees[fsPath] = syntaxTree;
-
- var modules = Module.Collect(_syntaxTrees.Select(x => x.Value).ToList());
-
- var typeChecker = new TypeChecker(syntaxTree, modules);
- var result = typeChecker.Check();
- diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
- _compilationUnits[fsPath] = result;
- }
-
- public void RemoveFile(DocumentUri path)
- {
- var fsPath = path.GetFileSystemPath();
- _syntaxTrees.Remove(fsPath);
- _compilationUnits.Remove(fsPath);
- }
-
- public CompilationUnit? GetCompilationUnit(DocumentUri path)
- {
- return _compilationUnits.GetValueOrDefault(path.GetFileSystemPath());
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Ast/CompilationUnit.cs b/compiler/NubLang/Ast/CompilationUnit.cs
deleted file mode 100644
index f2115c1..0000000
--- a/compiler/NubLang/Ast/CompilationUnit.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace NubLang.Ast;
-
-public sealed class CompilationUnit
-{
- public CompilationUnit(List functions, List importedStructTypes, List importedFunctions)
- {
- Functions = functions;
- ImportedStructTypes = importedStructTypes;
- ImportedFunctions = importedFunctions;
- }
-
- public List Functions { get; }
- public List ImportedStructTypes { get; }
- public List ImportedFunctions { get; }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs
deleted file mode 100644
index aca79fd..0000000
--- a/compiler/NubLang/Ast/Node.cs
+++ /dev/null
@@ -1,590 +0,0 @@
-using NubLang.Syntax;
-
-namespace NubLang.Ast;
-
-public abstract class Node(List tokens)
-{
- public List Tokens { get; } = tokens;
-
- public abstract IEnumerable Children();
-
- public IEnumerable Descendants()
- {
- foreach (var child in Children())
- {
- foreach (var descendant in child.DescendantsAndSelf())
- {
- yield return descendant;
- }
- }
- }
-
- public IEnumerable DescendantsAndSelf()
- {
- yield return this;
- foreach (var descendant in Descendants())
- {
- yield return descendant;
- }
- }
-}
-
-#region Definitions
-
-public abstract class DefinitionNode(List tokens, string module, string name) : Node(tokens)
-{
- public string Module { get; } = module;
- public string Name { get; } = name;
-}
-
-public class FuncParameterNode(List tokens, string name, NubType type) : Node(tokens)
-{
- public string Name { get; } = name;
- public NubType Type { get; } = type;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class FuncPrototypeNode(List tokens, string module, string name, string? externSymbol, List parameters, NubType returnType) : Node(tokens)
-{
- public string Module { get; } = module;
- public string Name { get; } = name;
- public string? ExternSymbol { get; } = externSymbol;
- public List Parameters { get; } = parameters;
- public NubType ReturnType { get; } = returnType;
-
- public override IEnumerable Children()
- {
- return Parameters;
- }
-}
-
-public class FuncNode(List tokens, FuncPrototypeNode prototype, BlockNode? body) : DefinitionNode(tokens, prototype.Module, prototype.Name)
-{
- public FuncPrototypeNode Prototype { get; } = prototype;
- public BlockNode? Body { get; } = body;
-
- public override IEnumerable Children()
- {
- yield return Prototype;
- if (Body != null)
- {
- yield return Body;
- }
- }
-}
-
-#endregion
-
-#region Statements
-
-public abstract class StatementNode(List tokens) : Node(tokens);
-
-public abstract class TerminalStatementNode(List tokens) : StatementNode(tokens);
-
-public class BlockNode(List tokens, List statements) : StatementNode(tokens)
-{
- public List Statements { get; } = statements;
-
- public override IEnumerable Children()
- {
- return Statements;
- }
-}
-
-public class StatementFuncCallNode(List tokens, FuncCallNode funcCall) : StatementNode(tokens)
-{
- public FuncCallNode FuncCall { get; } = funcCall;
-
- public override IEnumerable Children()
- {
- yield return FuncCall;
- }
-}
-
-public class ReturnNode(List tokens, ExpressionNode? value) : TerminalStatementNode(tokens)
-{
- public ExpressionNode? Value { get; } = value;
-
- public override IEnumerable Children()
- {
- if (Value != null) yield return Value;
- }
-}
-
-public class AssignmentNode(List tokens, LValueExpressionNode target, ExpressionNode value) : StatementNode(tokens)
-{
- public LValueExpressionNode Target { get; } = target;
- public ExpressionNode Value { get; } = value;
-
- public override IEnumerable Children()
- {
- yield return Target;
- yield return Value;
- }
-}
-
-public class IfNode(List tokens, ExpressionNode condition, BlockNode body, Variant? @else) : StatementNode(tokens)
-{
- public ExpressionNode Condition { get; } = condition;
- public BlockNode Body { get; } = body;
- public Variant? Else { get; } = @else;
-
- public override IEnumerable Children()
- {
- yield return Condition;
- yield return Body;
- if (Else.HasValue)
- {
- yield return Else.Value.Match(x => x, x => x);
- }
- }
-}
-
-public class VariableDeclarationNode(List tokens, string name, ExpressionNode? assignment, NubType type) : StatementNode(tokens)
-{
- public string Name { get; } = name;
- public ExpressionNode? Assignment { get; } = assignment;
- public NubType Type { get; } = type;
-
- public override IEnumerable Children()
- {
- if (Assignment != null) yield return Assignment;
- }
-}
-
-public class ContinueNode(List tokens) : TerminalStatementNode(tokens)
-{
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class BreakNode(List tokens) : TerminalStatementNode(tokens)
-{
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class WhileNode(List tokens, ExpressionNode condition, BlockNode body) : StatementNode(tokens)
-{
- public ExpressionNode Condition { get; } = condition;
- public BlockNode Body { get; } = body;
-
- public override IEnumerable Children()
- {
- yield return Condition;
- yield return Body;
- }
-}
-
-public class ForSliceNode(List tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens)
-{
- public string ElementName { get; } = elementName;
- public string? IndexName { get; } = indexName;
- public ExpressionNode Target { get; } = target;
- public BlockNode Body { get; } = body;
-
- public override IEnumerable Children()
- {
- yield return Target;
- yield return Body;
- }
-}
-
-public class ForConstArrayNode(List tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens)
-{
- public string ElementName { get; } = elementName;
- public string? IndexName { get; } = indexName;
- public ExpressionNode Target { get; } = target;
- public BlockNode Body { get; } = body;
-
- public override IEnumerable Children()
- {
- yield return Target;
- yield return Body;
- }
-}
-
-public class DeferNode(List tokens, StatementNode statement) : StatementNode(tokens)
-{
- public StatementNode Statement { get; } = statement;
-
- public override IEnumerable Children()
- {
- yield return Statement;
- }
-}
-
-#endregion
-
-#region Expressions
-
-public enum UnaryOperator
-{
- Negate,
- Invert
-}
-
-public enum BinaryOperator
-{
- Equal,
- NotEqual,
- GreaterThan,
- GreaterThanOrEqual,
- LessThan,
- LessThanOrEqual,
- LogicalAnd,
- LogicalOr,
- Plus,
- Minus,
- Multiply,
- Divide,
- Modulo,
- LeftShift,
- RightShift,
- BitwiseAnd,
- BitwiseXor,
- BitwiseOr
-}
-
-public abstract class ExpressionNode(List tokens, NubType type) : Node(tokens)
-{
- public NubType Type { get; } = type;
-}
-
-public abstract class LValueExpressionNode(List tokens, NubType type) : ExpressionNode(tokens, type);
-
-public abstract class RValueExpressionNode(List tokens, NubType type) : ExpressionNode(tokens, type);
-
-public abstract class IntermediateExpression(List tokens) : ExpressionNode(tokens, new NubVoidType());
-
-public class StringLiteralNode(List tokens, string value) : RValueExpressionNode(tokens, new NubStringType())
-{
- public string Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class CStringLiteralNode(List tokens, string value) : RValueExpressionNode(tokens, new NubPointerType(new NubIntType(true, 8)))
-{
- public string Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class I8LiteralNode(List tokens, sbyte value) : RValueExpressionNode(tokens, new NubIntType(true, 8))
-{
- public sbyte Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class I16LiteralNode(List tokens, short value) : RValueExpressionNode(tokens, new NubIntType(true, 16))
-{
- public short Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class I32LiteralNode(List tokens, int value) : RValueExpressionNode(tokens, new NubIntType(true, 32))
-{
- public int Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class I64LiteralNode(List tokens, long value) : RValueExpressionNode(tokens, new NubIntType(true, 64))
-{
- public long Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class U8LiteralNode(List tokens, byte value) : RValueExpressionNode(tokens, new NubIntType(false, 8))
-{
- public byte Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class U16LiteralNode(List tokens, ushort value) : RValueExpressionNode(tokens, new NubIntType(false, 16))
-{
- public ushort Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class U32LiteralNode(List tokens, uint value) : RValueExpressionNode(tokens, new NubIntType(false, 32))
-{
- public uint Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class U64LiteralNode(List tokens, ulong value) : RValueExpressionNode(tokens, new NubIntType(false, 64))
-{
- public ulong Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class Float32LiteralNode(List tokens, float value) : RValueExpressionNode(tokens, new NubFloatType(32))
-{
- public float Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class Float64LiteralNode(List tokens, double value) : RValueExpressionNode(tokens, new NubFloatType(64))
-{
- public double Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class BoolLiteralNode(List tokens, NubType type, bool value) : RValueExpressionNode(tokens, type)
-{
- public bool Value { get; } = value;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class BinaryExpressionNode(List tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : RValueExpressionNode(tokens, type)
-{
- public ExpressionNode Left { get; } = left;
- public BinaryOperator Operator { get; } = @operator;
- public ExpressionNode Right { get; } = right;
-
- public override IEnumerable Children()
- {
- yield return Left;
- yield return Right;
- }
-}
-
-public class UnaryExpressionNode(List tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : RValueExpressionNode(tokens, type)
-{
- public UnaryOperator Operator { get; } = @operator;
- public ExpressionNode Operand { get; } = operand;
-
- public override IEnumerable Children()
- {
- yield return Operand;
- }
-}
-
-public class FuncCallNode(List tokens, NubType type, ExpressionNode expression, List parameters) : RValueExpressionNode(tokens, type)
-{
- public ExpressionNode Expression { get; } = expression;
- public List Parameters { get; } = parameters;
-
- public override IEnumerable Children()
- {
- yield return Expression;
- foreach (var expressionNode in Parameters)
- {
- yield return expressionNode;
- }
- }
-}
-
-public class VariableIdentifierNode(List tokens, NubType type, string name) : LValueExpressionNode(tokens, type)
-{
- public string Name { get; } = name;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class FuncIdentifierNode(List tokens, NubType type, string module, string name, string? externSymbol) : RValueExpressionNode(tokens, type)
-{
- public string Module { get; } = module;
- public string Name { get; } = name;
- public string? ExternSymbol { get; } = externSymbol;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class ArrayInitializerNode(List tokens, NubType type, List values) : RValueExpressionNode(tokens, type)
-{
- public List Values { get; } = values;
-
- public override IEnumerable Children()
- {
- return Values;
- }
-}
-
-public class ConstArrayInitializerNode(List tokens, NubType type, List values) : RValueExpressionNode(tokens, type)
-{
- public List Values { get; } = values;
-
- public override IEnumerable Children()
- {
- return Values;
- }
-}
-
-public class ArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type)
-{
- public ExpressionNode Target { get; } = target;
- public ExpressionNode Index { get; } = index;
-
- public override IEnumerable Children()
- {
- yield return Target;
- yield return Index;
- }
-}
-
-public class ConstArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type)
-{
- public ExpressionNode Target { get; } = target;
- public ExpressionNode Index { get; } = index;
-
- public override IEnumerable Children()
- {
- yield return Target;
- yield return Index;
- }
-}
-
-public class SliceIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type)
-{
- public ExpressionNode Target { get; } = target;
- public ExpressionNode Index { get; } = index;
-
- public override IEnumerable Children()
- {
- yield return Target;
- yield return Index;
- }
-}
-
-public class AddressOfNode(List tokens, NubType type, LValueExpressionNode lValue) : RValueExpressionNode(tokens, type)
-{
- public LValueExpressionNode LValue { get; } = lValue;
-
- public override IEnumerable Children()
- {
- yield return LValue;
- }
-}
-
-public class StructFieldAccessNode(List tokens, NubType type, ExpressionNode target, string field) : LValueExpressionNode(tokens, type)
-{
- public ExpressionNode Target { get; } = target;
- public string Field { get; } = field;
-
- public override IEnumerable Children()
- {
- yield return Target;
- }
-}
-
-public class StructInitializerNode(List tokens, NubType type, Dictionary initializers) : RValueExpressionNode(tokens, type)
-{
- public Dictionary Initializers { get; } = initializers;
-
- public override IEnumerable Children()
- {
- foreach (var initializer in Initializers)
- {
- yield return initializer.Value;
- }
- }
-}
-
-public class DereferenceNode(List tokens, NubType type, ExpressionNode target) : LValueExpressionNode(tokens, type)
-{
- public ExpressionNode Target { get; } = target;
-
- public override IEnumerable Children()
- {
- yield return Target;
- }
-}
-
-public class SizeNode(List tokens, NubType TargetType) : RValueExpressionNode(tokens, new NubIntType(false, 64))
-{
- public NubType TargetType { get; } = TargetType;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-public class CastNode(List tokens, NubType type, ExpressionNode value) : RValueExpressionNode(tokens, type)
-{
- public ExpressionNode Value { get; } = value;
-
- public override IEnumerable Children()
- {
- yield return Value;
- }
-}
-
-public class EnumReferenceIntermediateNode(List tokens, string module, string name) : IntermediateExpression(tokens)
-{
- public string Module { get; } = module;
- public string Name { get; } = name;
-
- public override IEnumerable Children()
- {
- return [];
- }
-}
-
-#endregion
\ No newline at end of file
diff --git a/compiler/NubLang/Ast/NubType.cs b/compiler/NubLang/Ast/NubType.cs
deleted file mode 100644
index ef0981b..0000000
--- a/compiler/NubLang/Ast/NubType.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using System.Security.Cryptography;
-using System.Text;
-
-namespace NubLang.Ast;
-
-public abstract class NubType : IEquatable
-{
- public override bool Equals(object? obj) => obj is NubType other && Equals(other);
- public abstract bool Equals(NubType? other);
-
- public abstract override int GetHashCode();
- public abstract override string ToString();
-
- public static bool operator ==(NubType? left, NubType? right) => Equals(left, right);
- public static bool operator !=(NubType? left, NubType? right) => !Equals(left, right);
-}
-
-public class NubVoidType : NubType
-{
- public override string ToString() => "void";
- public override bool Equals(NubType? other) => other is NubVoidType;
- public override int GetHashCode() => HashCode.Combine(typeof(NubVoidType));
-}
-
-public sealed class NubIntType(bool signed, int width) : NubType
-{
- public bool Signed { get; } = signed;
- public int Width { get; } = width;
-
- public override string ToString() => $"{(Signed ? "i" : "u")}{Width}";
- public override bool Equals(NubType? other) => other is NubIntType @int && @int.Width == Width && @int.Signed == Signed;
- public override int GetHashCode() => HashCode.Combine(typeof(NubIntType), Signed, Width);
-}
-
-public sealed class NubFloatType(int width) : NubType
-{
- public int Width { get; } = width;
-
- public override string ToString() => $"f{Width}";
- public override bool Equals(NubType? other) => other is NubFloatType @float && @float.Width == Width;
- public override int GetHashCode() => HashCode.Combine(typeof(NubFloatType), Width);
-}
-
-public class NubBoolType : NubType
-{
- public override string ToString() => "bool";
- public override bool Equals(NubType? other) => other is NubBoolType;
- public override int GetHashCode() => HashCode.Combine(typeof(NubBoolType));
-}
-
-public sealed class NubPointerType(NubType baseType) : NubType
-{
- public NubType BaseType { get; } = baseType;
-
- public override string ToString() => "^" + BaseType;
- public override bool Equals(NubType? other) => other is NubPointerType pointer && BaseType.Equals(pointer.BaseType);
- public override int GetHashCode() => HashCode.Combine(typeof(NubPointerType), BaseType);
-}
-
-public class NubFuncType(List parameters, NubType returnType) : NubType
-{
- public List Parameters { get; } = parameters;
- public NubType ReturnType { get; } = returnType;
-
- public override string ToString() => $"func({string.Join(", ", Parameters)}): {ReturnType}";
- public override bool Equals(NubType? other) => other is NubFuncType func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
-
- public override int GetHashCode()
- {
- var hash = new HashCode();
- hash.Add(typeof(NubFuncType));
- hash.Add(ReturnType);
- foreach (var param in Parameters)
- {
- hash.Add(param);
- }
-
- return hash.ToHashCode();
- }
-}
-
-public class NubStructType(string module, string name, List fields) : NubType
-{
- public string Module { get; } = module;
- public string Name { get; } = name;
- public List Fields { get; set; } = fields;
-
- public override string ToString() => $"{Module}::{Name}";
- public override bool Equals(NubType? other) => other is NubStructType structType && Name == structType.Name && Module == structType.Module;
- public override int GetHashCode() => HashCode.Combine(typeof(NubStructType), Module, Name);
-}
-
-public class NubStructFieldType(string name, NubType type, bool hasDefaultValue)
-{
- public string Name { get; } = name;
- public NubType Type { get; } = type;
- public bool HasDefaultValue { get; } = hasDefaultValue;
-}
-
-public class NubSliceType(NubType elementType) : NubType
-{
- public NubType ElementType { get; } = elementType;
-
- public override string ToString() => "[]" + ElementType;
- public override bool Equals(NubType? other) => other is NubSliceType slice && ElementType.Equals(slice.ElementType);
- public override int GetHashCode() => HashCode.Combine(typeof(NubSliceType), ElementType);
-}
-
-public class NubConstArrayType(NubType elementType, long size) : NubType
-{
- public NubType ElementType { get; } = elementType;
- public long Size { get; } = size;
-
- public override string ToString() => $"[{Size}]{ElementType}";
- public override bool Equals(NubType? other) => other is NubConstArrayType array && ElementType.Equals(array.ElementType) && Size == array.Size;
- public override int GetHashCode() => HashCode.Combine(typeof(NubConstArrayType), ElementType, Size);
-}
-
-public class NubArrayType(NubType elementType) : NubType
-{
- public NubType ElementType { get; } = elementType;
-
- public override string ToString() => $"[?]{ElementType}";
- public override bool Equals(NubType? other) => other is NubArrayType array && ElementType.Equals(array.ElementType);
- public override int GetHashCode() => HashCode.Combine(typeof(NubArrayType), ElementType);
-}
-
-public class NubStringType : NubType
-{
- public override string ToString() => "string";
- public override bool Equals(NubType? other) => other is NubStringType;
- public override int GetHashCode() => HashCode.Combine(typeof(NubStringType));
-}
-
-public static class NameMangler
-{
- public static string Mangle(params IEnumerable types)
- {
- var readable = string.Join(":", types.Select(EncodeType));
- return ComputeShortHash(readable);
- }
-
- private static string EncodeType(NubType node) => node switch
- {
- NubVoidType => "V",
- NubBoolType => "B",
- NubIntType i => (i.Signed ? "I" : "U") + i.Width,
- NubFloatType f => "F" + f.Width,
- NubStringType => "S",
- NubArrayType a => $"A({EncodeType(a.ElementType)})",
- NubConstArrayType ca => $"CA({EncodeType(ca.ElementType)})",
- NubSliceType a => $"SL{EncodeType(a.ElementType)}()",
- NubPointerType p => $"P({EncodeType(p.BaseType)})",
- NubFuncType fn => $"FN({string.Join(":", fn.Parameters.Select(EncodeType))}:{EncodeType(fn.ReturnType)})",
- NubStructType st => $"ST({st.Module}:{st.Name})",
- _ => throw new NotSupportedException($"Cannot encode type: {node}")
- };
-
- private static string ComputeShortHash(string input)
- {
- var bytes = Encoding.UTF8.GetBytes(input);
- var hash = SHA256.HashData(bytes);
- return Convert.ToHexString(hash[..8]).ToLower();
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs
deleted file mode 100644
index 4224115..0000000
--- a/compiler/NubLang/Ast/TypeChecker.cs
+++ /dev/null
@@ -1,1167 +0,0 @@
-using System.Diagnostics;
-using NubLang.Diagnostics;
-using NubLang.Syntax;
-
-namespace NubLang.Ast;
-
-public sealed class TypeChecker
-{
- private readonly SyntaxTree _syntaxTree;
- private readonly Dictionary _importedModules;
-
- private readonly Stack _scopes = [];
- private readonly Dictionary<(string Module, string Name), NubType> _typeCache = new();
- private readonly HashSet<(string Module, string Name)> _resolvingTypes = [];
-
- private Scope Scope => _scopes.Peek();
-
- public List Diagnostics { get; } = [];
-
- public TypeChecker(SyntaxTree syntaxTree, Dictionary modules)
- {
- _syntaxTree = syntaxTree;
- _importedModules = modules
- .Where(x => syntaxTree.Imports.Contains(x.Key) || _syntaxTree.ModuleName == x.Key)
- .ToDictionary();
- }
-
- public CompilationUnit Check()
- {
- _scopes.Clear();
- _typeCache.Clear();
- _resolvingTypes.Clear();
-
- var functions = new List();
-
- using (BeginRootScope(_syntaxTree.ModuleName))
- {
- foreach (var funcSyntax in _syntaxTree.Definitions.OfType())
- {
- try
- {
- functions.Add(CheckFuncDefinition(funcSyntax));
- }
- catch (TypeCheckerException e)
- {
- Diagnostics.Add(e.Diagnostic);
- }
- }
- }
-
- var importedStructTypes = new List();
- var importedFunctions = new List();
-
- foreach (var (name, module) in _importedModules)
- {
- using (BeginRootScope(name))
- {
- foreach (var structSyntax in module.Structs(true))
- {
- try
- {
- var fields = structSyntax.Fields
- .Select(f => new NubStructFieldType(f.Name, ResolveType(f.Type), f.Value != null))
- .ToList();
-
- importedStructTypes.Add(new NubStructType(name, structSyntax.Name, fields));
- }
- catch (TypeCheckerException e)
- {
- Diagnostics.Add(e.Diagnostic);
- }
- }
-
- foreach (var funcSyntax in module.Functions(true))
- {
- try
- {
- importedFunctions.Add(CheckFuncPrototype(funcSyntax.Prototype));
- }
- catch (TypeCheckerException e)
- {
- Diagnostics.Add(e.Diagnostic);
- }
- }
- }
- }
-
- return new CompilationUnit(functions, importedStructTypes, importedFunctions);
- }
-
- private ScopeDisposer BeginScope()
- {
- _scopes.Push(Scope.SubScope());
- return new ScopeDisposer(this);
- }
-
- private ScopeDisposer BeginRootScope(string moduleName)
- {
- _scopes.Push(new Scope(moduleName));
- return new ScopeDisposer(this);
- }
-
- private sealed class ScopeDisposer(TypeChecker owner) : IDisposable
- {
- private bool _disposed;
-
- public void Dispose()
- {
- if (_disposed) return;
- owner._scopes.Pop();
- _disposed = true;
- }
- }
-
- private FuncNode CheckFuncDefinition(FuncSyntax node)
- {
- using (BeginScope())
- {
- var prototype = CheckFuncPrototype(node.Prototype);
-
- Scope.SetReturnType(prototype.ReturnType);
- foreach (var parameter in prototype.Parameters)
- {
- Scope.DeclareVariable(new Variable(parameter.Name, parameter.Type));
- }
-
- var body = node.Body == null ? null : CheckBlock(node.Body);
- return new FuncNode(node.Tokens, prototype, body);
- }
- }
-
- private AssignmentNode CheckAssignment(AssignmentSyntax statement)
- {
- var target = CheckExpression(statement.Target);
- if (target is not LValueExpressionNode lValue)
- {
- throw new TypeCheckerException(Diagnostic.Error("Cannot assign to an rvalue").At(statement).Build());
- }
-
- var value = CheckExpression(statement.Value, lValue.Type);
-
- if (value.Type != lValue.Type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Cannot assign {value.Type} to {lValue.Type}")
- .At(statement.Value)
- .Build());
- }
-
- return new AssignmentNode(statement.Tokens, lValue, value);
- }
-
- private IfNode CheckIf(IfSyntax statement)
- {
- var condition = CheckExpression(statement.Condition);
- var body = CheckBlock(statement.Body);
- Variant? elseStatement = null;
- if (statement.Else.HasValue)
- {
- elseStatement = statement.Else.Value.Match>(elif => CheckIf(elif), el => CheckBlock(el));
- }
-
- return new IfNode(statement.Tokens, condition, body, elseStatement);
- }
-
- private ReturnNode CheckReturn(ReturnSyntax statement)
- {
- ExpressionNode? value = null;
-
- if (statement.Value != null)
- {
- var expectedReturnType = Scope.GetReturnType();
- value = CheckExpression(statement.Value, expectedReturnType);
- }
-
- return new ReturnNode(statement.Tokens, value);
- }
-
- private StatementNode CheckStatementExpression(StatementExpressionSyntax statement)
- {
- var expression = CheckExpression(statement.Expression);
-
- return expression switch
- {
- FuncCallNode funcCall => new StatementFuncCallNode(statement.Tokens, funcCall),
- _ => throw new TypeCheckerException(Diagnostic.Error("Expressions statements can only be function calls").At(statement).Build())
- };
- }
-
- private VariableDeclarationNode CheckVariableDeclaration(VariableDeclarationSyntax statement)
- {
- NubType? type = null;
- ExpressionNode? assignmentNode = null;
-
- if (statement.ExplicitType != null)
- {
- type = ResolveType(statement.ExplicitType);
- }
-
- if (statement.Assignment != null)
- {
- assignmentNode = CheckExpression(statement.Assignment, type);
-
- if (type == null)
- {
- type = assignmentNode.Type;
- }
- else if (assignmentNode.Type != type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Cannot assign {assignmentNode.Type} to variable of type {type}")
- .At(statement.Assignment)
- .Build());
- }
- }
-
- if (type == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Cannot infer type of variable {statement.Name}")
- .At(statement)
- .Build());
- }
-
- Scope.DeclareVariable(new Variable(statement.Name, type));
-
- return new VariableDeclarationNode(statement.Tokens, statement.Name, assignmentNode, type);
- }
-
- private WhileNode CheckWhile(WhileSyntax statement)
- {
- var condition = CheckExpression(statement.Condition);
- var body = CheckBlock(statement.Body);
- return new WhileNode(statement.Tokens, condition, body);
- }
-
- private StatementNode CheckFor(ForSyntax forSyntax)
- {
- var target = CheckExpression(forSyntax.Target);
-
-
- switch (target.Type)
- {
- case NubSliceType sliceType:
- {
- using (BeginScope())
- {
- Scope.DeclareVariable(new Variable(forSyntax.ElementName, sliceType.ElementType));
- if (forSyntax.IndexName != null)
- {
- Scope.DeclareVariable(new Variable(forSyntax.IndexName, new NubIntType(false, 64)));
- }
-
- var body = CheckBlock(forSyntax.Body);
- return new ForSliceNode(forSyntax.Tokens, forSyntax.ElementName, forSyntax.IndexName, target, body);
- }
- }
- case NubConstArrayType constArrayType:
- {
- using (BeginScope())
- {
- Scope.DeclareVariable(new Variable(forSyntax.ElementName, constArrayType.ElementType));
- if (forSyntax.IndexName != null)
- {
- Scope.DeclareVariable(new Variable(forSyntax.IndexName, new NubIntType(false, 64)));
- }
-
- var body = CheckBlock(forSyntax.Body);
- return new ForConstArrayNode(forSyntax.Tokens, forSyntax.ElementName, forSyntax.IndexName, target, body);
- }
- }
- default:
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Cannot iterate over type {target.Type} which does not have size information")
- .At(forSyntax.Target)
- .Build());
- }
- }
- }
-
- private FuncPrototypeNode CheckFuncPrototype(FuncPrototypeSyntax statement)
- {
- var parameters = new List();
- foreach (var parameter in statement.Parameters)
- {
- parameters.Add(new FuncParameterNode(parameter.Tokens, parameter.Name, ResolveType(parameter.Type)));
- }
-
- return new FuncPrototypeNode(statement.Tokens, Scope.Module, statement.Name, statement.ExternSymbol, parameters, ResolveType(statement.ReturnType));
- }
-
- private ExpressionNode CheckExpression(ExpressionSyntax node, NubType? expectedType = null)
- {
- var result = node switch
- {
- AddressOfSyntax expression => CheckAddressOf(expression, expectedType),
- ArrayIndexAccessSyntax expression => CheckArrayIndexAccess(expression, expectedType),
- ArrayInitializerSyntax expression => CheckArrayInitializer(expression, expectedType),
- BinaryExpressionSyntax expression => CheckBinaryExpression(expression, expectedType),
- UnaryExpressionSyntax expression => CheckUnaryExpression(expression, expectedType),
- DereferenceSyntax expression => CheckDereference(expression, expectedType),
- FuncCallSyntax expression => CheckFuncCall(expression, expectedType),
- LocalIdentifierSyntax expression => CheckLocalIdentifier(expression, expectedType),
- ModuleIdentifierSyntax expression => CheckModuleIdentifier(expression, expectedType),
- BoolLiteralSyntax expression => CheckBoolLiteral(expression, expectedType),
- StringLiteralSyntax expression => CheckStringLiteral(expression, expectedType),
- IntLiteralSyntax expression => CheckIntLiteral(expression, expectedType),
- FloatLiteralSyntax expression => CheckFloatLiteral(expression, expectedType),
- MemberAccessSyntax expression => CheckMemberAccess(expression, expectedType),
- StructInitializerSyntax expression => CheckStructInitializer(expression, expectedType),
- SizeSyntax expression => new SizeNode(node.Tokens, ResolveType(expression.Type)),
- CastSyntax expression => CheckCast(expression, expectedType),
- _ => throw new ArgumentOutOfRangeException(nameof(node))
- };
-
- if (expectedType != null)
- {
- if (result.Type == expectedType)
- {
- return result;
- }
-
- if (IsCastAllowed(result.Type, expectedType))
- {
- return new CastNode(result.Tokens, expectedType, result);
- }
- }
-
- return result;
- }
-
- private ExpressionNode CheckCast(CastSyntax expression, NubType? expectedType)
- {
- if (expectedType == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Unable to infer target type of cast")
- .At(expression)
- .WithHelp("Specify target type where value is used")
- .Build());
- }
-
- var value = CheckExpression(expression.Value, expectedType);
-
- if (value.Type == expectedType)
- {
- Diagnostics.Add(Diagnostic
- .Warning("Target type of cast is same as the value. Cast is unnecessary")
- .At(expression)
- .Build());
-
- return value;
- }
-
- if (!IsCastAllowed(value.Type, expectedType, false))
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Cannot cast from {value.Type} to {expectedType}")
- .Build());
- }
-
- return new CastNode(expression.Tokens, expectedType, value);
- }
-
- private static bool IsCastAllowed(NubType from, NubType to, bool strict = true)
- {
- // note(nub31): Implicit casts
- switch (from)
- {
- case NubIntType fromInt when to is NubIntType toInt && fromInt.Width < toInt.Width:
- case NubPointerType when to is NubPointerType { BaseType: NubVoidType }:
- case NubConstArrayType constArrayType1 when to is NubArrayType arrayType && constArrayType1.ElementType == arrayType.ElementType:
- case NubConstArrayType constArrayType3 when to is NubSliceType sliceType2 && constArrayType3.ElementType == sliceType2.ElementType:
- {
- return true;
- }
- }
-
- if (!strict)
- {
- // note(nub31): Explicit casts
- switch (from)
- {
- case NubIntType when to is NubIntType:
- case NubIntType when to is NubFloatType:
- case NubFloatType when to is NubIntType:
- case NubFloatType when to is NubFloatType:
- case NubPointerType when to is NubPointerType:
- case NubPointerType when to is NubIntType:
- case NubIntType when to is NubPointerType:
- {
- return true;
- }
- }
- }
-
-
- return false;
- }
-
- private AddressOfNode CheckAddressOf(AddressOfSyntax expression, NubType? expectedType)
- {
- var target = CheckExpression(expression.Target, (expectedType as NubPointerType)?.BaseType);
- if (target is not LValueExpressionNode lvalue)
- {
- throw new TypeCheckerException(Diagnostic.Error("Cannot take address of an rvalue").At(expression).Build());
- }
-
- var type = new NubPointerType(target.Type);
- return new AddressOfNode(expression.Tokens, type, lvalue);
- }
-
- private ExpressionNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression, NubType? _)
- {
- var index = CheckExpression(expression.Index);
- if (index.Type is not NubIntType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Array indexer must be of type int")
- .At(expression.Index)
- .Build());
- }
-
- var target = CheckExpression(expression.Target);
-
- return target.Type switch
- {
- NubArrayType arrayType => new ArrayIndexAccessNode(expression.Tokens, arrayType.ElementType, target, index),
- NubConstArrayType constArrayType => new ConstArrayIndexAccessNode(expression.Tokens, constArrayType.ElementType, target, index),
- NubSliceType sliceType => new SliceIndexAccessNode(expression.Tokens, sliceType.ElementType, target, index),
- _ => throw new TypeCheckerException(Diagnostic.Error($"Cannot use array indexer on type {target.Type}").At(expression).Build())
- };
- }
-
- private ExpressionNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType)
- {
- var elementType = expectedType switch
- {
- NubArrayType arrayType => arrayType.ElementType,
- NubConstArrayType constArrayType => constArrayType.ElementType,
- NubSliceType sliceType => sliceType.ElementType,
- _ => null
- };
-
- if (elementType == null)
- {
- var firstValue = expression.Values.FirstOrDefault();
- if (firstValue != null)
- {
- elementType = CheckExpression(firstValue).Type;
- }
- }
-
- if (elementType == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Unable to infer type of array initializer")
- .At(expression)
- .WithHelp("Provide a type for a variable assignment")
- .Build());
- }
-
- var values = new List();
- foreach (var valueExpression in expression.Values)
- {
- var value = CheckExpression(valueExpression, elementType);
- if (value.Type != elementType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Value in array initializer is not the same as the array type")
- .At(valueExpression)
- .Build());
- }
-
- values.Add(value);
- }
-
- return expectedType switch
- {
- NubArrayType => new ArrayInitializerNode(expression.Tokens, new NubArrayType(elementType), values),
- NubConstArrayType constArrayType => new ConstArrayInitializerNode(expression.Tokens, constArrayType, values),
- _ => new ConstArrayInitializerNode(expression.Tokens, new NubConstArrayType(elementType, expression.Values.Count), values)
- };
- }
-
- private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType)
- {
- var op = expression.Operator switch
- {
- BinaryOperatorSyntax.Equal => BinaryOperator.Equal,
- BinaryOperatorSyntax.NotEqual => BinaryOperator.NotEqual,
- BinaryOperatorSyntax.GreaterThan => BinaryOperator.GreaterThan,
- BinaryOperatorSyntax.GreaterThanOrEqual => BinaryOperator.GreaterThanOrEqual,
- BinaryOperatorSyntax.LessThan => BinaryOperator.LessThan,
- BinaryOperatorSyntax.LessThanOrEqual => BinaryOperator.LessThanOrEqual,
- BinaryOperatorSyntax.LogicalAnd => BinaryOperator.LogicalAnd,
- BinaryOperatorSyntax.LogicalOr => BinaryOperator.LogicalOr,
- BinaryOperatorSyntax.Plus => BinaryOperator.Plus,
- BinaryOperatorSyntax.Minus => BinaryOperator.Minus,
- BinaryOperatorSyntax.Multiply => BinaryOperator.Multiply,
- BinaryOperatorSyntax.Divide => BinaryOperator.Divide,
- BinaryOperatorSyntax.Modulo => BinaryOperator.Modulo,
- BinaryOperatorSyntax.LeftShift => BinaryOperator.LeftShift,
- BinaryOperatorSyntax.RightShift => BinaryOperator.RightShift,
- BinaryOperatorSyntax.BitwiseAnd => BinaryOperator.BitwiseAnd,
- BinaryOperatorSyntax.BitwiseXor => BinaryOperator.BitwiseXor,
- BinaryOperatorSyntax.BitwiseOr => BinaryOperator.BitwiseOr,
- _ => throw new ArgumentOutOfRangeException()
- };
-
- switch (expression.Operator)
- {
- case BinaryOperatorSyntax.Equal:
- case BinaryOperatorSyntax.NotEqual:
- {
- var left = CheckExpression(expression.Left);
- if (left.Type is not NubIntType and not NubFloatType and not NubBoolType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Equal and not equal operators must must be used with int, float or bool types")
- .At(expression.Left)
- .Build());
- }
-
- var right = CheckExpression(expression.Right, left.Type);
- if (right.Type != left.Type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
- .At(expression.Right)
- .Build());
- }
-
- return new BinaryExpressionNode(expression.Tokens, new NubBoolType(), left, op, right);
- }
- case BinaryOperatorSyntax.GreaterThan:
- case BinaryOperatorSyntax.GreaterThanOrEqual:
- case BinaryOperatorSyntax.LessThan:
- case BinaryOperatorSyntax.LessThanOrEqual:
- {
- var left = CheckExpression(expression.Left);
- if (left.Type is not NubIntType and not NubFloatType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Greater than and less than operators must must be used with int or float types")
- .At(expression.Left)
- .Build());
- }
-
- var right = CheckExpression(expression.Right, left.Type);
- if (right.Type != left.Type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
- .At(expression.Right)
- .Build());
- }
-
- return new BinaryExpressionNode(expression.Tokens, new NubBoolType(), left, op, right);
- }
- case BinaryOperatorSyntax.LogicalAnd:
- case BinaryOperatorSyntax.LogicalOr:
- {
- var left = CheckExpression(expression.Left);
- if (left.Type is not NubBoolType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Logical and/or must must be used with bool types")
- .At(expression.Left)
- .Build());
- }
-
- var right = CheckExpression(expression.Right, left.Type);
- if (right.Type != left.Type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
- .At(expression.Right)
- .Build());
- }
-
- return new BinaryExpressionNode(expression.Tokens, new NubBoolType(), left, op, right);
- }
- case BinaryOperatorSyntax.Plus:
- {
- var left = CheckExpression(expression.Left, expectedType);
- if (left.Type is not NubIntType and not NubFloatType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("The plus operator must only be used with int and float types")
- .At(expression.Left)
- .Build());
- }
-
- var right = CheckExpression(expression.Right, left.Type);
- if (right.Type != left.Type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
- .At(expression.Right)
- .Build());
- }
-
- return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right);
- }
- case BinaryOperatorSyntax.Minus:
- case BinaryOperatorSyntax.Multiply:
- case BinaryOperatorSyntax.Divide:
- case BinaryOperatorSyntax.Modulo:
- {
- var left = CheckExpression(expression.Left, expectedType);
- if (left.Type is not NubIntType and not NubFloatType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Math operators must be used with int or float types")
- .At(expression.Left)
- .Build());
- }
-
- var right = CheckExpression(expression.Right, left.Type);
- if (right.Type != left.Type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
- .At(expression.Right)
- .Build());
- }
-
- return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right);
- }
- case BinaryOperatorSyntax.LeftShift:
- case BinaryOperatorSyntax.RightShift:
- case BinaryOperatorSyntax.BitwiseAnd:
- case BinaryOperatorSyntax.BitwiseXor:
- case BinaryOperatorSyntax.BitwiseOr:
- {
- var left = CheckExpression(expression.Left, expectedType);
- if (left.Type is not NubIntType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Bitwise operators must be used with int types")
- .At(expression.Left)
- .Build());
- }
-
- var right = CheckExpression(expression.Right, left.Type);
- if (right.Type != left.Type)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
- .At(expression.Right)
- .Build());
- }
-
- return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right);
- }
- default:
- {
- throw new ArgumentOutOfRangeException();
- }
- }
- }
-
- private UnaryExpressionNode CheckUnaryExpression(UnaryExpressionSyntax expression, NubType? expectedType)
- {
- switch (expression.Operator)
- {
- case UnaryOperatorSyntax.Negate:
- {
- var operand = CheckExpression(expression.Operand, expectedType);
- if (operand.Type is not NubIntType { Signed: true } and not NubFloatType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Negation operator must be used with signed integer or float types")
- .At(expression)
- .Build());
- }
-
- return new UnaryExpressionNode(expression.Tokens, operand.Type, UnaryOperator.Negate, operand);
- }
- case UnaryOperatorSyntax.Invert:
- {
- var operand = CheckExpression(expression.Operand, expectedType);
- if (operand.Type is not NubBoolType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Invert operator must be used with booleans")
- .At(expression)
- .Build());
- }
-
- return new UnaryExpressionNode(expression.Tokens, operand.Type, UnaryOperator.Invert, operand);
- }
- default:
- {
- throw new ArgumentOutOfRangeException();
- }
- }
- }
-
- private DereferenceNode CheckDereference(DereferenceSyntax expression, NubType? _)
- {
- var target = CheckExpression(expression.Target);
- if (target.Type is not NubPointerType pointerType)
- {
- throw new TypeCheckerException(Diagnostic.Error($"Cannot dereference non-pointer type {target.Type}").At(expression).Build());
- }
-
- return new DereferenceNode(expression.Tokens, pointerType.BaseType, target);
- }
-
- private FuncCallNode CheckFuncCall(FuncCallSyntax expression, NubType? _)
- {
- var accessor = CheckExpression(expression.Expression);
- if (accessor.Type is not NubFuncType funcType)
- {
- throw new TypeCheckerException(Diagnostic.Error($"Cannot call non-function type {accessor.Type}").At(expression.Expression).Build());
- }
-
- if (expression.Parameters.Count != funcType.Parameters.Count)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Function {funcType} expects {funcType.Parameters.Count} parameters but got {expression.Parameters.Count}")
- .At(expression.Parameters.LastOrDefault(expression))
- .Build());
- }
-
- var parameters = new List();
- for (var i = 0; i < expression.Parameters.Count; i++)
- {
- var parameter = expression.Parameters[i];
- var expectedParameterType = funcType.Parameters[i];
-
- var parameterExpression = CheckExpression(parameter, expectedParameterType);
- if (parameterExpression.Type != expectedParameterType)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Parameter {i + 1} does not match the type {expectedParameterType} for function {funcType}")
- .At(parameter)
- .Build());
- }
-
- parameters.Add(parameterExpression);
- }
-
- return new FuncCallNode(expression.Tokens, funcType.ReturnType, accessor, parameters);
- }
-
- private ExpressionNode? CheckIdentifier(ExpressionSyntax expression, string moduleName, string name)
- {
- if (!_importedModules.TryGetValue(moduleName, out var module))
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Module {moduleName} not found")
- .WithHelp($"import \"{moduleName}\"")
- .At(expression)
- .Build());
- }
-
- var function = module.Functions(IsCurretModule(moduleName)).FirstOrDefault(x => x.Name == name);
- if (function != null)
- {
- using (BeginRootScope(moduleName))
- {
- var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList();
- var type = new NubFuncType(parameters, ResolveType(function.Prototype.ReturnType));
- return new FuncIdentifierNode(expression.Tokens, type, moduleName, name, function.Prototype.ExternSymbol);
- }
- }
-
- var enumDef = module.Enums(IsCurretModule(moduleName)).FirstOrDefault(x => x.Name == name);
- if (enumDef != null)
- {
- return new EnumReferenceIntermediateNode(expression.Tokens, moduleName, name);
- }
-
- return null;
- }
-
- private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression, NubType? _)
- {
- // note(nub31): Local identifiers can be variables or a symbol in a module
- var scopeIdent = Scope.LookupVariable(expression.Name);
- if (scopeIdent != null)
- {
- return new VariableIdentifierNode(expression.Tokens, scopeIdent.Type, expression.Name);
- }
-
- var ident = CheckIdentifier(expression, Scope.Module, expression.Name);
- if (ident == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"There is no identifier named {expression.Name}")
- .At(expression)
- .Build());
- }
-
- return ident;
- }
-
- private ExpressionNode CheckModuleIdentifier(ModuleIdentifierSyntax expression, NubType? _)
- {
- // note(nub31): Unlike local identifiers, module identifiers does not look for local variables
- var ident = CheckIdentifier(expression, expression.Module, expression.Name);
- if (ident == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Module {expression.Module} does not export a member named {expression.Name}")
- .At(expression)
- .Build());
- }
-
- return ident;
- }
-
- private ExpressionNode CheckStringLiteral(StringLiteralSyntax expression, NubType? expectedType)
- {
- if (expectedType is NubPointerType { BaseType: NubIntType { Signed: true, Width: 8 } })
- {
- return new CStringLiteralNode(expression.Tokens, expression.Value);
- }
-
- return new StringLiteralNode(expression.Tokens, expression.Value);
- }
-
- private ExpressionNode CheckIntLiteral(IntLiteralSyntax expression, NubType? expectedType)
- {
- if (expectedType is NubIntType intType)
- {
- return intType.Width switch
- {
- 8 => intType.Signed ? new I8LiteralNode(expression.Tokens, Convert.ToSByte(expression.Value, expression.Base)) : new U8LiteralNode(expression.Tokens, Convert.ToByte(expression.Value, expression.Base)),
- 16 => intType.Signed ? new I16LiteralNode(expression.Tokens, Convert.ToInt16(expression.Value, expression.Base)) : new U16LiteralNode(expression.Tokens, Convert.ToUInt16(expression.Value, expression.Base)),
- 32 => intType.Signed ? new I32LiteralNode(expression.Tokens, Convert.ToInt32(expression.Value, expression.Base)) : new U32LiteralNode(expression.Tokens, Convert.ToUInt32(expression.Value, expression.Base)),
- 64 => intType.Signed ? new I64LiteralNode(expression.Tokens, Convert.ToInt64(expression.Value, expression.Base)) : new U64LiteralNode(expression.Tokens, Convert.ToUInt64(expression.Value, expression.Base)),
- _ => throw new ArgumentOutOfRangeException()
- };
- }
-
- if (expectedType is NubFloatType floatType)
- {
- return floatType.Width switch
- {
- 32 => new Float32LiteralNode(expression.Tokens, Convert.ToSingle(expression.Value)),
- 64 => new Float64LiteralNode(expression.Tokens, Convert.ToDouble(expression.Value)),
- _ => throw new ArgumentOutOfRangeException()
- };
- }
-
- return new I64LiteralNode(expression.Tokens, Convert.ToInt64(expression.Value, expression.Base));
- }
-
- private ExpressionNode CheckFloatLiteral(FloatLiteralSyntax expression, NubType? expectedType)
- {
- if (expectedType is NubFloatType floatType)
- {
- return floatType.Width switch
- {
- 32 => new Float32LiteralNode(expression.Tokens, Convert.ToSingle(expression.Value)),
- 64 => new Float64LiteralNode(expression.Tokens, Convert.ToDouble(expression.Value)),
- _ => throw new ArgumentOutOfRangeException()
- };
- }
-
- return new Float64LiteralNode(expression.Tokens, Convert.ToDouble(expression.Value));
- }
-
- private BoolLiteralNode CheckBoolLiteral(BoolLiteralSyntax expression, NubType? _)
- {
- return new BoolLiteralNode(expression.Tokens, new NubBoolType(), expression.Value);
- }
-
- private ExpressionNode CheckMemberAccess(MemberAccessSyntax expression, NubType? _)
- {
- var target = CheckExpression(expression.Target);
-
- if (target is EnumReferenceIntermediateNode enumReferenceIntermediate)
- {
- var enumDef = _importedModules[enumReferenceIntermediate.Module]
- .Enums(IsCurretModule(enumReferenceIntermediate.Module))
- .First(x => x.Name == enumReferenceIntermediate.Name);
-
- var field = enumDef.Fields.FirstOrDefault(x => x.Name == expression.Member);
- if (field == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Enum {Scope.Module}::{enumReferenceIntermediate.Name} does not have a field named {expression.Member}")
- .At(enumDef)
- .Build());
- }
-
- var enumType = enumDef.Type != null ? ResolveType(enumDef.Type) : new NubIntType(false, 64);
- if (enumType is not NubIntType enumIntType)
- {
- throw new TypeCheckerException(Diagnostic.Error("Enum type must be an int type").At(enumDef.Type).Build());
- }
-
- return enumIntType.Width switch
- {
- 8 => enumIntType.Signed ? new I8LiteralNode(expression.Tokens, (sbyte)field.Value) : new U8LiteralNode(expression.Tokens, (byte)field.Value),
- 16 => enumIntType.Signed ? new I16LiteralNode(expression.Tokens, (short)field.Value) : new U16LiteralNode(expression.Tokens, (ushort)field.Value),
- 32 => enumIntType.Signed ? new I32LiteralNode(expression.Tokens, (int)field.Value) : new U32LiteralNode(expression.Tokens, (uint)field.Value),
- 64 => enumIntType.Signed ? new I64LiteralNode(expression.Tokens, field.Value) : new U64LiteralNode(expression.Tokens, (ulong)field.Value),
- _ => throw new ArgumentOutOfRangeException()
- };
- }
-
- if (target.Type is NubStructType structType)
- {
- var field = structType.Fields.FirstOrDefault(x => x.Name == expression.Member);
- if (field == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Struct {target.Type} does not have a field with the name {expression.Member}")
- .At(expression)
- .Build());
- }
-
- return new StructFieldAccessNode(expression.Tokens, field.Type, target, expression.Member);
- }
-
- throw new TypeCheckerException(Diagnostic
- .Error($"Cannot access struct member {expression.Member} on type {target.Type}")
- .At(expression)
- .Build());
- }
-
- private StructInitializerNode CheckStructInitializer(StructInitializerSyntax expression, NubType? expectedType)
- {
- NubStructType? structType = null;
-
- if (expression.StructType != null)
- {
- var checkedType = ResolveType(expression.StructType);
- if (checkedType is not NubStructType checkedStructType)
- {
- throw new UnreachableException("Parser fucked up");
- }
-
- structType = checkedStructType;
- }
- else if (expectedType is NubStructType expectedStructType)
- {
- structType = expectedStructType;
- }
-
- if (structType == null)
- {
- throw new TypeCheckerException(Diagnostic
- .Error("Cannot get implicit type of struct")
- .WithHelp("Specify struct type with struct {type_name} syntax")
- .At(expression)
- .Build());
- }
-
- var initializers = new Dictionary();
-
- foreach (var initializer in expression.Initializers)
- {
- var typeField = structType.Fields.FirstOrDefault(x => x.Name == initializer.Key);
- if (typeField == null)
- {
- Diagnostics.AddRange(Diagnostic
- .Error($"Struct {structType.Name} does not have a field named {initializer.Key}")
- .At(initializer.Value)
- .Build());
-
- continue;
- }
-
- initializers.Add(initializer.Key, CheckExpression(initializer.Value, typeField.Type));
- }
-
- var missingFields = structType.Fields
- .Where(x => !x.HasDefaultValue && !initializers.ContainsKey(x.Name))
- .Select(x => x.Name)
- .ToArray();
-
- if (missingFields.Length != 0)
- {
- Diagnostics.Add(Diagnostic
- .Warning($"Fields {string.Join(", ", missingFields)} are not initialized")
- .At(expression)
- .Build());
- }
-
- return new StructInitializerNode(expression.Tokens, structType, initializers);
- }
-
- private BlockNode CheckBlock(BlockSyntax node)
- {
- using (BeginScope())
- {
- var statements = new List();
- foreach (var statement in node.Statements)
- {
- try
- {
- statements.Add(CheckStatement(statement));
- }
- catch (TypeCheckerException e)
- {
- Diagnostics.Add(e.Diagnostic);
- }
- }
-
- return new BlockNode(node.Tokens, statements);
- }
- }
-
- private StatementNode CheckStatement(StatementSyntax statement)
- {
- return statement switch
- {
- AssignmentSyntax assignmentStmt => CheckAssignment(assignmentStmt),
- BlockSyntax blockStmt => CheckBlock(blockStmt),
- BreakSyntax => new BreakNode(statement.Tokens),
- ContinueSyntax => new ContinueNode(statement.Tokens),
- IfSyntax ifStmt => CheckIf(ifStmt),
- ReturnSyntax retStmt => CheckReturn(retStmt),
- StatementExpressionSyntax stmtExpr => CheckStatementExpression(stmtExpr),
- VariableDeclarationSyntax varDeclStmt => CheckVariableDeclaration(varDeclStmt),
- WhileSyntax whileStmt => CheckWhile(whileStmt),
- DeferSyntax defer => new DeferNode(statement.Tokens, CheckStatement(defer.Statement)),
- ForSyntax forSyntax => CheckFor(forSyntax),
- _ => throw new ArgumentOutOfRangeException(nameof(statement))
- };
- }
-
- private NubType ResolveType(TypeSyntax type)
- {
- return type switch
- {
- ArrayTypeSyntax arr => new NubArrayType(ResolveType(arr.BaseType)),
- BoolTypeSyntax => new NubBoolType(),
- IntTypeSyntax i => new NubIntType(i.Signed, i.Width),
- FloatTypeSyntax f => new NubFloatType(f.Width),
- FuncTypeSyntax func => new NubFuncType(func.Parameters.Select(ResolveType).ToList(), ResolveType(func.ReturnType)),
- SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType)),
- ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType), arr.Size),
- PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType)),
- StringTypeSyntax => new NubStringType(),
- CustomTypeSyntax c => ResolveCustomType(c),
- VoidTypeSyntax => new NubVoidType(),
- _ => throw new NotSupportedException($"Unknown type syntax: {type}")
- };
- }
-
- private NubType ResolveCustomType(CustomTypeSyntax customType)
- {
- if (!_importedModules.TryGetValue(customType.Module ?? Scope.Module, out var module))
- {
- throw new TypeCheckerException(Diagnostic
- .Error($"Module {customType.Module ?? Scope.Module} not found")
- .WithHelp($"import \"{customType.Module ?? Scope.Module}\"")
- .At(customType)
- .Build());
- }
-
- var enumDef = module.Enums(IsCurretModule(customType.Module)).FirstOrDefault(x => x.Name == customType.Name);
- if (enumDef != null)
- {
- return enumDef.Type != null ? ResolveType(enumDef.Type) : new NubIntType(false, 64);
- }
-
- var structDef = module.Structs(IsCurretModule(customType.Module)).FirstOrDefault(x => x.Name == customType.Name);
- if (structDef != null)
- {
- var key = (customType.Module ?? Scope.Module, customType.Name);
-
- if (_typeCache.TryGetValue(key, out var cachedType))
- {
- return cachedType;
- }
-
- if (!_resolvingTypes.Add(key))
- {
- var placeholder = new NubStructType(customType.Module ?? Scope.Module, customType.Name, []);
- _typeCache[key] = placeholder;
- return placeholder;
- }
-
- try
- {
- var result = new NubStructType(customType.Module ?? Scope.Module, structDef.Name, []);
- _typeCache[key] = result;
-
- var fields = structDef.Fields
- .Select(x => new NubStructFieldType(x.Name, ResolveType(x.Type), x.Value != null))
- .ToList();
-
- result.Fields.AddRange(fields);
- return result;
- }
- finally
- {
- _resolvingTypes.Remove(key);
- }
- }
-
- throw new TypeCheckerException(Diagnostic
- .Error($"Type {customType.Name} not found in module {customType.Module ?? Scope.Module}")
- .At(customType)
- .Build());
- }
-
- private bool IsCurretModule(string? module)
- {
- if (module == null)
- {
- return true;
- }
-
- return module == Scope.Module;
- }
-}
-
-public record Variable(string Name, NubType Type);
-
-public class Scope(string module, Scope? parent = null)
-{
- private NubType? _returnType;
- private readonly List _variables = [];
- public string Module { get; } = module;
-
- public void DeclareVariable(Variable variable)
- {
- _variables.Add(variable);
- }
-
- public void SetReturnType(NubType returnType)
- {
- _returnType = returnType;
- }
-
- public NubType? GetReturnType()
- {
- return _returnType ?? parent?.GetReturnType();
- }
-
- public Variable? LookupVariable(string name)
- {
- var variable = _variables.FirstOrDefault(x => x.Name == name);
- if (variable != null)
- {
- return variable;
- }
-
- return parent?.LookupVariable(name);
- }
-
- public Scope SubScope()
- {
- return new Scope(Module, this);
- }
-}
-
-public class TypeCheckerException : Exception
-{
- public Diagnostic Diagnostic { get; }
-
- public TypeCheckerException(Diagnostic diagnostic) : base(diagnostic.Message)
- {
- Diagnostic = diagnostic;
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Diagnostics/Diagnostic.cs b/compiler/NubLang/Diagnostics/Diagnostic.cs
deleted file mode 100644
index bf3cf54..0000000
--- a/compiler/NubLang/Diagnostics/Diagnostic.cs
+++ /dev/null
@@ -1,398 +0,0 @@
-using System.Text;
-using NubLang.Syntax;
-
-namespace NubLang.Diagnostics;
-
-public class Diagnostic
-{
- public class DiagnosticBuilder
- {
- private readonly DiagnosticSeverity _severity;
- private readonly string _message;
- private SourceSpan? _span;
- private string? _help;
-
- public DiagnosticBuilder(DiagnosticSeverity severity, string message)
- {
- _severity = severity;
- _message = message;
- }
-
- public DiagnosticBuilder At(SyntaxNode? node)
- {
- if (node != null)
- {
- _span = SourceSpan.Merge(node.Tokens.Select(x => x.Span));
- }
-
- return this;
- }
-
- public DiagnosticBuilder At(Token? token)
- {
- if (token != null)
- {
- At(token.Span);
- }
-
- return this;
- }
-
- public DiagnosticBuilder At(SourceSpan? span)
- {
- if (span != null)
- {
- _span = span;
- }
-
- return this;
- }
-
- public DiagnosticBuilder At(string filePath, int line, int column)
- {
- _span = new SourceSpan(filePath, new SourceLocation(line, column), new SourceLocation(line, column));
- return this;
- }
-
- public DiagnosticBuilder WithHelp(string help)
- {
- _help = help;
- return this;
- }
-
- public Diagnostic Build() => new(_severity, _message, _help, _span);
- }
-
- public static DiagnosticBuilder Error(string message) => new(DiagnosticSeverity.Error, message);
- public static DiagnosticBuilder Warning(string message) => new(DiagnosticSeverity.Warning, message);
- public static DiagnosticBuilder Info(string message) => new(DiagnosticSeverity.Info, message);
-
- public DiagnosticSeverity Severity { get; }
- public string Message { get; }
- public string? Help { get; }
- public SourceSpan? Span { get; }
-
- private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceSpan? span)
- {
- Severity = severity;
- Message = message;
- Help = help;
- Span = span;
- }
-
- public string FormatANSI()
- {
- var sb = new StringBuilder();
-
- sb.Append(Severity switch
- {
- DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red),
- DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow),
- DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue),
- _ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White)
- });
-
- if (Span.HasValue)
- {
- sb.Append(ConsoleColors.Colorize($" at {Span.Value}", ConsoleColors.Faint));
- }
-
- sb.Append(": ");
- sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite));
-
- if (Span.HasValue)
- {
- sb.AppendLine();
- var text = File.ReadAllText(Span.Value.FilePath);
-
- var tokenizer = new Tokenizer(Span.Value.FilePath, text);
- tokenizer.Tokenize();
-
- var lines = text.Split('\n');
-
- var startLine = Span.Value.Start.Line;
- var endLine = Span.Value.End.Line;
-
- const int CONTEXT_LINES = 3;
-
- var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES);
- var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES);
-
- var numberPadding = contextEndLine.ToString().Length;
-
- var codePadding = 0;
- for (var i = contextStartLine - 1; i < contextEndLine && i < lines.Length; i++)
- {
- var lineLength = lines[i].Length;
- if (lineLength > codePadding)
- {
- codePadding = lineLength;
- }
- }
-
- sb.Append('╭');
- sb.Append(new string('─', numberPadding + 2));
- sb.Append('┬');
- sb.Append(new string('─', codePadding + 2));
- sb.Append('╮');
- sb.AppendLine();
-
- for (var i = contextStartLine; i <= contextEndLine; i++)
- {
- var line = lines[i - 1];
-
- sb.Append("│ ");
- sb.Append(i.ToString().PadRight(numberPadding));
- sb.Append(" │ ");
- sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens));
- // sb.Append(line.PadRight(codePadding));
- sb.Append(" │");
- sb.AppendLine();
-
- if (i >= startLine && i <= endLine)
- {
- var markerStartColumn = 1;
- var markerEndColumn = line.Length;
-
- if (i == startLine)
- {
- markerStartColumn = Span.Value.Start.Column;
- }
-
- if (i == endLine)
- {
- markerEndColumn = Span.Value.End.Column;
- }
-
- var markerLength = markerEndColumn - markerStartColumn;
- var marker = new string('^', markerLength);
-
- var markerColor = Severity switch
- {
- DiagnosticSeverity.Info => ConsoleColors.Blue,
- DiagnosticSeverity.Warning => ConsoleColors.Yellow,
- DiagnosticSeverity.Error => ConsoleColors.Red,
- _ => ConsoleColors.White
- };
-
- sb.Append("│ ");
- sb.Append(new string(' ', numberPadding));
- sb.Append(" │ ");
- sb.Append(new string(' ', markerStartColumn - 1));
- sb.Append(ConsoleColors.Colorize(marker, markerColor));
- sb.Append(new string(' ', codePadding - (markerStartColumn - 1) - markerLength));
- sb.Append(" │");
- sb.AppendLine();
- }
- }
-
- sb.Append('╰');
- sb.Append(new string('─', numberPadding + 2));
- sb.Append('┴');
- sb.Append(new string('─', codePadding + 2));
- sb.Append('╯');
- }
-
- if (Help != null)
- {
- sb.AppendLine();
- sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan));
- }
-
- return sb.ToString();
- }
-
- private static string ApplySyntaxHighlighting(string line, int lineNumber, List tokens)
- {
- var sb = new StringBuilder();
- var lineTokens = tokens
- .Where(t => t.Span.Start.Line == lineNumber)
- .OrderBy(t => t.Span.Start.Column)
- .ToList();
-
- if (lineTokens.Count == 0)
- {
- return line;
- }
-
- var currentColumn = 1;
-
- foreach (var token in lineTokens)
- {
- var tokenStart = token.Span.Start.Column;
- var tokenEnd = token.Span.End.Column;
-
- if (tokenStart > currentColumn && currentColumn - 1 < line.Length)
- {
- var beforeLength = Math.Min(tokenStart - currentColumn, line.Length - (currentColumn - 1));
- if (beforeLength > 0)
- {
- var beforeToken = line.Substring(currentColumn - 1, beforeLength);
- sb.Append(beforeToken);
- }
- }
-
- var tokenLength = tokenEnd - tokenStart;
- if (tokenStart >= 1 && tokenStart - 1 < line.Length && tokenLength > 0)
- {
- var availableLength = line.Length - (tokenStart - 1);
- var actualLength = Math.Min(tokenLength, availableLength);
-
- if (actualLength > 0)
- {
- var tokenText = line.Substring(tokenStart - 1, actualLength);
- var coloredToken = ColorizeToken(token, tokenText);
- sb.Append(coloredToken);
- }
- }
-
- currentColumn = tokenEnd;
- }
-
- if (currentColumn - 1 < line.Length)
- {
- var remaining = line[(currentColumn - 1)..];
- sb.Append(remaining);
- }
-
- return sb.ToString();
- }
-
- private static string ColorizeToken(Token token, string tokenText)
- {
- switch (token)
- {
- case IdentifierToken:
- {
- return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightWhite);
- }
- case StringLiteralToken:
- {
- return ConsoleColors.Colorize(tokenText, ConsoleColors.Green);
- }
- case IntLiteralToken:
- case FloatLiteralToken:
- case BoolLiteralToken:
- {
- return ConsoleColors.Colorize(tokenText, ConsoleColors.Magenta);
- }
- case SymbolToken symbolToken:
- {
- switch (symbolToken.Symbol)
- {
- case Symbol.Func:
- case Symbol.Return:
- case Symbol.If:
- case Symbol.Else:
- case Symbol.While:
- case Symbol.Break:
- case Symbol.Continue:
- case Symbol.Struct:
- case Symbol.Let:
- case Symbol.Extern:
- case Symbol.For:
- case Symbol.In:
- {
- return ConsoleColors.Colorize(tokenText, ConsoleColors.Bold + ConsoleColors.Blue);
- }
- case Symbol.Assign:
- case Symbol.Bang:
- case Symbol.Equal:
- case Symbol.NotEqual:
- case Symbol.LessThan:
- case Symbol.LessThanOrEqual:
- case Symbol.GreaterThan:
- case Symbol.GreaterThanOrEqual:
- case Symbol.Plus:
- case Symbol.Minus:
- case Symbol.Star:
- case Symbol.ForwardSlash:
- case Symbol.Caret:
- case Symbol.Ampersand:
- {
- return ConsoleColors.Colorize(tokenText, ConsoleColors.Yellow);
- }
- case Symbol.Colon:
- case Symbol.OpenParen:
- case Symbol.CloseParen:
- case Symbol.OpenBrace:
- case Symbol.CloseBrace:
- case Symbol.OpenBracket:
- case Symbol.CloseBracket:
- case Symbol.Comma:
- case Symbol.Period:
- case Symbol.Semi:
- {
- return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightBlack);
- }
- }
-
- break;
- }
- }
-
- return tokenText;
- }
-}
-
-public enum DiagnosticSeverity
-{
- Info,
- Warning,
- Error
-}
-
-public static class ConsoleColors
-{
- public const string Reset = "\e[0m";
- public const string Bold = "\e[1m";
- public const string Faint = "\e[2m";
- public const string Italic = "\e[3m";
- public const string Underline = "\e[4m";
- public const string SlowBlink = "\e[5m";
- public const string RapidBlink = "\e[6m";
- public const string SwapBgAndFg = "\e[7m";
- public const string Conceal = "\e[8m";
- public const string CrossedOut = "\e[9m";
-
- public const string DefaultFont = "\e[10m";
- public const string AltFont1 = "\e[11m";
- public const string AltFont2 = "\e[12m";
- public const string AltFont3 = "\e[13m";
- public const string AltFont4 = "\e[14m";
- public const string AltFont5 = "\e[15m";
- public const string AltFont6 = "\e[16m";
- public const string AltFont7 = "\e[17m";
- public const string AltFont8 = "\e[18m";
- public const string AltFont9 = "\e[19m";
-
- public const string Black = "\e[30m";
- public const string Red = "\e[31m";
- public const string Green = "\e[32m";
- public const string Yellow = "\e[33m";
- public const string Blue = "\e[34m";
- public const string Magenta = "\e[35m";
- public const string Cyan = "\e[36m";
- public const string White = "\e[37m";
-
- public const string BrightBlack = "\e[90m";
- public const string BrightRed = "\e[91m";
- public const string BrightGreen = "\e[92m";
- public const string BrightYellow = "\e[93m";
- public const string BrightBlue = "\e[94m";
- public const string BrightMagenta = "\e[95m";
- public const string BrightCyan = "\e[96m";
- public const string BrightWhite = "\e[97m";
-
- private static bool IsColorSupported()
- {
- var term = Environment.GetEnvironmentVariable("TERM");
- var colorTerm = Environment.GetEnvironmentVariable("COLORTERM");
- return !string.IsNullOrEmpty(term) || !string.IsNullOrEmpty(colorTerm) || !Console.IsOutputRedirected;
- }
-
- public static string Colorize(string text, string color)
- {
- return IsColorSupported() ? $"{color}{text}{Reset}" : text;
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Diagnostics/SourceSpan.cs b/compiler/NubLang/Diagnostics/SourceSpan.cs
deleted file mode 100644
index 121fe6e..0000000
--- a/compiler/NubLang/Diagnostics/SourceSpan.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-namespace NubLang.Diagnostics;
-
-public readonly struct SourceSpan : IEquatable, IComparable
-{
- public static SourceSpan Merge(params IEnumerable spans)
- {
- var spanArray = spans as SourceSpan[] ?? spans.ToArray();
- if (spanArray.Length == 0)
- {
- return new SourceSpan(string.Empty, new SourceLocation(0, 0), new SourceLocation(0, 0));
- }
-
- var minStart = spanArray.Min(s => s.Start);
- var maxEnd = spanArray.Max(s => s.End);
-
- return new SourceSpan(spanArray[0].FilePath, minStart, maxEnd);
- }
-
- public SourceSpan(string filePath, SourceLocation start, SourceLocation end)
- {
- if (start > end)
- {
- throw new ArgumentException("Start location cannot be after end location");
- }
-
- FilePath = filePath;
- Start = start;
- End = end;
- }
-
- public string FilePath { get; }
- public SourceLocation Start { get; }
- public SourceLocation End { get; }
-
- public override string ToString()
- {
- if (Start == End)
- {
- return $"{FilePath}:{Start}";
- }
-
- if (Start.Line == End.Line)
- {
- return Start.Column == End.Column ? $"{FilePath}:{Start}" : $"{FilePath}:{Start.Line}:{Start.Column}-{End.Column}";
- }
-
- return $"{FilePath}:{Start}-{End}";
- }
-
- public bool Equals(SourceSpan other) => Start == other.Start && End == other.End;
- public override bool Equals(object? obj) => obj is SourceSpan other && Equals(other);
- public override int GetHashCode() => HashCode.Combine(typeof(SourceSpan), Start, End);
-
- public static bool operator ==(SourceSpan left, SourceSpan right) => Equals(left, right);
- public static bool operator !=(SourceSpan left, SourceSpan right) => !Equals(left, right);
-
- public static bool operator <(SourceSpan left, SourceSpan right) => left.CompareTo(right) < 0;
- public static bool operator <=(SourceSpan left, SourceSpan right) => left.CompareTo(right) <= 0;
- public static bool operator >(SourceSpan left, SourceSpan right) => left.CompareTo(right) > 0;
- public static bool operator >=(SourceSpan left, SourceSpan right) => left.CompareTo(right) >= 0;
-
- public int CompareTo(SourceSpan other)
- {
- var startComparison = Start.CompareTo(other.Start);
- return startComparison != 0 ? startComparison : End.CompareTo(other.End);
- }
-}
-
-public readonly struct SourceLocation : IEquatable, IComparable
-{
- public SourceLocation(int line, int column)
- {
- Line = line;
- Column = column;
- }
-
- public int Line { get; }
- public int Column { get; }
-
- public override string ToString()
- {
- return $"{Line}:{Column}";
- }
-
- public override bool Equals(object? obj)
- {
- return obj is SourceLocation other && Equals(other);
- }
-
- public bool Equals(SourceLocation other)
- {
- return Line == other.Line && Column == other.Column;
- }
-
- public override int GetHashCode()
- {
- return HashCode.Combine(typeof(SourceLocation), Line, Column);
- }
-
- public static bool operator ==(SourceLocation left, SourceLocation right) => Equals(left, right);
- public static bool operator !=(SourceLocation left, SourceLocation right) => !Equals(left, right);
- public static bool operator <(SourceLocation left, SourceLocation right) => left.Line < right.Line || (left.Line == right.Line && left.Column < right.Column);
- public static bool operator >(SourceLocation left, SourceLocation right) => left.Line > right.Line || (left.Line == right.Line && left.Column > right.Column);
- public static bool operator <=(SourceLocation left, SourceLocation right) => left.Line <= right.Line || (left.Line == right.Line && left.Column <= right.Column);
- public static bool operator >=(SourceLocation left, SourceLocation right) => left.Line >= right.Line || (left.Line == right.Line && left.Column >= right.Column);
-
- public int CompareTo(SourceLocation other)
- {
- var lineComparison = Line.CompareTo(other.Line);
- return lineComparison != 0 ? lineComparison : Column.CompareTo(other.Column);
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Generation/CType.cs b/compiler/NubLang/Generation/CType.cs
deleted file mode 100644
index 9fe4287..0000000
--- a/compiler/NubLang/Generation/CType.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using NubLang.Ast;
-
-namespace NubLang.Generation;
-
-public static class CType
-{
- public static string Create(NubType type, string? variableName = null, bool constArraysAsPointers = true)
- {
- return type switch
- {
- NubVoidType => "void" + (variableName != null ? $" {variableName}" : ""),
- NubBoolType => "bool" + (variableName != null ? $" {variableName}" : ""),
- NubIntType intType => CreateIntType(intType, variableName),
- NubFloatType floatType => CreateFloatType(floatType, variableName),
- NubPointerType ptr => CreatePointerType(ptr, variableName),
- NubSliceType => "struct nub_slice" + (variableName != null ? $" {variableName}" : ""),
- NubStringType => "struct nub_string" + (variableName != null ? $" {variableName}" : ""),
- NubConstArrayType arr => CreateConstArrayType(arr, variableName, constArraysAsPointers),
- NubArrayType arr => CreateArrayType(arr, variableName),
- NubFuncType fn => CreateFuncType(fn, variableName),
- NubStructType st => $"struct {st.Module}_{st.Name}_{NameMangler.Mangle(st)}" + (variableName != null ? $" {variableName}" : ""),
- _ => throw new NotSupportedException($"C type generation not supported for: {type}")
- };
- }
-
- private static string CreateIntType(NubIntType intType, string? varName)
- {
- var cType = intType.Width switch
- {
- 8 => intType.Signed ? "char" : "unsigned char",
- 16 => intType.Signed ? "short" : "unsigned short",
- 32 => intType.Signed ? "int" : "unsigned int",
- 64 => intType.Signed ? "long long" : "unsigned long long",
- _ => throw new NotSupportedException($"Unsupported integer width: {intType.Width}")
- };
- return cType + (varName != null ? $" {varName}" : "");
- }
-
- private static string CreateFloatType(NubFloatType floatType, string? varName)
- {
- var cType = floatType.Width switch
- {
- 32 => "float",
- 64 => "double",
- _ => throw new NotSupportedException($"Unsupported float width: {floatType.Width}")
- };
- return cType + (varName != null ? $" {varName}" : "");
- }
-
- private static string CreatePointerType(NubPointerType ptr, string? varName)
- {
- var baseType = Create(ptr.BaseType);
- return baseType + "*" + (varName != null ? $" {varName}" : "");
- }
-
- private static string CreateConstArrayType(NubConstArrayType arr, string? varName, bool inStructDef)
- {
- var elementType = Create(arr.ElementType);
-
- // Treat const arrays as pointers unless in a struct definition
- if (!inStructDef)
- {
- return elementType + "*" + (varName != null ? $" {varName}" : "");
- }
-
- if (varName != null)
- {
- return $"{elementType} {varName}[{arr.Size}]";
- }
-
- return $"{elementType}[{arr.Size}]";
- }
-
- private static string CreateArrayType(NubArrayType arr, string? varName)
- {
- var elementType = Create(arr.ElementType);
- return elementType + "*" + (varName != null ? $" {varName}" : "");
- }
-
- private static string CreateFuncType(NubFuncType fn, string? varName)
- {
- var returnType = Create(fn.ReturnType);
- var parameters = string.Join(", ", fn.Parameters.Select(p => Create(p)));
-
- if (string.IsNullOrEmpty(parameters))
- {
- parameters = "void";
- }
-
- if (varName != null)
- {
- return $"{returnType} (*{varName})({parameters})";
- }
-
- return $"{returnType} (*)({parameters})";
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Generation/Generator.cs b/compiler/NubLang/Generation/Generator.cs
deleted file mode 100644
index e7fc18e..0000000
--- a/compiler/NubLang/Generation/Generator.cs
+++ /dev/null
@@ -1,590 +0,0 @@
-using System.Diagnostics;
-using System.Text;
-using NubLang.Ast;
-using NubLang.Syntax;
-
-namespace NubLang.Generation;
-
-public class Generator
-{
- private readonly CompilationUnit _compilationUnit;
- private readonly IndentedTextWriter _writer;
- private readonly Stack> _deferStack = [];
- private int _tmpIndex;
-
- public Generator(CompilationUnit compilationUnit)
- {
- _compilationUnit = compilationUnit;
- _writer = new IndentedTextWriter();
- }
-
- // todo(nub31): Handle name collisions
- private string NewTmp()
- {
- return $"_t{++_tmpIndex}";
- }
-
- private static string FuncName(string module, string name, string? externSymbol)
- {
- return externSymbol ?? $"{module}_{name}";
- }
-
- public string Emit()
- {
- _writer.WriteLine("""
- struct nub_string
- {
- unsigned long long length;
- char *data;
- };
-
- struct nub_slice
- {
- unsigned long long length;
- void *data;
- };
-
- """);
-
- foreach (var structType in _compilationUnit.ImportedStructTypes)
- {
- _writer.WriteLine(CType.Create(structType));
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- foreach (var field in structType.Fields)
- {
- _writer.WriteLine($"{CType.Create(field.Type, field.Name, constArraysAsPointers: false)};");
- }
- }
-
- _writer.WriteLine("};");
- _writer.WriteLine();
- }
-
- // note(nub31): Forward declarations
- foreach (var prototype in _compilationUnit.ImportedFunctions)
- {
- EmitLine(prototype.Tokens.FirstOrDefault());
- var parameters = prototype.Parameters.Count != 0
- ? string.Join(", ", prototype.Parameters.Select(x => CType.Create(x.Type, x.Name)))
- : "void";
-
- var name = FuncName(prototype.Module, prototype.Name, prototype.ExternSymbol);
- _writer.WriteLine($"{CType.Create(prototype.ReturnType, name)}({parameters});");
- _writer.WriteLine();
- }
-
- // note(nub31): Normal functions
- foreach (var funcNode in _compilationUnit.Functions)
- {
- if (funcNode.Body == null) continue;
-
- EmitLine(funcNode.Tokens.FirstOrDefault());
- var parameters = funcNode.Prototype.Parameters.Count != 0
- ? string.Join(", ", funcNode.Prototype.Parameters.Select(x => CType.Create(x.Type, x.Name)))
- : "void";
-
- var name = FuncName(funcNode.Module, funcNode.Name, funcNode.Prototype.ExternSymbol);
- _writer.WriteLine($"{CType.Create(funcNode.Prototype.ReturnType, name)}({parameters})");
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- EmitBlock(funcNode.Body);
- }
-
- _writer.WriteLine("}");
- _writer.WriteLine();
- }
-
- return _writer.ToString();
- }
-
- private void EmitStatement(StatementNode statementNode)
- {
- EmitLine(statementNode.Tokens.FirstOrDefault());
- switch (statementNode)
- {
- case AssignmentNode assignmentNode:
- EmitAssignment(assignmentNode);
- break;
- case BlockNode blockNode:
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- EmitBlock(blockNode);
- }
-
- _writer.WriteLine("}");
- break;
- case BreakNode breakNode:
- EmitBreak(breakNode);
- break;
- case ContinueNode continueNode:
- EmitContinue(continueNode);
- break;
- case DeferNode deferNode:
- EmitDefer(deferNode);
- break;
- case ForConstArrayNode forConstArrayNode:
- EmitForConstArray(forConstArrayNode);
- break;
- case ForSliceNode forSliceNode:
- EmitForSlice(forSliceNode);
- break;
- case IfNode ifNode:
- EmitIf(ifNode);
- break;
- case ReturnNode returnNode:
- EmitReturn(returnNode);
- break;
- case StatementFuncCallNode statementFuncCallNode:
- EmitStatementFuncCall(statementFuncCallNode);
- break;
- case VariableDeclarationNode variableDeclarationNode:
- EmitVariableDeclaration(variableDeclarationNode);
- break;
- case WhileNode whileNode:
- EmitWhile(whileNode);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(statementNode));
- }
- }
-
- private void EmitLine(Token? token)
- {
- if (token == null) return;
- var file = token.Span.FilePath;
- var line = token.Span.Start.Line;
- _writer.WriteLine($"#line {line} \"{file}\"");
- }
-
- private void EmitAssignment(AssignmentNode assignmentNode)
- {
- var target = EmitExpression(assignmentNode.Target);
- var value = EmitExpression(assignmentNode.Value);
- _writer.WriteLine($"{target} = {value};");
- }
-
- private void EmitBreak(BreakNode _)
- {
- // todo(nub31): Emit deferred statements
- _writer.WriteLine("break;");
- }
-
- private void EmitContinue(ContinueNode _)
- {
- // todo(nub31): Emit deferred statements
- _writer.WriteLine("continue;");
- }
-
- private void EmitDefer(DeferNode deferNode)
- {
- _deferStack.Peek().Add(deferNode);
- }
-
- private void EmitForSlice(ForSliceNode forSliceNode)
- {
- var targetType = (NubSliceType)forSliceNode.Target.Type;
- var target = EmitExpression(forSliceNode.Target);
- var indexName = forSliceNode.IndexName ?? NewTmp();
-
- _writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {target}.length; ++{indexName})");
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- _writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementName)} = (({CType.Create(targetType.ElementType)}*){target}.data)[{indexName}];");
- EmitBlock(forSliceNode.Body);
- }
-
- _writer.WriteLine("}");
- }
-
- private void EmitForConstArray(ForConstArrayNode forConstArrayNode)
- {
- var targetType = (NubConstArrayType)forConstArrayNode.Target.Type;
- var target = EmitExpression(forConstArrayNode.Target);
- var indexName = forConstArrayNode.IndexName ?? NewTmp();
-
- _writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {targetType.Size}; ++{indexName})");
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- _writer.WriteLine($"{CType.Create(targetType.ElementType, forConstArrayNode.ElementName)} = {target}[{indexName}];");
- EmitBlock(forConstArrayNode.Body);
- }
-
- _writer.WriteLine("}");
- }
-
- private void EmitIf(IfNode ifNode, bool elseIf = false)
- {
- var condition = EmitExpression(ifNode.Condition);
- _writer.WriteLine($"{(elseIf ? "else " : "")}if ({condition})");
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- EmitBlock(ifNode.Body);
- }
-
- _writer.WriteLine("}");
- ifNode.Else?.Match
- (
- elseIfNode => EmitIf(elseIfNode, true),
- elseNode =>
- {
- _writer.WriteLine("else");
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- EmitBlock(elseNode);
- }
-
- _writer.WriteLine("}");
- }
- );
- }
-
- private void EmitReturn(ReturnNode returnNode)
- {
- if (returnNode.Value == null)
- {
- var blockDefers = _deferStack.Peek();
- for (var i = blockDefers.Count - 1; i >= 0; i--)
- {
- EmitStatement(blockDefers[i].Statement);
- }
-
- _writer.WriteLine("return;");
- }
- else
- {
- var returnValue = EmitExpression(returnNode.Value);
-
- if (_deferStack.Peek().Count != 0)
- {
- var tmp = NewTmp();
- _writer.WriteLine($"{CType.Create(returnNode.Value.Type, tmp)} = {returnValue};");
-
- var blockDefers = _deferStack.Peek();
- for (var i = blockDefers.Count - 1; i >= 0; i--)
- {
- EmitStatement(blockDefers[i].Statement);
- }
-
- EmitLine(returnNode.Tokens.FirstOrDefault());
- _writer.WriteLine($"return {tmp};");
- }
- else
- {
- EmitLine(returnNode.Tokens.FirstOrDefault());
- _writer.WriteLine($"return {returnValue};");
- }
- }
- }
-
- private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode)
- {
- var funcCall = EmitFuncCall(statementFuncCallNode.FuncCall);
- _writer.WriteLine($"{funcCall};");
- }
-
- private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode)
- {
- if (variableDeclarationNode.Assignment != null)
- {
- var value = EmitExpression(variableDeclarationNode.Assignment);
- _writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.Name)} = {value};");
- }
- else
- {
- _writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.Name)};");
- }
- }
-
- private void EmitWhile(WhileNode whileNode)
- {
- var condition = EmitExpression(whileNode.Condition);
- _writer.WriteLine($"while ({condition})");
- _writer.WriteLine("{");
- using (_writer.Indent())
- {
- EmitBlock(whileNode.Body);
- }
-
- _writer.WriteLine("}");
- }
-
- private string EmitExpression(ExpressionNode expressionNode)
- {
- if (expressionNode is IntermediateExpression)
- {
- throw new UnreachableException("Type checker fucked up");
- }
-
- var expr = expressionNode switch
- {
- ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(arrayIndexAccessNode),
- ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode),
- BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode),
- BoolLiteralNode boolLiteralNode => boolLiteralNode.Value ? "true" : "false",
- ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(constArrayIndexAccessNode),
- ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode),
- CStringLiteralNode cStringLiteralNode => $"\"{cStringLiteralNode.Value}\"",
- DereferenceNode dereferenceNode => EmitDereference(dereferenceNode),
- Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode),
- Float64LiteralNode float64LiteralNode => EmitFloat64Literal(float64LiteralNode),
- CastNode castNode => EmitCast(castNode),
- FuncCallNode funcCallNode => EmitFuncCall(funcCallNode),
- FuncIdentifierNode funcIdentifierNode => FuncName(funcIdentifierNode.Module, funcIdentifierNode.Name, funcIdentifierNode.ExternSymbol),
- AddressOfNode addressOfNode => EmitAddressOf(addressOfNode),
- SizeNode sizeBuiltinNode => $"sizeof({CType.Create(sizeBuiltinNode.TargetType)})",
- SliceIndexAccessNode sliceIndexAccessNode => EmitSliceArrayIndexAccess(sliceIndexAccessNode),
- StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode),
- StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(structFieldAccessNode),
- StructInitializerNode structInitializerNode => EmitStructInitializer(structInitializerNode),
- I8LiteralNode i8LiteralNode => EmitI8Literal(i8LiteralNode),
- I16LiteralNode i16LiteralNode => EmitI16Literal(i16LiteralNode),
- I32LiteralNode i32LiteralNode => EmitI32Literal(i32LiteralNode),
- I64LiteralNode i64LiteralNode => EmitI64Literal(i64LiteralNode),
- U8LiteralNode u8LiteralNode => EmitU8Literal(u8LiteralNode),
- U16LiteralNode u16LiteralNode => EmitU16Literal(u16LiteralNode),
- U32LiteralNode u32LiteralNode => EmitU32Literal(u32LiteralNode),
- U64LiteralNode u64LiteralNode => EmitU64Literal(u64LiteralNode),
- UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(unaryExpressionNode),
- VariableIdentifierNode variableIdentifierNode => variableIdentifierNode.Name,
- _ => throw new ArgumentOutOfRangeException(nameof(expressionNode))
- };
-
- return $"({expr})";
- }
-
- private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode)
- {
- var target = EmitExpression(arrayIndexAccessNode.Target);
- var index = EmitExpression(arrayIndexAccessNode.Index);
- return $"{target}[{index}]";
- }
-
- private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode)
- {
- var values = new List();
- foreach (var value in arrayInitializerNode.Values)
- {
- values.Add(EmitExpression(value));
- }
-
- var arrayType = (NubArrayType)arrayInitializerNode.Type;
- return $"({CType.Create(arrayType.ElementType)}[]){{{string.Join(", ", values)}}}";
- }
-
- private string EmitBinaryExpression(BinaryExpressionNode binaryExpressionNode)
- {
- var left = EmitExpression(binaryExpressionNode.Left);
- var right = EmitExpression(binaryExpressionNode.Right);
-
- var op = binaryExpressionNode.Operator switch
- {
- BinaryOperator.Plus => "+",
- BinaryOperator.Minus => "-",
- BinaryOperator.Multiply => "*",
- BinaryOperator.Divide => "/",
- BinaryOperator.Modulo => "%",
- BinaryOperator.Equal => "==",
- BinaryOperator.NotEqual => "!=",
- BinaryOperator.LessThan => "<",
- BinaryOperator.LessThanOrEqual => "<=",
- BinaryOperator.GreaterThan => ">",
- BinaryOperator.GreaterThanOrEqual => ">=",
- BinaryOperator.LogicalAnd => "&&",
- BinaryOperator.LogicalOr => "||",
- BinaryOperator.BitwiseAnd => "&",
- BinaryOperator.BitwiseOr => "|",
- BinaryOperator.BitwiseXor => "^",
- BinaryOperator.LeftShift => "<<",
- BinaryOperator.RightShift => ">>",
- _ => throw new ArgumentOutOfRangeException()
- };
-
- return $"{left} {op} {right}";
- }
-
- private string EmitConstArrayIndexAccess(ConstArrayIndexAccessNode constArrayIndexAccessNode)
- {
- var target = EmitExpression(constArrayIndexAccessNode.Target);
- var index = EmitExpression(constArrayIndexAccessNode.Index);
- // todo(nub31): We can emit bounds checking here
- return $"{target}[{index}]";
- }
-
- private string EmitConstArrayInitializer(ConstArrayInitializerNode arrayInitializerNode)
- {
- var values = new List();
- foreach (var value in arrayInitializerNode.Values)
- {
- values.Add(EmitExpression(value));
- }
-
- var arrayType = (NubConstArrayType)arrayInitializerNode.Type;
- return $"({CType.Create(arrayType.ElementType)}[{arrayType.Size}]){{{string.Join(", ", values)}}}";
- }
-
- private string EmitDereference(DereferenceNode dereferenceNode)
- {
- var pointer = EmitExpression(dereferenceNode.Target);
- return $"*{pointer}";
- }
-
- private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode)
- {
- var str = float32LiteralNode.Value.ToString("G9", System.Globalization.CultureInfo.InvariantCulture);
- if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E'))
- {
- str += ".0";
- }
-
- return str + "f";
- }
-
- private string EmitFloat64Literal(Float64LiteralNode float64LiteralNode)
- {
- var str = float64LiteralNode.Value.ToString("G17", System.Globalization.CultureInfo.InvariantCulture);
- if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E'))
- {
- str += ".0";
- }
-
- return str;
- }
-
- private string EmitCast(CastNode castNode)
- {
- var value = EmitExpression(castNode.Value);
-
- if (castNode is { Type: NubSliceType sliceType, Value.Type: NubConstArrayType arrayType })
- {
- return $"({CType.Create(sliceType)}){{.length = {arrayType.Size}, .data = (void*){value}}}";
- }
-
- return $"({CType.Create(castNode.Type)}){value}";
- }
-
- private string EmitFuncCall(FuncCallNode funcCallNode)
- {
- var name = EmitExpression(funcCallNode.Expression);
- var parameterNames = funcCallNode.Parameters.Select(EmitExpression).ToList();
- return $"{name}({string.Join(", ", parameterNames)})";
- }
-
- private string EmitAddressOf(AddressOfNode addressOfNode)
- {
- var value = EmitExpression(addressOfNode.LValue);
- return $"&{value}";
- }
-
- private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode)
- {
- var targetType = (NubSliceType)sliceIndexAccessNode.Target.Type;
- var target = EmitExpression(sliceIndexAccessNode.Target);
- var index = EmitExpression(sliceIndexAccessNode.Index);
- // todo(nub31): We can emit bounds checking here
- return $"(({CType.Create(targetType.ElementType)}*){target}.data)[{index}]";
- }
-
- private string EmitStringLiteral(StringLiteralNode stringLiteralNode)
- {
- var length = Encoding.UTF8.GetByteCount(stringLiteralNode.Value);
- return $"(nub_string){{.length = {length}, .data = \"{stringLiteralNode.Value}\"}}";
- }
-
- private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccessNode)
- {
- var structExpr = EmitExpression(structFieldAccessNode.Target);
- return $"{structExpr}.{structFieldAccessNode.Field}";
- }
-
- private string EmitStructInitializer(StructInitializerNode structInitializerNode)
- {
- var initValues = new List();
- foreach (var initializer in structInitializerNode.Initializers)
- {
- var value = EmitExpression(initializer.Value);
- initValues.Add($".{initializer.Key} = {value}");
- }
-
- var initString = initValues.Count == 0
- ? "0"
- : string.Join(", ", initValues);
-
- return $"({CType.Create(structInitializerNode.Type)}){{{initString}}}";
- }
-
- private string EmitI8Literal(I8LiteralNode i8LiteralNode)
- {
- return i8LiteralNode.Value.ToString();
- }
-
- private string EmitI16Literal(I16LiteralNode i16LiteralNode)
- {
- return i16LiteralNode.Value.ToString();
- }
-
- private string EmitI32Literal(I32LiteralNode i32LiteralNode)
- {
- return i32LiteralNode.Value.ToString();
- }
-
- private string EmitI64Literal(I64LiteralNode i64LiteralNode)
- {
- return i64LiteralNode.Value + "LL";
- }
-
- private string EmitU8Literal(U8LiteralNode u8LiteralNode)
- {
- return u8LiteralNode.Value.ToString();
- }
-
- private string EmitU16Literal(U16LiteralNode u16LiteralNode)
- {
- return u16LiteralNode.Value.ToString();
- }
-
- private string EmitU32Literal(U32LiteralNode u32LiteralNode)
- {
- return u32LiteralNode.Value.ToString();
- }
-
- private string EmitU64Literal(U64LiteralNode u64LiteralNode)
- {
- return u64LiteralNode.Value + "ULL";
- }
-
- private string EmitUnaryExpression(UnaryExpressionNode unaryExpressionNode)
- {
- var value = EmitExpression(unaryExpressionNode.Operand);
-
- return unaryExpressionNode.Operator switch
- {
- UnaryOperator.Negate => $"-{value}",
- UnaryOperator.Invert => $"!{value}",
- _ => throw new ArgumentOutOfRangeException()
- };
- }
-
- private void EmitBlock(BlockNode blockNode)
- {
- _deferStack.Push([]);
-
- foreach (var statementNode in blockNode.Statements)
- {
- EmitStatement(statementNode);
- }
-
- var blockDefers = _deferStack.Pop();
- for (var i = blockDefers.Count - 1; i >= 0; i--)
- {
- EmitStatement(blockDefers[i].Statement);
- }
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Generation/IndentedTextWriter.cs b/compiler/NubLang/Generation/IndentedTextWriter.cs
deleted file mode 100644
index 5ec5274..0000000
--- a/compiler/NubLang/Generation/IndentedTextWriter.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using System.Text;
-
-namespace NubLang.Generation;
-
-internal class IndentedTextWriter
-{
- private readonly StringBuilder _builder = new();
- private int _indentLevel;
-
- public IDisposable Indent()
- {
- _indentLevel++;
- return new IndentScope(this);
- }
-
- public void WriteLine(string text)
- {
- WriteIndent();
- _builder.AppendLine(text);
- }
-
- public void Write(string text)
- {
- WriteIndent();
- _builder.Append(text);
- }
-
- public void WriteLine()
- {
- _builder.AppendLine();
- }
-
- public override string ToString()
- {
- return _builder.ToString();
- }
-
- private void WriteIndent()
- {
- if (_builder.Length > 0)
- {
- var lastChar = _builder[^1];
- if (lastChar != '\n' && lastChar != '\r')
- return;
- }
-
- for (var i = 0; i < _indentLevel; i++)
- {
- _builder.Append(" ");
- }
- }
-
- private class IndentScope : IDisposable
- {
- private readonly IndentedTextWriter _writer;
- private bool _disposed;
-
- public IndentScope(IndentedTextWriter writer)
- {
- _writer = writer;
- }
-
- public void Dispose()
- {
- if (_disposed) return;
- _writer._indentLevel--;
- _disposed = true;
- }
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Syntax/Module.cs b/compiler/NubLang/Syntax/Module.cs
deleted file mode 100644
index b5776ae..0000000
--- a/compiler/NubLang/Syntax/Module.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-namespace NubLang.Syntax;
-
-public sealed class Module
-{
- public static Dictionary Collect(List syntaxTrees)
- {
- var modules = new Dictionary();
- foreach (var syntaxTree in syntaxTrees)
- {
- if (!modules.TryGetValue(syntaxTree.ModuleName, out var module))
- {
- module = new Module();
- modules.Add(syntaxTree.ModuleName, module);
- }
-
- module._definitions.AddRange(syntaxTree.Definitions);
- }
-
- return modules;
- }
-
- private readonly List _definitions = [];
-
- public List Structs(bool includePrivate)
- {
- return _definitions
- .OfType()
- .Where(x => x.Exported || includePrivate)
- .ToList();
- }
-
- public List Functions(bool includePrivate)
- {
- return _definitions
- .OfType()
- .Where(x => x.Exported || includePrivate)
- .ToList();
- }
-
- public List Enums(bool includePrivate)
- {
- return _definitions
- .OfType()
- .Where(x => x.Exported || includePrivate)
- .ToList();
- }
-}
\ No newline at end of file
diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs
deleted file mode 100644
index b6aacd6..0000000
--- a/compiler/NubLang/Syntax/Parser.cs
+++ /dev/null
@@ -1,951 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using NubLang.Diagnostics;
-
-namespace NubLang.Syntax;
-
-public sealed class Parser
-{
- private List _tokens = [];
- private int _tokenIndex;
-
- private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null;
- private bool HasToken => CurrentToken != null;
-
- public List Diagnostics { get; } = [];
-
- public SyntaxTree Parse(List tokens)
- {
- Diagnostics.Clear();
- _tokens = tokens;
- _tokenIndex = 0;
-
- string? moduleName = null;
- var imports = new List();
- var definitions = new List();
-
- while (HasToken)
- {
- try
- {
- var startIndex = _tokenIndex;
-
- if (TryExpectSymbol(Symbol.Import))
- {
- var name = ExpectStringLiteral();
- if (imports.Contains(name.Value))
- {
- Diagnostics.Add(Diagnostic
- .Warning($"Module {name.Value} is imported twice")
- .At(name)
- .WithHelp($"Remove duplicate import \"{name.Value}\"")
- .Build());
- }
- else
- {
- imports.Add(name.Value);
- }
-
- continue;
- }
-
- if (TryExpectSymbol(Symbol.Module))
- {
- if (moduleName != null)
- {
- throw new ParseException(Diagnostic
- .Error("Module is declared more than once")
- .At(CurrentToken)
- .WithHelp("Remove duplicate module declaration")
- .Build());
- }
-
- moduleName = ExpectStringLiteral().Value;
- continue;
- }
-
- var exported = TryExpectSymbol(Symbol.Export);
-
- if (TryExpectSymbol(Symbol.Extern))
- {
- var externSymbol = ExpectStringLiteral();
- ExpectSymbol(Symbol.Func);
- definitions.Add(ParseFunc(startIndex, exported, externSymbol.Value));
- continue;
- }
-
- var keyword = ExpectSymbol();
- DefinitionSyntax definition = keyword.Symbol switch
- {
- Symbol.Func => ParseFunc(startIndex, exported, null),
- Symbol.Struct => ParseStruct(startIndex, exported),
- Symbol.Enum => ParseEnum(startIndex, exported),
- _ => throw new ParseException(Diagnostic
- .Error($"Expected 'func', 'struct', 'enum', 'import' or 'module' but found '{keyword.Symbol}'")
- .WithHelp("Valid top level statements are 'func', 'struct', 'enum', 'import' and 'module'")
- .At(keyword)
- .Build())
- };
-
- definitions.Add(definition);
- }
- catch (ParseException e)
- {
- Diagnostics.Add(e.Diagnostic);
- while (HasToken)
- {
- if (CurrentToken is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct })
- {
- break;
- }
-
- Next();
- }
- }
- }
-
- return new SyntaxTree(definitions, moduleName ?? "default", imports);
- }
-
- private FuncParameterSyntax ParseFuncParameter()
- {
- var startIndex = _tokenIndex;
- var name = ExpectIdentifier();
- ExpectSymbol(Symbol.Colon);
- var type = ParseType();
-
- return new FuncParameterSyntax(GetTokens(startIndex), name.Value, type);
- }
-
- private FuncSyntax ParseFunc(int startIndex, bool exported, string? externSymbol)
- {
- var name = ExpectIdentifier();
- List parameters = [];
-
- ExpectSymbol(Symbol.OpenParen);
-
- while (!TryExpectSymbol(Symbol.CloseParen))
- {
- parameters.Add(ParseFuncParameter());
-
- if (!TryExpectSymbol(Symbol.Comma))
- {
- ExpectSymbol(Symbol.CloseParen);
- break;
- }
- }
-
- var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new VoidTypeSyntax([]);
-
- var prototype = new FuncPrototypeSyntax(GetTokens(startIndex), name.Value, exported, externSymbol, parameters, returnType);
-
- BlockSyntax? body = null;
- var bodyStartIndex = _tokenIndex;
- if (TryExpectSymbol(Symbol.OpenBrace))
- {
- body = ParseBlock(bodyStartIndex);
- }
-
- return new FuncSyntax(GetTokens(startIndex), prototype, body);
- }
-
- private StructSyntax ParseStruct(int startIndex, bool exported)
- {
- var name = ExpectIdentifier();
-
- ExpectSymbol(Symbol.OpenBrace);
-
- List fields = [];
-
- while (!TryExpectSymbol(Symbol.CloseBrace))
- {
- var memberStartIndex = _tokenIndex;
-
- var fieldName = ExpectIdentifier().Value;
- ExpectSymbol(Symbol.Colon);
- var fieldType = ParseType();
-
- ExpressionSyntax? fieldValue = null;
-
- if (TryExpectSymbol(Symbol.Assign))
- {
- fieldValue = ParseExpression();
- }
-
- fields.Add(new StructFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldType, fieldValue));
- }
-
- return new StructSyntax(GetTokens(startIndex), name.Value, exported, fields);
- }
-
- private EnumSyntax ParseEnum(int startIndex, bool exported)
- {
- var name = ExpectIdentifier();
-
- TypeSyntax? type = null;
-
- if (TryExpectSymbol(Symbol.Colon))
- {
- type = ParseType();
- }
-
- List fields = [];
-
- ExpectSymbol(Symbol.OpenBrace);
-
- long value = -1;
-
- while (!TryExpectSymbol(Symbol.CloseBrace))
- {
- var memberStartIndex = _tokenIndex;
- var fieldName = ExpectIdentifier().Value;
- long fieldValue;
-
- if (TryExpectSymbol(Symbol.Assign))
- {
- if (!TryExpectIntLiteral(out var intLiteralToken))
- {
- throw new ParseException(Diagnostic
- .Error("Value of enum field must be an integer literal")
- .At(CurrentToken)
- .Build());
- }
-
- fieldValue = Convert.ToInt64(intLiteralToken.Value, intLiteralToken.Base);
- value = fieldValue;
- }
- else
- {
- fieldValue = value + 1;
- value = fieldValue;
- }
-
- fields.Add(new EnumFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldValue));
- }
-
- return new EnumSyntax(GetTokens(startIndex), name.Value, exported, type, fields);
- }
-
- private StatementSyntax ParseStatement()
- {
- var startIndex = _tokenIndex;
-
- if (TryExpectSymbol(out var symbol))
- {
- switch (symbol)
- {
- case Symbol.OpenBrace:
- return ParseBlock(startIndex);
- case Symbol.Return:
- return ParseReturn(startIndex);
- case Symbol.If:
- return ParseIf(startIndex);
- case Symbol.While:
- return ParseWhile(startIndex);
- case Symbol.For:
- return ParseFor(startIndex);
- case Symbol.Let:
- return ParseVariableDeclaration(startIndex);
- case Symbol.Defer:
- return ParseDefer(startIndex);
- case Symbol.Break:
- return new BreakSyntax(GetTokens(startIndex));
- case Symbol.Continue:
- return new ContinueSyntax(GetTokens(startIndex));
- }
- }
-
- var expr = ParseExpression();
-
- if (TryExpectSymbol(Symbol.Assign))
- {
- var value = ParseExpression();
- return new AssignmentSyntax(GetTokens(startIndex), expr, value);
- }
-
- return new StatementExpressionSyntax(GetTokens(startIndex), expr);
- }
-
- private VariableDeclarationSyntax ParseVariableDeclaration(int startIndex)
- {
- var name = ExpectIdentifier().Value;
-
- TypeSyntax? explicitType = null;
- if (TryExpectSymbol(Symbol.Colon))
- {
- explicitType = ParseType();
- }
-
- ExpressionSyntax? assignment = null;
- if (TryExpectSymbol(Symbol.Assign))
- {
- assignment = ParseExpression();
- }
-
- return new VariableDeclarationSyntax(GetTokens(startIndex), name, explicitType, assignment);
- }
-
- private DeferSyntax ParseDefer(int startIndex)
- {
- var statement = ParseStatement();
- return new DeferSyntax(GetTokens(startIndex), statement);
- }
-
- private ReturnSyntax ParseReturn(int startIndex)
- {
- ExpressionSyntax? value = null;
-
- if (!TryExpectSymbol(Symbol.Semi))
- {
- value = ParseExpression();
- }
-
- return new ReturnSyntax(GetTokens(startIndex), value);
- }
-
- private IfSyntax ParseIf(int startIndex)
- {
- var condition = ParseExpression();
- var body = ParseBlock();
-
- Variant? elseStatement = null;
-
- var elseStartIndex = _tokenIndex;
- if (TryExpectSymbol(Symbol.Else))
- {
- if (TryExpectSymbol(Symbol.If))
- {
- elseStatement = (Variant)ParseIf(elseStartIndex);
- }
- else
- {
- elseStatement = (Variant)ParseBlock();
- }
- }
-
- return new IfSyntax(GetTokens(startIndex), condition, body, elseStatement);
- }
-
- private WhileSyntax ParseWhile(int startIndex)
- {
- var condition = ParseExpression();
- var body = ParseBlock();
- return new WhileSyntax(GetTokens(startIndex), condition, body);
- }
-
- private ForSyntax ParseFor(int startIndex)
- {
- var itemName = ExpectIdentifier().Value;
- string? indexName = null;
-
- if (TryExpectSymbol(Symbol.Comma))
- {
- indexName = ExpectIdentifier().Value;
- }
-
- ExpectSymbol(Symbol.In);
- var target = ParseExpression();
- var body = ParseBlock();
-
- return new ForSyntax(GetTokens(startIndex), itemName, indexName, target, body);
- }
-
- private ExpressionSyntax ParseExpression(int precedence = 0)
- {
- var startIndex = _tokenIndex;
- var left = ParsePrimaryExpression();
-
- while (CurrentToken is SymbolToken symbolToken && TryGetBinaryOperator(symbolToken.Symbol, out var op) && GetBinaryOperatorPrecedence(op.Value) >= precedence)
- {
- Next();
- var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1);
- left = new BinaryExpressionSyntax(GetTokens(startIndex), left, op.Value, right);
- }
-
- return left;
- }
-
- private static int GetBinaryOperatorPrecedence(BinaryOperatorSyntax operatorSyntax)
- {
- return operatorSyntax switch
- {
- BinaryOperatorSyntax.Multiply => 10,
- BinaryOperatorSyntax.Divide => 10,
- BinaryOperatorSyntax.Modulo => 10,
-
- BinaryOperatorSyntax.Plus => 9,
- BinaryOperatorSyntax.Minus => 9,
-
- BinaryOperatorSyntax.LeftShift => 8,
- BinaryOperatorSyntax.RightShift => 8,
-
- BinaryOperatorSyntax.GreaterThan => 7,
- BinaryOperatorSyntax.GreaterThanOrEqual => 7,
- BinaryOperatorSyntax.LessThan => 7,
- BinaryOperatorSyntax.LessThanOrEqual => 7,
-
- BinaryOperatorSyntax.Equal => 7,
- BinaryOperatorSyntax.NotEqual => 7,
-
- BinaryOperatorSyntax.BitwiseAnd => 6,
- BinaryOperatorSyntax.BitwiseXor => 5,
- BinaryOperatorSyntax.BitwiseOr => 4,
-
- BinaryOperatorSyntax.LogicalAnd => 3,
- BinaryOperatorSyntax.LogicalOr => 2,
-
- _ => throw new ArgumentOutOfRangeException(nameof(operatorSyntax), operatorSyntax, null)
- };
- }
-
- private bool TryGetBinaryOperator(Symbol symbol, [NotNullWhen(true)] out BinaryOperatorSyntax? binaryExpressionOperator)
- {
- switch (symbol)
- {
- case Symbol.Equal:
- binaryExpressionOperator = BinaryOperatorSyntax.Equal;
- return true;
- case Symbol.NotEqual:
- binaryExpressionOperator = BinaryOperatorSyntax.NotEqual;
- return true;
- case Symbol.LessThan:
- binaryExpressionOperator = BinaryOperatorSyntax.LessThan;
- return true;
- case Symbol.LessThanOrEqual:
- binaryExpressionOperator = BinaryOperatorSyntax.LessThanOrEqual;
- return true;
- case Symbol.GreaterThan:
- binaryExpressionOperator = BinaryOperatorSyntax.GreaterThan;
- return true;
- case Symbol.GreaterThanOrEqual:
- binaryExpressionOperator = BinaryOperatorSyntax.GreaterThanOrEqual;
- return true;
- case Symbol.And:
- binaryExpressionOperator = BinaryOperatorSyntax.LogicalAnd;
- return true;
- case Symbol.Or:
- binaryExpressionOperator = BinaryOperatorSyntax.LogicalOr;
- return true;
- case Symbol.Plus:
- binaryExpressionOperator = BinaryOperatorSyntax.Plus;
- return true;
- case Symbol.Minus:
- binaryExpressionOperator = BinaryOperatorSyntax.Minus;
- return true;
- case Symbol.Star:
- binaryExpressionOperator = BinaryOperatorSyntax.Multiply;
- return true;
- case Symbol.ForwardSlash:
- binaryExpressionOperator = BinaryOperatorSyntax.Divide;
- return true;
- case Symbol.Percent:
- binaryExpressionOperator = BinaryOperatorSyntax.Modulo;
- return true;
- case Symbol.LeftShift:
- binaryExpressionOperator = BinaryOperatorSyntax.LeftShift;
- return true;
- case Symbol.RightShift:
- binaryExpressionOperator = BinaryOperatorSyntax.RightShift;
- return true;
- case Symbol.Ampersand:
- binaryExpressionOperator = BinaryOperatorSyntax.BitwiseAnd;
- return true;
- case Symbol.Pipe:
- binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr;
- return true;
- case Symbol.Caret:
- binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor;
- return true;
- default:
- binaryExpressionOperator = null;
- return false;
- }
- }
-
- private ExpressionSyntax ParsePrimaryExpression()
- {
- var startIndex = _tokenIndex;
- var token = ExpectToken();
- var expr = token switch
- {
- BoolLiteralToken boolLiteral => new BoolLiteralSyntax(GetTokens(startIndex), boolLiteral.Value),
- StringLiteralToken stringLiteral => new StringLiteralSyntax(GetTokens(startIndex), stringLiteral.Value),
- FloatLiteralToken floatLiteral => new FloatLiteralSyntax(GetTokens(startIndex), floatLiteral.Value),
- IntLiteralToken intLiteral => new IntLiteralSyntax(GetTokens(startIndex), intLiteral.Value, intLiteral.Base),
- IdentifierToken identifier => ParseIdentifier(startIndex, identifier),
- SymbolToken symbolToken => symbolToken.Symbol switch
- {
- Symbol.OpenParen => ParseParenthesizedExpression(),
- Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()),
- Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()),
- Symbol.OpenBracket => ParseArrayInitializer(startIndex),
- Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()),
- Symbol.Struct => ParseStructInitializer(startIndex),
- Symbol.At => ParseBuiltinFunction(startIndex),
- _ => throw new ParseException(Diagnostic
- .Error($"Unexpected symbol '{symbolToken.Symbol}' in expression")
- .WithHelp("Expected '(', '-', '!', '[' or '{'")
- .At(symbolToken)
- .Build())
- },
- _ => throw new ParseException(Diagnostic
- .Error($"Unexpected token '{token.GetType().Name}' in expression")
- .WithHelp("Expected literal, identifier, or parenthesized expression")
- .At(token)
- .Build())
- };
-
- return ParsePostfixOperators(expr);
- }
-
- private ExpressionSyntax ParseBuiltinFunction(int startIndex)
- {
- var name = ExpectIdentifier();
- ExpectSymbol(Symbol.OpenParen);
-
- switch (name.Value)
- {
- case "size":
- {
- var type = ParseType();
- ExpectSymbol(Symbol.CloseParen);
- return new SizeSyntax(GetTokens(startIndex), type);
- }
- case "cast":
- {
- var expression = ParseExpression();
- ExpectSymbol(Symbol.CloseParen);
- return new CastSyntax(GetTokens(startIndex), expression);
- }
- default:
- {
- throw new ParseException(Diagnostic.Error($"Unknown builtin {name.Value}").At(name).Build());
- }
- }
- }
-
- private ExpressionSyntax ParseIdentifier(int startIndex, IdentifierToken identifier)
- {
- if (TryExpectSymbol(Symbol.DoubleColon))
- {
- var name = ExpectIdentifier();
- return new ModuleIdentifierSyntax(GetTokens(startIndex), identifier.Value, name.Value);
- }
-
- return new LocalIdentifierSyntax(GetTokens(startIndex), identifier.Value);
- }
-
- private ExpressionSyntax ParseParenthesizedExpression()
- {
- var expression = ParseExpression();
- ExpectSymbol(Symbol.CloseParen);
- return expression;
- }
-
- private ExpressionSyntax ParsePostfixOperators(ExpressionSyntax expr)
- {
- var startIndex = _tokenIndex;
- while (HasToken)
- {
- if (TryExpectSymbol(Symbol.Ampersand))
- {
- expr = new AddressOfSyntax(GetTokens(startIndex), expr);
- continue;
- }
-
- if (TryExpectSymbol(Symbol.Caret))
- {
- expr = new DereferenceSyntax(GetTokens(startIndex), expr);
- continue;
- }
-
- if (TryExpectSymbol(Symbol.Period))
- {
- var member = ExpectIdentifier().Value;
- expr = new MemberAccessSyntax(GetTokens(startIndex), expr, member);
- continue;
- }
-
- if (TryExpectSymbol(Symbol.OpenBracket))
- {
- var index = ParseExpression();
- ExpectSymbol(Symbol.CloseBracket);
- expr = new ArrayIndexAccessSyntax(GetTokens(startIndex), expr, index);
- continue;
- }
-
- if (TryExpectSymbol(Symbol.OpenParen))
- {
- var parameters = new List();
-
- while (!TryExpectSymbol(Symbol.CloseParen))
- {
- parameters.Add(ParseExpression());
- if (!TryExpectSymbol(Symbol.Comma))
- {
- ExpectSymbol(Symbol.CloseParen);
- break;
- }
- }
-
- expr = new FuncCallSyntax(GetTokens(startIndex), expr, parameters);
- continue;
- }
-
- break;
- }
-
- return expr;
- }
-
- private ExpressionSyntax ParseArrayInitializer(int startIndex)
- {
- var values = new List();
- while (!TryExpectSymbol(Symbol.CloseBracket))
- {
- values.Add(ParseExpression());
- if (!TryExpectSymbol(Symbol.Comma))
- {
- ExpectSymbol(Symbol.CloseBracket);
- break;
- }
- }
-
- return new ArrayInitializerSyntax(GetTokens(startIndex), values);
- }
-
- private StructInitializerSyntax ParseStructInitializer(int startIndex)
- {
- TypeSyntax? type = null;
- if (!TryExpectSymbol(Symbol.OpenBrace))
- {
- type = ParseType();
- ExpectSymbol(Symbol.OpenBrace);
- }
-
- var initializers = ParseStructInitializerBody();
-
- return new StructInitializerSyntax(GetTokens(startIndex), type, initializers);
- }
-
- private Dictionary ParseStructInitializerBody()
- {
- Dictionary initializers = [];
- while (!TryExpectSymbol(Symbol.CloseBrace))
- {
- var name = ExpectIdentifier().Value;
- ExpectSymbol(Symbol.Assign);
- var value = ParseExpression();
- initializers.Add(name, value);
- }
-
- return initializers;
- }
-
- private BlockSyntax ParseBlock()
- {
- var startIndex = _tokenIndex;
- ExpectSymbol(Symbol.OpenBrace);
- return ParseBlock(startIndex);
- }
-
- private BlockSyntax ParseBlock(int startIndex)
- {
- List statements = [];
-
- while (!TryExpectSymbol(Symbol.CloseBrace))
- {
- try
- {
- statements.Add(ParseStatement());
- }
- catch (ParseException ex)
- {
- Diagnostics.Add(ex.Diagnostic);
- if (HasToken)
- {
- Next();
- }
- else
- {
- break;
- }
- }
- }
-
- return new BlockSyntax(GetTokens(startIndex), statements);
- }
-
- private TypeSyntax ParseType()
- {
- var startIndex = _tokenIndex;
- if (TryExpectIdentifier(out var name))
- {
- if (name.Value[0] == 'u' && int.TryParse(name.Value[1..], out var size))
- {
- if (size is not 8 and not 16 and not 32 and not 64)
- {
- throw new ParseException(Diagnostic
- .Error("Arbitrary uint size is not supported")
- .WithHelp("Use u8, u16, u32 or u64")
- .At(name)
- .Build());
- }
-
- return new IntTypeSyntax(GetTokens(startIndex), false, size);
- }
-
- if (name.Value[0] == 'i' && int.TryParse(name.Value[1..], out size))
- {
- if (size is not 8 and not 16 and not 32 and not 64)
- {
- throw new ParseException(Diagnostic
- .Error("Arbitrary int size is not supported")
- .WithHelp("Use i8, i16, i32 or i64")
- .At(name)
- .Build());
- }
-
- return new IntTypeSyntax(GetTokens(startIndex), true, size);
- }
-
- if (name.Value[0] == 'f' && int.TryParse(name.Value[1..], out size))
- {
- if (size is not 32 and not 64)
- {
- throw new ParseException(Diagnostic
- .Error("Arbitrary float size is not supported")
- .WithHelp("Use f32 or f64")
- .At(name)
- .Build());
- }
-
- return new FloatTypeSyntax(GetTokens(startIndex), size);
- }
-
- switch (name.Value)
- {
- case "void":
- return new VoidTypeSyntax(GetTokens(startIndex));
- case "string":
- return new StringTypeSyntax(GetTokens(startIndex));
- case "bool":
- return new BoolTypeSyntax(GetTokens(startIndex));
- default:
- {
- string? module = null;
-
- if (TryExpectSymbol(Symbol.DoubleColon))
- {
- var customTypeName = ExpectIdentifier();
- module = name.Value;
- name = customTypeName;
- }
-
- return new CustomTypeSyntax(GetTokens(startIndex), module, name.Value);
- }
- }
- }
-
- if (TryExpectSymbol(Symbol.Caret))
- {
- var baseType = ParseType();
- return new PointerTypeSyntax(GetTokens(startIndex), baseType);
- }
-
- if (TryExpectSymbol(Symbol.Func))
- {
- ExpectSymbol(Symbol.OpenParen);
-
- List parameters = [];
- while (!TryExpectSymbol(Symbol.CloseParen))
- {
- parameters.Add(ParseType());
- if (!TryExpectSymbol(Symbol.Comma))
- {
- ExpectSymbol(Symbol.CloseParen);
- break;
- }
- }
-
- var returnType = TryExpectSymbol(Symbol.Colon)
- ? ParseType()
- : new VoidTypeSyntax([]);
-
- return new FuncTypeSyntax(GetTokens(startIndex), parameters, returnType);
- }
-
- if (TryExpectSymbol(Symbol.OpenBracket))
- {
- if (TryExpectIntLiteral(out var intLiteral))
- {
- ExpectSymbol(Symbol.CloseBracket);
- var baseType = ParseType();
- return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, Convert.ToInt64(intLiteral.Value, intLiteral.Base));
- }
- else if (TryExpectSymbol(Symbol.QuestionMark))
- {
- ExpectSymbol(Symbol.CloseBracket);
- var baseType = ParseType();
- return new ArrayTypeSyntax(GetTokens(startIndex), baseType);
- }
- else
- {
- ExpectSymbol(Symbol.CloseBracket);
- var baseType = ParseType();
- return new SliceTypeSyntax(GetTokens(startIndex), baseType);
- }
- }
-
- throw new ParseException(Diagnostic
- .Error("Invalid type syntax")
- .WithHelp("Expected type name, '^' for pointer, or '[]' for array")
- .At(CurrentToken)
- .Build());
- }
-
- private Token ExpectToken()
- {
- if (!HasToken)
- {
- throw new ParseException(Diagnostic
- .Error("Unexpected end of file")
- .WithHelp("Expected more tokens to complete the syntax")
- .At(_tokens[^1])
- .Build());
- }
-
- var token = CurrentToken!;
- Next();
- return token;
- }
-
- private SymbolToken ExpectSymbol()
- {
- var token = ExpectToken();
- if (token is not SymbolToken symbol)
- {
- throw new ParseException(Diagnostic
- .Error($"Expected symbol, but found {token.GetType().Name}")
- .WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.")
- .At(token)
- .Build());
- }
-
- return symbol;
- }
-
- private void ExpectSymbol(Symbol expectedSymbol)
- {
- var token = ExpectSymbol();
- if (token.Symbol != expectedSymbol)
- {
- throw new ParseException(Diagnostic
- .Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'")
- .WithHelp($"Insert '{expectedSymbol}' here")
- .At(token)
- .Build());
- }
- }
-
- private bool TryExpectSymbol(out Symbol symbol)
- {
- if (CurrentToken is SymbolToken symbolToken)
- {
- Next();
- symbol = symbolToken.Symbol;
- return true;
- }
-
- symbol = default;
- return false;
- }
-
- private bool TryExpectSymbol(Symbol symbol)
- {
- if (CurrentToken is SymbolToken symbolToken && symbolToken.Symbol == symbol)
- {
- Next();
- return true;
- }
-
- return false;
- }
-
- private bool TryExpectIdentifier([NotNullWhen(true)] out IdentifierToken? identifier)
- {
- if (CurrentToken is IdentifierToken identifierToken)
- {
- identifier = identifierToken;
- Next();
- return true;
- }
-
- identifier = null;
- return false;
- }
-
- private IdentifierToken ExpectIdentifier()
- {
- var token = ExpectToken();
- if (token is not IdentifierToken identifier)
- {
- throw new ParseException(Diagnostic
- .Error($"Expected identifier, but found {token.GetType().Name}")
- .WithHelp("Provide a valid identifier name here")
- .At(token)
- .Build());
- }
-
- return identifier;
- }
-
- private bool TryExpectIntLiteral([NotNullWhen(true)] out IntLiteralToken? stringLiteral)
- {
- if (CurrentToken is IntLiteralToken token)
- {
- stringLiteral = token;
- Next();
- return true;
- }
-
- stringLiteral = null;
- return false;
- }
-
- private StringLiteralToken ExpectStringLiteral()
- {
- var token = ExpectToken();
- if (token is not StringLiteralToken identifier)
- {
- throw new ParseException(Diagnostic
- .Error($"Expected string literal, but found {token.GetType().Name}")
- .WithHelp("Provide a valid string literal")
- .At(token)
- .Build());
- }
-
- return identifier;
- }
-
- private void Next()
- {
- _tokenIndex++;
- }
-
- private List GetTokens(int tokenStartIndex)
- {
- return _tokens.Skip(tokenStartIndex).Take(_tokenIndex - tokenStartIndex).ToList();
- }
-}
-
-public record SyntaxTree(List Definitions, string ModuleName, List Imports);
-
-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/compiler/NubLang/Syntax/Syntax.cs b/compiler/NubLang/Syntax/Syntax.cs
deleted file mode 100644
index 8d4472c..0000000
--- a/compiler/NubLang/Syntax/Syntax.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-namespace NubLang.Syntax;
-
-public abstract record SyntaxNode(List Tokens);
-
-#region Definitions
-
-public abstract record DefinitionSyntax(List Tokens, string Name, bool Exported) : SyntaxNode(Tokens);
-
-public record FuncParameterSyntax(List Tokens, string Name, TypeSyntax Type) : SyntaxNode(Tokens);
-
-public record FuncPrototypeSyntax(List Tokens, string Name, bool Exported, string? ExternSymbol, List