Array type works with args

This commit is contained in:
nub31
2025-05-22 19:31:11 +02:00
parent 8b9f8db26e
commit eba128463d
9 changed files with 250 additions and 105 deletions

View File

@@ -1,23 +1,10 @@
import c import c
struct Test { global func main(args: []string) {
name: ^string i = 0
} printf("%d\n", args.count)
while i < args.count {
struct Test2 { printf("%s\n", args[i])
parent: ^Test i = i + 1
}
global func main(argc: i64, argv: i64) {
name = "Oliver"
parent = new Test {
name = &name
} }
test = new Test2 {
parent = &parent
}
printf("%s\n", <string>(test.parent^.name^))
} }

View File

@@ -74,6 +74,7 @@ public class Generator
} }
case NubStructType: case NubStructType:
case NubPointerType: case NubPointerType:
case NubArrayType:
{ {
return "l"; return "l";
} }
@@ -120,6 +121,7 @@ public class Generator
return ":" + nubCustomType.Name; return ":" + nubCustomType.Name;
} }
case NubPointerType: case NubPointerType:
case NubArrayType:
{ {
return "l"; return "l";
} }
@@ -169,6 +171,7 @@ public class Generator
return ":" + nubCustomType.Name; return ":" + nubCustomType.Name;
} }
case NubPointerType: case NubPointerType:
case NubArrayType:
{ {
return "l"; return "l";
} }
@@ -221,6 +224,7 @@ public class Generator
return definition.Fields.Sum(f => QbeTypeSize(f.Type)); return definition.Fields.Sum(f => QbeTypeSize(f.Type));
} }
case NubPointerType: case NubPointerType:
case NubArrayType:
{ {
return 8; return 8;
} }
@@ -474,6 +478,13 @@ public class Generator
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
{ {
var result = GenerateExpression(variableAssignment.Value); var result = GenerateExpression(variableAssignment.Value);
if (_variables.TryGetValue(variableAssignment.Name, out var existingVariable))
{
_builder.AppendLine($" storel {result}, {existingVariable.Pointer}");
}
else
{
var pointerLabel = GenName(); var pointerLabel = GenName();
_builder.AppendLine($" %{pointerLabel} ={SQT(variableAssignment.Value.Type)} alloc8 {QbeTypeSize(variableAssignment.Value.Type)}"); _builder.AppendLine($" %{pointerLabel} ={SQT(variableAssignment.Value.Type)} alloc8 {QbeTypeSize(variableAssignment.Value.Type)}");
_builder.AppendLine($" storel {result}, %{pointerLabel}"); _builder.AppendLine($" storel {result}, %{pointerLabel}");
@@ -484,6 +495,7 @@ public class Generator
Type = variableAssignment.Value.Type Type = variableAssignment.Value.Type
}; };
} }
}
private void GenerateWhile(WhileNode whileStatement) private void GenerateWhile(WhileNode whileStatement)
{ {
@@ -511,6 +523,7 @@ public class Generator
return expression switch return expression switch
{ {
AddressOfNode addressOf => GenerateAddressOf(addressOf), AddressOfNode addressOf => GenerateAddressOf(addressOf),
ArrayIndexNode arrayIndex => GenerateArrayIndex(arrayIndex),
BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression), BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression),
CastNode cast => GenerateCast(cast), CastNode cast => GenerateCast(cast),
DereferenceNode dereference => GenerateDereference(dereference), DereferenceNode dereference => GenerateDereference(dereference),
@@ -519,11 +532,29 @@ public class Generator
LiteralNode literal => GenerateLiteral(literal), LiteralNode literal => GenerateLiteral(literal),
StructInitializerNode structInitializer => GenerateStructInitializer(structInitializer), StructInitializerNode structInitializer => GenerateStructInitializer(structInitializer),
UnaryExpressionNode unaryExpression => GenerateUnaryExpression(unaryExpression), UnaryExpressionNode unaryExpression => GenerateUnaryExpression(unaryExpression),
StructFieldAccessorNode structMemberAccessor => GenerateStructFieldAccessor(structMemberAccessor), MemberAccessNode memberAccess => GenerateMemberAccess(memberAccess),
_ => throw new ArgumentOutOfRangeException(nameof(expression)) _ => throw new ArgumentOutOfRangeException(nameof(expression))
}; };
} }
private string GenerateArrayIndex(ArrayIndexNode arrayIndex)
{
var array = GenerateExpression(arrayIndex.Expression);
var index = GenerateExpression(arrayIndex.Index);
var arrayBaseType = ((NubArrayType)arrayIndex.Expression.Type).BaseType;
var firstItem = GenName();
_builder.AppendLine($" %{firstItem} =l add {array}, 8");
var adjustedIndex = GenName();
_builder.AppendLine($" %{adjustedIndex} =l mul {index}, {QbeTypeSize(arrayBaseType)}");
var indexLabel = GenName();
_builder.AppendLine($" %{indexLabel} ={SQT(arrayIndex.Type)} add %{firstItem}, %{adjustedIndex}");
var outputLabel = GenName();
_builder.AppendLine($" %{outputLabel} =l load{SQT(arrayBaseType)} %{indexLabel}");
return $"%{outputLabel}";
}
private string GenerateDereference(DereferenceNode dereference) private string GenerateDereference(DereferenceNode dereference)
{ {
var result = GenerateExpression(dereference.Expression); var result = GenerateExpression(dereference.Expression);
@@ -1354,9 +1385,25 @@ public class Generator
throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported");
} }
private string GenerateStructFieldAccessor(StructFieldAccessorNode structFieldAccessor) private string GenerateMemberAccess(MemberAccessNode memberAccess)
{
var expression = GenerateExpression(memberAccess.Expression);
switch (memberAccess.Expression.Type)
{
case NubArrayType:
{
if (memberAccess.Member == "count")
{
var outputLabel = GenName();
_builder.AppendLine($" %{outputLabel} =l loadl {expression}");
return $"%{outputLabel}";
}
break;
}
case NubStructType structType:
{ {
var structType = structFieldAccessor.Struct.Type;
var structDefinition = _definitions var structDefinition = _definitions
.OfType<StructDefinitionNode>() .OfType<StructDefinitionNode>()
.FirstOrDefault(s => s.Name == structType.Name); .FirstOrDefault(s => s.Name == structType.Name);
@@ -1366,12 +1413,10 @@ public class Generator
throw new Exception($"Struct {structType.Name} is not defined"); throw new Exception($"Struct {structType.Name} is not defined");
} }
var @struct = GenerateExpression(structFieldAccessor.Struct);
var fieldIndex = -1; var fieldIndex = -1;
for (var i = 0; i < structDefinition.Fields.Count; i++) for (var i = 0; i < structDefinition.Fields.Count; i++)
{ {
if (structDefinition.Fields[i].Name == structFieldAccessor.Field) if (structDefinition.Fields[i].Name == memberAccess.Member)
{ {
fieldIndex = i; fieldIndex = i;
break; break;
@@ -1380,17 +1425,21 @@ public class Generator
if (fieldIndex == -1) if (fieldIndex == -1)
{ {
throw new Exception($"Field {structFieldAccessor.Field} is not defined in struct {structType.Name}"); throw new Exception($"Field {memberAccess.Member} is not defined in struct {structType.Name}");
} }
var offsetLabel = GenName(); var offsetLabel = GenName();
_builder.AppendLine($" %{offsetLabel} =l add {@struct}, {fieldIndex * QbeTypeSize(structFieldAccessor.Type)}"); _builder.AppendLine($" %{offsetLabel} =l add {expression}, {fieldIndex * QbeTypeSize(memberAccess.Type)}");
var outputLabel = GenName(); var outputLabel = GenName();
_builder.AppendLine($" %{outputLabel} ={SQT(structFieldAccessor.Type)} load{SQT(structFieldAccessor.Type)} %{offsetLabel}"); _builder.AppendLine($" %{outputLabel} ={SQT(memberAccess.Type)} load{SQT(memberAccess.Type)} %{offsetLabel}");
return $"%{outputLabel}"; return $"%{outputLabel}";
} }
}
throw new ArgumentOutOfRangeException(nameof(memberAccess.Expression.Type));
}
private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall) private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall)
{ {

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class ArrayIndexNode(ExpressionNode expression, ExpressionNode index) : ExpressionNode
{
public ExpressionNode Expression { get; } = expression;
public ExpressionNode Index { get; } = index;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class MemberAccessNode(ExpressionNode expression, string member) : ExpressionNode
{
public ExpressionNode Expression { get; } = expression;
public string Member { get; } = member;
}

View File

@@ -427,17 +427,17 @@ public class Parser
if (TryExpectSymbol(Symbol.Period)) if (TryExpectSymbol(Symbol.Period))
{ {
var structMember = ExpectIdentifier().Value; var structMember = ExpectIdentifier().Value;
expr = new StructFieldAccessorNode(expr, structMember); expr = new MemberAccessNode(expr, structMember);
continue; continue;
} }
// if (TryExpectSymbol(Symbol.OpenBracket)) if (TryExpectSymbol(Symbol.OpenBracket))
// { {
// var index = ParseExpression(); var index = ParseExpression();
// ExpectSymbol(Symbol.CloseBracket); ExpectSymbol(Symbol.CloseBracket);
// expr = new ArrayIndexNode(expr, index); expr = new ArrayIndexNode(expr, index);
// continue; continue;
// } }
break; break;
} }
@@ -459,10 +459,25 @@ public class Parser
private NubType ParseType() private NubType ParseType()
{ {
var pointer = TryExpectSymbol(Symbol.Caret); if (TryExpectIdentifier(out var name))
var name = ExpectIdentifier().Value; {
var type = NubType.Parse(name); return NubType.Parse(name);
return pointer ? new NubPointerType(type) : type; }
if (TryExpectSymbol(Symbol.Caret))
{
var baseType = ParseType();
return new NubPointerType(baseType);
}
if (TryExpectSymbol(Symbol.OpenBracket))
{
ExpectSymbol(Symbol.CloseBracket);
var baseType = ParseType();
return new NubArrayType(baseType);
}
throw new Exception($"Unexpected token {Peek()} when parsing type");
} }
private Token ExpectToken() private Token ExpectToken()
@@ -517,6 +532,19 @@ public class Parser
return false; return false;
} }
private bool TryExpectIdentifier([NotNullWhen(true)] out string? identifier)
{
if (Peek() is { HasValue: true, Value: IdentifierToken identifierToken })
{
identifier = identifierToken.Value;
Next();
return true;
}
identifier = null;
return false;
}
private IdentifierToken ExpectIdentifier() private IdentifierToken ExpectIdentifier()
{ {
var token = ExpectToken(); var token = ExpectToken();

View File

@@ -1,7 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructFieldAccessorNode(ExpressionNode @struct, string field) : ExpressionNode
{
public ExpressionNode Struct { get; } = @struct;
public string Field { get; } = field;
}

View File

@@ -253,6 +253,7 @@ public class TypeChecker
var resultType = expression switch var resultType = expression switch
{ {
AddressOfNode addressOf => TypeCheckAddressOf(addressOf), AddressOfNode addressOf => TypeCheckAddressOf(addressOf),
ArrayIndexNode arrayIndex => TypeCheckArrayIndex(arrayIndex),
LiteralNode literal => literal.LiteralType, LiteralNode literal => literal.LiteralType,
IdentifierNode identifier => TypeCheckIdentifier(identifier), IdentifierNode identifier => TypeCheckIdentifier(identifier),
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr), BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
@@ -261,7 +262,7 @@ public class TypeChecker
FuncCallExpressionNode funcCallExpr => TypeCheckFuncCall(funcCallExpr.FuncCall), FuncCallExpressionNode funcCallExpr => TypeCheckFuncCall(funcCallExpr.FuncCall),
StructInitializerNode structInit => TypeCheckStructInitializer(structInit), StructInitializerNode structInit => TypeCheckStructInitializer(structInit),
UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression), UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression),
StructFieldAccessorNode fieldAccess => TypeCheckStructFieldAccess(fieldAccess), MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess),
_ => throw new TypeCheckingException($"Unsupported expression type: {expression.GetType().Name}") _ => throw new TypeCheckingException($"Unsupported expression type: {expression.GetType().Name}")
}; };
@@ -269,6 +270,23 @@ public class TypeChecker
return resultType; return resultType;
} }
private NubType TypeCheckArrayIndex(ArrayIndexNode arrayIndex)
{
var expressionType = TypeCheckExpression(arrayIndex.Expression);
if (expressionType is not NubArrayType arrayType)
{
throw new TypeCheckingException($"Annot access index of non-array type {expressionType}");
}
var indexType = TypeCheckExpression(arrayIndex.Index);
if (!IsInteger(indexType))
{
throw new TypeCheckingException("Array index type must be an integer");
}
return arrayType.BaseType;
}
private NubType TypeCheckIdentifier(IdentifierNode identifier) private NubType TypeCheckIdentifier(IdentifierNode identifier)
{ {
if (!_variables.TryGetValue(identifier.Identifier, out var varType)) if (!_variables.TryGetValue(identifier.Identifier, out var varType))
@@ -282,7 +300,7 @@ public class TypeChecker
private NubType TypeCheckAddressOf(AddressOfNode addressOf) private NubType TypeCheckAddressOf(AddressOfNode addressOf)
{ {
TypeCheckExpression(addressOf.Expression); TypeCheckExpression(addressOf.Expression);
if (addressOf.Expression is not (IdentifierNode or StructFieldAccessorNode)) if (addressOf.Expression is not (IdentifierNode or MemberAccessNode))
{ {
throw new TypeCheckingException($"Cannot take the address of {addressOf.Expression.Type}"); throw new TypeCheckingException($"Cannot take the address of {addressOf.Expression.Type}");
} }
@@ -422,28 +440,40 @@ public class TypeChecker
} }
} }
private NubType TypeCheckStructFieldAccess(StructFieldAccessorNode fieldAccess) private NubType TypeCheckMemberAccess(MemberAccessNode memberAccess)
{ {
var structType = TypeCheckExpression(fieldAccess.Struct); var expressionType = TypeCheckExpression(memberAccess.Expression);
if (structType is not NubStructType customType) switch (expressionType)
{ {
throw new TypeCheckingException($"Cannot access field '{fieldAccess.Field}' on non-struct type '{structType}'"); case NubArrayType:
{
if (memberAccess.Member == "count")
{
return NubPrimitiveType.I64;
} }
var definition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == customType.Name); break;
}
case NubStructType structType:
{
var definition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == structType.Name);
if (definition == null) if (definition == null)
{ {
throw new TypeCheckingException($"Struct type '{customType.Name}' is not defined"); throw new TypeCheckingException($"Struct type '{structType.Name}' is not defined");
} }
var field = definition.Fields.FirstOrDefault(f => f.Name == fieldAccess.Field); var field = definition.Fields.FirstOrDefault(f => f.Name == memberAccess.Member);
if (field == null) if (field == null)
{ {
throw new TypeCheckingException($"Field '{fieldAccess.Field}' does not exist in struct '{customType.Name}'"); throw new TypeCheckingException($"Field '{memberAccess.Member}' does not exist in struct '{structType.Name}'");
} }
return field.Type; return field.Type;
} }
}
throw new TypeCheckingException($"Cannot access member '{memberAccess.Member}' on type '{expressionType}'");
}
private static bool AreTypesCompatible(NubType sourceType, NubType targetType) private static bool AreTypesCompatible(NubType sourceType, NubType targetType)
{ {
@@ -474,4 +504,27 @@ public class TypeChecker
return false; return false;
} }
} }
private static bool IsInteger(NubType type)
{
if (type is not NubPrimitiveType primitiveType)
{
return false;
}
switch (primitiveType.Kind)
{
case PrimitiveTypeKind.I8:
case PrimitiveTypeKind.I16:
case PrimitiveTypeKind.I32:
case PrimitiveTypeKind.I64:
case PrimitiveTypeKind.U8:
case PrimitiveTypeKind.U16:
case PrimitiveTypeKind.U32:
case PrimitiveTypeKind.U64:
return true;
default:
return false;
}
}
} }

View File

@@ -41,7 +41,29 @@ public class NubPointerType(NubType baseType) : NubType("^" + baseType)
return false; return false;
} }
public override int GetHashCode() => BaseType.GetHashCode() * 31; public override int GetHashCode()
{
return HashCode.Combine(base.GetHashCode(), BaseType);
}
}
public class NubArrayType(NubType baseType) : NubType("[]" + baseType)
{
public NubType BaseType { get; } = baseType;
public override bool Equals(object? obj)
{
if (obj is NubArrayType other)
{
return BaseType.Equals(other.BaseType);
}
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(base.GetHashCode(), BaseType);
}
} }
public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType(KindToString(kind)) public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType(KindToString(kind))

View File

@@ -3,11 +3,9 @@ extern main
section .text section .text
_start: _start:
; Extract argc and argv from the stack ; The args already match our array structure, so we pass the result directly
mov rdi, [rsp] ; rdi = argc mov rdi, rsp
lea rsi, [rsp + 8] ; rsi = argv (pointer to array of strings)
; Call main(argc, argv)
call main ; main returns int in rax call main ; main returns int in rax
; Exit with main's return value ; Exit with main's return value
@@ -34,3 +32,4 @@ nub_strcmp:
.equal: .equal:
mov rax, 1 mov rax, 1
ret ret