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 <stdbool.h>
#include <stdint.h> #include <stdint.h>
struct string { struct nub_core_string
{
const char *data; const char *data;
int length; int length;
}; };
"""); """);
writer.WriteLine();
foreach (var typeName in structTypeNames) foreach (var typeName in structTypeNames)
writer.WriteLine($"struct {typeName.Value};"); writer.WriteLine($"struct {typeName.Value};");
@@ -38,7 +40,14 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
foreach (var typeName in structTypeNames) 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("{"); writer.WriteLine("{");
using (writer.Indent()) using (writer.Indent())
{ {
@@ -74,13 +83,12 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
if (main != null) if (main != null)
{ {
writer.WriteLine("int main(int argc, char *argv[])"); writer.WriteLine($$"""
writer.WriteLine("{"); int main(int argc, char *argv[])
using (writer.Indent()) {
{ return {{main.GetMangledName()}}();
writer.WriteLine($"return {main.GetMangledName()}();"); }
} """);
writer.WriteLine("}");
} }
writer.WriteLine(); writer.WriteLine();
@@ -216,7 +224,7 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
TypedNodeExpressionUnary expression => EmitExpressionUnary(expression), TypedNodeExpressionUnary expression => EmitExpressionUnary(expression),
TypedNodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false", TypedNodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false",
TypedNodeExpressionIntLiteral expression => expression.Value.Value.ToString(), 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), TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression), TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression),
TypedNodeExpressionLocalIdent expression => expression.Value.Ident, 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}" : ""), NubTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""),
NubTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""), NubTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""),
NubTypePointer type => CType(type.To) + (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)))})", NubTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})",
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null) _ => 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 sealed class Module(string name)
{ {
public string Name { get; } = name; public string Name { get; } = name;
private readonly Dictionary<string, NubType> customTypes = new(); private readonly Dictionary<string, CustomTypeInfo> customTypes = new();
private readonly Dictionary<string, NubType> identifierTypes = new(); private readonly Dictionary<string, IdentifierInfo> identifierTypes = new();
public IReadOnlyList<NubType> GetCustomTypes() public IReadOnlyList<NubType> GetCustomTypes()
{ {
return customTypes.Values.ToList(); return customTypes.Values.Select(x => x.Type).ToList();
} }
public IReadOnlyDictionary<string, NubType> GetIdentifierTypes() 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); var info = customTypes.GetValueOrDefault(name);
return customType != null; 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); var info = identifierTypes.GetValueOrDefault(name);
return identifier != null; 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 public class Builder
@@ -91,7 +120,7 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
if (module == null) continue; if (module == null) continue;
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>()) 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 // 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>()) 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"); 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); ((NubTypeStruct)customType).ResolveFields(fields);
} }
} }
@@ -118,10 +147,10 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionFunc>()) foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionFunc>())
{ {
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type)).ToList(); var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, true)).ToList();
var returnType = ResolveType(funcDef.ReturnType); var returnType = ResolveType(funcDef.ReturnType, true);
var funcType = NubTypeFunc.Get(parameters, returnType); 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); return new ModuleGraph(modules);
NubType ResolveType(NodeType node) NubType ResolveType(NodeType node, bool includePrivate)
{ {
return node switch return node switch
{ {
NodeTypeBool => NubTypeBool.Instance, NodeTypeBool => NubTypeBool.Instance,
NodeTypeCustom type => ResolveCustomType(type), NodeTypeCustom type => ResolveCustomType(type, includePrivate),
NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(ResolveType).ToList(), ResolveType(type.ReturnType)), NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(x => ResolveType(x, includePrivate)).ToList(), ResolveType(type.ReturnType, includePrivate)),
NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To)), NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To, includePrivate)),
NodeTypeSInt type => NubTypeSInt.Get(type.Width), NodeTypeSInt type => NubTypeSInt.Get(type.Width),
NodeTypeUInt type => NubTypeUInt.Get(type.Width), NodeTypeUInt type => NubTypeUInt.Get(type.Width),
NodeTypeString => NubTypeString.Instance, 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); var module = modules.GetValueOrDefault(type.Module.Ident);
if (module == null) if (module == null)
throw new CompileException(Diagnostic.Error($"Unknown module: {type.Module.Ident}").Build()); 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()); throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").Build());
return customType; return customType;

View File

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

View File

@@ -43,8 +43,38 @@ public sealed class Parser(string fileName, List<Token> tokens)
{ {
var startIndex = index; 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)) 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 name = ExpectIdent();
var parameters = new List<NodeDefinitionFunc.Param>(); var parameters = new List<NodeDefinitionFunc.Param>();
@@ -63,11 +93,18 @@ public sealed class Parser(string fileName, List<Token> tokens)
var body = ParseStatement(); 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)) 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 name = ExpectIdent();
var fields = new List<NodeDefinitionStruct.Field>(); 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)); 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()); 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 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 TokenIdent Name { get; } = name;
public List<Param> Parameters { get; } = parameters; public List<Param> Parameters { get; } = parameters;
public NodeStatement Body { get; } = body; 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 TokenIdent Name { get; } = name;
public List<Field> Fields { get; } = fields; 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), "func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func),
"struct" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Struct), "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), "let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let),
"if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If), "if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If),
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else), "else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While), "while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return), "return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
"module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module), "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), "true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false), "false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
_ => new TokenIdent(line, startColumn, column - startColumn, value) _ => new TokenIdent(line, startColumn, column - startColumn, value)
@@ -523,12 +525,14 @@ public enum Keyword
{ {
Func, Func,
Struct, Struct,
Packed,
Let, Let,
If, If,
Else, Else,
While, While,
Return, Return,
Module, Module,
Export,
} }
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length) 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.Func => "func",
Keyword.Struct => "struct", Keyword.Struct => "struct",
Keyword.Packed => "packed",
Keyword.Let => "let", Keyword.Let => "let",
Keyword.If => "if", Keyword.If => "if",
Keyword.Else => "else", Keyword.Else => "else",
Keyword.While => "while", Keyword.While => "while",
Keyword.Return => "return", Keyword.Return => "return",
Keyword.Module => "module", Keyword.Module => "module",
Keyword.Export => "export",
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null) _ => 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)) 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()); 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()); 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); 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)) 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()); 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()); 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) 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)) 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()); 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()); throw new CompileException(Diagnostic.Error($"Custom type '{type.Module.Ident}::{type.Name.Ident}' not found").At(fileName, type.Name).Build());
return customType; return customType;

View File

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