From 31dc31b8f57ff124aa4d1d7f2e3304b1d8f92f52 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sat, 17 May 2025 18:22:23 +0200 Subject: [PATCH] pointers kinda works --- README.md | 7 ++ example/program.nub | 6 +- src/compiler/Nub.Lang/Backend/Generator.cs | 105 ++++++++++++++++++ .../Nub.Lang/Frontend/Parsing/Parser.cs | 20 ++++ .../Frontend/Parsing/UnaryExpressionNode.cs | 15 +++ .../Nub.Lang/Frontend/Typing/TypeChecker.cs | 87 +++++++++++++-- src/compiler/Nub.Lang/NubType.cs | 16 +++ 7 files changed, 240 insertions(+), 16 deletions(-) create mode 100644 README.md create mode 100644 src/compiler/Nub.Lang/Frontend/Parsing/UnaryExpressionNode.cs diff --git a/README.md b/README.md new file mode 100644 index 0000000..68ace08 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +```nub +import c + +global func main(argc: i64, argv: i64) { + printf("args: %d, starts at %p\n", argc, argv) +} +``` diff --git a/example/program.nub b/example/program.nub index b8a5f59..2198745 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,8 +1,6 @@ import c global func main(argc: i64, argv: i64) { - printf("args: %d, starts at %p\n", argc, argv) - - - printf("%s\n", list.text) + x = "TEST" + printf("%s\n", ^&x) } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 13abfbb..d5fe8c0 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -73,6 +73,7 @@ public class Generator } } case NubStructType: + case NubPointerType: { return "l"; } @@ -118,6 +119,10 @@ public class Generator { return ":" + nubCustomType.Name; } + case NubPointerType: + { + return "l"; + } default: { throw new NotImplementedException(); @@ -163,6 +168,10 @@ public class Generator { return ":" + nubCustomType.Name; } + case NubPointerType: + { + return "l"; + } default: { throw new NotImplementedException(); @@ -208,6 +217,7 @@ public class Generator { throw new Exception($"Cannot determine size of non-existent type {nubCustomType}"); } + return definition.Fields.Sum(f => QbeTypeSize(f.Type)); } default: @@ -490,6 +500,8 @@ public class Generator return GenerateLiteral(literal); case StructInitializerNode structInitializer: return GenerateStructInitializer(structInitializer); + case UnaryExpressionNode unaryExpression: + return GenerateUnaryExpression(unaryExpression); case StructFieldAccessorNode structMemberAccessor: return GenerateStructFieldAccessor(structMemberAccessor); default: @@ -1239,6 +1251,99 @@ public class Generator return $"%{structVar}"; } + private string GenerateUnaryExpression(UnaryExpressionNode unaryExpression) + { + var operand = GenerateExpression(unaryExpression.Operand); + var outputLabel = GenName(); + + switch (unaryExpression.Operator) + { + case UnaryExpressionOperator.Negate: + { + switch (unaryExpression.Operand.Type) + { + case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }: + _builder.AppendLine($" %{outputLabel} =l neg {operand}"); + return $"%{outputLabel}"; + case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }: + _builder.AppendLine($" %{outputLabel} =w neg {operand}"); + return $"%{outputLabel}"; + case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }: + _builder.AppendLine($" %{outputLabel} =d neg {operand}"); + return $"%{outputLabel}"; + case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }: + _builder.AppendLine($" %{outputLabel} =s neg {operand}"); + return $"%{outputLabel}"; + } + + break; + } + case UnaryExpressionOperator.Invert: + { + switch (unaryExpression.Operand.Type) + { + case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }: + _builder.AppendLine($" %{outputLabel} =w xor {operand}, 1"); + return $"%{outputLabel}"; + } + + break; + } + case UnaryExpressionOperator.AddressOf: + { + switch (unaryExpression.Operand.Type) + { + case NubPointerType: + case NubPrimitiveType { Kind: PrimitiveTypeKind.String }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.U64 }: + _builder.AppendLine($" %{outputLabel} =l alloc8 8"); + _builder.AppendLine($" storel {operand}, %{outputLabel}"); + return $"%{outputLabel}"; + case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.U32 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.I16 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.U16 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.I8 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.U8 }: + case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }: + _builder.AppendLine($" %{outputLabel} =l alloc8 4"); + _builder.AppendLine($" storew {operand}, %{outputLabel}"); + return $"%{outputLabel}"; + default: + throw new ArgumentOutOfRangeException(); + } + } + case UnaryExpressionOperator.Dereference: + { + // Handle dereference operator (assuming operand is a pointer) + // This would load the value from the address stored in the operand + if (unaryExpression.Type is NubPrimitiveType primitiveType) + { + _builder.AppendLine($" %{outputLabel} ={SQT(primitiveType)} load{SQT(primitiveType)} {operand}"); + return $"%{outputLabel}"; + } + + if (unaryExpression.Type is NubStructType structType) + { + // For struct types, we'd need to handle differently + // This is a simplified version + _builder.AppendLine($" %{outputLabel} =l copy {operand}"); + return $"%{outputLabel}"; + } + + break; + } + default: + { + throw new ArgumentOutOfRangeException(); + } + } + + throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); + } + private string GenerateStructFieldAccessor(StructFieldAccessorNode structFieldAccessor) { var structType = structFieldAccessor.Struct.Type; diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index 1ff5e83..c4d4bf4 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -359,6 +359,26 @@ public class Parser return new StructInitializerNode(type, initializers); } + case Symbol.Caret: + { + var expression = ParsePrimaryExpression(); + return new UnaryExpressionNode(UnaryExpressionOperator.Dereference, expression); + } + case Symbol.Ampersand: + { + var expression = ParsePrimaryExpression(); + return new UnaryExpressionNode(UnaryExpressionOperator.AddressOf, expression); + } + case Symbol.Minus: + { + var expression = ParsePrimaryExpression(); + return new UnaryExpressionNode(UnaryExpressionOperator.Negate, expression); + } + case Symbol.Bang: + { + var expression = ParsePrimaryExpression(); + return new UnaryExpressionNode(UnaryExpressionOperator.Invert, expression); + } default: { throw new Exception($"Unknown symbol: {symbolToken.Symbol}"); diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/UnaryExpressionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/UnaryExpressionNode.cs new file mode 100644 index 0000000..c0583a6 --- /dev/null +++ b/src/compiler/Nub.Lang/Frontend/Parsing/UnaryExpressionNode.cs @@ -0,0 +1,15 @@ +namespace Nub.Lang.Frontend.Parsing; + +public class UnaryExpressionNode(UnaryExpressionOperator @operator, ExpressionNode operand) : ExpressionNode +{ + public UnaryExpressionOperator Operator { get; } = @operator; + public ExpressionNode Operand { get; } = operand; +} + +public enum UnaryExpressionOperator +{ + AddressOf, + Dereference, + Negate, + Invert +} \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs b/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs index b5a59ba..107f613 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs @@ -4,7 +4,9 @@ namespace Nub.Lang.Frontend.Typing; public class TypeCheckingException : Exception { - public TypeCheckingException(string message) : base(message) { } + public TypeCheckingException(string message) : base(message) + { + } } public class TypeChecker @@ -25,7 +27,7 @@ public class TypeChecker { TypeCheckStructDef(structDef); } - + foreach (var funcDef in _definitions.OfType()) { TypeCheckFuncDef(funcDef); @@ -49,7 +51,7 @@ public class TypeChecker throw new TypeCheckingException("Default field initializer does not match the defined type"); } } - + fields[field.Name] = field.Type; } } @@ -111,7 +113,7 @@ public class TypeChecker private void TypeCheckVariableAssignment(VariableAssignmentNode varAssign) { var valueType = TypeCheckExpression(varAssign.Value); - + if (varAssign.ExplicitType.HasValue) { var explicitType = varAssign.ExplicitType.Value; @@ -119,6 +121,7 @@ public class TypeChecker { throw new TypeCheckingException($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{explicitType}'"); } + _variables[varAssign.Name] = explicitType; } else @@ -144,7 +147,7 @@ public class TypeChecker parameters = externFuncDef.Parameters; returnType = externFuncDef.ReturnType; } - + else { throw new TypeCheckingException($"Function '{funcCall.Name}' is not defined"); @@ -154,7 +157,7 @@ public class TypeChecker { throw new TypeCheckingException($"Function '{funcCall.Name}' has multiple variadic parameters"); } - + for (var i = 0; i < funcCall.Parameters.Count; i++) { var argType = TypeCheckExpression(funcCall.Parameters[i]); @@ -172,7 +175,7 @@ public class TypeChecker { throw new TypeCheckingException($"Function '{funcCall.Name}' does not take {funcCall.Parameters.Count} parameters"); } - + if (!AreTypesCompatible(argType, paramType)) { throw new TypeCheckingException($"Parameter {i} of function '{funcCall.Name}' expects type '{paramType}', but got '{argType}'"); @@ -217,12 +220,12 @@ public class TypeChecker if (returnNode.Value.HasValue) { var returnType = TypeCheckExpression(returnNode.Value.Value); - + if (_currentFunctionReturnType == null) { throw new TypeCheckingException("Cannot return a value from a function with no return type"); } - + if (!AreTypesCompatible(returnType, _currentFunctionReturnType)) { throw new TypeCheckingException($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'"); @@ -248,6 +251,7 @@ public class TypeChecker { throw new TypeCheckingException($"Variable '{identifier.Identifier}' is not defined"); } + resultType = varType; break; case BinaryExpressionNode binaryExpr: @@ -262,6 +266,9 @@ public class TypeChecker case StructInitializerNode structInit: resultType = TypeCheckStructInitializer(structInit); break; + case UnaryExpressionNode unaryExpression: + resultType = TypeCheckUnaryExpression(unaryExpression); + break; case StructFieldAccessorNode fieldAccess: resultType = TypeCheckStructFieldAccess(fieldAccess); break; @@ -296,6 +303,7 @@ public class TypeChecker { throw new TypeCheckingException($"Comparison operators require numeric operands, got '{leftType}' and '{rightType}'"); } + return NubPrimitiveType.Bool; case BinaryExpressionOperator.Plus: case BinaryExpressionOperator.Minus: @@ -305,6 +313,7 @@ public class TypeChecker { throw new TypeCheckingException($"Arithmetic operators require numeric operands, got '{leftType}' and '{rightType}'"); } + return leftType; default: throw new TypeCheckingException($"Unsupported binary operator: {binaryExpr.Operator}"); @@ -321,7 +330,7 @@ public class TypeChecker private NubType TypeCheckStructInitializer(StructInitializerNode structInit) { var initialized = new HashSet(); - + var structType = structInit.StructType; if (structType is not NubStructType customType) { @@ -347,10 +356,10 @@ public class TypeChecker { throw new TypeCheckingException($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'"); } - + initialized.Add(initializer.Key); } - + foreach (var field in definition.Fields.Where(f => f.Value.HasValue)) { initialized.Add(field.Name); @@ -367,6 +376,60 @@ public class TypeChecker return structType; } + private NubType TypeCheckUnaryExpression(UnaryExpressionNode unaryExpression) + { + var operandType = TypeCheckExpression(unaryExpression.Operand); + + switch (unaryExpression.Operator) + { + case UnaryExpressionOperator.AddressOf: + { + if (unaryExpression.Operand is not (IdentifierNode or StructFieldAccessorNode)) + { + throw new TypeCheckingException($"Cannot take the address of {unaryExpression.Operand.GetType().Name}"); + } + + return new NubPointerType(operandType); + } + case UnaryExpressionOperator.Dereference: + { + if (operandType is not NubPointerType nubPointerType) + { + throw new TypeCheckingException($"Cannot dereference a non-pointer type {operandType}"); + } + + return nubPointerType.BaseType; + } + case UnaryExpressionOperator.Negate: + { + if (operandType.Equals(NubPrimitiveType.I8) || + operandType.Equals(NubPrimitiveType.I16) || + operandType.Equals(NubPrimitiveType.I32) || + operandType.Equals(NubPrimitiveType.I64) || + operandType.Equals(NubPrimitiveType.F32) || + operandType.Equals(NubPrimitiveType.F64)) + { + return operandType; + } + + throw new TypeCheckingException($"Cannot negate non-numeric type {operandType}"); + } + case UnaryExpressionOperator.Invert: + { + if (!operandType.Equals(NubPrimitiveType.Bool)) + { + throw new TypeCheckingException($"Cannot invert non-boolean type {operandType}"); + } + + return operandType; + } + default: + { + throw new ArgumentOutOfRangeException(); + } + } + } + private NubType TypeCheckStructFieldAccess(StructFieldAccessorNode fieldAccess) { var structType = TypeCheckExpression(fieldAccess.Struct); diff --git a/src/compiler/Nub.Lang/NubType.cs b/src/compiler/Nub.Lang/NubType.cs index 75aa151..edd8f4d 100644 --- a/src/compiler/Nub.Lang/NubType.cs +++ b/src/compiler/Nub.Lang/NubType.cs @@ -28,6 +28,22 @@ public abstract class NubType public class NubStructType(string name) : NubType(name); +public class NubPointerType(NubType baseType) : NubType("^" + baseType) +{ + public NubType BaseType { get; } = baseType; + + public override bool Equals(object? obj) + { + if (obj is NubPointerType other) + { + return BaseType.Equals(other.BaseType); + } + return false; + } + + public override int GetHashCode() => BaseType.GetHashCode() * 31; +} + public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType(KindToString(kind)) { public PrimitiveTypeKind Kind { get; } = kind;