using System.Text; namespace Compiler; public abstract class NubType { public abstract override string ToString(); } public sealed class NubTypeVoid : NubType { public static readonly NubTypeVoid Instance = new(); private NubTypeVoid() { } public override string ToString() => "void"; } public sealed class NubTypeUInt : NubType { private static readonly Dictionary Cache = new(); public static NubTypeUInt Get(int width) { if (!Cache.TryGetValue(width, out var type)) Cache[width] = type = new NubTypeUInt(width); return type; } public int Width { get; } private NubTypeUInt(int width) { Width = width; } public override string ToString() => $"u{Width}"; } public sealed class NubTypeSInt : NubType { private static readonly Dictionary Cache = new(); public static NubTypeSInt Get(int width) { if (!Cache.TryGetValue(width, out var type)) Cache[width] = type = new NubTypeSInt(width); return type; } public int Width { get; } private NubTypeSInt(int width) { Width = width; } public override string ToString() => $"i{Width}"; } public sealed class NubTypeBool : NubType { public static readonly NubTypeBool Instance = new(); private NubTypeBool() { } public override string ToString() => "bool"; } public sealed class NubTypeString : NubType { public static readonly NubTypeString Instance = new(); private NubTypeString() { } public override string ToString() => "string"; } public sealed class NubTypeStruct : NubType { public string Name { get; } public string Module { get; } public bool Packed { get; } private IReadOnlyList? _resolvedFields; public IReadOnlyList Fields => _resolvedFields ?? throw new InvalidOperationException(); public NubTypeStruct(string module, string name, bool packed) { Module = module; Name = name; Packed = packed; } public void ResolveFields(IReadOnlyList fields) { if (_resolvedFields != null) throw new InvalidOperationException($"{Name} already resolved"); _resolvedFields = fields; } public override string ToString() => _resolvedFields == null ? $"struct {Module}::{Name} " : $"struct {Module}::{Name} {{ {string.Join(' ', Fields.Select(f => $"{f.Name}: {f.Type}"))} }}"; public sealed class Field(string name, NubType type) { public string Name { get; } = name; public NubType Type { get; } = type; } } public sealed class NubTypePointer : NubType { private static readonly Dictionary Cache = new(); public static NubTypePointer Get(NubType to) { if (!Cache.TryGetValue(to, out var ptr)) Cache[to] = ptr = new NubTypePointer(to); return ptr; } public NubType To { get; } private NubTypePointer(NubType to) { To = to; } public override string ToString() => $"^{To}"; } public sealed class NubTypeFunc : NubType { private static readonly Dictionary Cache = new(); public static NubTypeFunc Get(List parameters, NubType returnType) { var sig = new Signature(parameters, returnType); if (!Cache.TryGetValue(sig, out var func)) Cache[sig] = func = new NubTypeFunc(parameters, returnType); return func; } public IReadOnlyList Parameters { get; } public NubType ReturnType { get; } public string MangledName(string name, string module) => SymbolNameGen.Exported(name, module, this); private NubTypeFunc(List parameters, NubType returnType) { Parameters = parameters; ReturnType = returnType; } public override string ToString() => $"func({string.Join(' ', Parameters)}): {ReturnType}"; private readonly record struct Signature(IReadOnlyList Parameters, NubType ReturnType); } static class TypeMangler { public static string Encode(NubType type) { var sb = new StringBuilder(); Encode(type, sb); return sb.ToString(); } private static void Encode(NubType type, StringBuilder sb) { switch (type) { case NubTypeVoid: sb.Append('V'); break; case NubTypeBool: sb.Append('B'); break; case NubTypeUInt u: sb.Append("U("); sb.Append(u.Width); sb.Append(')'); break; case NubTypeSInt s: sb.Append("U("); sb.Append(s.Width); sb.Append(')'); break; case NubTypeString: sb.Append('S'); break; case NubTypePointer p: sb.Append("P("); Encode(p.To, sb); sb.Append(')'); break; case NubTypeStruct st: sb.Append($"T({st.Module}::{st.Name})").Append(st.Module).Append("::").Append(st.Name).Append(')'); break; case NubTypeFunc fn: sb.Append("F("); for (int i = 0; i < fn.Parameters.Count; i++) { Encode(fn.Parameters[i], sb); sb.Append(','); } Encode(fn.ReturnType, sb); sb.Append(')'); break; default: throw new NotSupportedException(type.GetType().Name); } } } static class Hashing { public static ulong Fnv1a64(string text) { const ulong offset = 14695981039346656037UL; const ulong prime = 1099511628211UL; ulong hash = offset; foreach (var c in Encoding.UTF8.GetBytes(text)) { hash ^= c; hash *= prime; } return hash; } } static class SymbolNameGen { public static string Exported(string module, string function, NubType type) { var canonical = TypeMangler.Encode(type); var hash = Hashing.Fnv1a64(canonical); return $"nub_{Sanitize(module)}_{Sanitize(function)}_{hash:x16}"; } private static string Sanitize(string s) { var sb = new StringBuilder(s.Length); foreach (var c in s) { if (char.IsLetterOrDigit(c)) sb.Append(c); else sb.Append('_'); } return sb.ToString(); } }