using System.Diagnostics.CodeAnalysis; using NubLang.Ast; using NubLang.Diagnostics; using NubLang.Syntax; using NubLang.Types; namespace NubLang.Modules; public sealed class ModuleRepository { public static ModuleRepository Create(List syntaxTrees) { var structTypes = new Dictionary<(string module, string name), NubStructType>(); var enumTypes = new Dictionary<(string module, string name), NubIntType>(); foreach (var syntaxTree in syntaxTrees) { var module = syntaxTree.TopLevelSyntaxNodes.OfType().FirstOrDefault(); if (module == null) { throw new CompileException(Diagnostic.Error("Module declaration missing").WithHelp("module \"main\"").Build()); } foreach (var structSyntax in syntaxTree.TopLevelSyntaxNodes.OfType()) { // note(nub31): Since not all struct types are registered yet, we cannot register field types as they might reference unregistered structs var key = (module.NameToken.Value, structSyntax.NameToken.Value); structTypes.Add(key, new NubStructType(module.NameToken.Value, structSyntax.NameToken.Value, structSyntax.Packed, [])); } foreach (var enumSyntax in syntaxTree.TopLevelSyntaxNodes.OfType()) { NubIntType? underlyingType = null; if (enumSyntax.Type != null) { if (enumSyntax.Type is not IntTypeSyntax intType) { throw new CompileException(Diagnostic.Error("Underlying type of enum must be an integer type").At(enumSyntax.Type).Build()); } underlyingType = new NubIntType(intType.Signed, intType.Width); } underlyingType ??= new NubIntType(false, 64); var key = (module.NameToken.Value, enumSyntax.NameToken.Value); enumTypes.Add(key, underlyingType); } } // note(nub31): Since all struct types are now registered, we can safely resolve the field types foreach (var syntaxTree in syntaxTrees) { var module = syntaxTree.TopLevelSyntaxNodes.OfType().FirstOrDefault(); if (module == null) { throw new CompileException(Diagnostic.Error("Module declaration missing").WithHelp("module \"main\"").Build()); } foreach (var structSyntax in syntaxTree.TopLevelSyntaxNodes.OfType()) { var key = (module.NameToken.Value, structSyntax.NameToken.Value); structTypes[key].Fields = structSyntax.Fields .Select(x => new NubStructFieldType(x.NameToken.Value, ResolveType(x.Type, module.NameToken.Value), x.Value != null)) .ToList(); } } var modules = new Dictionary(); foreach (var syntaxTree in syntaxTrees) { var moduleDecl = syntaxTree.TopLevelSyntaxNodes.OfType().FirstOrDefault(); if (moduleDecl == null) { throw new CompileException(Diagnostic.Error("Module declaration missing").WithHelp("module \"main\"").Build()); } var functionPrototypes = new List(); foreach (var funcSyntax in syntaxTree.TopLevelSyntaxNodes.OfType()) { var returnType = ResolveType(funcSyntax.Prototype.ReturnType, moduleDecl.NameToken.Value); var parameters = funcSyntax.Prototype.Parameters.Select(x => new FuncParameterNode(x.Tokens, x.NameToken, ResolveType(x.Type, moduleDecl.NameToken.Value))).ToList(); functionPrototypes.Add(new FuncPrototypeNode(funcSyntax.Prototype.Tokens, funcSyntax.Prototype.NameToken, funcSyntax.Prototype.ExternSymbolToken, parameters, returnType)); } var module = new Module { Name = moduleDecl.NameToken.Value, StructTypes = structTypes.Where(x => x.Key.module == moduleDecl.NameToken.Value).Select(x => x.Value).ToList(), EnumTypes = enumTypes .Where(x => x.Key.module == moduleDecl.NameToken.Value) .ToDictionary(x => x.Key.name, x => x.Value), FunctionPrototypes = functionPrototypes }; modules.Add(moduleDecl.NameToken.Value, module); } return new ModuleRepository(modules); NubType ResolveType(TypeSyntax type, string currentModule) { return type switch { ArrayTypeSyntax arr => new NubArrayType(ResolveType(arr.BaseType, currentModule)), BoolTypeSyntax => new NubBoolType(), IntTypeSyntax i => new NubIntType(i.Signed, i.Width), FloatTypeSyntax f => new NubFloatType(f.Width), FuncTypeSyntax func => new NubFuncType(func.Parameters.Select(x => ResolveType(x, currentModule)).ToList(), ResolveType(func.ReturnType, currentModule)), SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType, currentModule)), ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType, currentModule), arr.Size), PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType, currentModule)), StringTypeSyntax => new NubStringType(), CustomTypeSyntax c => ResolveCustomType(c, currentModule), VoidTypeSyntax => new NubVoidType(), _ => throw new NotSupportedException($"Unknown type syntax: {type}") }; } NubType ResolveCustomType(CustomTypeSyntax customType, string currentModule) { var customTypeKey = (customType.ModuleToken?.Value ?? currentModule, customType.NameToken.Value); var resolvedStructType = structTypes.GetValueOrDefault(customTypeKey); if (resolvedStructType != null) { return resolvedStructType; } var resolvedEnumType = enumTypes.GetValueOrDefault(customTypeKey); if (resolvedEnumType != null) { return resolvedEnumType; } throw new CompileException(Diagnostic .Error($"Type {customType.NameToken.Value} not found in module {customType.ModuleToken?.Value ?? currentModule}") .At(customType) .Build()); } } private ModuleRepository(Dictionary modules) { _modules = modules; } private readonly Dictionary _modules; public Module Get(IdentifierToken ident) { var module = _modules.GetValueOrDefault(ident.Value); if (module == null) { throw new CompileException(Diagnostic.Error($"Module {ident.Value} was not found").At(ident).Build()); } return module; } public bool TryGet(IdentifierToken ident, [NotNullWhen(true)] out Module? module) { module = _modules.GetValueOrDefault(ident.Value); return module != null; } public sealed class Module { public required string Name { get; init; } public required List FunctionPrototypes { get; init; } = []; public required List StructTypes { get; init; } = []; public required Dictionary EnumTypes { get; init; } = []; public bool TryResolveFunc(IdentifierToken name, [NotNullWhen(true)] out FuncPrototypeNode? value, [NotNullWhen(false)] out Diagnostic? diagnostic) { value = FunctionPrototypes.FirstOrDefault(x => x.NameToken.Value == name.Value); if (value == null) { value = null; diagnostic = Diagnostic.Error($"Func {name.Value} not found in module {Name}").At(name).Build(); return false; } diagnostic = null; return true; } public FuncPrototypeNode ResolveFunc(IdentifierToken name) { if (!TryResolveFunc(name, out var value, out var diagnostic)) { throw new CompileException(diagnostic); } return value; } public bool TryResolveStruct(IdentifierToken name, [NotNullWhen(true)] out NubStructType? value, [NotNullWhen(false)] out Diagnostic? diagnostic) { value = StructTypes.FirstOrDefault(x => x.Name == name.Value); if (value == null) { value = null; diagnostic = Diagnostic.Error($"Struct {name.Value} not found in module {Name}").At(name).Build(); return false; } diagnostic = null; return true; } public NubStructType ResolveStruct(IdentifierToken name) { if (!TryResolveStruct(name, out var value, out var diagnostic)) { throw new CompileException(diagnostic); } return value; } public bool TryResolveEnum(IdentifierToken name, [NotNullWhen(true)] out NubIntType? value, [NotNullWhen(false)] out Diagnostic? diagnostic) { value = EnumTypes.GetValueOrDefault(name.Value); if (value == null) { value = null; diagnostic = Diagnostic.Error($"Enum {name.Value} not found in module {Name}").At(name).Build(); return false; } diagnostic = null; return true; } public NubIntType ResolveEnum(IdentifierToken name) { if (!TryResolveEnum(name, out var value, out var diagnostic)) { throw new CompileException(diagnostic); } return value; } } }