diff --git a/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/compiler/NubLang/Generation/QBE/QBEGenerator.cs index f9c6b53..4b6264e 100644 --- a/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -379,7 +379,7 @@ public class QBEGenerator _labelIndex = 0; _tmpIndex = 0; - _writer.Write("export function "); + _writer.Write(funcDef.ExternSymbol != null ? "export function " : "function "); if (funcDef.Signature.ReturnType is not VoidTypeNode) { diff --git a/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs b/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs index b851d38..b61332e 100644 --- a/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs +++ b/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs @@ -6,7 +6,7 @@ public record FuncParameterNode(string Name, TypeNode Type) : Node; public record FuncSignatureNode(IReadOnlyList Parameters, TypeNode ReturnType) : Node; -public record FuncNode(string Module, string Name, FuncSignatureNode Signature, BlockNode? Body) : DefinitionNode(Module, Name); +public record FuncNode(string Module, string Name, string? ExternSymbol, FuncSignatureNode Signature, BlockNode? Body) : DefinitionNode(Module, Name); public record StructFieldNode(int Index, string Name, TypeNode Type, Optional Value) : Node; diff --git a/compiler/NubLang/TypeChecking/TypeChecker.cs b/compiler/NubLang/TypeChecking/TypeChecker.cs index 0f0e4a3..9c8d059 100644 --- a/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using NubLang.Diagnostics; using NubLang.Parsing.Syntax; using NubLang.Tokenization; @@ -59,7 +60,11 @@ public sealed class TypeChecker private InterfaceNode CheckInterfaceDefinition(InterfaceSyntax node) { - throw new NotImplementedException(); + var functions = node.Functions + .Select(function => new InterfaceFuncNode(function.Name, CheckFuncSignature(function.Signature))) + .ToList(); + + return new InterfaceNode(_syntaxTree.Metadata.ModuleName, node.Name, functions); } private StructNode CheckStructDefinition(StructSyntax node) @@ -124,7 +129,7 @@ public sealed class TypeChecker _funcReturnTypes.Pop(); } - return new FuncNode(_syntaxTree.Metadata.ModuleName, node.Name, CheckFuncSignature(node.Signature), body); + return new FuncNode(_syntaxTree.Metadata.ModuleName, node.Name, node.ExternSymbol, CheckFuncSignature(node.Signature), body); } private StatementNode CheckStatement(StatementSyntax node) @@ -308,7 +313,7 @@ public sealed class TypeChecker var parameterExpression = CheckExpression(parameter, expectedType); if (parameterExpression.Type != expectedType) { - throw new Exception($"Parameter {i + 1} does not match the type {expectedType} for function {funcType}"); + throw new TypeCheckerException(Diagnostic.Error($"Parameter {i + 1} does not match the type {expectedType} for function {funcType}").At(parameter).Build()); } parameters.Add(parameterExpression); @@ -402,12 +407,77 @@ public sealed class TypeChecker private StructFieldAccessNode CheckStructFieldAccess(StructFieldAccessSyntax expression) { - throw new NotImplementedException(); + var target = CheckExpression(expression.Target); + + if (target.Type is not StructTypeNode structType) + { + throw new TypeCheckerException(Diagnostic + .Error($"Cannot access struct member on non-struct type {target.Type}") + .At(expression) + .Build()); + } + + var field = structType.Fields.FirstOrDefault(x => x.Name == expression.Member); + if (field == null) + { + throw new TypeCheckerException(Diagnostic + .Error($"Struct {target.Type} does not have a field with the name {expression.Member}") + .At(expression) + .Build()); + } + + return new StructFieldAccessNode(field.Type, structType, target, expression.Member); } private StructInitializerNode CheckStructInitializer(StructInitializerSyntax expression, TypeNode? expectedType) { - throw new NotImplementedException(); + StructTypeNode? structType = null; + + if (expression.StructType.TryGetValue(out var customType)) + { + var checkedType = ResolveType(customType); + if (checkedType is not StructTypeNode checkedStructType) + { + throw new UnreachableException("Parser fucked up"); + } + + structType = checkedStructType; + } + else if (expectedType is StructTypeNode expectedStructType) + { + structType = expectedStructType; + } + + if (structType == null) + { + throw new TypeCheckerException(Diagnostic + .Error("Cannot get implicit type of struct") + .WithHelp("Specify struct type with struct {type_name} syntax") + .At(expression) + .Build()); + } + + var initializers = new Dictionary(); + + foreach (var initializer in expression.Initializers) + { + initializers.Add(initializer.Key, CheckExpression(initializer.Value)); + } + + var missingFields = structType.Fields + .Where(x => !x.HasDefaultValue && !initializers.ContainsKey(x.Name)) + .Select(x => x.Name) + .ToArray(); + + if (missingFields.Length != 0) + { + throw new TypeCheckerException(Diagnostic + .Error($"Fields {string.Join(", ", missingFields)} are not initialized") + .At(expression) + .Build()); + } + + return new StructInitializerNode(structType, initializers); } private UnaryExpressionNode CheckUnaryExpression(UnaryExpressionSyntax expression) @@ -453,7 +523,7 @@ public sealed class TypeChecker { if (!_importedModules.TryGetValue(customType.Module, out var module)) { - throw new Exception("Module not found: " + customType.Module); + throw new TypeCheckerException(Diagnostic.Error($"Module {customType.Module} not found").WithHelp($"import \"{customType.Module}\"").At(customType).Build()); } if (customType.Module == _syntaxTree.Metadata.ModuleName) diff --git a/example/main.nub b/example/main.nub deleted file mode 100644 index d49e124..0000000 --- a/example/main.nub +++ /dev/null @@ -1,20 +0,0 @@ -module "main" -import "test" - -export extern "puts" func puts(text: cstring) - -struct Human -{ - name: cstring - age: u64 -} - -func main(args: []cstring): i64 -{ - let me: main::Human = { - name = "Oliver" - age = 21 - } - - return 0 -} diff --git a/example/makefile b/example/makefile index d2eebc9..32cd5ab 100644 --- a/example/makefile +++ b/example/makefile @@ -3,8 +3,8 @@ NUBC = ../compiler/NubLang.CLI/bin/Debug/net9.0/nubc out: .build/out.o gcc -nostartfiles -o out x86_64.s .build/out.o -.build/out.o: $(NUBC) main.nub - $(NUBC) main.nub +.build/out.o: $(NUBC) src/main.nub src/test.nub + $(NUBC) src/main.nub src/test.nub .PHONY: $(NUBC) $(NUBC): diff --git a/example/src/main.nub b/example/src/main.nub new file mode 100644 index 0000000..9f1d059 --- /dev/null +++ b/example/src/main.nub @@ -0,0 +1,14 @@ +module "main" +import "test" + +export extern "puts" func puts(text: cstring) + +export struct Test1 +{ + test2: ^test::Test2 +} + +extern "main" func main(args: []cstring): i64 +{ + return 0 +} diff --git a/example/x86_64.s b/example/x86_64.s index cded159..f55a1d0 100644 --- a/example/x86_64.s +++ b/example/x86_64.s @@ -4,7 +4,7 @@ .globl _start _start: mov rdi, rsp - call main.main + call main mov rdi, rax mov rax, 60 syscall