diff --git a/compiler/Generator.cs b/compiler/Generator.cs index c3a5b3b..ed158a9 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -46,7 +46,9 @@ public class Generator if (!moduleGraph.TryResolveIdentifier(function.Module, function.Name.Ident, true, out var info)) throw new UnreachableException($"Module graph does not have info about the function {function.Module}::{function.Name.Ident}. This should have been caught earlier"); - if (info.Kind == Module.DefinitionKind.Internal) + if (info.Source == Module.DefinitionSource.Imported || info.Extern) + writer.Write("extern "); + else if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported) writer.Write("static "); var parameters = function.Parameters.Select(x => CType(x.Type, x.Name.Ident)); @@ -85,15 +87,10 @@ public class Generator { foreach (var (name, info) in module.GetIdentifiers()) { - switch (info.Kind) - { - case Module.DefinitionKind.External: - writer.Write("extern "); - break; - case Module.DefinitionKind.Internal: - writer.Write("static "); - break; - } + if (info.Source == Module.DefinitionSource.Imported || info.Extern) + writer.Write("extern "); + else if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported) + writer.Write("static "); if (info.Type is NubTypeFunc fn) writer.WriteLine($"{CType(fn.ReturnType, info.MangledName)}({string.Join(", ", fn.Parameters.Select(p => CType(p)))});"); @@ -122,7 +119,7 @@ public class Generator writer.WriteLine("{"); using (writer.Indent()) { - writer.WriteLine("const char *data;"); + writer.WriteLine("char *data;"); writer.WriteLine("size_t length;"); } writer.WriteLine("};"); @@ -366,7 +363,9 @@ public class Generator TypedNodeExpressionStringLiteral expression => $"({CType(expression.Type)}){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}", TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression), TypedNodeExpressionEnumLiteral expression => EmitExpressionEnumLiteral(expression), - TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression), + TypedNodeExpressionStructMemberAccess expression => EmitExpressionMemberAccess(expression), + TypedNodeExpressionStringLength expression => EmitExpressionStringLength(expression), + TypedNodeExpressionStringPointer expression => EmitExpressionStringPointer(expression), TypedNodeExpressionLocalIdent expression => expression.Name, TypedNodeExpressionGlobalIdent expression => EmitNodeExpressionGlobalIdent(expression), TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression), @@ -442,12 +441,24 @@ public class Generator return $"({CType(expression.Type)}){{ .tag = {tag}, .{enumVariantType.Variant} = {value} }}"; } - private string EmitExpressionMemberAccess(TypedNodeExpressionMemberAccess expression) + private string EmitExpressionMemberAccess(TypedNodeExpressionStructMemberAccess expression) { var target = EmitExpression(expression.Target); return $"{target}.{expression.Name.Ident}"; } + private string EmitExpressionStringLength(TypedNodeExpressionStringLength expression) + { + var target = EmitExpression(expression.Target); + return $"{target}.length"; + } + + private string EmitExpressionStringPointer(TypedNodeExpressionStringPointer expression) + { + var target = EmitExpression(expression.Target); + return $"{target}.data"; + } + private string EmitNodeExpressionGlobalIdent(TypedNodeExpressionGlobalIdent expression) { if (!moduleGraph.TryResolveIdentifier(expression.Module, expression.Name, true, out var info)) diff --git a/compiler/ModuleGraph.cs b/compiler/ModuleGraph.cs index 61460c1..9bcba46 100644 --- a/compiler/ModuleGraph.cs +++ b/compiler/ModuleGraph.cs @@ -86,7 +86,7 @@ public class ModuleGraph { case Manifest.Module.TypeInfoStruct s: { - var info = new Module.TypeInfoStruct(Module.DefinitionKind.External, s.Packed); + var info = new Module.TypeInfoStruct(Module.DefinitionSource.Imported, true, s.Packed); var fields = s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList(); info.SetFields(fields); module.AddType(name, info); @@ -94,7 +94,7 @@ public class ModuleGraph } case Manifest.Module.TypeInfoEnum s: { - var info = new Module.TypeInfoEnum(Module.DefinitionKind.External); + var info = new Module.TypeInfoEnum(Module.DefinitionSource.Imported, true); var variants = s.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList(); info.SetVariants(variants); module.AddType(name, info); @@ -107,7 +107,7 @@ public class ModuleGraph foreach (var (name, identifier) in manifestModule.Identifiers) { - module.AddIdentifier(name, new Module.IdentifierInfo(Module.DefinitionKind.External, identifier.Type, identifier.MangledName)); + module.AddIdentifier(name, new Module.IdentifierInfo(Module.DefinitionSource.Imported, true, true, identifier.Type, identifier.MangledName)); } } } @@ -118,14 +118,12 @@ public class ModuleGraph foreach (var structDef in ast.Definitions.OfType()) { - var kind = structDef.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; - module.AddType(structDef.Name.Ident, new Module.TypeInfoStruct(kind, structDef.Packed)); + module.AddType(structDef.Name.Ident, new Module.TypeInfoStruct(Module.DefinitionSource.Internal, structDef.Exported, structDef.Packed)); } foreach (var enumDef in ast.Definitions.OfType()) { - var kind = enumDef.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; - module.AddType(enumDef.Name.Ident, new Module.TypeInfoEnum(kind)); + module.AddType(enumDef.Name.Ident, new Module.TypeInfoEnum(Module.DefinitionSource.Internal, enumDef.Exported)); } } @@ -165,18 +163,32 @@ public class ModuleGraph foreach (var funcDef in ast.Definitions.OfType()) { var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList(); - var returnType = ResolveType(funcDef.ReturnType, module.Name); + var returnType = funcDef.ReturnType == null ? NubTypeVoid.Instance : ResolveType(funcDef.ReturnType, module.Name); var funcType = NubTypeFunc.Get(parameters, returnType); - var kind = funcDef.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; - var info = new Module.IdentifierInfo(kind, funcType, NameMangler.Mangle(module.Name, funcDef.Name.Ident, funcType)); + var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, funcDef.Exported, false, funcType, NameMangler.Mangle(module.Name, funcDef.Name.Ident, funcType)); + module.AddIdentifier(funcDef.Name.Ident, info); + } + + foreach (var funcDef in ast.Definitions.OfType()) + { + var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList(); + var returnType = funcDef.ReturnType == null ? NubTypeVoid.Instance : ResolveType(funcDef.ReturnType, module.Name); + var funcType = NubTypeFunc.Get(parameters, returnType); + var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, funcDef.Exported, true, funcType, funcDef.Name.Ident); module.AddIdentifier(funcDef.Name.Ident, info); } foreach (var globalVariable in ast.Definitions.OfType()) { var type = ResolveType(globalVariable.Type, module.Name); - var kind = globalVariable.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; - var info = new Module.IdentifierInfo(kind, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type)); + var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, globalVariable.Exported, false, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type)); + module.AddIdentifier(globalVariable.Name.Ident, info); + } + + foreach (var globalVariable in ast.Definitions.OfType()) + { + var type = ResolveType(globalVariable.Type, module.Name); + var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, globalVariable.Exported, true, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type)); module.AddIdentifier(globalVariable.Name.Ident, info); } } @@ -333,7 +345,7 @@ public class Module(string name) return false; } - if (searchPrivate || info.Kind is DefinitionKind.External or DefinitionKind.Exported) + if (searchPrivate || info.Source == DefinitionSource.Internal) { customType = info; return true; @@ -352,7 +364,7 @@ public class Module(string name) return false; } - if (searchPrivate || info.Kind is DefinitionKind.External or DefinitionKind.Exported) + if (searchPrivate || info.Source == DefinitionSource.Internal) { identifierType = info; return true; @@ -372,26 +384,28 @@ public class Module(string name) identifiers.Add(name, info); } - public enum DefinitionKind + public enum DefinitionSource { Internal, - Exported, - External, + Imported, } - public class IdentifierInfo(DefinitionKind kind, NubType type, string mangledName) + public class IdentifierInfo(DefinitionSource source, bool exported, bool @extern, NubType type, string mangledName) { - public DefinitionKind Kind { get; } = kind; + public DefinitionSource Source { get; } = source; + public bool Exported { get; } = exported; + public bool Extern { get; } = @extern; public NubType Type { get; } = type; public string MangledName { get; } = mangledName; } - public abstract class TypeInfo(DefinitionKind kind) + public abstract class TypeInfo(DefinitionSource source, bool exported) { - public DefinitionKind Kind { get; } = kind; + public DefinitionSource Source { get; } = source; + public bool Exported { get; } = exported; } - public class TypeInfoStruct(DefinitionKind kind, bool packed) : TypeInfo(kind) + public class TypeInfoStruct(DefinitionSource source, bool exported, bool packed) : TypeInfo(source, exported) { private IReadOnlyList? fields; @@ -410,7 +424,7 @@ public class Module(string name) } } - public class TypeInfoEnum(DefinitionKind kind) : TypeInfo(kind) + public class TypeInfoEnum(DefinitionSource source, bool exported) : TypeInfo(source, exported) { private IReadOnlyList? variants; diff --git a/compiler/NubLib.cs b/compiler/NubLib.cs index 599c62f..20b1ebf 100644 --- a/compiler/NubLib.cs +++ b/compiler/NubLib.cs @@ -75,7 +75,7 @@ public record Manifest(Dictionary Modules) foreach (var (name, typeInfo) in module.GetTypes()) { - if (typeInfo.Kind == Compiler.Module.DefinitionKind.Exported) + if (typeInfo.Exported) { types.Add(name, ConvertType(typeInfo)); } @@ -83,7 +83,7 @@ public record Manifest(Dictionary Modules) foreach (var (name, identifierInfo) in module.GetIdentifiers()) { - if (identifierInfo.Kind == Compiler.Module.DefinitionKind.Exported) + if (identifierInfo.Exported) { identifiers.Add(name, new Module.IdentifierInfo(identifierInfo.Type, identifierInfo.MangledName)); } diff --git a/compiler/Parser.cs b/compiler/Parser.cs index fdbe772..8e9fbc1 100644 --- a/compiler/Parser.cs +++ b/compiler/Parser.cs @@ -65,6 +65,10 @@ public class Parser Next(); modifiers[Keyword.Packed] = keyword; break; + case Keyword.Extern: + Next(); + modifiers[Keyword.Extern] = keyword; + break; default: goto modifier_done; } @@ -75,6 +79,7 @@ public class Parser if (TryExpectKeyword(Keyword.Func)) { var exported = modifiers.Remove(Keyword.Export); + var @extern = modifiers.Remove(Keyword.Extern); foreach (var modifier in modifiers) // todo(nub31): Add to diagnostics instead of throwing @@ -93,12 +98,19 @@ public class Parser parameters.Add(new NodeDefinitionFunc.Param(TokensFrom(paramStartIndex), parameterName, parameterType)); } - ExpectSymbol(Symbol.Colon); - var returnType = ParseType(); + NodeType? returnType = null; + if (TryExpectSymbol(Symbol.Colon)) + returnType = ParseType(); - var body = ParseStatement(); - - return new NodeDefinitionFunc(TokensFrom(startIndex), exported, name, parameters, body, returnType); + if (@extern) + { + return new NodeDefinitionExternFunc(TokensFrom(startIndex), exported, name, parameters, returnType); + } + else + { + var body = ParseStatement(); + return new NodeDefinitionFunc(TokensFrom(startIndex), exported, name, parameters, body, returnType); + } } if (TryExpectKeyword(Keyword.Struct)) @@ -704,13 +716,21 @@ public abstract class Node(List tokens) public abstract class NodeDefinition(List tokens) : Node(tokens); -public class NodeDefinitionFunc(List tokens, bool exported, TokenIdent name, List parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens) +public class NodeDefinitionExternFunc(List tokens, bool exported, TokenIdent name, List parameters, NodeType? returnType) : NodeDefinition(tokens) +{ + public bool Exported { get; } = exported; + public TokenIdent Name { get; } = name; + public List Parameters { get; } = parameters; + public NodeType? ReturnType { get; } = returnType; +} + +public 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; - public NodeType ReturnType { get; } = returnType; + public NodeType? ReturnType { get; } = returnType; public class Param(List tokens, TokenIdent name, NodeType type) : Node(tokens) { @@ -746,6 +766,13 @@ public class NodeDefinitionEnum(List tokens, bool exported, TokenIdent na } } +public class NodeDefinitionExternGlobalVariable(List tokens, bool exported, TokenIdent name, NodeType type) : NodeDefinition(tokens) +{ + public bool Exported { get; } = exported; + public TokenIdent Name { get; } = name; + public NodeType Type { get; } = type; +} + public class NodeDefinitionGlobalVariable(List tokens, bool exported, TokenIdent name, NodeType type) : NodeDefinition(tokens) { public bool Exported { get; } = exported; diff --git a/compiler/Program.cs b/compiler/Program.cs index 39a6ca3..8d3f98b 100644 --- a/compiler/Program.cs +++ b/compiler/Program.cs @@ -133,13 +133,13 @@ File.WriteAllText(".build/out.c", output); if (compileLib) { - Process.Start("gcc", ["-Og", "-fvisibility=hidden", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit(); + Process.Start("gcc", ["-Og", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit(); Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit(); NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph)); } else { - Process.Start("gcc", ["-Og", "-fvisibility=hidden", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit(); + Process.Start("gcc", ["-Og", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit(); } return 0; diff --git a/compiler/Tokenizer.cs b/compiler/Tokenizer.cs index 5d02d51..a928db2 100644 --- a/compiler/Tokenizer.cs +++ b/compiler/Tokenizer.cs @@ -397,6 +397,7 @@ public class Tokenizer "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), + "extern" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Extern), "true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true), "false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false), _ => new TokenIdent(line, startColumn, column - startColumn, value) @@ -545,6 +546,7 @@ public enum Keyword Return, Module, Export, + Extern, } public class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length) @@ -611,6 +613,7 @@ public static class TokenExtensions Keyword.Return => "return", Keyword.Module => "module", Keyword.Export => "export", + Keyword.Extern => "extern", _ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null) }; } diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index df641c2..8ac3bc6 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -34,14 +34,21 @@ public class TypeChecker var invalidParameter = false; TypedNodeStatement? body = null; - try + if (function.ReturnType == null) { - functionReturnType = ResolveType(function.ReturnType); + functionReturnType = NubTypeVoid.Instance; } - catch (CompileException e) + else { - diagnostics.Add(e.Diagnostic); - return null; + try + { + functionReturnType = ResolveType(function.ReturnType); + } + catch (CompileException e) + { + diagnostics.Add(e.Diagnostic); + return null; + } } using (scope.EnterScope()) @@ -395,15 +402,34 @@ public class TypeChecker private TypedNodeExpressionIntLiteral CheckExpressionIntLiteral(NodeExpressionIntLiteral expression, NubType? expectedType) { - return new TypedNodeExpressionIntLiteral(expression.Tokens, NubTypeSInt.Get(32), expression.Value); + NubType? type = null; + + if (expectedType is NubTypeSInt or NubTypeUInt) + type = expectedType; + + type ??= NubTypeSInt.Get(32); + + return new TypedNodeExpressionIntLiteral(expression.Tokens, type, expression.Value); } - private TypedNodeExpressionMemberAccess CheckExpressionMemberAccess(NodeExpressionMemberAccess expression, NubType? expectedType) + private TypedNodeExpression CheckExpressionMemberAccess(NodeExpressionMemberAccess expression, NubType? expectedType) { var target = CheckExpression(expression.Target, null); switch (target.Type) { + case NubTypeString stringType: + { + switch (expression.Name.Ident) + { + case "length": + return new TypedNodeExpressionStringLength(expression.Tokens, NubTypeUInt.Get(64), target); + case "ptr": + return new TypedNodeExpressionStringPointer(expression.Tokens, NubTypePointer.Get(NubTypeUInt.Get(8)), target); + default: + throw BasicError($"'{expression.Name.Ident}' is not a member of type string", expression.Name); + } + } case NubTypeStruct structType: { if (!moduleGraph.TryResolveModule(structType.Module, out var module)) @@ -419,7 +445,7 @@ public class TypeChecker if (field == null) throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target); - return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name); + return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name); } case NubTypeAnonymousStruct anonymousStructType: { @@ -427,24 +453,7 @@ public class TypeChecker if (field == null) throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target); - return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name); - } - case NubTypeEnumVariant enumVariantType: - { - if (!moduleGraph.TryResolveModule(enumVariantType.EnumType.Module, out var module)) - throw BasicError($"Module '{enumVariantType.EnumType.Module}' not found", expression.Target); - - if (!module.TryResolveType(enumVariantType.EnumType.Name, currentModule == enumVariantType.EnumType.Module, out var typeDef)) - throw BasicError($"Type '{enumVariantType.EnumType.Name}' not found in module '{enumVariantType.EnumType.Module}'", expression.Target); - - if (typeDef is not Module.TypeInfoEnum enumDef) - throw BasicError($"Type '{enumVariantType.EnumType.Module}::{enumVariantType.EnumType.Name}' is not an enum", expression.Target); - - var variant = enumDef.Variants.FirstOrDefault(x => x.Name == enumVariantType.Variant); - if (variant == null) - throw BasicError($"Type '{target.Type}' does not have a variant named '{enumVariantType.Variant}'", expression.Target); - - return new TypedNodeExpressionMemberAccess(expression.Tokens, variant.Type, target, expression.Name); + return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name); } default: throw BasicError($"{target.Type} has no member '{expression.Name.Ident}'", target); @@ -867,12 +876,22 @@ public class TypedNodeExpressionEnumLiteral(List tokens, NubType type, Ty public TypedNodeExpression Value { get; } = value; } -public class TypedNodeExpressionMemberAccess(List tokens, NubType type, TypedNodeExpression target, TokenIdent name) : TypedNodeExpression(tokens, type) +public class TypedNodeExpressionStructMemberAccess(List tokens, NubType type, TypedNodeExpression target, TokenIdent name) : TypedNodeExpression(tokens, type) { public TypedNodeExpression Target { get; } = target; public TokenIdent Name { get; } = name; } +public class TypedNodeExpressionStringLength(List tokens, NubType type, TypedNodeExpression target) : TypedNodeExpression(tokens, type) +{ + public TypedNodeExpression Target { get; } = target; +} + +public class TypedNodeExpressionStringPointer(List tokens, NubType type, TypedNodeExpression target) : TypedNodeExpression(tokens, type) +{ + public TypedNodeExpression Target { get; } = target; +} + public class TypedNodeExpressionFuncCall(List tokens, NubType type, TypedNodeExpression target, List parameters) : TypedNodeExpression(tokens, type) { public TypedNodeExpression Target { get; } = target; diff --git a/examples/build.sh b/examples/build.sh index fe2668e..2542e73 100755 --- a/examples/build.sh +++ b/examples/build.sh @@ -10,4 +10,6 @@ popd pushd program dotnet run --project ../../compiler main.nub ../math/.build/out.nublib ../core/.build/out.nublib -popd \ No newline at end of file +popd + +./program/.build/out \ No newline at end of file diff --git a/examples/core/print.nub b/examples/core/print.nub index abefd4c..2deb8c3 100644 --- a/examples/core/print.nub +++ b/examples/core/print.nub @@ -1,6 +1,11 @@ -module print +module core -extern func puts(^u8 text) +export extern func malloc(size: u64): ^void +export extern func free(size: ^void) -export func print(text: string) { +extern func puts(text: ^u8) + +export func print(text: string) +{ + puts(text.ptr) } \ No newline at end of file diff --git a/examples/program/main.nub b/examples/program/main.nub index d3a308d..fd39047 100644 --- a/examples/program/main.nub +++ b/examples/program/main.nub @@ -1,17 +1,9 @@ module main -struct Pos { - x: i32 - y: i32 -} - func main(): i32 { - test({ x = 23 y = 23 }) - - return 1 + core::print("Hello, world") + let ptr = core::malloc(64) + core::free(ptr) + return 0 } - -func test(x: Pos): void -{ -} \ No newline at end of file