From 6c517eb996c5f6f4533c3d7b1fe79e49772715d9 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 8 Sep 2025 17:33:13 +0200 Subject: [PATCH] ... --- example/src/main.nub | 17 ++- .../NubLang/Generation/QBE/QBEGenerator.cs | 143 ++++++++---------- .../TypeChecking/Node/ExpressionNode.cs | 34 ++--- 3 files changed, 95 insertions(+), 99 deletions(-) diff --git a/example/src/main.nub b/example/src/main.nub index d063ee0..70ad1ef 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -1,19 +1,30 @@ // c extern func puts(text: cstring) -struct Human { +interface Printable +{ + func print() +} + +struct Human : Printable +{ name: cstring age: u32 + + func print() + { + puts(this.name) + } } func main(args: []cstring): i64 { - let x: Human = struct { + let x: Printable = struct Human { name = "Oliver" age = 23 } - puts(x.name) + x.print() return 0 } diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs index af0acbd..6e7ac51 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -136,7 +136,7 @@ public class QBEGenerator _writer.Indented($"{store} {value}, {destination}"); } - private Val EmitLoad(TypeNode type, string from) + private string EmitLoad(TypeNode type, string from) { string load; @@ -164,7 +164,7 @@ public class QBEGenerator _writer.Indented($"{into} {QBEAssign(type)} {load} {from}"); - return new Val(into, type, ValKind.Direct); + return into; } private void EmitMemcpy(string source, string destination, string length) @@ -203,7 +203,7 @@ public class QBEGenerator { case ArrayInitializerNode arrayInitializer: { - EmitStore(source.Type, EmitUnwrap(EmitArrayInitializer(arrayInitializer)), destinationPointer); + EmitStore(source.Type, EmitArrayInitializer(arrayInitializer), destinationPointer); return true; } case StructInitializerNode structInitializer: @@ -218,7 +218,7 @@ public class QBEGenerator } case LiteralNode { Kind: LiteralKind.String } literal: { - EmitStore(source.Type, EmitUnwrap(EmitLiteral(literal)), destinationPointer); + EmitStore(source.Type, EmitLiteral(literal), destinationPointer); return true; } } @@ -368,7 +368,7 @@ public class QBEGenerator foreach (var parameter in funcDef.Signature.Parameters) { - scope.Declare(parameter.Name, new Val("%" + parameter.Name, parameter.Type, ValKind.Direct)); + scope.Declare(parameter.Name, new Val("%" + parameter.Name, parameter.Type, false)); } EmitBlock(funcDef.Body, scope); @@ -410,10 +410,10 @@ public class QBEGenerator var scope = new Scope(); - scope.Declare("this", new Val("%this", structDef.Type, ValKind.Direct)); + scope.Declare("this", new Val("%this", structDef.Type, false)); foreach (var parameter in function.Signature.Parameters) { - scope.Declare(parameter.Name, new Val("%" + parameter.Name, parameter.Type, ValKind.Direct)); + scope.Declare(parameter.Name, new Val("%" + parameter.Name, parameter.Type, false)); } EmitBlock(function.Body, scope); @@ -531,12 +531,12 @@ public class QBEGenerator private void EmitAssignment(AssignmentNode assignment) { - var destination = EmitExpression(assignment.Target); - if (destination.Kind != ValKind.Pointer) + if (!assignment.Target.IsLValue) { - throw new UnreachableException("Destination of assignment must be a pointer. This should be caught in the type checker"); + throw new UnreachableException("Destination of assignment must be an lvalue. This should have been caught in the type checker"); } + var destination = EmitExpression(assignment.Target); EmitCopyIntoOrInitialize(assignment.Value, destination.Name); } @@ -600,7 +600,7 @@ public class QBEGenerator EmitStore(variableDeclaration.Assignment.Value.Type, value, name); } - Scope.Declare(variableDeclaration.Name, new Val(name, variableDeclaration.Type, ValKind.Pointer)); + Scope.Declare(variableDeclaration.Name, new Val(name, variableDeclaration.Type, true)); } private void EmitWhile(WhileNode whileStatement) @@ -626,7 +626,7 @@ public class QBEGenerator private Val EmitExpression(ExpressionNode expression) { - return expression switch + var value = expression switch { ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), StructInitializerNode structInitializer => EmitStructInitializer(structInitializer), @@ -646,9 +646,11 @@ public class QBEGenerator ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), _ => throw new ArgumentOutOfRangeException(nameof(expression)) }; + + return new Val(value, expression.Type, expression.IsLValue); } - private Val EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) + private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) { var array = EmitUnwrap(EmitExpression(arrayIndexAccess.Target)); var index = EmitUnwrap(EmitExpression(arrayIndexAccess.Index)); @@ -661,7 +663,7 @@ public class QBEGenerator _writer.Indented($"{pointer} =l mul {index}, {SizeOf(elementType)}"); _writer.Indented($"{pointer} =l add {pointer}, 8"); _writer.Indented($"{pointer} =l add {array}, {pointer}"); - return new Val(pointer, arrayIndexAccess.Type, ValKind.Pointer); + return pointer; } private void EmitArraysCheck(string array, string index) @@ -688,7 +690,7 @@ public class QBEGenerator _writer.Indented(notOobLabel); } - private Val EmitArrayInitializer(ArrayInitializerNode arrayInitializer) + private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer) { var capacity = EmitUnwrap(EmitExpression(arrayInitializer.Capacity)); var elementSize = SizeOf(arrayInitializer.ElementType); @@ -706,26 +708,26 @@ public class QBEGenerator _writer.Indented($"{dataPointer} =l add {arrayPointer}, 8"); _writer.Indented($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); - return new Val(arrayPointer, arrayInitializer.Type, ValKind.Direct); + return arrayPointer; } - private Val EmitDereference(DereferenceNode dereference) + private string EmitDereference(DereferenceNode dereference) { return EmitLoad(dereference.Type, EmitUnwrap(EmitExpression(dereference.Expression))); } - private Val EmitAddressOf(AddressOfNode addressOf) + private string EmitAddressOf(AddressOfNode addressOf) { var value = EmitExpression(addressOf.Expression); - if (value.Kind != ValKind.Pointer) + if (!value.IsLValue) { - throw new UnreachableException("Tried to take address of non-pointer type. This should have been caught in the type checker"); + throw new UnreachableException("Tried to take address of rvalue. This should have been caught in the type checker"); } - return new Val(value.Name, addressOf.Type, ValKind.Direct); + return value.Name; } - private Val EmitBinaryExpression(BinaryExpressionNode binaryExpression) + private string EmitBinaryExpression(BinaryExpressionNode binaryExpression) { var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); @@ -735,7 +737,7 @@ public class QBEGenerator var instruction = EmitBinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type, left, right); _writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); - return new Val(outputName, binaryExpression.Type, ValKind.Direct); + return outputName; } private string EmitBinaryInstructionFor(BinaryOperator op, TypeNode type, string left, string right) @@ -832,24 +834,24 @@ public class QBEGenerator }; } - private Val EmitExternFuncIdent(ExternFuncIdentNode externFuncIdent) + private string EmitExternFuncIdent(ExternFuncIdentNode externFuncIdent) { var func = _definitionTable.LookupExternFunc(externFuncIdent.Name); - return new Val(ExternFuncName(func), externFuncIdent.Type, ValKind.Direct); + return ExternFuncName(func); } - private Val EmitLocalFuncIdent(LocalFuncIdentNode localFuncIdent) + private string EmitLocalFuncIdent(LocalFuncIdentNode localFuncIdent) { var func = _definitionTable.LookupLocalFunc(localFuncIdent.Name); - return new Val(LocalFuncName(func), localFuncIdent.Type, ValKind.Direct); + return LocalFuncName(func); } - private Val EmitVariableIdent(VariableIdentNode variableIdent) + private string EmitVariableIdent(VariableIdentNode variableIdent) { - return Scope.Lookup(variableIdent.Name); + return variableIdent.Name; } - private Val EmitLiteral(LiteralNode literal) + private string EmitLiteral(LiteralNode literal) { switch (literal.Kind) { @@ -859,19 +861,19 @@ public class QBEGenerator { var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); + return bits.ToString(); } if (literal.Type is FloatTypeNode { Width: 64 }) { var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); + return bits.ToString(); } if (literal.Type is IntTypeNode) { - return new Val(literal.Value, literal.Type, ValKind.Direct); + return literal.Value; } break; @@ -880,21 +882,21 @@ public class QBEGenerator { if (literal.Type is IntTypeNode) { - return new Val(literal.Value.Split(".").First(), literal.Type, ValKind.Direct); + return literal.Value.Split(".").First(); } if (literal.Type is FloatTypeNode { Width: 32 }) { var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); + return bits.ToString(); } if (literal.Type is FloatTypeNode { Width: 64 }) { var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); + return bits.ToString(); } break; @@ -905,14 +907,14 @@ public class QBEGenerator { var stringLiteral = new StringLiteral(literal.Value, StringName()); _stringLiterals.Add(stringLiteral); - return new Val(stringLiteral.Name, literal.Type, ValKind.Direct); + return stringLiteral.Name; } if (literal.Type is CStringTypeNode) { var cStringLiteral = new CStringLiteral(literal.Value, CStringName()); _cStringLiterals.Add(cStringLiteral); - return new Val(cStringLiteral.Name, literal.Type, ValKind.Direct); + return cStringLiteral.Name; } break; @@ -921,7 +923,7 @@ public class QBEGenerator { if (literal.Type is BoolTypeNode) { - return new Val(bool.Parse(literal.Value) ? "1" : "0", literal.Type, ValKind.Direct); + return bool.Parse(literal.Value) ? "1" : "0"; } break; @@ -931,7 +933,7 @@ public class QBEGenerator throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}"); } - private Val EmitStructInitializer(StructInitializerNode structInitializer, string? destination = null) + private string EmitStructInitializer(StructInitializerNode structInitializer, string? destination = null) { var structDef = _definitionTable.LookupStruct(structInitializer.StructType.Name); @@ -959,10 +961,10 @@ public class QBEGenerator EmitCopyIntoOrInitialize(valueExpression, offset); } - return new Val(destination, structInitializer.StructType, ValKind.Direct); + return destination; } - private Val EmitUnaryExpression(UnaryExpressionNode unaryExpression) + private string EmitUnaryExpression(UnaryExpressionNode unaryExpression) { var operand = EmitUnwrap(EmitExpression(unaryExpression.Operand)); var outputName = TmpName(); @@ -975,16 +977,16 @@ public class QBEGenerator { case IntTypeNode { Signed: true, Width: 64 }: _writer.Indented($"{outputName} =l neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); + return outputName; case IntTypeNode { Signed: true, Width: 8 or 16 or 32 }: _writer.Indented($"{outputName} =w neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); + return outputName; case FloatTypeNode { Width: 64 }: _writer.Indented($"{outputName} =d neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); + return outputName; case FloatTypeNode { Width: 32 }: _writer.Indented($"{outputName} =s neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); + return outputName; } break; @@ -995,7 +997,7 @@ public class QBEGenerator { case BoolTypeNode: _writer.Indented($"{outputName} =w xor {operand}, 1"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); + return outputName; } break; @@ -1009,7 +1011,7 @@ public class QBEGenerator throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); } - private Val EmitStructFieldAccess(StructFieldAccessNode structFieldAccess) + private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccess) { var target = EmitUnwrap(EmitExpression(structFieldAccess.Target)); @@ -1019,16 +1021,10 @@ public class QBEGenerator var output = TmpName(); _writer.Indented($"{output} =l add {target}, {offset}"); - // If the accessed member is an inline struct, it will not be a pointer - if (structFieldAccess.Type is StructTypeNode) - { - return new Val(output, structFieldAccess.Type, ValKind.Direct); - } - - return new Val(output, structFieldAccess.Type, ValKind.Pointer); + return output; } - private Val EmitStructFuncCall(StructFuncCallNode structFuncCall) + private string EmitStructFuncCall(StructFuncCallNode structFuncCall) { var structDef = _definitionTable.LookupStruct(structFuncCall.StructType.Name); var func = StructFuncName(structDef.Name, structFuncCall.Name); @@ -1046,17 +1042,17 @@ public class QBEGenerator if (structFuncCall.Type is VoidTypeNode) { _writer.Indented($"call {func}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, structFuncCall.Type, ValKind.Direct); + return string.Empty; } else { var outputName = TmpName(); _writer.Indented($"{outputName} {QBEAssign(structFuncCall.Type)} call {func}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, structFuncCall.Type, ValKind.Direct); + return outputName; } } - private Val EmitInterfaceFuncCall(InterfaceFuncCallNode interfaceFuncCall) + private string EmitInterfaceFuncCall(InterfaceFuncCallNode interfaceFuncCall) { var target = EmitUnwrap(EmitExpression(interfaceFuncCall.InterfaceExpression)); @@ -1087,17 +1083,17 @@ public class QBEGenerator if (interfaceFuncCall.Type is VoidTypeNode) { _writer.Indented($"call {func}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, interfaceFuncCall.Type, ValKind.Direct); + return string.Empty; } else { var outputName = TmpName(); _writer.Indented($"{outputName} {QBEAssign(interfaceFuncCall.Type)} call {func}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, interfaceFuncCall.Type, ValKind.Direct); + return outputName; } } - private Val EmitInterfaceInitializer(InterfaceInitializerNode interfaceInitializer, string? destination = null) + private string EmitInterfaceInitializer(InterfaceInitializerNode interfaceInitializer, string? destination = null) { var implementation = EmitUnwrap(EmitExpression(interfaceInitializer.Implementation)); @@ -1126,10 +1122,10 @@ public class QBEGenerator _writer.Indented($"{objectPointer} =l add {destination}, 8"); _writer.Indented($"storel {implementation}, {objectPointer}"); - return new Val(destination, interfaceInitializer.InterfaceType, ValKind.Direct); + return destination; } - private Val EmitFuncCall(FuncCallNode funcCall) + private string EmitFuncCall(FuncCallNode funcCall) { var expression = EmitExpression(funcCall.Expression); @@ -1145,24 +1141,19 @@ public class QBEGenerator if (funcCall.Type is VoidTypeNode) { _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, funcCall.Type, ValKind.Direct); + return string.Empty; } else { var outputName = TmpName(); _writer.Indented($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, funcCall.Type, ValKind.Direct); + return outputName; } } private string EmitUnwrap(Val val) { - return val.Kind switch - { - ValKind.Direct => val.Name, - ValKind.Pointer => EmitLoad(val.Type, val.Name).Name, - _ => throw new ArgumentOutOfRangeException() - }; + return val.Name; } private static int SizeOf(TypeNode type) @@ -1329,7 +1320,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, bool IsLValue); public class Scope(Scope? parent = null) { @@ -1355,10 +1346,4 @@ public class Scope(Scope? parent = null) { return new Scope(this); } -} - -public enum ValKind -{ - Pointer, - Direct, } \ 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 f72bceb..8978f5d 100644 --- a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs +++ b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs @@ -22,36 +22,36 @@ public enum BinaryOperator Divide } -public abstract record ExpressionNode(TypeNode Type) : Node; +public abstract record ExpressionNode(TypeNode Type, bool IsLValue) : Node; -public record BinaryExpressionNode(TypeNode Type, ExpressionNode Left, BinaryOperator Operator, ExpressionNode Right) : ExpressionNode(Type); +public record BinaryExpressionNode(TypeNode Type, ExpressionNode Left, BinaryOperator Operator, ExpressionNode Right) : ExpressionNode(Type, false); -public record UnaryExpressionNode(TypeNode Type, UnaryOperator Operator, ExpressionNode Operand) : ExpressionNode(Type); +public record UnaryExpressionNode(TypeNode Type, UnaryOperator Operator, ExpressionNode Operand) : ExpressionNode(Type, false); -public record FuncCallNode(TypeNode Type, ExpressionNode Expression, IReadOnlyList Parameters) : ExpressionNode(Type); +public record FuncCallNode(TypeNode Type, ExpressionNode Expression, IReadOnlyList Parameters) : ExpressionNode(Type, false); -public record StructFuncCallNode(TypeNode Type, string Name, StructTypeNode StructType, ExpressionNode StructExpression, IReadOnlyList Parameters) : ExpressionNode(Type); +public record StructFuncCallNode(TypeNode Type, string Name, StructTypeNode StructType, ExpressionNode StructExpression, IReadOnlyList Parameters) : ExpressionNode(Type, false); -public record InterfaceFuncCallNode(TypeNode Type, string Name, InterfaceTypeNode InterfaceType, ExpressionNode InterfaceExpression, IReadOnlyList Parameters) : ExpressionNode(Type); +public record InterfaceFuncCallNode(TypeNode Type, string Name, InterfaceTypeNode InterfaceType, ExpressionNode InterfaceExpression, IReadOnlyList Parameters) : ExpressionNode(Type, false); -public record VariableIdentNode(TypeNode Type, string Name) : ExpressionNode(Type); +public record VariableIdentNode(TypeNode Type, string Name) : ExpressionNode(Type, true); -public record LocalFuncIdentNode(TypeNode Type, string Name) : ExpressionNode(Type); +public record LocalFuncIdentNode(TypeNode Type, string Name) : ExpressionNode(Type, false); -public record ExternFuncIdentNode(TypeNode Type, string Name) : ExpressionNode(Type); +public record ExternFuncIdentNode(TypeNode Type, string Name) : ExpressionNode(Type, false); -public record ArrayInitializerNode(TypeNode Type, ExpressionNode Capacity, TypeNode ElementType) : ExpressionNode(Type); +public record ArrayInitializerNode(TypeNode Type, ExpressionNode Capacity, TypeNode ElementType) : ExpressionNode(Type, true); -public record ArrayIndexAccessNode(TypeNode Type, ExpressionNode Target, ExpressionNode Index) : ExpressionNode(Type); +public record ArrayIndexAccessNode(TypeNode Type, ExpressionNode Target, ExpressionNode Index) : ExpressionNode(Type, true); -public record AddressOfNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type); +public record AddressOfNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type, false); -public record LiteralNode(TypeNode Type, string Value, LiteralKind Kind) : ExpressionNode(Type); +public record LiteralNode(TypeNode Type, string Value, LiteralKind Kind) : ExpressionNode(Type, false); -public record StructFieldAccessNode(TypeNode Type, StructTypeNode StructType, ExpressionNode Target, string Field) : ExpressionNode(Type); +public record StructFieldAccessNode(TypeNode Type, StructTypeNode StructType, ExpressionNode Target, string Field) : ExpressionNode(Type, true); -public record StructInitializerNode(StructTypeNode StructType, Dictionary Initializers) : ExpressionNode(StructType); +public record StructInitializerNode(StructTypeNode StructType, Dictionary Initializers) : ExpressionNode(StructType, true); -public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type); +public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type, false); -public record InterfaceInitializerNode(TypeNode Type, InterfaceTypeNode InterfaceType, StructTypeNode StructType, ExpressionNode Implementation) : ExpressionNode(Type); \ No newline at end of file +public record InterfaceInitializerNode(TypeNode Type, InterfaceTypeNode InterfaceType, StructTypeNode StructType, ExpressionNode Implementation) : ExpressionNode(Type, true); \ No newline at end of file