From f31c16bbacb072b969b36661db8b56d87f4fd097 Mon Sep 17 00:00:00 2001 From: nub31 Date: Tue, 12 Aug 2025 20:01:43 +0200 Subject: [PATCH] ... --- example/src/main.nub | 24 +++++- .../Generation/QBE/QBEGenerator.Expression.cs | 34 ++++++++- .../NubLang/Generation/QBE/QBEGenerator.cs | 60 ++++++++++++--- .../Generation/TypedDefinitionTable.cs | 7 ++ src/compiler/NubLang/Parsing/Parser.cs | 19 +++-- .../Parsing/Syntax/DefinitionSyntax.cs | 2 +- .../NubLang/TypeChecking/DefinitionTable.cs | 9 ++- .../TypeChecking/Node/DefinitionNode.cs | 8 +- .../TypeChecking/Node/ExpressionNode.cs | 2 + .../NubLang/TypeChecking/TypeChecker.cs | 76 ++++++++++++++++--- 10 files changed, 204 insertions(+), 37 deletions(-) diff --git a/example/src/main.nub b/example/src/main.nub index b97d3d7..516aaab 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -1,5 +1,27 @@ +interface Printable +{ + func print() +} + +struct Human : Printable { + name: cstring + + func print_name() + { + puts(this^.name) + } + + func print() + { + puts("example") + } +} + func main(args: []cstring): i64 { - puts("test") + let human: Human = alloc Human { + name = "oliver" + } + human.print() return 0 } diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs index 9f1e007..c4c5460 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs @@ -18,12 +18,14 @@ public partial class QBEGenerator ArrowFuncNode arrowFunc => EmitArrowFunc(arrowFunc), BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), + InterfaceFuncAccessNode interfaceFuncAccess => EmitInterfaceFuncAccess(interfaceFuncAccess), ExternFuncIdentNode externFuncIdent => EmitExternFuncIdent(externFuncIdent), LocalFuncIdentNode localFuncIdent => EmitLocalFuncIdent(localFuncIdent), VariableIdentNode variableIdent => EmitVariableIdent(variableIdent), LiteralNode literal => EmitLiteral(literal), UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess), + StructFuncAccessNode structFuncAccess => EmitStructFuncAccess(structFuncAccess), ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), _ => throw new ArgumentOutOfRangeException(nameof(expression)) }; @@ -321,16 +323,22 @@ public partial class QBEGenerator private Val EmitStructInitializer(StructInitializerNode structInitializer, string? destination = null) { - var @struct = _definitionTable.LookupStruct(structInitializer.StructType.Name); + var structDef = _definitionTable.LookupStruct(structInitializer.StructType.Name); if (destination == null) { destination = TmpName(); var size = structInitializer.StructType.Size(_definitionTable); + + if (structDef.InterfaceImplementations.Any()) + { + size += 8; + } + _writer.Indented($"{destination} =l alloc8 {size}"); } - foreach (var field in @struct.Fields) + foreach (var field in structDef.Fields) { if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression)) { @@ -340,7 +348,7 @@ public partial class QBEGenerator Debug.Assert(valueExpression != null); var offset = TmpName(); - _writer.Indented($"{offset} =l add {destination}, {OffsetOf(@struct, field.Name)}"); + _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}"); EmitCopyIntoOrInitialize(valueExpression, offset); } @@ -413,6 +421,20 @@ public partial class QBEGenerator return new Val(output, structFieldAccess.Type, ValKind.Pointer); } + private Val EmitStructFuncAccess(StructFuncAccessNode structFuncAccess) + { + var target = EmitExpression(structFuncAccess.Target); + var structDef = _definitionTable.LookupStruct(structFuncAccess.StructType.Name); + var func = StructFuncName(structDef.Name, structFuncAccess.Func); + + return new Val(func, structFuncAccess.Type, ValKind.Direct, target); + } + + private Val EmitInterfaceFuncAccess(InterfaceFuncAccessNode interfaceFuncAccess) + { + throw new NotImplementedException(); + } + private Val EmitFuncCall(FuncCallNode funcCall) { var expression = EmitExpression(funcCall.Expression); @@ -420,6 +442,12 @@ public partial class QBEGenerator var parameterStrings = new List(); + if (expression.ThisArg != null) + { + Debug.Assert(expression.ThisArg.Kind == ValKind.Pointer); + parameterStrings.Add($"l {expression.ThisArg.Name}"); + } + foreach (var parameter in funcCall.Parameters) { var copy = EmitCreateCopyOrInitialize(parameter); diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs index bd760f5..5fe9ba7 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -51,10 +51,20 @@ public partial class QBEGenerator foreach (var structDef in _definitionTable.GetStructs()) { - EmitStructDefinition(structDef); + EmitStructTypeDefinition(structDef); _writer.NewLine(); } + foreach (var structDef in _syntaxTree.Definitions.OfType()) + { + foreach (var func in structDef.Functions) + { + var funcName = StructFuncName(structDef.Name, func.Name); + EmitFuncDefinition(funcName, func.Signature.Parameters, func.Signature.ReturnType, func.Body); + _writer.NewLine(); + } + } + foreach (var funcDef in _syntaxTree.Definitions.OfType()) { EmitFuncDefinition(LocalFuncName(funcDef), funcDef.Signature.Parameters, funcDef.Signature.ReturnType, funcDef.Body); @@ -67,6 +77,22 @@ public partial class QBEGenerator _writer.NewLine(); } + foreach (var structDef in _syntaxTree.Definitions.OfType().Where(x => x.InterfaceImplementations.Count > 0)) + { + _writer.Write($"data {StructVtableName(structDef.Name)} = {{ "); + + foreach (var interfaceImplementation in structDef.InterfaceImplementations) + { + var interfaceDef = _definitionTable.LookupInterface(interfaceImplementation.Name); + foreach (var func in interfaceDef.Functions) + { + _writer.Write($"l {StructFuncName(structDef.Name, func.Name)}, "); + } + } + + _writer.WriteLine("}"); + } + foreach (var cStringLiteral in _cStringLiterals) { _writer.WriteLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}"); @@ -315,7 +341,7 @@ public partial class QBEGenerator if (complexType is CustomTypeNode customType) { - return CustomTypeName(customType); + return CustomTypeName(customType.Name); } return "l"; @@ -359,7 +385,7 @@ public partial class QBEGenerator _writer.WriteLine("}"); } - private void EmitStructDefinition(StructNode structDef) + private void EmitStructTypeDefinition(StructNode structDef) { _writer.WriteLine($"type {CustomTypeName(structDef.Name)} = {{ "); @@ -398,7 +424,7 @@ public partial class QBEGenerator if (complexType is CustomTypeNode customType) { - return CustomTypeName(customType); + return CustomTypeName(customType.Name); } return "l"; @@ -432,11 +458,16 @@ public partial class QBEGenerator }; } - private int OffsetOf(StructNode structDefinition, string member) + private int OffsetOf(StructNode structDef, string member) { var offset = 0; - foreach (var field in structDefinition.Fields) + if (structDef.InterfaceImplementations.Any()) + { + offset = 8; + } + + foreach (var field in structDef.Fields) { if (field.Name == member) { @@ -484,16 +515,21 @@ public partial class QBEGenerator return $"${funcDef.CallName}"; } - private string CustomTypeName(CustomTypeNode customType) - { - return CustomTypeName(customType.Name); - } - private string CustomTypeName(string name) { return $":{name}"; } + private string StructFuncName(string structName, string funcName) + { + return $"${structName}_{funcName}"; + } + + private string StructVtableName(string structName) + { + return $"${structName}_vtable"; + } + #endregion } @@ -509,7 +545,7 @@ public class CStringLiteral(string value, string name) public string Name { get; } = name; } -public record Val(string Name, TypeNode Type, ValKind Kind); +public record Val(string Name, TypeNode Type, ValKind Kind, Val? ThisArg = null); public class Scope(Scope? parent = null) { diff --git a/src/compiler/NubLang/Generation/TypedDefinitionTable.cs b/src/compiler/NubLang/Generation/TypedDefinitionTable.cs index 6bfac39..e54fb1c 100644 --- a/src/compiler/NubLang/Generation/TypedDefinitionTable.cs +++ b/src/compiler/NubLang/Generation/TypedDefinitionTable.cs @@ -32,6 +32,13 @@ public sealed class TypedDefinitionTable .First(x => x.Name == name); } + public InterfaceNode LookupInterface(string name) + { + return _definitions + .OfType() + .First(x => x.Name == name); + } + public IEnumerable GetStructs() { return _definitions.OfType(); diff --git a/src/compiler/NubLang/Parsing/Parser.cs b/src/compiler/NubLang/Parsing/Parser.cs index 6cf2220..2719afc 100644 --- a/src/compiler/NubLang/Parsing/Parser.cs +++ b/src/compiler/NubLang/Parsing/Parser.cs @@ -66,14 +66,14 @@ public sealed class Parser return new SyntaxTree(GetTokens(_tokenIndex), definitions); } - private FuncSignatureSyntax ParseFuncSignature(FuncParameterSyntax? thisArg = null) + private FuncSignatureSyntax ParseFuncSignature(TypeSyntax? thisArg = null) { var startIndex = _tokenIndex; List parameters = []; if (thisArg != null) { - parameters.Add(thisArg); + parameters.Add(new FuncParameterSyntax([], "this", new PointerTypeSyntax([], thisArg))); } ExpectSymbol(Symbol.OpenParen); @@ -150,6 +150,16 @@ public sealed class Parser private DefinitionSyntax ParseStruct(int startIndex) { var name = ExpectIdentifier(); + var interfaceImplementations = new List(); + + if (TryExpectSymbol(Symbol.Colon)) + { + do + { + var interfaceType = ParseType(); + interfaceImplementations.Add(interfaceType); + } while (TryExpectSymbol(Symbol.Comma)); + } ExpectSymbol(Symbol.OpenBrace); @@ -165,8 +175,7 @@ public sealed class Parser if (TryExpectSymbol(Symbol.Func)) { var funcName = ExpectIdentifier().Value; - var thisArg = new FuncParameterSyntax([], "this", new CustomTypeSyntax([], name.Value)); - var funcSignature = ParseFuncSignature(thisArg); + var funcSignature = ParseFuncSignature(new CustomTypeSyntax([], name.Value)); var funcBody = ParseBlock(); funcs.Add(new StructFuncSyntax(GetTokens(memberStartIndex), funcName, funcSignature, funcBody)); @@ -188,7 +197,7 @@ public sealed class Parser } } - return new StructSyntax(GetTokens(startIndex), name.Value, fields, funcs); + return new StructSyntax(GetTokens(startIndex), name.Value, fields, funcs, interfaceImplementations); } private InterfaceSyntax ParseInterface(int startIndex) diff --git a/src/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs b/src/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs index 2cfeb55..aaf6d1e 100644 --- a/src/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs +++ b/src/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs @@ -63,7 +63,7 @@ public record StructFuncSyntax(IEnumerable Tokens, string Name, FuncSigna } } -public record StructSyntax(IEnumerable Tokens, string Name, IReadOnlyList Fields, IReadOnlyList Functions) : DefinitionSyntax(Tokens) +public record StructSyntax(IEnumerable Tokens, string Name, IReadOnlyList Fields, IReadOnlyList Functions, IReadOnlyList InterfaceImplementations) : DefinitionSyntax(Tokens) { public override IEnumerable GetChildren() { diff --git a/src/compiler/NubLang/TypeChecking/DefinitionTable.cs b/src/compiler/NubLang/TypeChecking/DefinitionTable.cs index 37e5c63..ee094d4 100644 --- a/src/compiler/NubLang/TypeChecking/DefinitionTable.cs +++ b/src/compiler/NubLang/TypeChecking/DefinitionTable.cs @@ -33,9 +33,14 @@ public class DefinitionTable .Where(x => x.Name == type.Name); } - public IEnumerable LookupStructField(StructSyntax structNode, string field) + public IEnumerable LookupStructField(StructSyntax @struct, string field) { - return structNode.Fields.Where(x => x.Name == field); + return @struct.Fields.Where(x => x.Name == field); + } + + public IEnumerable LookupStructFunc(StructSyntax @struct, string func) + { + return @struct.Functions.Where(x => x.Name == func); } public IEnumerable LookupInterface(CustomTypeNode type) diff --git a/src/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs b/src/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs index ff081c3..f7ad577 100644 --- a/src/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs +++ b/src/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs @@ -12,8 +12,10 @@ public record ExternFuncNode(string Name, string CallName, FuncSignatureNode Sig public record StructFieldNode(int Index, string Name, TypeNode Type, Optional Value) : Node; -public record StructNode(string Name, IReadOnlyList Fields) : DefinitionNode; +public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node; -public record InterfacenFuncNode(string Name, FuncSignatureNode Signature) : Node; +public record StructNode(string Name, IReadOnlyList Fields, IReadOnlyList Functions, IReadOnlyList InterfaceImplementations) : DefinitionNode; -public record InterfaceNode(string Name, IReadOnlyList Functions) : DefinitionNode; \ No newline at end of file +public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node; + +public record InterfaceNode(string Name, IReadOnlyList Functions) : DefinitionNode; \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs index b1ec4da..754ba22 100644 --- a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs +++ b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs @@ -48,6 +48,8 @@ public record LiteralNode(TypeNode Type, string Value, LiteralKind Kind) : Expre public record StructFieldAccessNode(TypeNode Type, CustomTypeNode StructType, ExpressionNode Target, string Field) : ExpressionNode(Type); +public record StructFuncAccessNode(TypeNode Type, CustomTypeNode StructType, ExpressionNode Target, string Func) : ExpressionNode(Type); + public record InterfaceFuncAccessNode(TypeNode Type, CustomTypeNode InterfaceType, ExpressionNode Target, string FuncName) : ExpressionNode(Type); public record StructInitializerNode(CustomTypeNode StructType, Dictionary Initializers) : ExpressionNode(StructType); diff --git a/src/compiler/NubLang/TypeChecking/TypeChecker.cs b/src/compiler/NubLang/TypeChecking/TypeChecker.cs index b464d2e..9c6f06d 100644 --- a/src/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/src/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -61,11 +61,11 @@ public sealed class TypeChecker private InterfaceNode CheckInterfaceDefinition(InterfaceSyntax node) { - var functions = new List(); + var functions = new List(); foreach (var function in node.Functions) { - functions.Add(new InterfacenFuncNode(function.Name, CheckFuncSignature(function.Signature))); + functions.Add(new InterfaceFuncNode(function.Name, CheckFuncSignature(function.Signature))); } return new InterfaceNode(node.Name, functions); @@ -87,7 +87,48 @@ public sealed class TypeChecker structFields.Add(new StructFieldNode(field.Index, field.Name, CheckType(field.Type), value)); } - return new StructNode(node.Name, structFields); + var funcs = new List(); + + foreach (var func in node.Functions) + { + var parameters = new List(); + foreach (var parameter in func.Signature.Parameters) + { + parameters.Add(new FuncParameterNode(parameter.Name, CheckType(parameter.Type))); + } + + funcs.Add(new StructFuncNode(func.Name, CheckFuncSignature(func.Signature), CheckFuncBody(func.Body, CheckType(func.Signature.ReturnType), parameters))); + } + + var interfaceImplementations = new List(); + + foreach (var interfaceImplementation in node.InterfaceImplementations) + { + var interfaceType = CheckType(interfaceImplementation); + if (interfaceType is not CustomTypeNode customType) + { + _diagnostics.Add(Diagnostic.Error("Interface implementation is not a custom type").Build()); + continue; + } + + var interfaceDefs = _definitionTable.LookupInterface(customType).ToArray(); + + if (interfaceDefs.Length == 0) + { + _diagnostics.Add(Diagnostic.Error($"Interface {customType.Name} is not defined").Build()); + continue; + } + + if (interfaceDefs.Length > 1) + { + _diagnostics.Add(Diagnostic.Error($"Interface {customType.Name} has multiple definitions").Build()); + continue; + } + + interfaceImplementations.Add(customType); + } + + return new StructNode(node.Name, structFields, funcs, interfaceImplementations); } private ExternFuncNode CheckExternFuncDefinition(ExternFuncSyntax node) @@ -163,11 +204,6 @@ public sealed class TypeChecker { TypeNode? type = null; - if (statement.ExplicitType.HasValue) - { - type = CheckType(statement.ExplicitType.Value); - } - var assignment = Optional.Empty(); if (statement.Assignment.HasValue) { @@ -176,6 +212,11 @@ public sealed class TypeChecker type = boundValue.Type; } + if (statement.ExplicitType.HasValue) + { + type = CheckType(statement.ExplicitType.Value); + } + if (type == null) { throw new NotImplementedException("Diagnostics not implemented"); @@ -373,9 +414,9 @@ public sealed class TypeChecker var @interface = interfaces[0]; var interfaceFuncs = _definitionTable.LookupInterfaceFunc(@interface, expression.Member).ToArray(); - if (interfaces.Length > 0) + if (interfaceFuncs.Length > 0) { - if (interfaces.Length > 1) + if (interfaceFuncs.Length > 1) { throw new CheckException(Diagnostic.Error($"Interface {customType} has multiple functions with the name {expression.Member}").Build()); } @@ -411,6 +452,21 @@ public sealed class TypeChecker return new StructFieldAccessNode(CheckType(field.Type), customType, boundExpression, expression.Member); } + + var funcs = _definitionTable.LookupStructFunc(@struct, expression.Member).ToArray(); + if (funcs.Length > 0) + { + if (funcs.Length > 1) + { + throw new CheckException(Diagnostic.Error($"Struct {customType} has multiple functions with the name {expression.Member}").Build()); + } + + var func = funcs[0]; + + var parameters = func.Signature.Parameters.Select(x => CheckType(x.Type)).ToList(); + var returnType = CheckType(func.Signature.ReturnType); + return new StructFuncAccessNode(new FuncTypeNode(parameters, returnType), customType, boundExpression, expression.Member); + } } }