module exports and name mangling
This commit is contained in:
@@ -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($$"""
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
writer.WriteLine($"return {main.GetMangledName()}();");
|
||||
return {{main.GetMangledName()}}();
|
||||
}
|
||||
writer.WriteLine("}");
|
||||
""");
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
public bool TryResolveIdentifierType(string name, [NotNullWhen(true)] out NubType? identifier)
|
||||
if (info.Exported || searchPrivate)
|
||||
{
|
||||
identifier = identifierTypes.GetValueOrDefault(name);
|
||||
return identifier != null;
|
||||
customType = info.Type;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddCustomType(string name, NubType type)
|
||||
{
|
||||
customTypes.Add(name, type);
|
||||
customType = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void AddIdentifier(string name, NubType identifier)
|
||||
public bool TryResolveIdentifierType(string name, bool searchPrivate, [NotNullWhen(true)] out NubType? identifierType)
|
||||
{
|
||||
identifierTypes.Add(name, identifier);
|
||||
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
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user