diff --git a/example/program.nub b/example/program.nub index 2a0f552..1d6f058 100644 --- a/example/program.nub +++ b/example/program.nub @@ -1,12 +1,20 @@ import "c"; struct Human { - age: int64; + age: int64 = 0; name: string; } global func main() { - printName(); + while true { + let dad = new Human + { + name = "John"; + }; + + printName(dad); + } + } func printName(human: Human) { diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 654f50b..6bb766c 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -44,17 +44,22 @@ public class Generator private string QbeTypeName(NubType type) { - if (type.Equals(NubType.Int64) || type.Equals(NubType.String)) - { - return "l"; - } - if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) { return "w"; } - return $":{type.Name}"; + return "l"; + } + + private int QbeTypeSize(NubType type) + { + if (type.Equals(NubType.Int32) || type.Equals(NubType.Bool)) + { + return 4; + } + + return 8; } private void GenerateFuncDefinition(LocalFuncDefinitionNode node) @@ -261,25 +266,49 @@ public class Generator return GenerateLiteral(literal); case StructInitializerNode structInitializer: return GenerateStructInitializer(structInitializer); - case StructMemberAccessorNode structMemberAccessor: - return GenerateStructMemberAccessor(structMemberAccessor); + case StructFieldAccessorNode structMemberAccessor: + return GenerateStructFieldAccessor(structMemberAccessor); default: throw new ArgumentOutOfRangeException(nameof(expression)); } } - private string GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor) + private string GenerateStructFieldAccessor(StructFieldAccessorNode structFieldAccessor) { - var type = _variables.First(x => x.Key == structMemberAccessor.Fields[0]).Value.Type; - var def = _definitions.OfType().FirstOrDefault(s => s.Name == type.Name); - if (def == null) - { - throw new Exception("def is null"); - } + var structType = structFieldAccessor.Struct.Type; + var structDefinition = _definitions + .OfType() + .FirstOrDefault(s => s.Name == structType.Name); - throw new NotImplementedException("Not finished yet"); + if (structDefinition == null) + { + throw new Exception($"Struct {structType.Name} is not defined"); + } + + var @struct = GenerateExpression(structFieldAccessor.Struct); + + var fieldIndex = -1; + for (var i = 0; i < structDefinition.Fields.Count; i++) + { + if (structDefinition.Fields[i].Name == structFieldAccessor.Field) + { + fieldIndex = i; + break; + } + } + + if (fieldIndex == -1) + { + throw new Exception($"Field {structFieldAccessor.Field} is not defined in struct {structType.Name}"); + } + + var offsetLabel = GenName("offset"); + _builder.AppendLine($" %{offsetLabel} ={QbeTypeName(structFieldAccessor.Type)} add {@struct}, {fieldIndex * QbeTypeSize(structFieldAccessor.Type)}"); - return $"load{QbeTypeName(structMemberAccessor.Type)} "; + var outputLabel = GenName("field"); + _builder.AppendLine($" %{outputLabel} ={QbeTypeName(structFieldAccessor.Type)} load{QbeTypeName(structFieldAccessor.Type)} %{offsetLabel}"); + + return $"%{outputLabel}"; } private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression) @@ -317,7 +346,44 @@ public class Generator private string GenerateStructInitializer(StructInitializerNode structInitializer) { - throw new NotImplementedException(); + var structDefinition = _definitions.OfType() + .FirstOrDefault(s => s.Name == structInitializer.StructType.Name); + + if (structDefinition == null) + { + throw new Exception($"Struct {structInitializer.StructType.Name} is not defined"); + } + + var structVar = GenName("struct"); + + var size = structDefinition.Fields.Sum(x => QbeTypeSize(x.Type)); + _builder.AppendLine($" %{structVar} =l alloc8 {size}"); + + for (var i = 0; i < structDefinition.Fields.Count; i++) + { + var field = structDefinition.Fields[i]; + + if (structInitializer.Initializers.TryGetValue(field.Name, out var fieldValue)) + { + var var = GenerateExpression(fieldValue); + var offsetLabel = GenName("offset"); + _builder.AppendLine($" %{offsetLabel} =l add %{structVar}, {i * QbeTypeSize(field.Type)}"); + _builder.AppendLine($" store{QbeTypeName(field.Type)} {var}, %{offsetLabel}"); + } + else if (field.Value.HasValue) + { + var var = GenerateExpression(field.Value.Value); + var offsetLabel = GenName("offset"); + _builder.AppendLine($" %{offsetLabel} =l add %{structVar}, {i * QbeTypeSize(field.Type)}"); + _builder.AppendLine($" store{QbeTypeName(field.Type)} {var}, %{offsetLabel}"); + } + else + { + throw new Exception($"Field {field.Name} on struct {structInitializer.StructType.Name} is not initialized"); + } + } + + return $"%{structVar}"; } private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall) diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index 2fdf890..c37f2ed 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -396,7 +396,9 @@ public class Parser return new StructInitializerNode(type, initializers); } default: + { throw new Exception($"Unknown symbol: {symbolToken.Symbol}"); + } } } default: @@ -421,18 +423,15 @@ public class Parser case Symbol.Period: { Next(); - List members = - [ - identifier.Value, - ExpectIdentifier().Value - ]; - - while (TryExpectSymbol(Symbol.Period)) + ExpressionNode result = new IdentifierNode(identifier.Value); + + do { - members.Add(ExpectIdentifier().Value); - } + var field = ExpectIdentifier(); + result = new StructFieldAccessorNode(result, field.Value); + } while (TryExpectSymbol(Symbol.Period)); - return new StructMemberAccessorNode(members); + return result; } case Symbol.OpenParen: { diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs index 3297940..cd7e285 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/StructFieldAccessorNode.cs @@ -1,6 +1,7 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructFieldAccessorNode(List fields) : ExpressionNode +public class StructFieldAccessorNode(ExpressionNode @struct, string field) : ExpressionNode { - public List Fields { get; } = fields; + public ExpressionNode Struct { get; } = @struct; + public string Field { get; } = field; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index fd358a0..d637940 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -179,8 +179,8 @@ public class ExpressionTyper case StructInitializerNode structInitializer: PopulateStructInitializer(structInitializer); break; - case StructMemberAccessorNode structMemberAccessor: - GenerateStructMemberAccessorNode(structMemberAccessor); + case StructFieldAccessorNode structMemberAccessor: + PopulateStructMemberAccessorNode(structMemberAccessor); break; default: throw new ArgumentOutOfRangeException(nameof(expression)); @@ -262,43 +262,29 @@ public class ExpressionTyper structInitializer.Type = structInitializer.StructType; } - // TODO: Fix this ugly ass code - private void GenerateStructMemberAccessorNode(StructMemberAccessorNode structMemberAccessor) + private void PopulateStructMemberAccessorNode(StructFieldAccessorNode structFieldAccessor) { - var variable = _variables.FirstOrDefault(v => v.Name == structMemberAccessor.Fields[0]); - if (variable == null) + PopulateExpression(structFieldAccessor.Struct); + + var structType = structFieldAccessor.Struct.Type; + if (structType == null) { - throw new Exception($"Variable {structMemberAccessor.Fields[0]} is not defined"); + throw new Exception($"Cannot access field on non-struct type: {structFieldAccessor.Struct}"); } - - var definition = _structDefinitions.FirstOrDefault(sd => sd.Name == variable.Type.Name); - if (definition == null) + + var structDefinition = _structDefinitions.FirstOrDefault(s => s.Name == structType.Name); + if (structDefinition == null) { - throw new Exception($"Struct {structMemberAccessor.Fields[0]} is not defined"); + throw new Exception($"Struct {structType.Name} is not defined"); } - - for (var i = 1; i < structMemberAccessor.Fields.Count - 1; i++) + + var field = structDefinition.Fields.FirstOrDefault(f => f.Name == structFieldAccessor.Field); + if (field == null) { - var member = definition.Fields.FirstOrDefault(m => m.Name == structMemberAccessor.Fields[i]); - if (member == null) - { - throw new Exception($"Member {structMemberAccessor.Fields[i]} does not exist on struct {definition.Name}"); - } - - definition = _structDefinitions.FirstOrDefault(sd => sd.Name == member.Type.Name); - if (definition == null) - { - throw new Exception($"Struct {structMemberAccessor.Fields[i]} is not defined"); - } + throw new Exception($"Field {structFieldAccessor.Field} is not defined in struct {structType.Name}"); } - - var tmp = definition.Fields.FirstOrDefault(m => m.Name == structMemberAccessor.Fields.Last()); - if (tmp == null) - { - throw new Exception($"Member {structMemberAccessor.Fields.Last()} does not exist on struct {definition.Name}"); - } - - structMemberAccessor.Type = tmp.Type; + + structFieldAccessor.Type = field.Type; } private class Variable(string name, NubType type)