diff --git a/compiler/ModuleGraph.cs b/compiler/ModuleGraph.cs index cfb8837..655b9c7 100644 --- a/compiler/ModuleGraph.cs +++ b/compiler/ModuleGraph.cs @@ -23,8 +23,6 @@ public class ModuleGraph(Dictionary modules) return new Manifest(1, GetModules().Select(x => x.CreateManifestModule()).ToList()); } - public record Manifest(int Version, IReadOnlyList Modules); - public sealed class Module(string name) { public string Name { get; } = name; @@ -110,7 +108,7 @@ public class ModuleGraph(Dictionary modules) public ManifestCustomTypeInfo CreateManifestCustomTypeInfo() { - return new ManifestCustomTypeInfo(Type, Exported); + return new ManifestCustomTypeInfo(TypeMangler.Encode(Type), Exported); } } @@ -122,13 +120,9 @@ public class ModuleGraph(Dictionary modules) public ManifestIdentifierInfo CreateManifestIdentifierInfo() { - return new ManifestIdentifierInfo(Type, Exported); + return new ManifestIdentifierInfo(TypeMangler.Encode(Type), Exported); } } - - public record ManifestModule(string Name, Dictionary CustomTypes, Dictionary Identifiers); - public record ManifestCustomTypeInfo(NubType Type, bool Exported); - public record ManifestIdentifierInfo(NubType Type, bool Exported); } public class Builder @@ -165,12 +159,14 @@ public class ModuleGraph(Dictionary modules) foreach (var customType in manifestModule.CustomTypes) { - module.AddCustomType(customType.Key, new Module.CustomTypeInfo(customType.Value.Type, customType.Value.Exported, Module.Source.Lib)); + var decoded = TypeMangler.Decode(customType.Value.EncodedType); + module.AddCustomType(customType.Key, new Module.CustomTypeInfo(decoded, customType.Value.Exported, Module.Source.Lib)); } - foreach (var customType in manifestModule.Identifiers) + foreach (var identifier in manifestModule.Identifiers) { - module.AddIdentifier(customType.Key, new Module.IdentifierInfo(customType.Value.Type, customType.Value.Exported, Module.Source.Lib)); + var decoded = TypeMangler.Decode(identifier.Value.EncodedType); + module.AddIdentifier(identifier.Key, new Module.IdentifierInfo(decoded, identifier.Value.Exported, Module.Source.Lib)); } } } @@ -274,4 +270,9 @@ public class ModuleGraph(Dictionary modules) } } } -} \ No newline at end of file +} + +public record Manifest(int Version, IReadOnlyList Modules); +public record ManifestModule(string Name, Dictionary CustomTypes, Dictionary Identifiers); +public record ManifestCustomTypeInfo(string EncodedType, bool Exported); +public record ManifestIdentifierInfo(string EncodedType, bool Exported); \ No newline at end of file diff --git a/compiler/NubType.cs b/compiler/NubType.cs index 96c415a..7a0e871 100644 --- a/compiler/NubType.cs +++ b/compiler/NubType.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Text; namespace Compiler; @@ -5,7 +6,6 @@ namespace Compiler; public abstract class NubType { public abstract override string ToString(); - public string GetSignature() => TypeMangler.Encode(this); } public sealed class NubTypeVoid : NubType @@ -193,15 +193,11 @@ static class TypeMangler break; case NubTypeUInt u: - sb.Append("U("); - sb.Append(u.Width); - sb.Append(')'); + sb.Append("U(").Append(u.Width).Append(')'); break; case NubTypeSInt s: - sb.Append("U("); - sb.Append(s.Width); - sb.Append(')'); + sb.Append("I(").Append(s.Width).Append(')'); break; case NubTypeString: @@ -215,17 +211,29 @@ static class TypeMangler break; case NubTypeStruct st: - sb.Append($"T({st.Module}::{st.Name})"); + sb.Append("T("); + 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; case NubTypeFunc fn: sb.Append("F("); - for (int i = 0; i < fn.Parameters.Count; i++) + foreach (var parameter in fn.Parameters) { - Encode(fn.Parameters[i], sb); + sb.Append(Encode(parameter)); sb.Append(','); } - Encode(fn.ReturnType, sb); + sb.Append(Encode(fn.ReturnType)); sb.Append(')'); break; @@ -233,6 +241,155 @@ static class TypeMangler throw new NotSupportedException(type.GetType().Name); } } + + public static NubType Decode(string encoded) + { + int pos = 0; + return Parse(encoded, ref pos); + } + + private static NubType Parse(string s, ref int pos) + { + if (pos >= s.Length) + throw new InvalidOperationException("Unexpected end of string"); + + char c = s[pos++]; + return c switch + { + 'V' => NubTypeVoid.Instance, + 'B' => NubTypeBool.Instance, + 'S' => NubTypeString.Instance, + 'U' => ParseUInt(s, ref pos), + 'I' => ParseSInt(s, ref pos), + 'P' => ParsePointer(s, ref pos), + 'T' => ParseStruct(s, ref pos), + 'F' => ParseFunc(s, ref pos), + _ => throw new NotSupportedException($"Unknown type code '{c}' at position {pos - 1}") + }; + } + + private static NubTypeUInt ParseUInt(string s, ref int pos) + { + ExpectChar(s, ref pos, '('); + int width = ReadNumber(s, ref pos); + ExpectChar(s, ref pos, ')'); + return NubTypeUInt.Get(width); + } + + private static NubTypeSInt ParseSInt(string s, ref int pos) + { + ExpectChar(s, ref pos, '('); + int width = ReadNumber(s, ref pos); + ExpectChar(s, ref pos, ')'); + return NubTypeSInt.Get(width); + } + + private static NubTypePointer ParsePointer(string s, ref int pos) + { + ExpectChar(s, ref pos, '('); + var to = Parse(s, ref pos); + ExpectChar(s, ref pos, ')'); + return NubTypePointer.Get(to); + } + + private static NubTypeStruct ParseStruct(string s, ref int pos) + { + ExpectChar(s, ref pos, '('); + int start = pos; + while (pos < s.Length && s[pos] != ',' && s[pos] != '{') pos++; + var fullName = s[start..pos]; + var parts = fullName.Split("::"); + if (parts.Length != 2) + throw new InvalidOperationException($"Invalid struct name: {fullName}"); + + string module = parts[0], name = parts[1]; + + bool packed = false; + if (s[pos] == ',') + { + pos += 1; + packed = s[pos += 1] == '1'; + } + + var st = new NubTypeStruct(module, name, packed); + + ExpectChar(s, ref pos, '{'); + + var fields = new List(); + while (s[pos] != '}') + { + int nameStart = pos; + while (s[pos] != ':') pos += 1; + string fieldName = s[nameStart..pos]; + pos += 1; + + var fieldType = Parse(s, ref pos); + fields.Add(new NubTypeStruct.Field(fieldName, fieldType)); + + if (s[pos] == ',') pos += 1; + } + + ExpectChar(s, ref pos, '}'); + ExpectChar(s, ref pos, ')'); + + st.ResolveFields(fields); + return st; + } + + private static NubTypeFunc ParseFunc(string s, ref int pos) + { + ExpectChar(s, ref pos, '('); + var parameters = new List(); + while (true) + { + if (s[pos] == ')') + { + pos++; + break; + } + + var param = Parse(s, ref pos); + parameters.Add(param); + + if (s[pos] == ',') + { + pos += 1; + } + else if (s[pos] == ')') + { + pos += 1; + break; + } + else + { + throw new InvalidOperationException($"Unexpected char '{s[pos]}' in function type at {pos}"); + } + } + + if (parameters.Count == 0) + 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 static void ExpectChar(string s, ref int pos, char expected) + { + if (pos >= s.Length || s[pos] != expected) + throw new InvalidOperationException($"Expected '{expected}' at position {pos}"); + + pos += 1; + } + + private static int ReadNumber(string s, ref int pos) + { + int start = pos; + while (pos < s.Length && char.IsDigit(s[pos])) pos += 1; + if (start == pos) throw new InvalidOperationException($"Expected number at position {start}"); + return int.Parse(s[start..pos]); + } } static class Hashing diff --git a/compiler/Program.cs b/compiler/Program.cs index 8fb08f9..82fbb3e 100644 --- a/compiler/Program.cs +++ b/compiler/Program.cs @@ -163,7 +163,7 @@ static void CleanDirectory(string dirName) } } -static void WriteNublib(string outputPath, string archivePath, ModuleGraph.Manifest manifest) +static void WriteNublib(string outputPath, string archivePath, Manifest manifest) { using var fs = new FileStream(outputPath, FileMode.Create); using var zip = new ZipArchive(fs, ZipArchiveMode.Create); @@ -174,6 +174,8 @@ static void WriteNublib(string outputPath, string archivePath, ModuleGraph.Manif WriteIndented = true, }); + File.WriteAllText(".build/manifest.json", serialized); + var manifestEntry = zip.CreateEntry("manifest.json"); using (var writer = new StreamWriter(manifestEntry.Open())) { @@ -195,11 +197,11 @@ static NublibLoadResult ReadNublib(string nublibPath) var manifestEntry = zip.GetEntry("manifest.json") ?? throw new FileNotFoundException("Manifest not found in nublib", "manifest.json"); - ModuleGraph.Manifest manifest; + Manifest manifest; using (var reader = new StreamReader(manifestEntry.Open())) { var json = reader.ReadToEnd(); - manifest = JsonSerializer.Deserialize(json) ?? throw new InvalidDataException("Failed to deserialize manifest.json"); + manifest = JsonSerializer.Deserialize(json) ?? throw new InvalidDataException("Failed to deserialize manifest.json"); } var archiveEntry = zip.Entries.FirstOrDefault(e => e.Name.EndsWith(".a")) ?? throw new FileNotFoundException("Archive not found in nublib", "*.a"); @@ -214,4 +216,4 @@ static NublibLoadResult ReadNublib(string nublibPath) return new NublibLoadResult(manifest, tempArchivePath); } -public record NublibLoadResult(ModuleGraph.Manifest Manifest, string ArchivePath); +public record NublibLoadResult(Manifest Manifest, string ArchivePath); diff --git a/examples/math/.build/manifest.json b/examples/math/.build/manifest.json new file mode 100644 index 0000000..19a7145 --- /dev/null +++ b/examples/math/.build/manifest.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "modules": [ + { + "name": "math", + "customTypes": {}, + "identifiers": { + "add": { + "encodedType": "F(I(32),I(32),I(32))", + "exported": true + } + } + } + ] +} \ No newline at end of file diff --git a/examples/math/.build/out.a b/examples/math/.build/out.a index 1032a05..967be10 100644 Binary files a/examples/math/.build/out.a and b/examples/math/.build/out.a differ diff --git a/examples/math/.build/out.c b/examples/math/.build/out.c index 787fadd..ca42bc4 100644 --- a/examples/math/.build/out.c +++ b/examples/math/.build/out.c @@ -12,10 +12,10 @@ struct nub_core_string -int32_t nub_math_add_cc3fc9d68812b10d(int32_t, int32_t); +int32_t nub_math_add_500748c2c6d70959(int32_t, int32_t); -int32_t nub_math_add_cc3fc9d68812b10d(int32_t a, int32_t b) +int32_t nub_math_add_500748c2c6d70959(int32_t a, int32_t b) { { return (a + b); diff --git a/examples/math/.build/out.nublib b/examples/math/.build/out.nublib index 0ef4938..341b865 100644 Binary files a/examples/math/.build/out.nublib and b/examples/math/.build/out.nublib differ diff --git a/examples/math/.build/out.o b/examples/math/.build/out.o index f8e2349..20786e3 100644 Binary files a/examples/math/.build/out.o and b/examples/math/.build/out.o differ diff --git a/examples/program/.build/out b/examples/program/.build/out new file mode 100755 index 0000000..4eace4f Binary files /dev/null and b/examples/program/.build/out differ diff --git a/examples/program/.build/out.c b/examples/program/.build/out.c new file mode 100644 index 0000000..f5e8710 --- /dev/null +++ b/examples/program/.build/out.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +struct nub_core_string +{ + const char *data; + int length; +}; + + + +extern int32_t nub_math_add_500748c2c6d70959(int32_t, int32_t); +int32_t nub_main_main_55882df37f903935(); + +int main(int argc, char *argv[]) +{ + return nub_main_main_55882df37f903935(); +} + +int32_t nub_main_main_55882df37f903935() +{ + { + return nub_math_add_500748c2c6d70959(1, 2); + } +} + diff --git a/examples/program/main.nub b/examples/program/main.nub index 99f5d86..4693c99 100644 --- a/examples/program/main.nub +++ b/examples/program/main.nub @@ -2,5 +2,5 @@ module main func main(): i32 { - return math::add(1, 2) + return math::add(1 2) } \ No newline at end of file diff --git a/compiler/test.nub b/examples/test/test.nub similarity index 100% rename from compiler/test.nub rename to examples/test/test.nub diff --git a/compiler/test2.nub b/examples/test/test2.nub similarity index 100% rename from compiler/test2.nub rename to examples/test/test2.nub