This commit is contained in:
nub31
2026-02-11 21:47:51 +01:00
parent 0a5b39b217
commit 21a095f691
4 changed files with 284 additions and 166 deletions

View File

@@ -198,7 +198,7 @@ public class ModuleGraph
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>()) foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
{ {
var type = new NubTypeStruct(module.Name, structDef.Name.Ident, structDef.Packed); var type = NubTypeStruct.CreateWithoutFields(structDef.Packed);
var info = new Module.CustomTypeInfo(type, structDef.Exported, Module.Source.Ast); var info = new Module.CustomTypeInfo(type, structDef.Exported, Module.Source.Ast);
module.AddCustomType(structDef.Name.Ident, info); module.AddCustomType(structDef.Name.Ident, info);
} }
@@ -214,8 +214,11 @@ public class ModuleGraph
if (!module.TryResolveCustomType(structDef.Name.Ident, true, 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, module.Name))).ToList(); if (customType is NubTypeStruct structType)
((NubTypeStruct)customType).ResolveFields(fields); {
var fields = structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, ResolveType(f.Type, module.Name))).ToList();
structType.SetFields(fields);
}
} }
} }

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Text; using System.Text;
namespace Compiler; namespace Compiler;
@@ -86,36 +87,61 @@ public class NubTypeString : NubType
public class NubTypeStruct : NubType public class NubTypeStruct : NubType
{ {
public string Name { get; } private static readonly Dictionary<Signature, NubTypeStruct> Cache = new();
public string Module { get; }
public bool Packed { get; }
private IReadOnlyList<Field>? _resolvedFields; public static NubTypeStruct Get(bool packed, List<Field> fields)
{
public IReadOnlyList<Field> Fields => _resolvedFields ?? throw new InvalidOperationException(); var sig = new Signature(packed, fields);
public NubTypeStruct(string module, string name, bool packed) if (!Cache.TryGetValue(sig, out var sturctType))
Cache[sig] = sturctType = new NubTypeStruct(packed, fields);
return sturctType;
}
private NubTypeStruct(bool packed, List<Field> fields)
{
Packed = packed;
this.fields = fields;
}
private NubTypeStruct(bool packed)
{ {
Module = module;
Name = name;
Packed = packed; Packed = packed;
} }
public void ResolveFields(IReadOnlyList<Field> fields) public bool Packed { get; }
{
if (_resolvedFields != null)
throw new InvalidOperationException($"{Name} already resolved");
_resolvedFields = fields; private IReadOnlyList<Field>? fields;
public IReadOnlyList<Field> Fields
{
get
{
if (fields == null)
throw new InvalidOperationException("Fields has not been set");
return fields;
}
} }
public override string ToString() => _resolvedFields == null ? $"struct {Module}::{Name} <unresolved>" : $"struct {Module}::{Name} {{ {string.Join(' ', Fields.Select(f => $"{f.Name}: {f.Type}"))} }}"; public static NubTypeStruct CreateWithoutFields(bool packed)
public class Field(string name, NubType type)
{ {
public string Name { get; } = name; return new NubTypeStruct(packed);
public NubType Type { get; } = type;
} }
public void SetFields(List<Field> fields)
{
if (this.fields != null)
throw new InvalidOperationException("Fields can only be set once");
this.fields = fields;
Cache[new Signature(Packed, this.fields)] = this;
}
public override string ToString() => $"struct {{ {string.Join(' ', Fields.Select(f => $"{f.Name}: {f.Type}"))} }}";
public record Field(string Name, NubType Type);
private record Signature(bool Packed, IReadOnlyList<Field> Fields);
} }
public class NubTypePointer : NubType public class NubTypePointer : NubType
@@ -167,19 +193,38 @@ public class NubTypeFunc : NubType
public override string ToString() => $"func({string.Join(' ', Parameters)}): {ReturnType}"; public override string ToString() => $"func({string.Join(' ', Parameters)}): {ReturnType}";
private readonly record struct Signature(IReadOnlyList<NubType> Parameters, NubType ReturnType); private record Signature(IReadOnlyList<NubType> Parameters, NubType ReturnType);
} }
static class TypeEncoder public class TypeEncoder
{ {
public static string Encode(NubType type) public static string Encode(NubType type)
{
return new TypeEncoder().EncodeRoot(type);
}
private TypeEncoder()
{
}
private Dictionary<NubTypeStruct, Definition> structDefinitions = new();
private string EncodeRoot(NubType type)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
Encode(type, sb);
EncodeType(sb, type);
foreach (var definition in structDefinitions.Values.OrderBy(x => x.Index))
{
Debug.Assert(definition.Encoded != null);
sb.Insert(0, definition.Encoded);
}
return sb.ToString(); return sb.ToString();
} }
private static void Encode(NubType type, StringBuilder sb) private void EncodeType(StringBuilder sb, NubType type)
{ {
switch (type) switch (type)
{ {
@@ -192,11 +237,15 @@ static class TypeEncoder
break; break;
case NubTypeUInt u: case NubTypeUInt u:
sb.Append("U(").Append(u.Width).Append(')'); sb.Append($"U(");
sb.Append(u.Width);
sb.Append(')');
break; break;
case NubTypeSInt s: case NubTypeSInt s:
sb.Append("I(").Append(s.Width).Append(')'); sb.Append($"I(");
sb.Append(s.Width);
sb.Append(')');
break; break;
case NubTypeString: case NubTypeString:
@@ -205,34 +254,21 @@ static class TypeEncoder
case NubTypePointer p: case NubTypePointer p:
sb.Append("P("); sb.Append("P(");
Encode(p.To, sb); EncodeType(sb, p.To);
sb.Append(')'); sb.Append(')');
break; break;
case NubTypeStruct st: case NubTypeStruct st:
sb.Append("T("); sb.Append(GetOrCreateStructDefinition(st));
sb.Append(st.Module).Append("::").Append(st.Name);
sb.Append(',').Append(st.Packed ? '1' : '0');
sb.Append('{');
for (int i = 0; i < st.Fields.Count; i++)
{
var field = st.Fields[i];
sb.Append(field.Name).Append(':');
Encode(field.Type, sb);
if (i < st.Fields.Count - 1)
sb.Append(',');
}
sb.Append("})");
break; break;
case NubTypeFunc fn: case NubTypeFunc fn:
sb.Append("F("); sb.Append("F(");
foreach (var parameter in fn.Parameters) for (int i = 0; i < fn.Parameters.Count; i++)
{ {
sb.Append(Encode(parameter)); EncodeType(sb, fn.Parameters[i]);
sb.Append(',');
} }
sb.Append(Encode(fn.ReturnType)); EncodeType(sb, fn.ReturnType);
sb.Append(')'); sb.Append(')');
break; break;
@@ -240,13 +276,48 @@ static class TypeEncoder
throw new NotSupportedException(type.GetType().Name); throw new NotSupportedException(type.GetType().Name);
} }
} }
private string GetOrCreateStructDefinition(NubTypeStruct st)
{
if (!structDefinitions.TryGetValue(st, out var definition))
{
definition = new Definition(structDefinitions.Count);
structDefinitions[st] = definition;
var sb = new StringBuilder();
sb.Append("D(");
sb.Append(st.Packed ? '1' : '0');
sb.Append('{');
for (var i = 0; i < st.Fields.Count; i++)
{
var field = st.Fields[i];
sb.Append(field.Name);
sb.Append(':');
EncodeType(sb, field.Type);
}
sb.Append('}');
sb.Append(')');
var encoded = sb.ToString();
definition.Encoded = encoded;
}
return $"T({definition.Index})";
}
private class Definition(int index)
{
public int Index { get; } = index;
public string? Encoded { get; set; }
}
} }
class TypeDecoder public class TypeDecoder
{ {
public static NubType Decode(string encoded) public static NubType Decode(string encoded)
{ {
return new TypeDecoder(encoded).Decode(); return new TypeDecoder(encoded).DecodeRoot();
} }
private TypeDecoder(string encoded) private TypeDecoder(string encoded)
@@ -255,158 +326,198 @@ class TypeDecoder
} }
private readonly string encoded; private readonly string encoded;
private int pos; private int index;
private readonly List<NubTypeStruct> structDefinitions = new();
private NubType Decode() private NubType DecodeRoot()
{ {
return Parse(); // First pass: Collect all declarations
while (TryExpect('D'))
{
Expect('(');
var packedChar = Consume();
if (packedChar is not '1' and not '0')
throw new Exception("Expected '0' or '1' for struct packing");
Expect('{');
while (TryPeek(out var c))
{
Consume();
if (c == '}')
break;
}
Expect(')');
structDefinitions.Add(NubTypeStruct.CreateWithoutFields(packedChar == '1'));
}
index = 0;
var defIndex = 0;
// Second pass: Set field types
while (TryExpect('D'))
{
var fields = new List<NubTypeStruct.Field>();
Consume();
Consume();
Consume();
while (!TryExpect('}'))
{
var sb = new StringBuilder();
while (!TryExpect(':'))
{
sb.Append(Consume());
}
var type = DecodeType();
fields.Add(new NubTypeStruct.Field(sb.ToString(), type));
}
Expect(')');
structDefinitions[defIndex].SetFields(fields);
defIndex += 1;
}
return DecodeType();
} }
private NubType Parse() private NubType DecodeType()
{ {
if (pos >= encoded.Length) var start = Consume();
throw new InvalidOperationException("Unexpected end of string"); return start switch
char c = encoded[pos++];
return c switch
{ {
'V' => NubTypeVoid.Instance, 'V' => NubTypeVoid.Instance,
'B' => NubTypeBool.Instance, 'B' => NubTypeBool.Instance,
'U' => DecodeUInt(),
'I' => DecodeSInt(),
'S' => NubTypeString.Instance, 'S' => NubTypeString.Instance,
'U' => ParseUInt(), 'P' => DecodePointer(),
'I' => ParseSInt(), 'F' => DecodeFunc(),
'P' => ParsePointer(), 'T' => DecodeStruct(),
'T' => ParseStruct(), _ => throw new Exception($"'{start}' is not a valid start to a type")
'F' => ParseFunc(),
_ => throw new NotSupportedException($"Unknown type code '{c}' at position {pos - 1}")
}; };
} }
private NubTypeUInt ParseUInt() private NubTypeUInt DecodeUInt()
{ {
ExpectChar('('); Expect('(');
int width = ReadNumber(); var width = ExpectInt();
ExpectChar(')'); Expect(')');
return NubTypeUInt.Get(width); return NubTypeUInt.Get(width);
} }
private NubTypeSInt ParseSInt() private NubTypeSInt DecodeSInt()
{ {
ExpectChar('('); Expect('(');
int width = ReadNumber(); var width = ExpectInt();
ExpectChar(')'); Expect(')');
return NubTypeSInt.Get(width); return NubTypeSInt.Get(width);
} }
private NubTypePointer ParsePointer() private NubTypePointer DecodePointer()
{ {
ExpectChar('('); Expect('(');
var to = Parse(); var to = DecodeType();
ExpectChar(')'); Expect(')');
return NubTypePointer.Get(to); return NubTypePointer.Get(to);
} }
private NubTypeStruct ParseStruct() private NubTypeFunc DecodeFunc()
{ {
ExpectChar('('); var types = new List<NubType>();
int start = pos;
while (pos < encoded.Length && encoded[pos] != ',' && encoded[pos] != '{') pos++;
var fullName = encoded[start..pos];
var parts = fullName.Split("::");
if (parts.Length != 2)
throw new InvalidOperationException($"Invalid struct name: {fullName}");
string module = parts[0], name = parts[1]; Expect('(');
while (!TryExpect(')'))
bool packed = false;
if (encoded[pos] == ',')
{ {
pos += 1; types.Add(DecodeType());
packed = encoded[pos += 1] == '1';
} }
var st = new NubTypeStruct(module, name, packed); return NubTypeFunc.Get(types.Take(types.Count - 1).ToList(), types.Last());
ExpectChar('{');
var fields = new List<NubTypeStruct.Field>();
while (encoded[pos] != '}')
{
int nameStart = pos;
while (encoded[pos] != ':') pos += 1;
string fieldName = encoded[nameStart..pos];
pos += 1;
var fieldType = Parse();
fields.Add(new NubTypeStruct.Field(fieldName, fieldType));
if (encoded[pos] == ',') pos += 1;
}
ExpectChar('}');
ExpectChar(')');
st.ResolveFields(fields);
return st;
} }
private NubTypeFunc ParseFunc() private NubTypeStruct DecodeStruct()
{ {
ExpectChar('('); Expect('(');
var parameters = new List<NubType>(); var index = ExpectInt();
while (true) Expect(')');
return structDefinitions[index];
}
private bool TryPeek(out char c)
{
if (index >= encoded.Length)
{ {
if (encoded[pos] == ')') c = '\0';
{ return false;
pos++; }
c = encoded[index];
return true;
}
private bool TryConsume(out char c)
{
if (index >= encoded.Length)
{
c = '\0';
return false;
}
c = encoded[index];
index += 1;
return true;
}
private char Consume()
{
if (!TryConsume(out var c))
throw new Exception("Unexpected end of string");
return c;
}
private bool TryExpect(char c)
{
if (index >= encoded.Length)
return false;
if (encoded[index] != c)
return false;
Consume();
return true;
}
private void Expect(char c)
{
if (!TryExpect(c))
throw new Exception($"Expected '{c}'");
}
private int ExpectInt()
{
var buf = string.Empty;
while (TryPeek(out var c))
{
if (!char.IsDigit(c))
break; break;
}
var param = Parse(); buf += Consume();
parameters.Add(param);
if (encoded[pos] == ',')
{
pos += 1;
}
else if (encoded[pos] == ')')
{
pos += 1;
break;
}
else
{
throw new InvalidOperationException($"Unexpected char '{encoded[pos]}' in function type at {pos}");
}
} }
if (parameters.Count == 0) return int.Parse(buf);
throw new InvalidOperationException("Function must have a return type");
var returnType = parameters[^1];
var paramTypes = parameters.Take(parameters.Count - 1).ToList();
return NubTypeFunc.Get(paramTypes, returnType);
}
private void ExpectChar(char expected)
{
if (pos >= encoded.Length || encoded[pos] != expected)
throw new InvalidOperationException($"Expected '{expected}' at position {pos}");
pos += 1;
}
private int ReadNumber()
{
int start = pos;
while (pos < encoded.Length && char.IsDigit(encoded[pos])) pos += 1;
if (start == pos) throw new InvalidOperationException($"Expected number at position {start}");
return int.Parse(encoded[start..pos]);
} }
} }
static class Hashing public static class Hashing
{ {
public static ulong Fnv1a64(string text) public static ulong Fnv1a64(string text)
{ {
@@ -424,7 +535,7 @@ static class Hashing
} }
} }
static class SymbolNameGen public static class SymbolNameGen
{ {
public static string Exported(string module, string function, NubType type) public static string Exported(string module, string function, NubType type)
{ {

View File

@@ -1,11 +1,10 @@
pushd math pushd math
dotnet run --project ../../compiler math.nub --type=lib dotnet run --project ../../compiler math.nub --type=lib
pushd .build
unzip out.nublib
popd
popd popd
pushd program pushd program
dotnet run --project ../../compiler main.nub ../math/.build/out.nublib dotnet run --project ../../compiler main.nub ../math/.build/out.nublib
popd popd

View File

@@ -1,5 +1,10 @@
module math module math
struct vec2 { x: i32 y: i32 }
struct vec3 { x: i32 y: i32 z: i32 }
struct color { r: i32 g: i32 b: i32 a: i32 }
struct example { a: math::vec2 b: math::vec3 c: math::color }
export func add(a: i32 b: i32): i32 export func add(a: i32 b: i32): i32
{ {
return a + b return a + b