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(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, NubType type, bool exported) { customTypes.Add(name, new CustomTypeInfo(type, exported)); } public void AddIdentifier(string name, NubType type, bool exported) { identifierTypes.Add(name, new IdentifierInfo(type, exported)); } private record CustomTypeInfo(NubType Type, bool Exported); private record IdentifierInfo(NubType Type, bool Exported); } 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) { if (!modules.ContainsKey(ast.ModuleName.Ident)) { var module = new Module(ast.ModuleName.Ident); modules.Add(ast.ModuleName.Ident, 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(module.Name, structDef.Name.Ident, structDef.Packed), structDef.Exported); } // 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, true, 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, true))).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, true)).ToList(); var returnType = ResolveType(funcDef.ReturnType, true); var funcType = NubTypeFunc.Get(parameters, returnType); module.AddIdentifier(funcDef.Name.Ident, funcType, funcDef.Exported); } } if (diagnostics.Any(x => x.Severity == DiagnosticSeverity.Error)) return null; return new ModuleGraph(modules); NubType ResolveType(NodeType node, bool includePrivate) { return node switch { NodeTypeBool => NubTypeBool.Instance, NodeTypeCustom type => ResolveCustomType(type, includePrivate), NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(x => ResolveType(x, includePrivate)).ToList(), ResolveType(type.ReturnType, includePrivate)), NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To, includePrivate)), NodeTypeSInt type => NubTypeSInt.Get(type.Width), NodeTypeUInt type => NubTypeUInt.Get(type.Width), NodeTypeString => NubTypeString.Instance, NodeTypeVoid => NubTypeVoid.Instance, _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } NubType ResolveCustomType(NodeTypeCustom type, bool includePrivate) { 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, includePrivate, out var customType)) throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").Build()); return customType; } } } }