From ee485e41192d72b5ac7ae068032afdc3c4190b3c Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 13 Feb 2026 00:27:59 +0100 Subject: [PATCH] ... --- compiler/Generator.cs | 68 ++++++---- compiler/ModuleGraph.cs | 289 +++++++++++++++++++--------------------- compiler/NubLib.cs | 47 ++++++- compiler/NubType.cs | 205 ++++++++-------------------- compiler/Program.cs | 2 +- compiler/TypeChecker.cs | 135 +++++++++++-------- 6 files changed, 347 insertions(+), 399 deletions(-) diff --git a/compiler/Generator.cs b/compiler/Generator.cs index 14b266b..96e4145 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -20,13 +20,9 @@ public class Generator private readonly ModuleGraph moduleGraph; private readonly bool compileLib; private readonly IndentedTextWriter writer = new(); - private readonly Dictionary structTypeNames = new(); private string Emit() { - foreach (var (i, structType) in moduleGraph.GetModules().SelectMany(x => x.GetCustomTypes().OfType().Index())) - structTypeNames[structType] = $"s{i}"; - writer.WriteLine(""" #include #include @@ -43,46 +39,61 @@ public class Generator writer.WriteLine(); - foreach (var typeName in structTypeNames) - writer.WriteLine($"struct {typeName.Value};"); - - writer.WriteLine(); - - foreach (var typeName in structTypeNames) + foreach (var module in moduleGraph.GetModules()) { - writer.Write("struct "); - - if (typeName.Key.Packed) + foreach (var (name, info) in module.GetTypes()) { - writer.Write("__attribute__((__packed__)) "); + if (info is Module.TypeInfoStruct s) + { + writer.WriteLine($"struct {Name.Create(module.Name, name, NubTypeStruct.Get(module.Name, name))};"); + } } + } - writer.WriteLine(typeName.Value); - writer.WriteLine("{"); - using (writer.Indent()) + foreach (var module in moduleGraph.GetModules()) + { + foreach (var (name, info) in module.GetTypes()) { - foreach (var field in typeName.Key.Fields) - writer.WriteLine($"{CType(field.Type, field.Name)};"); + if (info is Module.TypeInfoStruct s) + { + writer.WriteLine(); + writer.Write("struct "); + + if (s.Packed) + { + writer.Write("__attribute__((__packed__)) "); + } + + writer.WriteLine(Name.Create(module.Name, name, NubTypeStruct.Get(module.Name, name))); + writer.WriteLine("{"); + using (writer.Indent()) + { + foreach (var field in s.Fields) + { + writer.WriteLine($"{CType(field.Type, field.Name)};"); + } + } + writer.WriteLine("};"); + } } - writer.WriteLine("};"); } writer.WriteLine(); foreach (var module in moduleGraph.GetModules()) { - foreach (var (name, type) in module.GetIdentifierTypes()) + foreach (var (name, info) in module.GetIdentifiers()) { - if (type is NubTypeFunc fn) + if (info.Type is NubTypeFunc fn) { - if (!functions.Any(x => x.GetMangledName() == SymbolNameGen.Exported(module.Name, name, type))) + if (!functions.Any(x => x.GetMangledName() == Name.Create(module.Name, name, info.Type))) writer.Write("extern "); - writer.WriteLine($"{CType(fn.ReturnType, SymbolNameGen.Exported(module.Name, name, type))}({string.Join(", ", fn.Parameters.Select(p => CType(p)))});"); + writer.WriteLine($"{CType(fn.ReturnType, Name.Create(module.Name, name, info.Type))}({string.Join(", ", fn.Parameters.Select(p => CType(p)))});"); } else { - writer.WriteLine($"{CType(type, SymbolNameGen.Exported(module.Name, name, type))};"); + writer.WriteLine($"{CType(info.Type, Name.Create(module.Name, name, info.Type))};"); } } } @@ -238,7 +249,7 @@ public class Generator TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression), TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression), TypedNodeExpressionLocalIdent expression => expression.Value.Ident, - TypedNodeExpressionModuleIdent expression => SymbolNameGen.Exported(expression.Module.Ident, expression.Value.Ident, expression.Type), + TypedNodeExpressionModuleIdent expression => Name.Create(expression.Module.Ident, expression.Value.Ident, expression.Type), TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression), _ => throw new ArgumentOutOfRangeException(nameof(node), node, null) }; @@ -293,8 +304,9 @@ public class Generator } var initializerStrings = initializerValues.Select(x => $".{x.Key} = {x.Value}"); + var structType = (NubTypeStruct)expression.Type; - return $"(struct {structTypeNames[(NubTypeStruct)expression.Type]}){{ {string.Join(", ", initializerStrings)} }}"; + return $"(struct {Name.Create(structType.Module, structType.Name, structType)}){{ {string.Join(", ", initializerStrings)} }}"; } private string EmitExpressionMemberAccess(TypedNodeExpressionMemberAccess expression) @@ -316,7 +328,7 @@ public class Generator { NubTypeVoid => "void" + (varName != null ? $" {varName}" : ""), NubTypeBool => "bool" + (varName != null ? $" {varName}" : ""), - NubTypeStruct type => $"struct {structTypeNames[type]}" + (varName != null ? $" {varName}" : ""), + NubTypeStruct type => $"struct {Name.Create(type.Module, type.Name, type)}" + (varName != null ? $" {varName}" : ""), NubTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""), NubTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""), NubTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"), diff --git a/compiler/ModuleGraph.cs b/compiler/ModuleGraph.cs index 109a2fb..e2af09b 100644 --- a/compiler/ModuleGraph.cs +++ b/compiler/ModuleGraph.cs @@ -25,113 +25,6 @@ public class ModuleGraph return module != null; } - public Manifest CreateManifest() - { - return new Manifest(1, GetModules().Select(x => x.CreateManifestModule()).ToList()); - } - - public class Module(string name) - { - public string Name { get; } = name; - private readonly Dictionary customTypes = new(); - private readonly Dictionary identifierTypes = new(); - - public IReadOnlyList GetCustomTypes() - { - return customTypes.Values.Select(x => x.Type).ToList(); - } - - public IReadOnlyDictionary GetIdentifierTypes() - { - return identifierTypes.ToDictionary(x => x.Key, x => x.Value.Type); - } - - public bool TryResolveCustomType(string name, bool searchPrivate, [NotNullWhen(true)] out NubType? customType) - { - var info = customTypes.GetValueOrDefault(name); - if (info == null) - { - customType = null; - return false; - } - - if (info.Exported || searchPrivate) - { - customType = info.Type; - return true; - } - - customType = null; - return false; - } - - public bool TryResolveIdentifierType(string name, bool searchPrivate, [NotNullWhen(true)] out NubType? identifierType) - { - var info = identifierTypes.GetValueOrDefault(name); - if (info == null) - { - identifierType = null; - return false; - } - - if (info.Exported || searchPrivate) - { - identifierType = info.Type; - return true; - } - - identifierType = null; - return false; - } - - public void AddCustomType(string name, CustomTypeInfo info) - { - customTypes.Add(name, info); - } - - public void AddIdentifier(string name, IdentifierInfo info) - { - identifierTypes.Add(name, info); - } - - public ManifestModule CreateManifestModule() - { - var manifestCustomTypes = customTypes.ToDictionary(x => x.Key, x => x.Value.CreateManifestCustomTypeInfo()); - var manifestIdentifiers = identifierTypes.ToDictionary(x => x.Key, x => x.Value.CreateManifestIdentifierInfo()); - return new ManifestModule(Name, manifestCustomTypes, manifestIdentifiers); - } - - public enum Source - { - Ast, - Lib, - } - - public class CustomTypeInfo(NubType type, bool exported, Source source) - { - public NubType Type { get; } = type; - public bool Exported { get; } = exported; - public Source Source { get; } = source; - - public ManifestCustomTypeInfo CreateManifestCustomTypeInfo() - { - return new ManifestCustomTypeInfo(TypeEncoder.Encode(Type), Exported); - } - } - - public class IdentifierInfo(NubType type, bool exported, Source source) - { - public NubType Type { get; } = type; - public bool Exported { get; } = exported; - public Source Source { get; } = source; - - public ManifestIdentifierInfo CreateManifestIdentifierInfo() - { - return new ManifestIdentifierInfo(TypeEncoder.Encode(Type), Exported); - } - } - } - public class Builder { private readonly List asts = []; @@ -153,93 +46,78 @@ public class ModuleGraph var modules = new Dictionary(); - // First pass: Register libraries foreach (var manifest in manifests) { - foreach (var manifestModule in manifest.Modules) + foreach (var (moduleName, manifestModule) in manifest.Modules) { - if (!modules.TryGetValue(manifestModule.Name, out var module)) + var module = GetOrCreateModule(moduleName); + + foreach (var (name, type) in manifestModule.Types) { - module = new Module(manifestModule.Name); - modules.Add(manifestModule.Name, module); + switch (type) + { + case Manifest.Module.TypeInfoStruct s: + var info = new Module.TypeInfoStruct(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; + default: + throw new ArgumentOutOfRangeException(nameof(type)); + } } - foreach (var customType in manifestModule.CustomTypes) + foreach (var (name, identifier) in manifestModule.Identifiers) { - var decoded = TypeDecoder.Decode(customType.Value.EncodedType); - module.AddCustomType(customType.Key, new Module.CustomTypeInfo(decoded, customType.Value.Exported, Module.Source.Lib)); - } - - foreach (var identifier in manifestModule.Identifiers) - { - var decoded = TypeDecoder.Decode(identifier.Value.EncodedType); - module.AddIdentifier(identifier.Key, new Module.IdentifierInfo(decoded, identifier.Value.Exported, Module.Source.Lib)); + module.AddIdentifier(name, new Module.IdentifierInfo(identifier.Exported, identifier.Type)); } } } - var astModuleCache = new Dictionary(); - - // Second pass: Register modules from ast foreach (var ast in asts) { - if (!modules.ContainsKey(ast.ModuleName.Ident)) - { - var module = new Module(ast.ModuleName.Ident); - modules.Add(ast.ModuleName.Ident, module); - astModuleCache[ast] = module; - } - } - - // Third pass: Register struct types without fields - foreach (var ast in asts) - { - var module = astModuleCache[ast]; + var module = GetOrCreateModule(ast.ModuleName.Ident); foreach (var structDef in ast.Definitions.OfType()) { - var type = NubTypeStruct.CreateWithoutFields(structDef.Packed); - var info = new Module.CustomTypeInfo(type, structDef.Exported, Module.Source.Ast); - module.AddCustomType(structDef.Name.Ident, info); + module.AddType(structDef.Name.Ident, new Module.TypeInfoStruct(structDef.Exported, structDef.Packed)); } } - // Fourth pass: Resolve struct fields foreach (var ast in asts) { - var module = astModuleCache[ast]; + var module = GetOrCreateModule(ast.ModuleName.Ident); foreach (var structDef in ast.Definitions.OfType()) { - if (!module.TryResolveCustomType(structDef.Name.Ident, true, out var customType)) - throw new UnreachableException($"{nameof(customType)} should always be registered"); + if (!module.TryResolveType(structDef.Name.Ident, true, out var typeInfo)) + throw new UnreachableException($"{nameof(typeInfo)} should always be registered"); - if (customType is NubTypeStruct structType) + if (typeInfo is Module.TypeInfoStruct structType) { - var fields = structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, ResolveType(f.Type, module.Name))).ToList(); + var fields = structDef.Fields.Select(f => new Module.TypeInfoStruct.Field(f.Name.Ident, ResolveType(f.Type, module.Name))).ToList(); structType.SetFields(fields); } } } - // Fifth pass: Register identifiers foreach (var ast in asts) { - var module = astModuleCache[ast]; + 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 = ResolveType(funcDef.ReturnType, module.Name); var funcType = NubTypeFunc.Get(parameters, returnType); - var info = new Module.IdentifierInfo(funcType, funcDef.Exported, Module.Source.Ast); + var info = new Module.IdentifierInfo(funcDef.Exported, funcType); 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(type, globalVariable.Exported, Module.Source.Ast); + var info = new Module.IdentifierInfo(globalVariable.Exported, type); module.AddIdentifier(globalVariable.Name.Ident, info); } } @@ -273,16 +151,117 @@ public class ModuleGraph var includePrivate = currentModule == type.Module.Ident; - if (!module.TryResolveCustomType(type.Name.Ident, includePrivate, out var customType)) + if (!module.TryResolveType(type.Name.Ident, includePrivate, out var customType)) throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").Build()); - return customType; + return customType switch + { + Module.TypeInfoStruct => NubTypeStruct.Get(type.Module.Ident, type.Name.Ident), + _ => throw new ArgumentOutOfRangeException(nameof(customType)) + }; + } + + Module GetOrCreateModule(string name) + { + if (!modules.TryGetValue(name, out var module)) + { + module = new Module(name); + modules.Add(name, module); + } + + return module; } } } } -public record Manifest(int Version, IReadOnlyList Modules); -public record ManifestModule(string Name, Dictionary CustomTypes, Dictionary Identifiers); -public record ManifestCustomTypeInfo(string EncodedType, bool Exported); -public record ManifestIdentifierInfo(string EncodedType, bool Exported); \ No newline at end of file +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 (info.Exported || searchPrivate) + { + 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 (info.Exported || searchPrivate) + { + 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 class IdentifierInfo(bool exported, NubType type) + { + public bool Exported { get; } = exported; + public NubType Type { get; } = type; + } + + + public abstract class TypeInfo(bool exported) + { + public bool Exported { get; } = exported; + } + + + public class TypeInfoStruct(bool exported, bool packed) : TypeInfo(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; + } + } +} \ No newline at end of file diff --git a/compiler/NubLib.cs b/compiler/NubLib.cs index 98d5927..66d4600 100644 --- a/compiler/NubLib.cs +++ b/compiler/NubLib.cs @@ -1,5 +1,6 @@ using System.IO.Compression; using System.Text.Json; +using System.Text.Json.Serialization; namespace Compiler; @@ -32,7 +33,7 @@ public class NubLib fileStream.CopyTo(entryStream); } - public static NublibLoadResult Unpack(string nublibPath) + public static NubLibLoadResult Unpack(string nublibPath) { using var fs = new FileStream(nublibPath, FileMode.Open, FileAccess.Read); using var zip = new ZipArchive(fs, ZipArchiveMode.Read); @@ -55,8 +56,48 @@ public class NubLib entryStream.CopyTo(tempFile); } - return new NublibLoadResult(manifest, tempArchivePath); + return new NubLibLoadResult(manifest, tempArchivePath); } - public record NublibLoadResult(Manifest Manifest, string ArchivePath); + public record NubLibLoadResult(Manifest Manifest, string ArchivePath); +} + +public record Manifest(Dictionary Modules) +{ + public static Manifest Create(ModuleGraph moduleGraph) + { + var modules = new Dictionary(); + + foreach (var module in moduleGraph.GetModules()) + { + var types = module.GetTypes().ToDictionary(x => x.Key, x => ConvertType(x.Value)); + var identifiers = module.GetIdentifiers().ToDictionary(x => x.Key, x => new Module.IdentifierInfo(x.Value.Exported, x.Value.Type)); + modules[module.Name] = new Module(types, identifiers); + } + + return new Manifest(modules); + + static Module.TypeInfo ConvertType(Compiler.Module.TypeInfo typeInfo) + { + return typeInfo switch + { + Compiler.Module.TypeInfoStruct s => new Module.TypeInfoStruct(s.Exported, s.Packed, s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList()), + _ => throw new ArgumentOutOfRangeException(nameof(typeInfo)) + }; + } + } + + + public record Module(Dictionary Types, Dictionary Identifiers) + { + public record IdentifierInfo(bool Exported, NubType Type); + + [JsonDerivedType(typeof(TypeInfoStruct), "struct")] + public abstract record TypeInfo(bool Exported); + + public record TypeInfoStruct(bool Exported, bool Packed, IReadOnlyList Fields) : TypeInfo(Exported) + { + public record Field(string Name, NubType Type); + } + } } \ No newline at end of file diff --git a/compiler/NubType.cs b/compiler/NubType.cs index 91683fa..f50ab4b 100644 --- a/compiler/NubType.cs +++ b/compiler/NubType.cs @@ -1,8 +1,10 @@ -using System.Diagnostics; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Compiler; +[JsonConverter(typeof(NubTypeJsonConverter))] public abstract class NubType { public abstract override string ToString(); @@ -87,61 +89,26 @@ public class NubTypeString : NubType public class NubTypeStruct : NubType { - private static readonly Dictionary Cache = new(); + private static readonly Dictionary<(string Module, string Name), NubTypeStruct> Cache = new(); - public static NubTypeStruct Get(bool packed, List fields) + public static NubTypeStruct Get(string module, string name) { - var sig = new Signature(packed, fields); + if (!Cache.TryGetValue((module, name), out var structType)) + Cache[(module, name)] = structType = new NubTypeStruct(module, name); - if (!Cache.TryGetValue(sig, out var sturctType)) - Cache[sig] = sturctType = new NubTypeStruct(packed, fields); - - return sturctType; + return structType; } - private NubTypeStruct(bool packed, List fields) + private NubTypeStruct(string module, string name) { - Packed = packed; - this.fields = fields; + Module = module; + Name = name; } - private NubTypeStruct(bool packed) - { - Packed = packed; - } + public string Module { get; } + public string Name { get; } - public bool Packed { get; } - - private IReadOnlyList? fields; - public IReadOnlyList Fields - { - get - { - if (fields == null) - throw new InvalidOperationException("Fields has not been set"); - - return fields; - } - } - - public static NubTypeStruct CreateWithoutFields(bool packed) - { - return new NubTypeStruct(packed); - } - - public void SetFields(List fields) - { - if (this.fields != null) - throw new InvalidOperationException("Fields can only be set once"); - - this.fields = fields; - Cache[new Signature(Packed, this.fields)] = this; - } - - public override string ToString() => $"struct {{ {string.Join(' ', Fields.Select(f => $"{f.Name}: {f.Type}"))} }}"; - - public record Field(string Name, NubType Type); - private record Signature(bool Packed, IReadOnlyList Fields); + public override string ToString() => $"struct {Module}::{Name}"; } public class NubTypePointer : NubType @@ -183,7 +150,7 @@ public class NubTypeFunc : NubType public IReadOnlyList Parameters { get; } public NubType ReturnType { get; } - public string MangledName(string name, string module) => SymbolNameGen.Exported(name, module, this); + public string MangledName(string name, string module) => Name.Create(name, module, this); private NubTypeFunc(List parameters, NubType returnType) { @@ -212,15 +179,7 @@ public class TypeEncoder private string EncodeRoot(NubType type) { var sb = new StringBuilder(); - EncodeType(sb, type); - - foreach (var definition in structDefinitions.Values.OrderBy(x => x.Index)) - { - Debug.Assert(definition.Encoded != null); - sb.Insert(0, definition.Encoded); - } - return sb.ToString(); } @@ -259,7 +218,11 @@ public class TypeEncoder break; case NubTypeStruct st: - sb.Append(GetOrCreateStructDefinition(st)); + sb.Append("T("); + sb.Append(st.Module); + sb.Append(':'); + sb.Append(st.Name); + sb.Append(')'); break; case NubTypeFunc fn: @@ -277,35 +240,6 @@ public class TypeEncoder } } - private string GetOrCreateStructDefinition(NubTypeStruct st) - { - if (!structDefinitions.TryGetValue(st, out var definition)) - { - definition = new Definition(structDefinitions.Count); - structDefinitions[st] = definition; - - var sb = new StringBuilder(); - - sb.Append("D("); - sb.Append(st.Packed ? '1' : '0'); - sb.Append('{'); - for (var i = 0; i < st.Fields.Count; i++) - { - var field = st.Fields[i]; - sb.Append(field.Name); - sb.Append(':'); - EncodeType(sb, field.Type); - } - sb.Append('}'); - sb.Append(')'); - - var encoded = sb.ToString(); - definition.Encoded = encoded; - } - - return $"T({definition.Index})"; - } - private class Definition(int index) { public int Index { get; } = index; @@ -317,7 +251,7 @@ public class TypeDecoder { public static NubType Decode(string encoded) { - return new TypeDecoder(encoded).DecodeRoot(); + return new TypeDecoder(encoded).DecodeType(); } private TypeDecoder(string encoded) @@ -326,66 +260,7 @@ public class TypeDecoder } private readonly string encoded; - private int index; - private readonly List structDefinitions = new(); - - private NubType DecodeRoot() - { - // First pass: Collect all declarations - while (TryExpect('D')) - { - Expect('('); - var packedChar = Consume(); - if (packedChar is not '1' and not '0') - throw new Exception("Expected '0' or '1' for struct packing"); - - Expect('{'); - while (TryPeek(out var c)) - { - Consume(); - if (c == '}') - break; - } - - Expect(')'); - - structDefinitions.Add(NubTypeStruct.CreateWithoutFields(packedChar == '1')); - } - - index = 0; - var defIndex = 0; - - // Second pass: Set field types - while (TryExpect('D')) - { - var fields = new List(); - - Consume(); - Consume(); - Consume(); - - while (!TryExpect('}')) - { - var sb = new StringBuilder(); - - while (!TryExpect(':')) - { - sb.Append(Consume()); - } - - var type = DecodeType(); - - fields.Add(new NubTypeStruct.Field(sb.ToString(), type)); - } - Expect(')'); - - structDefinitions[defIndex].SetFields(fields); - - defIndex += 1; - } - - return DecodeType(); - } + private int index = 0; private NubType DecodeType() { @@ -443,10 +318,21 @@ public class TypeDecoder private NubTypeStruct DecodeStruct() { + var sb = new StringBuilder(); + Expect('('); - var index = ExpectInt(); - Expect(')'); - return structDefinitions[index]; + while (!TryExpect(':')) + sb.Append(Consume()); + + var module = sb.ToString(); + sb.Clear(); + + while (!TryExpect(')')) + sb.Append(Consume()); + + var name = sb.ToString(); + + return NubTypeStruct.Get(module, name); } private bool TryPeek(out char c) @@ -517,6 +403,19 @@ public class TypeDecoder } } +public class NubTypeJsonConverter : JsonConverter +{ + public override NubType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return TypeDecoder.Decode(reader.GetString()!); + } + + public override void Write(Utf8JsonWriter writer, NubType value, JsonSerializerOptions options) + { + writer.WriteStringValue(TypeEncoder.Encode(value)); + } +} + public static class Hashing { public static ulong Fnv1a64(string text) @@ -535,14 +434,14 @@ public static class Hashing } } -public static class SymbolNameGen +public static class Name { - public static string Exported(string module, string function, NubType type) + public static string Create(string module, string name, NubType type) { var canonical = TypeEncoder.Encode(type); var hash = Hashing.Fnv1a64(canonical); - return $"nub_{Sanitize(module)}_{Sanitize(function)}_{hash:x16}"; + return $"nub_{Sanitize(module)}_{Sanitize(name)}_{hash:x16}"; } private static string Sanitize(string s) diff --git a/compiler/Program.cs b/compiler/Program.cs index c16fade..7c3557e 100644 --- a/compiler/Program.cs +++ b/compiler/Program.cs @@ -135,7 +135,7 @@ if (compileLib) File.WriteAllText(".build/out.c", output); Process.Start("gcc", ["-Og", "-fvisibility=hidden", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit(); Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit(); - NubLib.Pack(".build/out.nublib", ".build/out.a", moduleGraph.CreateManifest()); + NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph)); } else { diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index 349e7a0..0a8a563 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -172,56 +172,56 @@ public class TypeChecker case NodeExpressionBinary.Op.Multiply: case NodeExpressionBinary.Op.Divide: case NodeExpressionBinary.Op.Modulo: - { - if (left.Type is not NubTypeSInt and not NubTypeUInt) - throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side arithmetic operation: {left.Type}").At(fileName, left).Build()); + { + if (left.Type is not NubTypeSInt and not NubTypeUInt) + throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side arithmetic operation: {left.Type}").At(fileName, left).Build()); - if (right.Type is not NubTypeSInt and not NubTypeUInt) - throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side arithmetic operation: {right.Type}").At(fileName, right).Build()); + if (right.Type is not NubTypeSInt and not NubTypeUInt) + throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side arithmetic operation: {right.Type}").At(fileName, right).Build()); - type = left.Type; - break; - } + type = left.Type; + break; + } case NodeExpressionBinary.Op.LeftShift: case NodeExpressionBinary.Op.RightShift: - { - if (left.Type is not NubTypeUInt) - throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of left/right shift operation: {left.Type}").At(fileName, left).Build()); + { + if (left.Type is not NubTypeUInt) + throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of left/right shift operation: {left.Type}").At(fileName, left).Build()); - if (right.Type is not NubTypeUInt) - throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of left/right shift operation: {right.Type}").At(fileName, right).Build()); + if (right.Type is not NubTypeUInt) + throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of left/right shift operation: {right.Type}").At(fileName, right).Build()); - type = left.Type; - break; - } + type = left.Type; + break; + } case NodeExpressionBinary.Op.Equal: case NodeExpressionBinary.Op.NotEqual: case NodeExpressionBinary.Op.LessThan: case NodeExpressionBinary.Op.LessThanOrEqual: case NodeExpressionBinary.Op.GreaterThan: case NodeExpressionBinary.Op.GreaterThanOrEqual: - { - if (left.Type is not NubTypeSInt and not NubTypeUInt) - throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of comparison: {left.Type}").At(fileName, left).Build()); + { + if (left.Type is not NubTypeSInt and not NubTypeUInt) + throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of comparison: {left.Type}").At(fileName, left).Build()); - if (right.Type is not NubTypeSInt and not NubTypeUInt) - throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of comparison: {right.Type}").At(fileName, right).Build()); + if (right.Type is not NubTypeSInt and not NubTypeUInt) + throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of comparison: {right.Type}").At(fileName, right).Build()); - type = NubTypeBool.Instance; - break; - } + type = NubTypeBool.Instance; + break; + } case NodeExpressionBinary.Op.LogicalAnd: case NodeExpressionBinary.Op.LogicalOr: - { - if (left.Type is not NubTypeBool) - throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of logical operation: {left.Type}").At(fileName, left).Build()); + { + if (left.Type is not NubTypeBool) + throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of logical operation: {left.Type}").At(fileName, left).Build()); - if (right.Type is not NubTypeBool) - throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of logical operation: {right.Type}").At(fileName, right).Build()); + if (right.Type is not NubTypeBool) + throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of logical operation: {right.Type}").At(fileName, right).Build()); - type = NubTypeBool.Instance; - break; - } + type = NubTypeBool.Instance; + break; + } default: throw new ArgumentOutOfRangeException(); } @@ -260,21 +260,21 @@ public class TypeChecker switch (expression.Operation) { case NodeExpressionUnary.Op.Negate: - { - if (target.Type is not NubTypeSInt and not NubTypeUInt) - throw new CompileException(Diagnostic.Error($"Unsupported type for negation: {target.Type}").At(fileName, target).Build()); + { + if (target.Type is not NubTypeSInt and not NubTypeUInt) + throw new CompileException(Diagnostic.Error($"Unsupported type for negation: {target.Type}").At(fileName, target).Build()); - type = target.Type; - break; - } + type = target.Type; + break; + } case NodeExpressionUnary.Op.Invert: - { - if (target.Type is not NubTypeBool) - throw new CompileException(Diagnostic.Error($"Unsupported type for inversion: {target.Type}").At(fileName, target).Build()); + { + if (target.Type is not NubTypeBool) + throw new CompileException(Diagnostic.Error($"Unsupported type for inversion: {target.Type}").At(fileName, target).Build()); - type = NubTypeBool.Instance; - break; - } + type = NubTypeBool.Instance; + break; + } default: throw new ArgumentOutOfRangeException(); } @@ -313,10 +313,10 @@ public class TypeChecker var includePrivate = expression.Module.Ident == moduleName; - if (!module.TryResolveIdentifierType(expression.Value.Ident, includePrivate, out var identifierType)) + if (!module.TryResolveIdentifier(expression.Value.Ident, includePrivate, out var identifierType)) throw new CompileException(Diagnostic.Error($"Identifier '{expression.Module.Ident}::{expression.Value.Ident}' not found").At(fileName, expression.Value).Build()); - return new TypedNodeExpressionModuleIdent(expression.Tokens, identifierType, expression.Module, expression.Value); + return new TypedNodeExpressionModuleIdent(expression.Tokens, identifierType.Type, expression.Module, expression.Value); } private TypedNodeExpressionIntLiteral CheckExpressionIntLiteral(NodeExpressionIntLiteral expression) @@ -327,14 +327,27 @@ public class TypeChecker private TypedNodeExpressionMemberAccess CheckExpressionMemberAccess(NodeExpressionMemberAccess expression) { var target = CheckExpression(expression.Target); - if (target.Type is not NubTypeStruct structType) - throw new CompileException(Diagnostic.Error($"Cannot access member of non-struct type {target.Type}").At(fileName, target).Build()); - var field = structType.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident); - if (field == null) - throw new CompileException(Diagnostic.Error($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'").At(fileName, target).Build()); + switch (target.Type) + { + case NubTypeStruct structType: + if (!moduleGraph.TryResolveModule(structType.Module, out var module)) + throw new CompileException(Diagnostic.Error($"Module '{structType.Module}' not found").At(fileName, expression.Target).Build()); - return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name); + if (!module.TryResolveType(structType.Name, moduleName == structType.Module, out var typeDef)) + throw new CompileException(Diagnostic.Error($"Type '{structType.Name}' not found in module '{structType.Module}'").At(fileName, expression.Target).Build()); + + if (typeDef is not Module.TypeInfoStruct structDef) + throw new CompileException(Diagnostic.Error($"Type '{structType.Module}::{structType.Name}' is not a struct").At(fileName, expression.Target).Build()); + + var field = structDef.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident); + if (field == null) + throw new CompileException(Diagnostic.Error($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'").At(fileName, target).Build()); + + return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name); + default: + throw new CompileException(Diagnostic.Error($"{target.Type} has no member '{expression.Name.Ident}'").At(fileName, target).Build()); + } } private TypedNodeExpressionFuncCall CheckExpressionFuncCall(NodeExpressionFuncCall expression) @@ -360,16 +373,16 @@ public class TypeChecker var includePrivate = expression.Module.Ident == moduleName; - if (!module.TryResolveCustomType(expression.Name.Ident, includePrivate, out var customType)) + if (!module.TryResolveType(expression.Name.Ident, includePrivate, out var typeDef)) throw new CompileException(Diagnostic.Error($"Struct '{expression.Module.Ident}::{expression.Name.Ident}' not found").At(fileName, expression.Name).Build()); - if (customType is not NubTypeStruct structType) + if (typeDef is not Module.TypeInfoStruct structDef) throw new CompileException(Diagnostic.Error($"Cannot create struct literal of non-struct type '{expression.Module.Ident}::{expression.Name.Ident}'").At(fileName, expression.Name).Build()); var initializers = new List(); foreach (var initializer in expression.Initializers) { - var field = structType.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident); + var field = structDef.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident); if (field == null) throw new CompileException(Diagnostic.Error($"Field '{initializer.Name.Ident}' does not exist on struct '{expression.Name.Ident}'").At(fileName, initializer.Name).Build()); @@ -380,7 +393,7 @@ public class TypeChecker initializers.Add(new TypedNodeExpressionStructLiteral.Initializer(initializer.Tokens, initializer.Name, value)); } - return new TypedNodeExpressionStructLiteral(expression.Tokens, structType, initializers); + return new TypedNodeExpressionStructLiteral(expression.Tokens, NubTypeStruct.Get(expression.Module.Ident, expression.Name.Ident), initializers); } private NubType ResolveType(NodeType node) @@ -406,10 +419,14 @@ public class TypeChecker var includePrivate = type.Module.Ident == moduleName; - if (!module.TryResolveCustomType(type.Name.Ident, includePrivate, out var customType)) + if (!module.TryResolveType(type.Name.Ident, includePrivate, out var customType)) throw new CompileException(Diagnostic.Error($"Custom type '{type.Module.Ident}::{type.Name.Ident}' not found").At(fileName, type.Name).Build()); - return customType; + return customType switch + { + Module.TypeInfoStruct => NubTypeStruct.Get(type.Module.Ident, type.Name.Ident), + _ => throw new ArgumentOutOfRangeException(nameof(customType)) + }; } private sealed class Scope @@ -479,7 +496,7 @@ public class TypedNodeDefinitionFunc(List tokens, string module, TokenIde public string GetMangledName() { - return SymbolNameGen.Exported(Module, Name.Ident, GetNubType()); + return Compiler.Name.Create(Module, Name.Ident, GetNubType()); } public class Param(List tokens, TokenIdent name, NubType type) : TypedNode(tokens)