module exports and name mangling

This commit is contained in:
nub31
2026-02-10 20:33:27 +01:00
parent 6ae10d5f90
commit 7872a4b6b8
7 changed files with 139 additions and 48 deletions

View File

@@ -24,13 +24,15 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
#include <stdbool.h>
#include <stdint.h>
struct string {
struct nub_core_string
{
const char *data;
int length;
};
""");
writer.WriteLine();
foreach (var typeName in structTypeNames)
writer.WriteLine($"struct {typeName.Value};");
@@ -38,7 +40,14 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
foreach (var typeName in structTypeNames)
{
writer.WriteLine($"struct {typeName.Value}");
writer.Write("struct ");
if (typeName.Key.Packed)
{
writer.Write("__attribute__((__packed__)) ");
}
writer.WriteLine(typeName.Value);
writer.WriteLine("{");
using (writer.Indent())
{
@@ -74,13 +83,12 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
if (main != null)
{
writer.WriteLine("int main(int argc, char *argv[])");
writer.WriteLine("{");
using (writer.Indent())
{
writer.WriteLine($"return {main.GetMangledName()}();");
}
writer.WriteLine("}");
writer.WriteLine($$"""
int main(int argc, char *argv[])
{
return {{main.GetMangledName()}}();
}
""");
}
writer.WriteLine();
@@ -216,7 +224,7 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
TypedNodeExpressionUnary expression => EmitExpressionUnary(expression),
TypedNodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false",
TypedNodeExpressionIntLiteral expression => expression.Value.Value.ToString(),
TypedNodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
TypedNodeExpressionStringLiteral expression => $"(struct nub_core_string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression),
TypedNodeExpressionLocalIdent expression => expression.Value.Ident,
@@ -302,7 +310,7 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
NubTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""),
NubTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""),
NubTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"),
NubTypeString => "struct string" + (varName != null ? $" {varName}" : ""),
NubTypeString => "struct nub_core_string" + (varName != null ? $" {varName}" : ""),
NubTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})",
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
};

View File

@@ -21,40 +21,69 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
public sealed class Module(string name)
{
public string Name { get; } = name;
private readonly Dictionary<string, NubType> customTypes = new();
private readonly Dictionary<string, NubType> identifierTypes = new();
private readonly Dictionary<string, CustomTypeInfo> customTypes = new();
private readonly Dictionary<string, IdentifierInfo> identifierTypes = new();
public IReadOnlyList<NubType> GetCustomTypes()
{
return customTypes.Values.ToList();
return customTypes.Values.Select(x => x.Type).ToList();
}
public IReadOnlyDictionary<string, NubType> GetIdentifierTypes()
{
return identifierTypes;
return identifierTypes.ToDictionary(x => x.Key, x => x.Value.Type);
}
public bool TryResolveCustomType(string name, [NotNullWhen(true)] out NubType? customType)
public bool TryResolveCustomType(string name, bool searchPrivate, [NotNullWhen(true)] out NubType? customType)
{
customType = customTypes.GetValueOrDefault(name);
return customType != null;
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, [NotNullWhen(true)] out NubType? identifier)
public bool TryResolveIdentifierType(string name, bool searchPrivate, [NotNullWhen(true)] out NubType? identifierType)
{
identifier = identifierTypes.GetValueOrDefault(name);
return identifier != null;
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)
public void AddCustomType(string name, NubType type, bool exported)
{
customTypes.Add(name, type);
customTypes.Add(name, new CustomTypeInfo(type, exported));
}
public void AddIdentifier(string name, NubType identifier)
public void AddIdentifier(string name, NubType type, bool exported)
{
identifierTypes.Add(name, identifier);
identifierTypes.Add(name, new IdentifierInfo(type, exported));
}
private record CustomTypeInfo(NubType Type, bool Exported);
private record IdentifierInfo(NubType Type, bool Exported);
}
public class Builder
@@ -91,7 +120,7 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
if (module == null) continue;
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
module.AddCustomType(structDef.Name.Ident, new NubTypeStruct(module.Name, structDef.Name.Ident));
module.AddCustomType(structDef.Name.Ident, new NubTypeStruct(module.Name, structDef.Name.Ident, structDef.Packed), structDef.Exported);
}
// Third pass: Resolve struct fields
@@ -102,10 +131,10 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
{
if (!module.TryResolveCustomType(structDef.Name.Ident, out var customType))
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))).ToList();
var fields = structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, ResolveType(f.Type, true))).ToList();
((NubTypeStruct)customType).ResolveFields(fields);
}
}
@@ -118,10 +147,10 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionFunc>())
{
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type)).ToList();
var returnType = ResolveType(funcDef.ReturnType);
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);
module.AddIdentifier(funcDef.Name.Ident, funcType, funcDef.Exported);
}
}
@@ -130,14 +159,14 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
return new ModuleGraph(modules);
NubType ResolveType(NodeType node)
NubType ResolveType(NodeType node, bool includePrivate)
{
return node switch
{
NodeTypeBool => NubTypeBool.Instance,
NodeTypeCustom type => ResolveCustomType(type),
NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(ResolveType).ToList(), ResolveType(type.ReturnType)),
NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To)),
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,
@@ -146,13 +175,13 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
};
}
NubType ResolveCustomType(NodeTypeCustom type)
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, out var customType))
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;

View File

@@ -88,15 +88,17 @@ public sealed class NubTypeStruct : NubType
{
public string Name { get; }
public string Module { get; }
public bool Packed { get; }
private IReadOnlyList<Field>? _resolvedFields;
public IReadOnlyList<Field> Fields => _resolvedFields ?? throw new InvalidOperationException();
public NubTypeStruct(string module, string name)
public NubTypeStruct(string module, string name, bool packed)
{
Module = module;
Name = name;
Packed = packed;
}
public void ResolveFields(IReadOnlyList<Field> fields)

View File

@@ -43,8 +43,38 @@ public sealed class Parser(string fileName, List<Token> tokens)
{
var startIndex = index;
Dictionary<Keyword, TokenKeyword> modifiers = [];
while (true)
{
if (Peek() is TokenKeyword keyword)
{
switch (keyword.Keyword)
{
case Keyword.Export:
Next();
modifiers[Keyword.Export] = keyword;
break;
case Keyword.Packed:
Next();
modifiers[Keyword.Packed] = keyword;
break;
default:
goto modifier_done;
}
}
}
modifier_done:
if (TryExpectKeyword(Keyword.Func))
{
var exported = modifiers.Remove(Keyword.Export);
foreach (var modifier in modifiers)
// todo(nub31): Add to diagnostics instead of throwing
throw new CompileException(Diagnostic.Error("Invalid modifier for function").At(fileName, modifier.Value).Build());
var name = ExpectIdent();
var parameters = new List<NodeDefinitionFunc.Param>();
@@ -63,11 +93,18 @@ public sealed class Parser(string fileName, List<Token> tokens)
var body = ParseStatement();
return new NodeDefinitionFunc(TokensFrom(startIndex), name, parameters, body, returnType);
return new NodeDefinitionFunc(TokensFrom(startIndex), exported, name, parameters, body, returnType);
}
if (TryExpectKeyword(Keyword.Struct))
{
var exported = modifiers.Remove(Keyword.Export);
var packed = modifiers.Remove(Keyword.Packed);
foreach (var modifier in modifiers)
// todo(nub31): Add to diagnostics instead of throwing
throw new CompileException(Diagnostic.Error("Invalid modifier for struct").At(fileName, modifier.Value).Build());
var name = ExpectIdent();
var fields = new List<NodeDefinitionStruct.Field>();
@@ -81,7 +118,7 @@ public sealed class Parser(string fileName, List<Token> tokens)
fields.Add(new NodeDefinitionStruct.Field(TokensFrom(fieldStartIndex), fieldName, fieldType));
}
return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields);
return new NodeDefinitionStruct(TokensFrom(startIndex), exported, packed, name, fields);
}
throw new CompileException(Diagnostic.Error("Not a valid definition").At(fileName, Peek()).Build());
@@ -556,8 +593,9 @@ public abstract class Node(List<Token> tokens)
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
public sealed class NodeDefinitionFunc(List<Token> tokens, bool exported, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
{
public bool Exported { get; } = exported;
public TokenIdent Name { get; } = name;
public List<Param> Parameters { get; } = parameters;
public NodeStatement Body { get; } = body;
@@ -570,8 +608,10 @@ public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List
}
}
public sealed class NodeDefinitionStruct(List<Token> tokens, TokenIdent name, List<NodeDefinitionStruct.Field> fields) : NodeDefinition(tokens)
public sealed class NodeDefinitionStruct(List<Token> tokens, bool exported, bool packed, TokenIdent name, List<NodeDefinitionStruct.Field> fields) : NodeDefinition(tokens)
{
public bool Exported { get; } = exported;
public bool Packed { get; } = packed;
public TokenIdent Name { get; } = name;
public List<Field> Fields { get; } = fields;

View File

@@ -379,12 +379,14 @@ public sealed class Tokenizer(string fileName, string contents)
{
"func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func),
"struct" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Struct),
"packed" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Packed),
"let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let),
"if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If),
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
"module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module),
"export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export),
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
_ => new TokenIdent(line, startColumn, column - startColumn, value)
@@ -523,12 +525,14 @@ public enum Keyword
{
Func,
Struct,
Packed,
Let,
If,
Else,
While,
Return,
Module,
Export,
}
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
@@ -585,12 +589,14 @@ public static class TokenExtensions
{
Keyword.Func => "func",
Keyword.Struct => "struct",
Keyword.Packed => "packed",
Keyword.Let => "let",
Keyword.If => "if",
Keyword.Else => "else",
Keyword.While => "while",
Keyword.Return => "return",
Keyword.Module => "module",
Keyword.Export => "export",
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
};
}

View File

@@ -291,7 +291,9 @@ public sealed class TypeChecker(string fileName, string moduleName, NodeDefiniti
if (!moduleGraph.TryResolveModule(expression.Module.Ident, out var module))
throw new CompileException(Diagnostic.Error($"Module '{expression.Module.Ident}' not found").At(fileName, expression.Module).Build());
if (!module.TryResolveIdentifierType(expression.Value.Ident, out var identifierType))
var includePrivate = expression.Module.Ident == moduleName;
if (!module.TryResolveIdentifierType(expression.Value.Ident, includePrivate, out var identifierType))
throw new CompileException(Diagnostic.Error($"Identifier '{expression.Module.Ident}::{expression.Value.Ident}' not found").At(fileName, expression.Value).Build());
return new TypedNodeExpressionModuleIdent(expression.Tokens, identifierType, expression.Module, expression.Value);
@@ -336,7 +338,9 @@ public sealed class TypeChecker(string fileName, string moduleName, NodeDefiniti
if (!moduleGraph.TryResolveModule(expression.Module.Ident, out var module))
throw new CompileException(Diagnostic.Error($"Module '{expression.Module.Ident}' not found").At(fileName, expression.Module).Build());
if (!module.TryResolveCustomType(expression.Name.Ident, out var customType))
var includePrivate = expression.Module.Ident == moduleName;
if (!module.TryResolveCustomType(expression.Name.Ident, includePrivate, out var customType))
throw new CompileException(Diagnostic.Error($"Struct '{expression.Module.Ident}::{expression.Name.Ident}' not found").At(fileName, expression.Name).Build());
if (customType is not NubTypeStruct structType)
@@ -380,7 +384,9 @@ public sealed class TypeChecker(string fileName, string moduleName, NodeDefiniti
if (!moduleGraph.TryResolveModule(type.Module.Ident, out var module))
throw new CompileException(Diagnostic.Error($"Module '{type.Module.Ident}' not found").At(fileName, type.Module).Build());
if (!module.TryResolveCustomType(type.Name.Ident, out var customType))
var includePrivate = type.Module.Ident == moduleName;
if (!module.TryResolveCustomType(type.Name.Ident, includePrivate, out var customType))
throw new CompileException(Diagnostic.Error($"Custom type '{type.Module.Ident}::{type.Name.Ident}' not found").At(fileName, type.Name).Build());
return customType;

View File

@@ -1,10 +1,10 @@
module test
struct person {
export packed struct person {
age: i32
name: string
}
func do_something(name: string): i32 {
export func do_something(name: string): i32 {
return 3
}