From 7a3a461519c93a8c4a6934b6bff1b853f7bc030a Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 20 Oct 2025 20:15:29 +0200 Subject: [PATCH] ... --- bindings/generate.py | 4 ++ compiler/NubLang.CLI/Program.cs | 7 +-- compiler/NubLang/Ast/Node.cs | 6 +++ compiler/NubLang/Ast/NubType.cs | 64 ++++++------------------ compiler/NubLang/Ast/TypeChecker.cs | 30 ++++++++--- compiler/NubLang/Generation/Generator.cs | 47 +++++++++++++++-- compiler/NubLang/Syntax/Parser.cs | 29 ++++++++--- compiler/NubLang/Syntax/Syntax.cs | 6 ++- compiler/NubLang/Syntax/Token.cs | 1 + compiler/NubLang/Syntax/Tokenizer.cs | 1 + 10 files changed, 125 insertions(+), 70 deletions(-) diff --git a/bindings/generate.py b/bindings/generate.py index 4e2868b..27ec48b 100755 --- a/bindings/generate.py +++ b/bindings/generate.py @@ -45,6 +45,10 @@ def map_type(clang_type: Type): size = canonical.get_array_size() return f"[{size}]{map_type(element_type)}" + if kind == TypeKind.INCOMPLETEARRAY: + element_type = canonical.get_array_element_type() + return f"[?]{map_type(element_type)}" + if kind == TypeKind.FUNCTIONPROTO or kind == TypeKind.FUNCTIONNOPROTO: arg_types = [] diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index 9e835f7..18c4bd7 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -103,10 +103,11 @@ if (modules.TryGetValue("main", out var mainModule)) .text .globl _start _start: - mov rdi, rsp # Pass stack pointer to main (length + cstring pointers) + mov rdi, [rsp] # argc + mov rsi, [rsp + 8] # argv call {mainFunction.Prototype.ExternSymbol} - mov rdi, rax # Move return value into rdi - mov rax, 60 # syscall: exit + mov rdi, rax # Move return value into rdi + mov rax, 60 # syscall: exit syscall """; diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs index 04c63fe..0e9cb19 100644 --- a/compiler/NubLang/Ast/Node.cs +++ b/compiler/NubLang/Ast/Node.cs @@ -108,8 +108,14 @@ public record FuncIdentifierNode(List Tokens, NubType Type, string Module public record ArrayInitializerNode(List Tokens, NubType Type, ExpressionNode Capacity, NubType ElementType) : RValueExpressionNode(Tokens, Type); +public record ConstArrayInitializerNode(List Tokens, NubType Type, long Capacity, NubType ElementType) : RValueExpressionNode(Tokens, Type); + public record ArrayIndexAccessNode(List Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type); +public record ConstArrayIndexAccessNode(List Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type); + +public record SliceIndexAccessNode(List Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type); + public record AddressOfNode(List Tokens, NubType Type, LValueExpressionNode LValue) : RValueExpressionNode(Tokens, Type); public record StructFieldAccessNode(List Tokens, NubType Type, ExpressionNode Target, string Field) : LValueExpressionNode(Tokens, Type); diff --git a/compiler/NubLang/Ast/NubType.cs b/compiler/NubLang/Ast/NubType.cs index 6e33635..e99c160 100644 --- a/compiler/NubLang/Ast/NubType.cs +++ b/compiler/NubLang/Ast/NubType.cs @@ -5,9 +5,6 @@ namespace NubLang.Ast; public abstract class NubType : IEquatable { - public abstract bool IsValueType { get; } - public abstract bool IsScalar { get; } - public override bool Equals(object? obj) => obj is NubType other && Equals(other); public abstract bool Equals(NubType? other); @@ -20,9 +17,6 @@ public abstract class NubType : IEquatable public class NubVoidType : NubType { - public override bool IsValueType => false; - public override bool IsScalar => false; - public override string ToString() => "void"; public override bool Equals(NubType? other) => other is NubVoidType; public override int GetHashCode() => HashCode.Combine(typeof(NubVoidType)); @@ -30,9 +24,6 @@ public class NubVoidType : NubType public sealed class NubIntType(bool signed, int width) : NubType { - public override bool IsValueType => true; - public override bool IsScalar => true; - public bool Signed { get; } = signed; public int Width { get; } = width; @@ -43,9 +34,6 @@ public sealed class NubIntType(bool signed, int width) : NubType public sealed class NubFloatType(int width) : NubType { - public override bool IsValueType => true; - public override bool IsScalar => true; - public int Width { get; } = width; public override string ToString() => $"f{Width}"; @@ -55,9 +43,6 @@ public sealed class NubFloatType(int width) : NubType public class NubBoolType : NubType { - public override bool IsValueType => true; - public override bool IsScalar => true; - public override string ToString() => "bool"; public override bool Equals(NubType? other) => other is NubBoolType; public override int GetHashCode() => HashCode.Combine(typeof(NubBoolType)); @@ -65,9 +50,6 @@ public class NubBoolType : NubType public sealed class NubPointerType(NubType baseType) : NubType { - public override bool IsValueType => true; - public override bool IsScalar => true; - public NubType BaseType { get; } = baseType; public override string ToString() => "^" + BaseType; @@ -77,9 +59,6 @@ public sealed class NubPointerType(NubType baseType) : NubType public class NubFuncType(List parameters, NubType returnType) : NubType { - public override bool IsValueType => true; - public override bool IsScalar => true; - public List Parameters { get; } = parameters; public NubType ReturnType { get; } = returnType; @@ -102,9 +81,6 @@ public class NubFuncType(List parameters, NubType returnType) : NubType public class NubStructType(string module, string name, List fields) : NubType { - public override bool IsValueType => true; - public override bool IsScalar => false; - public string Module { get; } = module; public string Name { get; } = name; public List Fields { get; set; } = fields; @@ -121,43 +97,36 @@ public class NubStructFieldType(string name, NubType type, bool hasDefaultValue) public bool HasDefaultValue { get; } = hasDefaultValue; } -public class NubStructFuncType(string name, List parameters, NubType returnType) +public class NubSliceType(NubType elementType) : NubType { - public string Name { get; } = name; - public List Parameters { get; } = parameters; - public NubType ReturnType { get; } = returnType; -} - -public class NubArrayType(NubType elementType) : NubType -{ - public override bool IsValueType => false; - public override bool IsScalar => true; - public NubType ElementType { get; } = elementType; public override string ToString() => "[]" + ElementType; - public override bool Equals(NubType? other) => other is NubArrayType array && ElementType.Equals(array.ElementType); - public override int GetHashCode() => HashCode.Combine(typeof(NubArrayType), ElementType); + public override bool Equals(NubType? other) => other is NubSliceType slice && ElementType.Equals(slice.ElementType); + public override int GetHashCode() => HashCode.Combine(typeof(NubSliceType), ElementType); } -public class NubConstArrayType(NubType elementType, int size) : NubType +public class NubConstArrayType(NubType elementType, long size) : NubType { - public override bool IsValueType => false; - public override bool IsScalar => true; - public NubType ElementType { get; } = elementType; - public int Size { get; } = size; + public long Size { get; } = size; public override string ToString() => $"[{Size}]{ElementType}"; public override bool Equals(NubType? other) => other is NubConstArrayType array && ElementType.Equals(array.ElementType) && Size == array.Size; public override int GetHashCode() => HashCode.Combine(typeof(NubConstArrayType), ElementType, Size); } +public class NubArrayType(NubType elementType) : NubType +{ + public NubType ElementType { get; } = elementType; + + public override string ToString() => $"[?]{ElementType}"; + public override bool Equals(NubType? other) => other is NubArrayType array && ElementType.Equals(array.ElementType); + public override int GetHashCode() => HashCode.Combine(typeof(NubArrayType), ElementType); +} + public class NubCStringType : NubType { - public override bool IsValueType => false; - public override bool IsScalar => false; - public override string ToString() => "cstring"; public override bool Equals(NubType? other) => other is NubCStringType; public override int GetHashCode() => HashCode.Combine(typeof(NubCStringType)); @@ -165,9 +134,6 @@ public class NubCStringType : NubType public class NubStringType : NubType { - public override bool IsValueType => false; - public override bool IsScalar => false; - public override string ToString() => "string"; public override bool Equals(NubType? other) => other is NubStringType; public override int GetHashCode() => HashCode.Combine(typeof(NubStringType)); @@ -190,7 +156,7 @@ public static class NameMangler NubCStringType => "CS", NubStringType => "S", NubPointerType p => "P" + EncodeType(p.BaseType), - NubArrayType a => "A" + EncodeType(a.ElementType), + NubSliceType a => "A" + EncodeType(a.ElementType), NubFuncType fn => "FN(" + string.Join(",", fn.Parameters.Select(EncodeType)) + ")" + EncodeType(fn.ReturnType), NubStructType st => "ST(" + st.Module + "." + st.Name + ")", _ => throw new NotSupportedException($"Cannot encode type: {node}") diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs index 6c13ae2..9813b79 100644 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ b/compiler/NubLang/Ast/TypeChecker.cs @@ -248,6 +248,7 @@ public sealed class TypeChecker LocalIdentifierSyntax expression => CheckLocalIdentifier(expression), ModuleIdentifierSyntax expression => CheckModuleIdentifier(expression), BoolLiteralSyntax expression => CheckBoolLiteral(expression), + ConstArrayInitializerSyntax expression => CheckConstArrayInitializer(expression), StringLiteralSyntax expression => CheckStringLiteral(expression, expectedType), IntLiteralSyntax expression => CheckIntLiteral(expression, expectedType), FloatLiteralSyntax expression => CheckFloatLiteral(expression, expectedType), @@ -264,6 +265,11 @@ public sealed class TypeChecker return result; } + if (result.Type is NubConstArrayType && expectedType is NubArrayType) + { + return result; + } + if (result.Type is NubIntType sourceIntType && expectedType is NubIntType targetIntType) { if (sourceIntType.Signed == targetIntType.Signed && sourceIntType.Width < targetIntType.Width) @@ -283,6 +289,13 @@ public sealed class TypeChecker throw new TypeCheckerException(Diagnostic.Error($"Cannot convert {result.Type} to {expectedType}").At(node).Build()); } + private ConstArrayInitializerNode CheckConstArrayInitializer(ConstArrayInitializerSyntax expression) + { + var elementType = ResolveType(expression.ElementType); + var type = new NubConstArrayType(elementType, expression.Capacity); + return new ConstArrayInitializerNode(expression.Tokens, type, expression.Capacity, elementType); + } + private FloatToIntBuiltinNode CheckFloatToInt(FloatToIntBuiltinSyntax expression) { var value = CheckExpression(expression.Value); @@ -318,16 +331,18 @@ public sealed class TypeChecker return new AddressOfNode(expression.Tokens, type, lvalue); } - private ArrayIndexAccessNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression) + private ExpressionNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression) { var index = CheckExpression(expression.Index, new NubIntType(false, 64)); var target = CheckExpression(expression.Target); - if (target.Type is not NubArrayType arrayType) - { - throw new TypeCheckerException(Diagnostic.Error($"Cannot use array indexer on type {target.Type}").At(expression).Build()); - } - return new ArrayIndexAccessNode(expression.Tokens, arrayType.ElementType, target, index); + return target.Type switch + { + NubArrayType arrayType => new ArrayIndexAccessNode(expression.Tokens, arrayType.ElementType, target, index), + NubConstArrayType constArrayType => new ConstArrayIndexAccessNode(expression.Tokens, constArrayType.ElementType, target, index), + NubSliceType sliceType => new SliceIndexAccessNode(expression.Tokens, sliceType.ElementType, target, index), + _ => throw new TypeCheckerException(Diagnostic.Error($"Cannot use array indexer on type {target.Type}").At(expression).Build()) + }; } private ArrayInitializerNode CheckArrayInitializer(ArrayInitializerSyntax expression) @@ -808,12 +823,13 @@ public sealed class TypeChecker { return type switch { + ArrayTypeSyntax arr => new NubArrayType(ResolveType(arr.BaseType)), BoolTypeSyntax => new NubBoolType(), CStringTypeSyntax => new NubCStringType(), IntTypeSyntax i => new NubIntType(i.Signed, i.Width), FloatTypeSyntax f => new NubFloatType(f.Width), FuncTypeSyntax func => new NubFuncType(func.Parameters.Select(ResolveType).ToList(), ResolveType(func.ReturnType)), - ArrayTypeSyntax arr => new NubArrayType(ResolveType(arr.BaseType)), + SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType)), ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType), arr.Size), PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType)), StringTypeSyntax => new NubStringType(), diff --git a/compiler/NubLang/Generation/Generator.cs b/compiler/NubLang/Generation/Generator.cs index 309b7d1..ae15c21 100644 --- a/compiler/NubLang/Generation/Generator.cs +++ b/compiler/NubLang/Generation/Generator.cs @@ -22,7 +22,7 @@ public class Generator return $"_t{++_tmpIndex}"; } - private string MapNameWithType(NubType nubType, string name) + private static string MapNameWithType(NubType nubType, string name) { var prefix = ""; var postfix = ""; @@ -46,8 +46,9 @@ public class Generator { return type switch { + NubSliceType sliceType => throw new NotImplementedException(), + NubConstArrayType constArrayType => MapBaseType(constArrayType.ElementType), NubArrayType arrayType => MapBaseType(arrayType.ElementType), - NubConstArrayType arrayType => MapBaseType(arrayType.ElementType), NubBoolType => "bool", NubCStringType => "char", NubFloatType floatType => floatType.Width switch @@ -121,6 +122,22 @@ public class Generator _writer.WriteLine(); + // note(nub31): declare extern functions + foreach (var funcNode in _compilationUnit.Functions) + { + if (funcNode.Body != null) continue; + + EmitLine(funcNode.Tokens.FirstOrDefault()); + var parameters = funcNode.Prototype.Parameters.Count != 0 + ? string.Join(", ", funcNode.Prototype.Parameters.Select(x => MapNameWithType(x.Type, x.Name))) + : "void"; + + var name = FuncName(funcNode.Module, funcNode.Name, funcNode.Prototype.ExternSymbol); + _writer.WriteLine($"{MapNameWithType(funcNode.Prototype.ReturnType, name)}({parameters});"); + } + + _writer.WriteLine(); + // note(nub31): Normal functions foreach (var funcNode in _compilationUnit.Functions) { @@ -317,7 +334,7 @@ public class Generator private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode) { - if (variableDeclarationNode.Assignment != null) + if (variableDeclarationNode.Assignment is { Type: not NubArrayType and not NubConstArrayType }) { var value = EmitExpression(variableDeclarationNode.Assignment); _writer.WriteLine($"{MapNameWithType(variableDeclarationNode.Type, variableDeclarationNode.Name)} = {value};"); @@ -343,6 +360,8 @@ public class Generator ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode), BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode), BoolLiteralNode boolLiteralNode => EmitBoolLiteral(boolLiteralNode), + ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(constArrayIndexAccessNode), + ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode), ConvertFloatNode convertFloatNode => EmitConvertFloat(convertFloatNode), ConvertIntNode convertIntNode => EmitConvertInt(convertIntNode), CStringLiteralNode cStringLiteralNode => EmitCStringLiteral(cStringLiteralNode), @@ -357,6 +376,7 @@ public class Generator LValueIdentifierNode lValueIdentifierNode => EmitLValueIdentifier(lValueIdentifierNode), RValueIdentifierNode rValueIdentifierNode => EmitRValueIdentifier(rValueIdentifierNode), SizeBuiltinNode sizeBuiltinNode => EmitSizeBuiltin(sizeBuiltinNode), + SliceIndexAccessNode sliceIndexAccessNode => EmitSliceArrayIndexAccess(sliceIndexAccessNode), StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode), StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(structFieldAccessNode), StructInitializerNode structInitializerNode => EmitStructInitializer(structInitializerNode), @@ -416,6 +436,19 @@ public class Generator return boolLiteralNode.Value ? "true" : "false"; } + private string EmitConstArrayIndexAccess(ConstArrayIndexAccessNode constArrayIndexAccessNode) + { + var array = EmitExpression(constArrayIndexAccessNode.Target); + var index = EmitExpression(constArrayIndexAccessNode.Index); + // todo(nub31): We can emit bounds checking here + return $"{array}[{index}]"; + } + + private string EmitConstArrayInitializer(ConstArrayInitializerNode constArrayInitializerNode) + { + return string.Empty; + } + private string EmitConvertFloat(ConvertFloatNode convertFloatNode) { var value = EmitExpression(convertFloatNode.Value); @@ -544,6 +577,14 @@ public class Generator throw new NotImplementedException(); } + private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode) + { + var value = EmitExpression(sliceIndexAccessNode.Target); + var index = EmitExpression(sliceIndexAccessNode.Index); + // todo(nub31): We can emit bounds checking here + return $"{value}.data[{index}]"; + } + private string EmitStringLiteral(StringLiteralNode stringLiteralNode) { throw new NotImplementedException(); diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index bdc373d..130e332 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -530,12 +530,21 @@ public sealed class Parser return expr; } - private ArrayInitializerSyntax ParseArrayInitializer(int startIndex) + private ExpressionSyntax ParseArrayInitializer(int startIndex) { - var capacity = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - var type = ParseType(); - return new ArrayInitializerSyntax(GetTokens(startIndex), capacity, type); + if (TryExpectIntLiteral(out var intLiteral)) + { + ExpectSymbol(Symbol.CloseBracket); + var type = ParseType(); + return new ConstArrayInitializerSyntax(GetTokens(startIndex), Convert.ToInt64(intLiteral.Value, intLiteral.Base), type); + } + else + { + var capacity = ParseExpression(); + ExpectSymbol(Symbol.CloseBracket); + var type = ParseType(); + return new ArrayInitializerSyntax(GetTokens(startIndex), capacity, type); + } } private StructInitializerSyntax ParseStructInitializer(int startIndex) @@ -707,13 +716,19 @@ public sealed class Parser { ExpectSymbol(Symbol.CloseBracket); var baseType = ParseType(); - return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, int.Parse(intLiteral.Value)); + return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, Convert.ToInt64(intLiteral.Value, intLiteral.Base)); + } + else if (TryExpectSymbol(Symbol.QuestionMark)) + { + ExpectSymbol(Symbol.CloseBracket); + var baseType = ParseType(); + return new ArrayTypeSyntax(GetTokens(startIndex), baseType); } else { ExpectSymbol(Symbol.CloseBracket); var baseType = ParseType(); - return new ArrayTypeSyntax(GetTokens(startIndex), baseType); + return new SliceTypeSyntax(GetTokens(startIndex), baseType); } } diff --git a/compiler/NubLang/Syntax/Syntax.cs b/compiler/NubLang/Syntax/Syntax.cs index c532b9c..31977be 100644 --- a/compiler/NubLang/Syntax/Syntax.cs +++ b/compiler/NubLang/Syntax/Syntax.cs @@ -88,6 +88,8 @@ public record ModuleIdentifierSyntax(List Tokens, string Module, string N public record ArrayInitializerSyntax(List Tokens, ExpressionSyntax Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens); +public record ConstArrayInitializerSyntax(List Tokens, long Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens); + public record ArrayIndexAccessSyntax(List Tokens, ExpressionSyntax Target, ExpressionSyntax Index) : ExpressionSyntax(Tokens); public record AddressOfSyntax(List Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens); @@ -134,9 +136,11 @@ public record StringTypeSyntax(List Tokens) : TypeSyntax(Tokens); public record CStringTypeSyntax(List Tokens) : TypeSyntax(Tokens); +public record SliceTypeSyntax(List Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); + public record ArrayTypeSyntax(List Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); -public record ConstArrayTypeSyntax(List Tokens, TypeSyntax BaseType, int Size) : TypeSyntax(Tokens); +public record ConstArrayTypeSyntax(List Tokens, TypeSyntax BaseType, long Size) : TypeSyntax(Tokens); public record CustomTypeSyntax(List Tokens, string Module, string Name) : TypeSyntax(Tokens); diff --git a/compiler/NubLang/Syntax/Token.cs b/compiler/NubLang/Syntax/Token.cs index aa7aaa4..e99beaf 100644 --- a/compiler/NubLang/Syntax/Token.cs +++ b/compiler/NubLang/Syntax/Token.cs @@ -56,6 +56,7 @@ public enum Symbol Module, Import, At, + QuestionMark } public abstract record Token(SourceSpan Span); diff --git a/compiler/NubLang/Syntax/Tokenizer.cs b/compiler/NubLang/Syntax/Tokenizer.cs index fcd7786..f1fe662 100644 --- a/compiler/NubLang/Syntax/Tokenizer.cs +++ b/compiler/NubLang/Syntax/Tokenizer.cs @@ -56,6 +56,7 @@ public sealed class Tokenizer [['%']] = Symbol.Percent, [['|']] = Symbol.Pipe, [['@']] = Symbol.At, + [['?']] = Symbol.QuestionMark, }; private static readonly (char[] Pattern, Symbol Symbol)[] OrderedSymbols = Symbols