diff --git a/compiler/Generator.cs b/compiler/Generator.cs index f565224..b383dc8 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -24,13 +24,15 @@ public sealed class Generator(List functions, ModuleGra #include #include - 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 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 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 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 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) }; diff --git a/compiler/ModuleGraph.cs b/compiler/ModuleGraph.cs index 70cad75..a10c279 100644 --- a/compiler/ModuleGraph.cs +++ b/compiler/ModuleGraph.cs @@ -21,40 +21,69 @@ public class ModuleGraph(Dictionary modules) public sealed class Module(string name) { public string Name { get; } = name; - private readonly Dictionary customTypes = new(); - private readonly Dictionary identifierTypes = new(); + private readonly Dictionary customTypes = new(); + private readonly Dictionary identifierTypes = new(); public IReadOnlyList GetCustomTypes() { - return customTypes.Values.ToList(); + return customTypes.Values.Select(x => x.Type).ToList(); } public IReadOnlyDictionary 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 modules) if (module == null) continue; foreach (var structDef in ast.Definitions.OfType()) - 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 modules) foreach (var structDef in ast.Definitions.OfType()) { - 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 modules) foreach (var funcDef in ast.Definitions.OfType()) { - 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 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 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; diff --git a/compiler/NubType.cs b/compiler/NubType.cs index 3223327..51f98e9 100644 --- a/compiler/NubType.cs +++ b/compiler/NubType.cs @@ -88,15 +88,17 @@ public sealed class NubTypeStruct : NubType { public string Name { get; } public string Module { get; } + public bool Packed { get; } private IReadOnlyList? _resolvedFields; public IReadOnlyList 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 fields) diff --git a/compiler/Parser.cs b/compiler/Parser.cs index 02e586d..f4ab0b3 100644 --- a/compiler/Parser.cs +++ b/compiler/Parser.cs @@ -43,8 +43,38 @@ public sealed class Parser(string fileName, List tokens) { var startIndex = index; + Dictionary 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(); @@ -63,11 +93,18 @@ public sealed class Parser(string fileName, List 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(); @@ -81,7 +118,7 @@ public sealed class Parser(string fileName, List 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 tokens) public abstract class NodeDefinition(List tokens) : Node(tokens); -public sealed class NodeDefinitionFunc(List tokens, TokenIdent name, List parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens) +public sealed class NodeDefinitionFunc(List tokens, bool exported, TokenIdent name, List parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens) { + public bool Exported { get; } = exported; public TokenIdent Name { get; } = name; public List Parameters { get; } = parameters; public NodeStatement Body { get; } = body; @@ -570,8 +608,10 @@ public sealed class NodeDefinitionFunc(List tokens, TokenIdent name, List } } -public sealed class NodeDefinitionStruct(List tokens, TokenIdent name, List fields) : NodeDefinition(tokens) +public sealed class NodeDefinitionStruct(List tokens, bool exported, bool packed, TokenIdent name, List fields) : NodeDefinition(tokens) { + public bool Exported { get; } = exported; + public bool Packed { get; } = packed; public TokenIdent Name { get; } = name; public List Fields { get; } = fields; diff --git a/compiler/Tokenizer.cs b/compiler/Tokenizer.cs index f4b2014..db0f9c0 100644 --- a/compiler/Tokenizer.cs +++ b/compiler/Tokenizer.cs @@ -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) }; } diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index 8a2838d..40d6798 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -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; diff --git a/compiler/test2.nub b/compiler/test2.nub index 62d9828..6f1fc1a 100644 --- a/compiler/test2.nub +++ b/compiler/test2.nub @@ -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 } \ No newline at end of file