This commit is contained in:
nub31
2025-10-22 20:16:50 +02:00
parent 1fb88f2073
commit caacf3d402
7 changed files with 169 additions and 43 deletions

View File

@@ -122,10 +122,14 @@ public record VariableIdentifierNode(List<Token> Tokens, NubType Type, string Na
public record FuncIdentifierNode(List<Token> Tokens, NubType Type, string Module, string Name, string? ExternSymbol) : RValueExpressionNode(Tokens, Type); public record FuncIdentifierNode(List<Token> Tokens, NubType Type, string Module, string Name, string? ExternSymbol) : RValueExpressionNode(Tokens, Type);
public record ArrayInitializerNode(List<Token> Tokens, NubType Type, ExpressionNode Capacity, NubType ElementType) : RValueExpressionNode(Tokens, Type); public record ArrayInitializerNode(List<Token> Tokens, NubType Type, List<ExpressionNode> Values) : RValueExpressionNode(Tokens, Type);
public record ConstArrayInitializerNode(List<Token> Tokens, NubType Type, List<ExpressionNode> Values) : RValueExpressionNode(Tokens, Type);
public record ArrayIndexAccessNode(List<Token> Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type); public record ArrayIndexAccessNode(List<Token> Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type);
public record ConstArrayIndexAccessNode(List<Token> Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type);
public record SliceIndexAccessNode(List<Token> Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type); public record SliceIndexAccessNode(List<Token> Tokens, NubType Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Tokens, Type);
public record AddressOfNode(List<Token> Tokens, NubType Type, LValueExpressionNode LValue) : RValueExpressionNode(Tokens, Type); public record AddressOfNode(List<Token> Tokens, NubType Type, LValueExpressionNode LValue) : RValueExpressionNode(Tokens, Type);
@@ -140,14 +144,14 @@ public record ConvertIntNode(List<Token> Tokens, ExpressionNode Value, int Start
public record ConvertFloatNode(List<Token> Tokens, ExpressionNode Value, int StartWidth, int TargetWidth) : RValueExpressionNode(Tokens, new NubFloatType(TargetWidth)); public record ConvertFloatNode(List<Token> Tokens, ExpressionNode Value, int StartWidth, int TargetWidth) : RValueExpressionNode(Tokens, new NubFloatType(TargetWidth));
public record ConvertStringToCStringNode(List<Token> Tokens, ExpressionNode Value) : RValueExpressionNode(Tokens, new NubCStringType());
public record ConvertCStringToStringNode(List<Token> Tokens, ExpressionNode Value) : RValueExpressionNode(Tokens, new NubStringType()); public record ConvertCStringToStringNode(List<Token> Tokens, ExpressionNode Value) : RValueExpressionNode(Tokens, new NubStringType());
public record SizeBuiltinNode(List<Token> Tokens, NubType Type, NubType TargetType) : RValueExpressionNode(Tokens, Type); public record SizeBuiltinNode(List<Token> Tokens, NubType Type, NubType TargetType) : RValueExpressionNode(Tokens, Type);
public record FloatToIntBuiltinNode(List<Token> Tokens, NubType Type, ExpressionNode Value, NubFloatType ValueType, NubIntType TargetType) : RValueExpressionNode(Tokens, Type); public record FloatToIntBuiltinNode(List<Token> Tokens, NubType Type, ExpressionNode Value, NubFloatType ValueType, NubIntType TargetType) : RValueExpressionNode(Tokens, Type);
public record ConstArrayToSliceNode(List<Token> Tokens, NubType Type, ExpressionNode Array) : RValueExpressionNode(Tokens, Type);
public record EnumReferenceIntermediateNode(List<Token> Tokens, string Module, string Name) : IntermediateExpression(Tokens); public record EnumReferenceIntermediateNode(List<Token> Tokens, string Module, string Name) : IntermediateExpression(Tokens);
#endregion #endregion

View File

@@ -272,8 +272,8 @@ public sealed class TypeChecker
default: default:
{ {
throw new TypeCheckerException(Diagnostic throw new TypeCheckerException(Diagnostic
.Error($"For statement not supported for type {target.Type}") .Error($"Cannot iterate over type {target.Type} which does not have size information")
.At(forSyntax) .At(forSyntax.Target)
.Build()); .Build());
} }
} }
@@ -292,7 +292,7 @@ public sealed class TypeChecker
private ExpressionNode CheckExpression(ExpressionSyntax node, NubType? expectedType = null) private ExpressionNode CheckExpression(ExpressionSyntax node, NubType? expectedType = null)
{ {
return node switch var result = node switch
{ {
AddressOfSyntax expression => CheckAddressOf(expression, expectedType), AddressOfSyntax expression => CheckAddressOf(expression, expectedType),
ArrayIndexAccessSyntax expression => CheckArrayIndexAccess(expression, expectedType), ArrayIndexAccessSyntax expression => CheckArrayIndexAccess(expression, expectedType),
@@ -314,6 +314,40 @@ public sealed class TypeChecker
FloatToIntBuiltinSyntax expression => CheckFloatToInt(expression, expectedType), FloatToIntBuiltinSyntax expression => CheckFloatToInt(expression, expectedType),
_ => throw new ArgumentOutOfRangeException(nameof(node)) _ => 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 // todo(nub31): Infer int type instead of explicit type syntax
@@ -368,18 +402,61 @@ public sealed class TypeChecker
return target.Type switch return target.Type switch
{ {
NubArrayType arrayType => new ArrayIndexAccessNode(expression.Tokens, arrayType.ElementType, target, index), 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), 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()) _ => 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 ExpressionNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType)
private ArrayInitializerNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? _)
{ {
var elementType = ResolveType(expression.ElementType); var elementType = expectedType switch
var capacity = CheckExpression(expression.Capacity); {
var type = new NubArrayType(elementType); NubArrayType arrayType => arrayType.ElementType,
return new ArrayInitializerNode(expression.Tokens, type, capacity, 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<ExpressionNode>();
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) private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType)

View File

@@ -4,7 +4,7 @@ namespace NubLang.Generation;
public static class CType 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 return type switch
{ {
@@ -16,7 +16,7 @@ public static class CType
NubPointerType ptr => CreatePointerType(ptr, variableName), NubPointerType ptr => CreatePointerType(ptr, variableName),
NubSliceType => "slice" + (variableName != null ? $" {variableName}" : ""), NubSliceType => "slice" + (variableName != null ? $" {variableName}" : ""),
NubStringType => "string" + (variableName != null ? $" {variableName}" : ""), NubStringType => "string" + (variableName != null ? $" {variableName}" : ""),
NubConstArrayType arr => CreateConstArrayType(arr, variableName), NubConstArrayType arr => CreateConstArrayType(arr, variableName, constArraysAsPointers),
NubArrayType arr => CreateArrayType(arr, variableName), NubArrayType arr => CreateArrayType(arr, variableName),
NubFuncType fn => CreateFuncType(fn, variableName), NubFuncType fn => CreateFuncType(fn, variableName),
NubStructType st => $"{st.Module}_{st.Name}" + (variableName != null ? $" {variableName}" : ""), NubStructType st => $"{st.Module}_{st.Name}" + (variableName != null ? $" {variableName}" : ""),
@@ -54,9 +54,16 @@ public static class CType
return baseType + "*" + (varName != null ? $" {varName}" : ""); 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); 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) if (varName != null)
{ {
return $"{elementType} {varName}[{arr.Size}]"; return $"{elementType} {varName}[{arr.Size}]";

View File

@@ -43,7 +43,7 @@ public class Generator
typedef struct typedef struct
{ {
size_t length; size_t length;
uint8_t *data; char *data;
} string; } string;
typedef struct typedef struct
@@ -62,7 +62,7 @@ public class Generator
{ {
foreach (var field in structType.Fields) 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("{"); _writer.WriteLine("{");
using (_writer.Indent()) 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); EmitBlock(forSliceNode.Body);
} }
@@ -337,10 +337,12 @@ public class Generator
ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode), ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode),
BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode), BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode),
BoolLiteralNode boolLiteralNode => boolLiteralNode.Value ? "true" : "false", BoolLiteralNode boolLiteralNode => boolLiteralNode.Value ? "true" : "false",
ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(constArrayIndexAccessNode),
ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode),
ConstArrayToSliceNode constArrayToSliceNode => EmitConstArrayToSlice(constArrayToSliceNode),
ConvertCStringToStringNode convertCStringToStringNode => EmitConvertCStringToString(convertCStringToStringNode), ConvertCStringToStringNode convertCStringToStringNode => EmitConvertCStringToString(convertCStringToStringNode),
ConvertFloatNode convertFloatNode => EmitConvertFloat(convertFloatNode), ConvertFloatNode convertFloatNode => EmitConvertFloat(convertFloatNode),
ConvertIntNode convertIntNode => EmitConvertInt(convertIntNode), ConvertIntNode convertIntNode => EmitConvertInt(convertIntNode),
ConvertStringToCStringNode convertStringToCStringNode => EmitConvertStringToCString(convertStringToCStringNode),
CStringLiteralNode cStringLiteralNode => $"\"{cStringLiteralNode.Value}\"", CStringLiteralNode cStringLiteralNode => $"\"{cStringLiteralNode.Value}\"",
DereferenceNode dereferenceNode => EmitDereference(dereferenceNode), DereferenceNode dereferenceNode => EmitDereference(dereferenceNode),
Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode), Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode),
@@ -372,16 +374,22 @@ public class Generator
private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode) private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode)
{ {
var array = EmitExpression(arrayIndexAccessNode.Target); var target = EmitExpression(arrayIndexAccessNode.Target);
var index = EmitExpression(arrayIndexAccessNode.Index); var index = EmitExpression(arrayIndexAccessNode.Index);
return $"{array}[{index}]"; return $"{target}[{index}]";
} }
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode) private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode)
{ {
var capacity = EmitExpression(arrayInitializerNode.Capacity); var values = new List<string>();
foreach (var value in arrayInitializerNode.Values)
{
values.Add(EmitExpression(value));
}
var arrayType = (NubArrayType)arrayInitializerNode.Type;
var tmp = NewTmp(); var tmp = NewTmp();
_writer.WriteLine($"{CType.Create(arrayInitializerNode.ElementType)} {tmp}[{capacity}];"); _writer.WriteLine($"{CType.Create(arrayType.ElementType)} {tmp}[] = {{{string.Join(", ", values)}}};");
return tmp; return tmp;
} }
@@ -416,6 +424,35 @@ public class Generator
return $"{left} {op} {right}"; 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<string>();
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) private string EmitConvertCStringToString(ConvertCStringToStringNode convertCStringToStringNode)
{ {
var value = EmitExpression(convertCStringToStringNode.Value); var value = EmitExpression(convertCStringToStringNode.Value);
@@ -449,12 +486,6 @@ public class Generator
return $"({targetType}){value}"; return $"({targetType}){value}";
} }
private string EmitConvertStringToCString(ConvertStringToCStringNode convertStringToCStringNode)
{
var value = EmitExpression(convertStringToCStringNode.Value);
return $"(char*){value}.data";
}
private string EmitDereference(DereferenceNode dereferenceNode) private string EmitDereference(DereferenceNode dereferenceNode)
{ {
var pointer = EmitExpression(dereferenceNode.Target); var pointer = EmitExpression(dereferenceNode.Target);
@@ -512,10 +543,11 @@ public class Generator
private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode) 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); var index = EmitExpression(sliceIndexAccessNode.Index);
// todo(nub31): We can emit bounds checking here // 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) private string EmitStringLiteral(StringLiteralNode stringLiteralNode)

View File

@@ -609,10 +609,18 @@ public sealed class Parser
private ExpressionSyntax ParseArrayInitializer(int startIndex) private ExpressionSyntax ParseArrayInitializer(int startIndex)
{ {
var capacity = ParseExpression(); var values = new List<ExpressionSyntax>();
ExpectSymbol(Symbol.CloseBracket); while (!TryExpectSymbol(Symbol.CloseBracket))
var type = ParseType(); {
return new ArrayInitializerSyntax(GetTokens(startIndex), capacity, type); values.Add(ParseExpression());
if (!TryExpectSymbol(Symbol.Comma))
{
ExpectSymbol(Symbol.CloseBracket);
break;
}
}
return new ArrayInitializerSyntax(GetTokens(startIndex), values);
} }
private StructInitializerSyntax ParseStructInitializer(int startIndex) private StructInitializerSyntax ParseStructInitializer(int startIndex)

View File

@@ -92,7 +92,7 @@ public record LocalIdentifierSyntax(List<Token> Tokens, string Name) : Expressio
public record ModuleIdentifierSyntax(List<Token> Tokens, string Module, string Name) : ExpressionSyntax(Tokens); public record ModuleIdentifierSyntax(List<Token> Tokens, string Module, string Name) : ExpressionSyntax(Tokens);
public record ArrayInitializerSyntax(List<Token> Tokens, ExpressionSyntax Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens); public record ArrayInitializerSyntax(List<Token> Tokens, List<ExpressionSyntax> Values) : ExpressionSyntax(Tokens);
public record ArrayIndexAccessSyntax(List<Token> Tokens, ExpressionSyntax Target, ExpressionSyntax Index) : ExpressionSyntax(Tokens); public record ArrayIndexAccessSyntax(List<Token> Tokens, ExpressionSyntax Target, ExpressionSyntax Index) : ExpressionSyntax(Tokens);

View File

@@ -2,16 +2,14 @@ module "main"
extern "puts" func puts(text: cstring) extern "puts" func puts(text: cstring)
struct Test
{
data: [23]cstring
}
extern "main" func main(argc: i64, argv: [?]cstring): i64 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) puts(name)
} }