296 lines
10 KiB
C#
296 lines
10 KiB
C#
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace Compiler;
|
|
|
|
public class ModuleGraph
|
|
{
|
|
public static Builder CreateBuilder() => new();
|
|
|
|
private ModuleGraph(Dictionary<string, Module> modules)
|
|
{
|
|
this.modules = modules;
|
|
}
|
|
|
|
private readonly Dictionary<string, Module> modules;
|
|
|
|
public List<Module> GetModules()
|
|
{
|
|
return modules.Values.ToList();
|
|
}
|
|
|
|
public bool TryResolveIdentifier(string moduleName, string identifierName, bool searchPrivate, [NotNullWhen(true)] out Module.IdentifierInfo? info)
|
|
{
|
|
if (!TryResolveModule(moduleName, out var module))
|
|
{
|
|
info = null;
|
|
return false;
|
|
}
|
|
|
|
if (!module.TryResolveIdentifier(identifierName, searchPrivate, out info))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool TryResolveType(string moduleName, string typeName, bool searchPrivate, [NotNullWhen(true)] out Module.TypeInfo? info)
|
|
{
|
|
if (!TryResolveModule(moduleName, out var module))
|
|
{
|
|
info = null;
|
|
return false;
|
|
}
|
|
|
|
if (!module.TryResolveType(typeName, searchPrivate, out info))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool TryResolveModule(string moduleName, [NotNullWhen(true)] out Module? module)
|
|
{
|
|
module = modules.GetValueOrDefault(moduleName);
|
|
return module != null;
|
|
}
|
|
|
|
public class Builder
|
|
{
|
|
private readonly List<Ast> asts = [];
|
|
private readonly List<Manifest> manifests = [];
|
|
|
|
public void AddAst(Ast ast)
|
|
{
|
|
asts.Add(ast);
|
|
}
|
|
|
|
public void AddManifest(Manifest manifest)
|
|
{
|
|
manifests.Add(manifest);
|
|
}
|
|
|
|
public ModuleGraph? Build(out List<Diagnostic> diagnostics)
|
|
{
|
|
diagnostics = [];
|
|
|
|
var modules = new Dictionary<string, Module>();
|
|
|
|
foreach (var manifest in manifests)
|
|
{
|
|
foreach (var (moduleName, manifestModule) in manifest.Modules)
|
|
{
|
|
var module = GetOrCreateModule(moduleName);
|
|
|
|
foreach (var (name, type) in manifestModule.Types)
|
|
{
|
|
switch (type)
|
|
{
|
|
case Manifest.Module.TypeInfoStruct s:
|
|
var info = new Module.TypeInfoStruct(true, s.Packed, true);
|
|
var fields = s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList();
|
|
info.SetFields(fields);
|
|
module.AddType(name, info);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(type));
|
|
}
|
|
}
|
|
|
|
foreach (var (name, identifier) in manifestModule.Identifiers)
|
|
{
|
|
module.AddIdentifier(name, new Module.IdentifierInfo(true, identifier.Type, identifier.MangledName, true));
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var ast in asts)
|
|
{
|
|
var module = GetOrCreateModule(ast.ModuleName.Ident);
|
|
|
|
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
|
{
|
|
module.AddType(structDef.Name.Ident, new Module.TypeInfoStruct(structDef.Exported, structDef.Packed, false));
|
|
}
|
|
}
|
|
|
|
foreach (var ast in asts)
|
|
{
|
|
var module = GetOrCreateModule(ast.ModuleName.Ident);
|
|
|
|
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
|
{
|
|
if (!module.TryResolveType(structDef.Name.Ident, true, out var typeInfo))
|
|
throw new UnreachableException($"{nameof(typeInfo)} should always be registered");
|
|
|
|
if (typeInfo is Module.TypeInfoStruct structType)
|
|
{
|
|
var fields = structDef.Fields.Select(f => new Module.TypeInfoStruct.Field(f.Name.Ident, ResolveType(f.Type, module.Name))).ToList();
|
|
structType.SetFields(fields);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var ast in asts)
|
|
{
|
|
var module = GetOrCreateModule(ast.ModuleName.Ident);
|
|
|
|
foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionFunc>())
|
|
{
|
|
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList();
|
|
var returnType = ResolveType(funcDef.ReturnType, module.Name);
|
|
var funcType = NubTypeFunc.Get(parameters, returnType);
|
|
var info = new Module.IdentifierInfo(funcDef.Exported, funcType, NameMangler.Mangle(module.Name, funcDef.Name.Ident, funcType), false);
|
|
module.AddIdentifier(funcDef.Name.Ident, info);
|
|
}
|
|
|
|
foreach (var globalVariable in ast.Definitions.OfType<NodeDefinitionGlobalVariable>())
|
|
{
|
|
var type = ResolveType(globalVariable.Type, module.Name);
|
|
var info = new Module.IdentifierInfo(globalVariable.Exported, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type), false);
|
|
module.AddIdentifier(globalVariable.Name.Ident, info);
|
|
}
|
|
}
|
|
|
|
if (diagnostics.Any(x => x.Severity == Diagnostic.DiagnosticSeverity.Error))
|
|
return null;
|
|
|
|
return new ModuleGraph(modules);
|
|
|
|
NubType ResolveType(NodeType node, string currentModule)
|
|
{
|
|
return node switch
|
|
{
|
|
NodeTypeBool => NubTypeBool.Instance,
|
|
NodeTypeCustom type => ResolveCustomType(type, currentModule),
|
|
NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(x => ResolveType(x, currentModule)).ToList(), ResolveType(type.ReturnType, currentModule)),
|
|
NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To, currentModule)),
|
|
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, string currentModule)
|
|
{
|
|
var module = modules.GetValueOrDefault(type.Module.Ident);
|
|
if (module == null)
|
|
throw new CompileException(Diagnostic.Error($"Unknown module: {type.Module.Ident}").Build());
|
|
|
|
var includePrivate = currentModule == type.Module.Ident;
|
|
|
|
if (!module.TryResolveType(type.Name.Ident, includePrivate, out var customType))
|
|
throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").Build());
|
|
|
|
return customType switch
|
|
{
|
|
Module.TypeInfoStruct => NubTypeStruct.Get(type.Module.Ident, type.Name.Ident),
|
|
_ => throw new ArgumentOutOfRangeException(nameof(customType))
|
|
};
|
|
}
|
|
|
|
Module GetOrCreateModule(string name)
|
|
{
|
|
if (!modules.TryGetValue(name, out var module))
|
|
{
|
|
module = new Module(name);
|
|
modules.Add(name, module);
|
|
}
|
|
|
|
return module;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Module(string name)
|
|
{
|
|
public string Name { get; } = name;
|
|
|
|
private Dictionary<string, TypeInfo> types = new();
|
|
private Dictionary<string, IdentifierInfo> identifiers = new();
|
|
|
|
public IReadOnlyDictionary<string, TypeInfo> GetTypes() => types;
|
|
public IReadOnlyDictionary<string, IdentifierInfo> GetIdentifiers() => identifiers;
|
|
|
|
public bool TryResolveType(string name, bool searchPrivate, [NotNullWhen(true)] out TypeInfo? customType)
|
|
{
|
|
var info = types.GetValueOrDefault(name);
|
|
if (info == null)
|
|
{
|
|
customType = null;
|
|
return false;
|
|
}
|
|
|
|
if (info.Exported || searchPrivate)
|
|
{
|
|
customType = info;
|
|
return true;
|
|
}
|
|
|
|
customType = null;
|
|
return false;
|
|
}
|
|
|
|
public bool TryResolveIdentifier(string name, bool searchPrivate, [NotNullWhen(true)] out IdentifierInfo? identifierType)
|
|
{
|
|
var info = identifiers.GetValueOrDefault(name);
|
|
if (info == null)
|
|
{
|
|
identifierType = null;
|
|
return false;
|
|
}
|
|
|
|
if (info.Exported || searchPrivate)
|
|
{
|
|
identifierType = info;
|
|
return true;
|
|
}
|
|
|
|
identifierType = null;
|
|
return false;
|
|
}
|
|
|
|
public void AddType(string name, TypeInfo info)
|
|
{
|
|
types.Add(name, info);
|
|
}
|
|
|
|
public void AddIdentifier(string name, IdentifierInfo info)
|
|
{
|
|
identifiers.Add(name, info);
|
|
}
|
|
|
|
public class IdentifierInfo(bool exported, NubType type, string mangledName, bool external)
|
|
{
|
|
public bool Exported { get; } = exported;
|
|
public NubType Type { get; } = type;
|
|
public string MangledName { get; } = mangledName;
|
|
public bool External { get; } = external;
|
|
}
|
|
|
|
public abstract class TypeInfo(bool exported, bool external)
|
|
{
|
|
public bool Exported { get; } = exported;
|
|
public bool External { get; } = external;
|
|
}
|
|
|
|
public class TypeInfoStruct(bool exported, bool packed, bool external) : TypeInfo(exported, external)
|
|
{
|
|
private IReadOnlyList<Field>? fields;
|
|
|
|
public bool Packed { get; } = packed;
|
|
public IReadOnlyList<Field> Fields => fields ?? throw new InvalidOperationException("Fields has not been set yet");
|
|
|
|
public void SetFields(IReadOnlyList<Field> fields)
|
|
{
|
|
this.fields = fields;
|
|
}
|
|
|
|
public class Field(string name, NubType type)
|
|
{
|
|
public string Name { get; } = name;
|
|
public NubType Type { get; } = type;
|
|
}
|
|
}
|
|
} |