diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs index 6389cf1..4db39b8 100644 --- a/compiler/NubLang/Ast/Node.cs +++ b/compiler/NubLang/Ast/Node.cs @@ -122,10 +122,14 @@ public record VariableIdentifierNode(List Tokens, NubType Type, string Na public record FuncIdentifierNode(List Tokens, NubType Type, string Module, string Name, string? ExternSymbol) : RValueExpressionNode(Tokens, Type); -public record ArrayInitializerNode(List Tokens, NubType Type, ExpressionNode Capacity, NubType ElementType) : RValueExpressionNode(Tokens, Type); +public record ArrayInitializerNode(List Tokens, NubType Type, List Values) : RValueExpressionNode(Tokens, Type); + +public record ConstArrayInitializerNode(List Tokens, NubType Type, List Values) : 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); @@ -140,14 +144,14 @@ public record ConvertIntNode(List Tokens, ExpressionNode Value, int Start public record ConvertFloatNode(List Tokens, ExpressionNode Value, int StartWidth, int TargetWidth) : RValueExpressionNode(Tokens, new NubFloatType(TargetWidth)); -public record ConvertStringToCStringNode(List Tokens, ExpressionNode Value) : RValueExpressionNode(Tokens, new NubCStringType()); - public record ConvertCStringToStringNode(List Tokens, ExpressionNode Value) : RValueExpressionNode(Tokens, new NubStringType()); public record SizeBuiltinNode(List Tokens, NubType Type, NubType TargetType) : RValueExpressionNode(Tokens, Type); public record FloatToIntBuiltinNode(List Tokens, NubType Type, ExpressionNode Value, NubFloatType ValueType, NubIntType TargetType) : RValueExpressionNode(Tokens, Type); +public record ConstArrayToSliceNode(List Tokens, NubType Type, ExpressionNode Array) : RValueExpressionNode(Tokens, Type); + public record EnumReferenceIntermediateNode(List Tokens, string Module, string Name) : IntermediateExpression(Tokens); #endregion \ No newline at end of file diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs index 37aebd0..81c95be 100644 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ b/compiler/NubLang/Ast/TypeChecker.cs @@ -272,8 +272,8 @@ public sealed class TypeChecker default: { throw new TypeCheckerException(Diagnostic - .Error($"For statement not supported for type {target.Type}") - .At(forSyntax) + .Error($"Cannot iterate over type {target.Type} which does not have size information") + .At(forSyntax.Target) .Build()); } } @@ -292,7 +292,7 @@ public sealed class TypeChecker private ExpressionNode CheckExpression(ExpressionSyntax node, NubType? expectedType = null) { - return node switch + var result = node switch { AddressOfSyntax expression => CheckAddressOf(expression, expectedType), ArrayIndexAccessSyntax expression => CheckArrayIndexAccess(expression, expectedType), @@ -314,6 +314,40 @@ public sealed class TypeChecker FloatToIntBuiltinSyntax expression => CheckFloatToInt(expression, expectedType), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; + + switch (expectedType) + { + // note(nub31): Implicit conversion of const array to unsized array + case NubArrayType when result.Type is NubConstArrayType constArrayType: + { + return result with { Type = new NubArrayType(constArrayType.ElementType) }; + } + // note(nub31): Implicit conversion of const array to slice + case NubSliceType when result.Type is NubConstArrayType constArrayType: + { + return new ConstArrayToSliceNode(result.Tokens, new NubSliceType(constArrayType.ElementType), result); + } + // note(nub31): Implicit conversion of int to larger int + case NubIntType expectedIntType when result.Type is NubIntType intType && expectedIntType.Width > intType.Width: + { + return new ConvertIntNode(result.Tokens, result, intType.Width, expectedIntType.Width, intType.Signed, expectedIntType.Signed); + } + // note(nub31): Implicit conversion of f32 to f64 + case NubFloatType expectedFloatType when result.Type is NubFloatType floatType && expectedFloatType.Width > floatType.Width: + { + return new ConvertFloatNode(result.Tokens, result, floatType.Width, expectedFloatType.Width); + } + // note(nub31): Implicit conversion of cstring to string + case NubStringType when result.Type is NubCStringType: + { + return new ConvertCStringToStringNode(result.Tokens, result); + } + // note(nub31): No implicit conversion was possible or the result value was already the correct type + default: + { + return result; + } + } } // todo(nub31): Infer int type instead of explicit type syntax @@ -368,18 +402,61 @@ public sealed class TypeChecker 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()) }; } - // todo(nub31): Allow type inference instead of specifying type in syntax. Something like just [] - private ArrayInitializerNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? _) + private ExpressionNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType) { - var elementType = ResolveType(expression.ElementType); - var capacity = CheckExpression(expression.Capacity); - var type = new NubArrayType(elementType); - return new ArrayInitializerNode(expression.Tokens, type, capacity, elementType); + var elementType = expectedType switch + { + NubArrayType arrayType => arrayType.ElementType, + NubConstArrayType constArrayType => constArrayType.ElementType, + NubSliceType sliceType => sliceType.ElementType, + _ => null + }; + + if (elementType == null) + { + var firstValue = expression.Values.FirstOrDefault(); + if (firstValue != null) + { + elementType = CheckExpression(firstValue).Type; + } + } + + if (elementType == null) + { + throw new TypeCheckerException(Diagnostic + .Error("Unable to infer type of array initializer") + .At(expression) + .WithHelp("Provide a type for a variable assignment") + .Build()); + } + + var values = new List(); + foreach (var valueExpression in expression.Values) + { + var value = CheckExpression(valueExpression, elementType); + if (value.Type != elementType) + { + throw new TypeCheckerException(Diagnostic + .Error("Value in array initializer is not the same as the array type") + .At(valueExpression) + .Build()); + } + + values.Add(value); + } + + return expectedType switch + { + NubArrayType => new ArrayInitializerNode(expression.Tokens, new NubArrayType(elementType), values), + NubConstArrayType constArrayType => new ConstArrayInitializerNode(expression.Tokens, constArrayType, values), + _ => new ConstArrayInitializerNode(expression.Tokens, new NubConstArrayType(elementType, expression.Values.Count), values) + }; } private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType) diff --git a/compiler/NubLang/Generation/CType.cs b/compiler/NubLang/Generation/CType.cs index 58f5fdd..07adb7a 100644 --- a/compiler/NubLang/Generation/CType.cs +++ b/compiler/NubLang/Generation/CType.cs @@ -4,7 +4,7 @@ namespace NubLang.Generation; public static class CType { - public static string Create(NubType type, string? variableName = null) + public static string Create(NubType type, string? variableName = null, bool constArraysAsPointers = true) { return type switch { @@ -16,7 +16,7 @@ public static class CType NubPointerType ptr => CreatePointerType(ptr, variableName), NubSliceType => "slice" + (variableName != null ? $" {variableName}" : ""), NubStringType => "string" + (variableName != null ? $" {variableName}" : ""), - NubConstArrayType arr => CreateConstArrayType(arr, variableName), + NubConstArrayType arr => CreateConstArrayType(arr, variableName, constArraysAsPointers), NubArrayType arr => CreateArrayType(arr, variableName), NubFuncType fn => CreateFuncType(fn, variableName), NubStructType st => $"{st.Module}_{st.Name}" + (variableName != null ? $" {variableName}" : ""), @@ -54,9 +54,16 @@ public static class CType return baseType + "*" + (varName != null ? $" {varName}" : ""); } - private static string CreateConstArrayType(NubConstArrayType arr, string? varName) + private static string CreateConstArrayType(NubConstArrayType arr, string? varName, bool inStructDef) { var elementType = Create(arr.ElementType); + + // Treat const arrays as pointers unless in a struct definition + if (!inStructDef) + { + return elementType + "*" + (varName != null ? $" {varName}" : ""); + } + if (varName != null) { return $"{elementType} {varName}[{arr.Size}]"; diff --git a/compiler/NubLang/Generation/Generator.cs b/compiler/NubLang/Generation/Generator.cs index 1b1575f..f88b46b 100644 --- a/compiler/NubLang/Generation/Generator.cs +++ b/compiler/NubLang/Generation/Generator.cs @@ -43,7 +43,7 @@ public class Generator typedef struct { size_t length; - uint8_t *data; + char *data; } string; typedef struct @@ -62,7 +62,7 @@ public class Generator { foreach (var field in structType.Fields) { - _writer.WriteLine($"{CType.Create(field.Type, field.Name)};"); + _writer.WriteLine($"{CType.Create(field.Type, field.Name, constArraysAsPointers: false)};"); } } @@ -202,7 +202,7 @@ public class Generator _writer.WriteLine("{"); using (_writer.Indent()) { - _writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementName)} = {target}.data[{indexName}];"); + _writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementName)} = (({CType.Create(targetType.ElementType)}*){target}.data)[{indexName}];"); EmitBlock(forSliceNode.Body); } @@ -337,10 +337,12 @@ public class Generator ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode), BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode), BoolLiteralNode boolLiteralNode => boolLiteralNode.Value ? "true" : "false", + ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(constArrayIndexAccessNode), + ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode), + ConstArrayToSliceNode constArrayToSliceNode => EmitConstArrayToSlice(constArrayToSliceNode), ConvertCStringToStringNode convertCStringToStringNode => EmitConvertCStringToString(convertCStringToStringNode), ConvertFloatNode convertFloatNode => EmitConvertFloat(convertFloatNode), ConvertIntNode convertIntNode => EmitConvertInt(convertIntNode), - ConvertStringToCStringNode convertStringToCStringNode => EmitConvertStringToCString(convertStringToCStringNode), CStringLiteralNode cStringLiteralNode => $"\"{cStringLiteralNode.Value}\"", DereferenceNode dereferenceNode => EmitDereference(dereferenceNode), Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode), @@ -372,16 +374,22 @@ public class Generator private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode) { - var array = EmitExpression(arrayIndexAccessNode.Target); + var target = EmitExpression(arrayIndexAccessNode.Target); var index = EmitExpression(arrayIndexAccessNode.Index); - return $"{array}[{index}]"; + return $"{target}[{index}]"; } private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode) { - var capacity = EmitExpression(arrayInitializerNode.Capacity); + var values = new List(); + foreach (var value in arrayInitializerNode.Values) + { + values.Add(EmitExpression(value)); + } + + var arrayType = (NubArrayType)arrayInitializerNode.Type; var tmp = NewTmp(); - _writer.WriteLine($"{CType.Create(arrayInitializerNode.ElementType)} {tmp}[{capacity}];"); + _writer.WriteLine($"{CType.Create(arrayType.ElementType)} {tmp}[] = {{{string.Join(", ", values)}}};"); return tmp; } @@ -416,6 +424,35 @@ public class Generator return $"{left} {op} {right}"; } + private string EmitConstArrayIndexAccess(ConstArrayIndexAccessNode constArrayIndexAccessNode) + { + var target = EmitExpression(constArrayIndexAccessNode.Target); + var index = EmitExpression(constArrayIndexAccessNode.Index); + // todo(nub31): We can emit bounds checking here + return $"{target}[{index}]"; + } + + private string EmitConstArrayInitializer(ConstArrayInitializerNode arrayInitializerNode) + { + var values = new List(); + foreach (var value in arrayInitializerNode.Values) + { + values.Add(EmitExpression(value)); + } + + var arrayType = (NubConstArrayType)arrayInitializerNode.Type; + var tmp = NewTmp(); + _writer.WriteLine($"{CType.Create(arrayType.ElementType)} {tmp}[{arrayType.Size}] = {{{string.Join(", ", values)}}};"); + return tmp; + } + + private string EmitConstArrayToSlice(ConstArrayToSliceNode constArrayToSliceNode) + { + var arrayType = (NubConstArrayType)constArrayToSliceNode.Array.Type; + var array = EmitExpression(constArrayToSliceNode.Array); + return $"(slice){{.length = {arrayType.Size}, .data = (void*){array}}}"; + } + private string EmitConvertCStringToString(ConvertCStringToStringNode convertCStringToStringNode) { var value = EmitExpression(convertCStringToStringNode.Value); @@ -449,12 +486,6 @@ public class Generator return $"({targetType}){value}"; } - private string EmitConvertStringToCString(ConvertStringToCStringNode convertStringToCStringNode) - { - var value = EmitExpression(convertStringToCStringNode.Value); - return $"(char*){value}.data"; - } - private string EmitDereference(DereferenceNode dereferenceNode) { var pointer = EmitExpression(dereferenceNode.Target); @@ -512,10 +543,11 @@ public class Generator private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode) { - var value = EmitExpression(sliceIndexAccessNode.Target); + var targetType = (NubSliceType)sliceIndexAccessNode.Target.Type; + var target = EmitExpression(sliceIndexAccessNode.Target); var index = EmitExpression(sliceIndexAccessNode.Index); // todo(nub31): We can emit bounds checking here - return $"(({CType.Create(sliceIndexAccessNode.Target.Type)}){value}.data)[{index}]"; + return $"(({CType.Create(targetType.ElementType)}*){target}.data)[{index}]"; } private string EmitStringLiteral(StringLiteralNode stringLiteralNode) diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index a0f8f95..fba5bf9 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -609,10 +609,18 @@ public sealed class Parser private ExpressionSyntax ParseArrayInitializer(int startIndex) { - var capacity = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - var type = ParseType(); - return new ArrayInitializerSyntax(GetTokens(startIndex), capacity, type); + var values = new List(); + while (!TryExpectSymbol(Symbol.CloseBracket)) + { + values.Add(ParseExpression()); + if (!TryExpectSymbol(Symbol.Comma)) + { + ExpectSymbol(Symbol.CloseBracket); + break; + } + } + + return new ArrayInitializerSyntax(GetTokens(startIndex), values); } private StructInitializerSyntax ParseStructInitializer(int startIndex) diff --git a/compiler/NubLang/Syntax/Syntax.cs b/compiler/NubLang/Syntax/Syntax.cs index 8781644..1f4d48b 100644 --- a/compiler/NubLang/Syntax/Syntax.cs +++ b/compiler/NubLang/Syntax/Syntax.cs @@ -92,7 +92,7 @@ public record LocalIdentifierSyntax(List Tokens, string Name) : Expressio public record ModuleIdentifierSyntax(List Tokens, string Module, string Name) : ExpressionSyntax(Tokens); -public record ArrayInitializerSyntax(List Tokens, ExpressionSyntax Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens); +public record ArrayInitializerSyntax(List Tokens, List Values) : ExpressionSyntax(Tokens); public record ArrayIndexAccessSyntax(List Tokens, ExpressionSyntax Target, ExpressionSyntax Index) : ExpressionSyntax(Tokens); diff --git a/examples/hello-world/main.nub b/examples/hello-world/main.nub index d63504f..5cab1ab 100644 --- a/examples/hello-world/main.nub +++ b/examples/hello-world/main.nub @@ -2,16 +2,14 @@ module "main" extern "puts" func puts(text: cstring) -struct Test -{ - data: [23]cstring -} - extern "main" func main(argc: i64, argv: [?]cstring): i64 { - let names = struct Test {} + let names: []cstring = ["test", "test2"] - for name, i in names.data + puts(names[0]) + puts(names[1]) + + for name, i in names { puts(name) }