using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace Compiler; public class ModuleGraph(Dictionary modules) { public static Builder Create() => new(); public List GetModules() { return modules.Values.ToList(); } public bool TryResolveModule(string moduleName, [NotNullWhen(true)] out Module? module) { module = modules.GetValueOrDefault(moduleName); return module != null; } public sealed class Module { private readonly Dictionary customTypes = new(); private readonly Dictionary identifierTypes = new(); public List GetCustomTypes() { return customTypes.Values.ToList(); } public bool TryResolveCustomType(string name, [NotNullWhen(true)] out NubType? customType) { customType = customTypes.GetValueOrDefault(name); return customType != null; } public bool TryResolveIdentifierType(string name, [NotNullWhen(true)] out NubType? identifier) { identifier = identifierTypes.GetValueOrDefault(name); return identifier != null; } public void AddCustomType(string name, NubType type) { customTypes.Add(name, type); } public void AddIdentifier(string name, NubType identifier) { identifierTypes.Add(name, identifier); } } public class Builder { private readonly List asts = []; public void AddAst(Ast ast) { asts.Add(ast); } public ModuleGraph Build(out List diagnostics) { diagnostics = []; var astModuleCache = new Dictionary(); var modules = new Dictionary(); // First pass: Register modules foreach (var ast in asts) { var moduleDefinitions = ast.Definitions.OfType().ToList(); if (moduleDefinitions.Count == 0) diagnostics.Add(Diagnostic.Error("Missing module declaration").At(ast.FileName, 1, 1, 1).Build()); foreach (var extraModuleDefinition in moduleDefinitions.Skip(1)) diagnostics.Add(Diagnostic.Warning("Duplicate module declaration will be ignored").At(ast.FileName, extraModuleDefinition).Build()); if (moduleDefinitions.Count >= 1) { var currentModule = moduleDefinitions[0].Name.Ident; if (!modules.ContainsKey(currentModule)) { var module = new Module(); modules.Add(currentModule, module); astModuleCache[ast] = module; } } } // Second pass: Register struct types without fields foreach (var ast in asts) { var module = astModuleCache.GetValueOrDefault(ast); if (module == null) continue; foreach (var structDef in ast.Definitions.OfType()) module.AddCustomType(structDef.Name.Ident, new NubTypeStruct()); } // Third pass: Resolve struct fields foreach (var ast in asts) { var module = astModuleCache.GetValueOrDefault(ast); if (module == null) continue; foreach (var structDef in ast.Definitions.OfType()) { if (!module.TryResolveCustomType(structDef.Name.Ident, out var customType)) throw new UnreachableException($"{nameof(customType)} should always be registered"); var fields = structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, ResolveType(f.Type))).ToList(); ((NubTypeStruct)customType).ResolveFields(fields); } } // Fourth pass: Register identifiers foreach (var ast in asts) { var module = astModuleCache.GetValueOrDefault(ast); if (module == null) continue; foreach (var funcDef in ast.Definitions.OfType()) { var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type)).ToList(); var returnType = ResolveType(funcDef.ReturnType); var funcType = new NubTypeFunc(parameters, returnType); module.AddIdentifier(funcDef.Name.Ident, funcType); } } return new ModuleGraph(modules); NubType ResolveType(NodeType node) { return node switch { NodeTypeBool => new NubTypeBool(), NodeTypeCustom type => ResolveCustomType(type), NodeTypeFunc type => new NubTypeFunc(type.Parameters.Select(ResolveType).ToList(), ResolveType(type.ReturnType)), NodeTypePointer type => new NubTypePointer(ResolveType(type.To)), NodeTypeSInt type => new NubTypeSInt(type.Width), NodeTypeUInt type => new NubTypeUInt(type.Width), NodeTypeString => new NubTypeString(), NodeTypeVoid => new NubTypeVoid(), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } NubType ResolveCustomType(NodeTypeCustom type) { var module = modules.GetValueOrDefault(type.Module.Ident); if (module == null) throw new CompileException(Diagnostic.Error($"Unknown module: {type.Module.Ident}").Build()); if (!module.TryResolveCustomType(type.Name.Ident, out var customType)) throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").Build()); return customType; } } } }