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 structTypes = new(); private readonly Dictionary identifierTypes = new(); public List GetStructTypes() { return structTypes.Values.ToList(); } public bool TryResolveStructType(string structName, [NotNullWhen(true)] out NubTypeStruct? structType) { structType = structTypes.GetValueOrDefault(structName); return structType != null; } public bool TryResolveIdentifierType(string identifierName, [NotNullWhen(true)] out NubType? identifier) { identifier = identifierTypes.GetValueOrDefault(identifierName); return identifier != null; } public void AddStruct(string name, NubTypeStruct structType) { structTypes.Add(name, structType); } 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.AddStruct(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.TryResolveStructType(structDef.Name.Ident, out var structType)) throw new UnreachableException($"{nameof(structType)} should always be registered"); var fields = structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, Resolve(f.Type))).ToList(); structType.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 => Resolve(x.Type)).ToList(); var returnType = Resolve(funcDef.ReturnType); var funcType = new NubTypeFunc(parameters, returnType); module.AddIdentifier(funcDef.Name.Ident, funcType); } } return new ModuleGraph(modules); NubType Resolve(NodeType node) { return node switch { NodeTypeBool => new NubTypeBool(), NodeTypeCustom type => ResolveStruct(type), NodeTypeFunc type => new NubTypeFunc(type.Parameters.Select(Resolve).ToList(), Resolve(type.ReturnType)), NodeTypePointer type => new NubTypePointer(Resolve(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)) }; } NubTypeStruct ResolveStruct(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.TryResolveStructType(type.Name.Ident, out var structType)) throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").Build()); return structType; } } } }