...
This commit is contained in:
@@ -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 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 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 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 ConvertStringToCStringNode(List<Token> Tokens, ExpressionNode Value) : RValueExpressionNode(Tokens, new NubCStringType());
|
||||
|
||||
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 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);
|
||||
|
||||
#endregion
|
||||
@@ -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<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)
|
||||
|
||||
@@ -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}]";
|
||||
|
||||
@@ -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<string>();
|
||||
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<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)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -609,10 +609,18 @@ public sealed class Parser
|
||||
|
||||
private ExpressionSyntax ParseArrayInitializer(int startIndex)
|
||||
{
|
||||
var capacity = ParseExpression();
|
||||
var values = new List<ExpressionSyntax>();
|
||||
while (!TryExpectSymbol(Symbol.CloseBracket))
|
||||
{
|
||||
values.Add(ParseExpression());
|
||||
if (!TryExpectSymbol(Symbol.Comma))
|
||||
{
|
||||
ExpectSymbol(Symbol.CloseBracket);
|
||||
var type = ParseType();
|
||||
return new ArrayInitializerSyntax(GetTokens(startIndex), capacity, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayInitializerSyntax(GetTokens(startIndex), values);
|
||||
}
|
||||
|
||||
private StructInitializerSyntax ParseStructInitializer(int startIndex)
|
||||
|
||||
@@ -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 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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user