diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs b/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs index dc22c8d..b4a8c9e 100644 --- a/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs +++ b/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs @@ -354,6 +354,9 @@ public class Generator case StructInitializerNode structInitializer: GenerateStructInitializer(structInitializer, func); break; + case StructMemberAccessorNode structMemberAccessor: + GenerateStructMemberAccessor(structMemberAccessor, func); + break; case SyscallExpressionNode syscallExpression: GenerateSyscall(syscallExpression.Syscall, func); break; @@ -362,6 +365,45 @@ public class Generator } } + private void GenerateStructMemberAccessor(StructMemberAccessorNode structMemberAccessor, LocalFunc func) + { + var variable = func.ResolveLocalVariable(structMemberAccessor.Members[0]); + + if (variable.Type is not StructType structType) + { + throw new Exception($"Cannot access struct member on {variable} since it is not a struct type"); + } + + _builder.AppendLine($" mov rax, [rbp - {variable.Offset}]"); + _builder.AppendLine(" mov rax, [rax]"); + + Type prevMemberType = structType; + for (var i = 1; i < structMemberAccessor.Members.Count; i++) + { + if (prevMemberType is not StructType prevMemberStructType) + { + throw new Exception($"Cannot access {structMemberAccessor.Members[i]} on type {prevMemberType} because it is not a struct type"); + } + + var structDefinition = _definitions.OfType().FirstOrDefault(sd => sd.Name == prevMemberStructType.Name); + if (structDefinition == null) + { + throw new Exception($"Struct {prevMemberStructType} is not defined"); + } + + var member = structDefinition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members[i]); + if (member == null) + { + throw new Exception($"Struct {prevMemberStructType} has no member with name {structMemberAccessor.Members[i]}"); + } + + var offset = structDefinition.Members.IndexOf(member); + _builder.AppendLine($" mov rax, [rax + {offset * 8}]"); + + prevMemberType = member.Type; + } + } + private void GenerateArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess, LocalFunc func) { GenerateArrayIndexPointerAccess(arrayIndexAccess.Identifier, arrayIndexAccess.Index, func); @@ -601,7 +643,7 @@ public class Generator throw new Exception($"Struct {structInitializer.StructType} is not defined"); } - _builder.AppendLine($" add rsp, {structDefinition.Members.Count * 8}"); + _builder.AppendLine($" sub rsp, {structDefinition.Members.Count * 8}"); foreach (var initializer in structInitializer.Initializers) { diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs b/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs index 278e1de..00a9ed3 100644 --- a/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs +++ b/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs @@ -137,6 +137,8 @@ public abstract class Variable(string name, Type type) { public string Name { get; } = name; public Type Type { get; } = type; + + public override string ToString() => $"{Name}: {Type}"; } public class LocalVariable(string name, Type type, int offset) : Variable(name, type) diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs index 64a5d9d..cb550a0 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -423,28 +423,61 @@ public class Parser private ExpressionNode ParseExpressionIdentifier(IdentifierToken identifier) { - if (TryExpectSymbol(Symbol.OpenBracket)) + var token = Peek(); + if (!token.HasValue) { - var index = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index); + return new IdentifierNode(identifier.Value); } - - if (TryExpectSymbol(Symbol.OpenParen)) - { - List parameters = []; - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseExpression()); - TryExpectSymbol(Symbol.Comma); - } - - if (identifier.Value == "syscall") - { - return new SyscallExpressionNode(new Syscall(parameters)); - } - return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters)); + switch (token.Value) + { + case SymbolToken symbolToken: + { + switch (symbolToken.Symbol) + { + case Symbol.Period: + { + Next(); + List members = + [ + identifier.Value, + ExpectIdentifier().Value + ]; + + while (TryExpectSymbol(Symbol.Period)) + { + members.Add(ExpectIdentifier().Value); + } + + return new StructMemberAccessorNode(members); + } + case Symbol.OpenBracket: + { + Next(); + var index = ParseExpression(); + ExpectSymbol(Symbol.CloseBracket); + return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index); + } + case Symbol.OpenParen: + { + Next(); + List parameters = []; + while (!TryExpectSymbol(Symbol.CloseParen)) + { + parameters.Add(ParseExpression()); + TryExpectSymbol(Symbol.Comma); + } + + if (identifier.Value == "syscall") + { + return new SyscallExpressionNode(new Syscall(parameters)); + } + + return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters)); + } + } + break; + } } return new IdentifierNode(identifier.Value); diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs new file mode 100644 index 0000000..a265b62 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/StructMemberAccessorNode.cs @@ -0,0 +1,6 @@ +namespace Nub.Lang.Frontend.Parsing; + +public class StructMemberAccessorNode(List members) : ExpressionNode +{ + public List Members { get; } = members; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index eab4ef0..894c117 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -15,7 +15,7 @@ public class ExpressionTyper { private readonly List _functions; private readonly List _variableDefinitions; - private readonly List _classes; + private readonly List _structDefinitions; private readonly Stack _variables; public ExpressionTyper(List definitions) @@ -24,7 +24,7 @@ public class ExpressionTyper _functions = []; _variableDefinitions = []; - _classes = definitions.OfType().ToList(); + _structDefinitions = definitions.OfType().ToList(); var functions = definitions .OfType() @@ -45,7 +45,7 @@ public class ExpressionTyper { _variables.Clear(); - foreach (var @class in _classes) + foreach (var @class in _structDefinitions) { foreach (var variable in @class.Members) { @@ -216,6 +216,9 @@ public class ExpressionTyper case StructInitializerNode structInitializer: PopulateStructInitializer(structInitializer); break; + case StructMemberAccessorNode structMemberAccessor: + GenerateStructMemberAccessorNode(structMemberAccessor); + break; case SyscallExpressionNode syscall: PopulateSyscallExpression(syscall); break; @@ -323,6 +326,55 @@ public class ExpressionTyper structInitializer.Type = structInitializer.StructType; } + // TODO: Fix this ugly ass code + private void GenerateStructMemberAccessorNode(StructMemberAccessorNode structMemberAccessor) + { + var variable = _variables.FirstOrDefault(v => v.Name == structMemberAccessor.Members[0]); + if (variable == null) + { + throw new Exception($"Variable {structMemberAccessor.Members[0]} is not defined"); + } + + if (variable.Type is not StructType variableType) + { + throw new Exception("Variable " + structMemberAccessor.Members[0] + " is not a struct"); + } + + var definition = _structDefinitions.FirstOrDefault(sd => sd.Name == variableType.Name); + if (definition == null) + { + throw new Exception($"Struct {structMemberAccessor.Members[0]} is not defined"); + } + + for (var i = 1; i < structMemberAccessor.Members.Count - 1; i++) + { + var member = definition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members[i]); + if (member == null) + { + throw new Exception($"Member {structMemberAccessor.Members[i]} does not exist on struct {definition.Name}"); + } + + if (member.Type is not StructType memberType) + { + throw new Exception($"Member {structMemberAccessor.Members[i]} on struct {definition.Name} is not a struct"); + } + + definition = _structDefinitions.FirstOrDefault(sd => sd.Name == memberType.Name); + if (definition == null) + { + throw new Exception($"Struct {structMemberAccessor.Members[i]} is not defined"); + } + } + + var tmp = definition.Members.FirstOrDefault(m => m.Name == structMemberAccessor.Members.Last()); + if (tmp == null) + { + throw new Exception($"Member {structMemberAccessor.Members.Last()} does not exist on struct {definition.Name}"); + } + + structMemberAccessor.Type = tmp.Type; + } + private void PopulateSyscallExpression(SyscallExpressionNode syscall) { foreach (var parameter in syscall.Syscall.Parameters) diff --git a/input/program.nub b/input/program.nub index 4dc63f7..3700167 100644 --- a/input/program.nub +++ b/input/program.nub @@ -3,12 +3,25 @@ import "core"; func main() { let x = new Test { - some_string = "test2", + some_struct = new Test + { + some_struct = 0, + some_int = 420, + some_string = "blaze it" + }, some_int = 69, + some_string = "nice" }; + + println(x.some_int); + println(x.some_string); + + println(x.some_struct.some_int); + println(x.some_struct.some_string); } struct Test { + let some_struct: Test; let some_string: String; let some_int: int64; }