From 00714ea4b055c27ba09dc75a9b16da7aa22c4c2c Mon Sep 17 00:00:00 2001 From: Oliver Stene Date: Sun, 8 Feb 2026 18:43:50 +0100 Subject: [PATCH] ... --- compiler/Compiler/Generator.cs | 42 +++++++++++- compiler/Compiler/Parser.cs | 117 ++++++++++++++++++++++++++------- compiler/Compiler/Program.cs | 13 +++- compiler/Compiler/Tokenizer.cs | 8 +++ 4 files changed, 154 insertions(+), 26 deletions(-) diff --git a/compiler/Compiler/Generator.cs b/compiler/Compiler/Generator.cs index 22fb510..4c196c7 100644 --- a/compiler/Compiler/Generator.cs +++ b/compiler/Compiler/Generator.cs @@ -35,6 +35,23 @@ public sealed class Generator(List nodes) writer.WriteLine(); + foreach (var node in nodes.OfType()) + { + writer.WriteLine($"struct {node.Name.Ident}"); + writer.WriteLine("{"); + using (writer.Indent()) + { + foreach (var field in node.Fields) + { + writer.WriteLine($"{CType(field.Type, field.Name.Ident)};"); + } + } + + writer.WriteLine("};"); + } + + writer.WriteLine(); + foreach (var node in nodes.OfType()) { var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident)); @@ -171,6 +188,8 @@ public sealed class Generator(List nodes) NodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false", NodeExpressionIntLiteral expression => expression.Value.Value.ToString(), NodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}", + NodeExpressionStructLiteral expression => EmitStructLiteral(expression), + NodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression), NodeExpressionIdent expression => expression.Value.Ident, _ => throw new ArgumentOutOfRangeException(nameof(node), node, null) }; @@ -208,7 +227,7 @@ public sealed class Generator(List nodes) { NodeTypeVoid => "void" + (varName != null ? $" {varName}" : ""), NodeTypeBool => "bool" + (varName != null ? $" {varName}" : ""), - NodeTypeCustom type => $"struct {type}" + (varName != null ? $" {varName}" : ""), + NodeTypeCustom type => $"struct {type.Name.Ident}" + (varName != null ? $" {varName}" : ""), NodeTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""), NodeTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""), NodeTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"), @@ -217,6 +236,27 @@ public sealed class Generator(List nodes) _ => throw new ArgumentOutOfRangeException(nameof(node), node, null) }; } + + private string EmitStructLiteral(NodeExpressionStructLiteral expression) + { + var initializerValues = new Dictionary(); + + foreach (var initializer in expression.Initializers) + { + var values = EmitExpression(initializer.Value); + initializerValues[initializer.Name.Ident] = values; + } + + var initializerStrings = initializerValues.Select(x => $".{x.Key} = {x.Value}"); + + return $"(struct {expression.Name.Ident}){{ {string.Join(", ", initializerStrings)} }}"; + } + + private string EmitExpressionMemberAccess(NodeExpressionMemberAccess expression) + { + var target = EmitExpression(expression.Target); + return $"{target}.{expression.Name.Ident}"; + } } internal class IndentedTextWriter diff --git a/compiler/Compiler/Parser.cs b/compiler/Compiler/Parser.cs index a7f632b..413b7db 100644 --- a/compiler/Compiler/Parser.cs +++ b/compiler/Compiler/Parser.cs @@ -48,6 +48,24 @@ public sealed class Parser(List tokens) return new NodeDefinitionFunc(TokensFrom(startIndex), name, parameters, body, returnType); } + if (TryExpectKeyword(Keyword.Struct)) + { + var name = ExpectIdent(); + var fields = new List(); + + ExpectSymbol(Symbol.OpenCurly); + while (!TryExpectSymbol(Symbol.CloseCurly)) + { + var fieldStartIndex = index; + var fieldName = ExpectIdent(); + ExpectSymbol(Symbol.Colon); + var fieldType = ParseType(); + fields.Add(new NodeDefinitionStruct.Field(TokensFrom(fieldStartIndex), fieldName, fieldType)); + } + + return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields); + } + throw new Exception("Not a valid definition"); } @@ -172,34 +190,59 @@ public sealed class Parser(List tokens) { var startIndex = index; + NodeExpression expr; + if (TryExpectSymbol(Symbol.OpenParen)) { var value = ParseExpression(); ExpectSymbol(Symbol.CloseParen); - return value; + expr = value; } - - if (TryExpectIntLiteral(out var intLiteral)) + else if (TryExpectIntLiteral(out var intLiteral)) { - return new NodeExpressionIntLiteral(TokensFrom(startIndex), intLiteral); + expr = new NodeExpressionIntLiteral(TokensFrom(startIndex), intLiteral); } - - if (TryExpectStringLiteral(out var stringLiteral)) + else if (TryExpectStringLiteral(out var stringLiteral)) { - return new NodeExpressionStringLiteral(TokensFrom(startIndex), stringLiteral); + expr = new NodeExpressionStringLiteral(TokensFrom(startIndex), stringLiteral); } - - if (TryExpectBoolLiteral(out var boolLiteral)) + else if (TryExpectBoolLiteral(out var boolLiteral)) { - return new NodeExpressionBoolLiteral(TokensFrom(startIndex), boolLiteral); + expr = new NodeExpressionBoolLiteral(TokensFrom(startIndex), boolLiteral); } - - if (TryExpectIdent(out var ident)) + else if (TryExpectIdent(out var ident)) { - return new NodeExpressionIdent(TokensFrom(startIndex), ident); + expr = new NodeExpressionIdent(TokensFrom(startIndex), ident); + } + else if (TryExpectKeyword(Keyword.Struct)) + { + var name = ExpectIdent(); + var initializers = new List(); + + ExpectSymbol(Symbol.OpenCurly); + while (!TryExpectSymbol(Symbol.CloseCurly)) + { + var initializerStartIndex = startIndex; + var fieldName = ExpectIdent(); + ExpectSymbol(Symbol.Equal); + var fieldValue = ParseExpression(); + initializers.Add(new NodeExpressionStructLiteral.Initializer(TokensFrom(initializerStartIndex), fieldName, fieldValue)); + } + + expr = new NodeExpressionStructLiteral(TokensFrom(startIndex), name, initializers); + } + else + { + throw new Exception("Not a valid expression leaf"); } - throw new Exception("Not a valid expression leaf"); + if (TryExpectSymbol(Symbol.Period)) + { + var name = ExpectIdent(); + expr = new NodeExpressionMemberAccess(TokensFrom(startIndex), expr, name); + } + + return expr; } private NodeType ParseType() @@ -462,6 +505,18 @@ public sealed class NodeDefinitionFunc(List tokens, TokenIdent name, List } } +public sealed class NodeDefinitionStruct(List tokens, TokenIdent name, List fields) : NodeDefinition(tokens) +{ + public readonly TokenIdent Name = name; + public readonly List Fields = fields; + + public sealed class Field(List tokens, TokenIdent name, NodeType type) : Node(tokens) + { + public readonly TokenIdent Name = name; + public readonly NodeType Type = type; + } +} + public abstract class NodeStatement(List tokens) : Node(tokens); public sealed class NodeStatementBlock(List tokens, List statements) : NodeStatement(tokens) @@ -475,32 +530,32 @@ public sealed class NodeStatementFuncCall(List tokens, NodeExpression tar public readonly List Parameters = parameters; } -public class NodeStatementReturn(List tokens, NodeExpression value) : NodeStatement(tokens) +public sealed class NodeStatementReturn(List tokens, NodeExpression value) : NodeStatement(tokens) { public readonly NodeExpression Value = value; } -public class NodeStatementVariableDeclaration(List tokens, TokenIdent name, NodeType type, NodeExpression value) : NodeStatement(tokens) +public sealed class NodeStatementVariableDeclaration(List tokens, TokenIdent name, NodeType type, NodeExpression value) : NodeStatement(tokens) { public readonly TokenIdent Name = name; public readonly NodeType Type = type; public readonly NodeExpression Value = value; } -public class NodeStatementAssignment(List tokens, NodeExpression target, NodeExpression value) : NodeStatement(tokens) +public sealed class NodeStatementAssignment(List tokens, NodeExpression target, NodeExpression value) : NodeStatement(tokens) { public readonly NodeExpression Target = target; public readonly NodeExpression Value = value; } -public class NodeStatementIf(List tokens, NodeExpression condition, NodeStatement thenBlock, NodeStatement? elseBlock) : NodeStatement(tokens) +public sealed class NodeStatementIf(List tokens, NodeExpression condition, NodeStatement thenBlock, NodeStatement? elseBlock) : NodeStatement(tokens) { public readonly NodeExpression Condition = condition; public readonly NodeStatement ThenBlock = thenBlock; public readonly NodeStatement? ElseBlock = elseBlock; } -public class NodeStatementWhile(List tokens, NodeExpression condition, NodeStatement block) : NodeStatement(tokens) +public sealed class NodeStatementWhile(List tokens, NodeExpression condition, NodeStatement block) : NodeStatement(tokens) { public readonly NodeExpression Condition = condition; public readonly NodeStatement Block = block; @@ -523,16 +578,34 @@ public sealed class NodeExpressionBoolLiteral(List tokens, TokenBoolLiter public readonly TokenBoolLiteral Value = value; } +public sealed class NodeExpressionStructLiteral(List tokens, TokenIdent name, List initializers) : NodeExpression(tokens) +{ + public readonly TokenIdent Name = name; + public readonly List Initializers = initializers; + + public sealed class Initializer(List tokens, TokenIdent name, NodeExpression value) : Node(tokens) + { + public readonly TokenIdent Name = name; + public readonly NodeExpression Value = value; + } +} + +public sealed class NodeExpressionMemberAccess(List tokens, NodeExpression target, TokenIdent name) : NodeExpression(tokens) +{ + public readonly NodeExpression Target = target; + public readonly TokenIdent Name = name; +} + public sealed class NodeExpressionIdent(List tokens, TokenIdent value) : NodeExpression(tokens) { public readonly TokenIdent Value = value; } -public class NodeExpressionBinary(List tokens, NodeExpression left, NodeExpressionBinary.Op operation, NodeExpression right) : NodeExpression(tokens) +public sealed class NodeExpressionBinary(List tokens, NodeExpression left, NodeExpressionBinary.Op operation, NodeExpression right) : NodeExpression(tokens) { - public NodeExpression Left { get; } = left; + public readonly NodeExpression Left = left; public readonly Op Operation = operation; - public NodeExpression Right { get; } = right; + public readonly NodeExpression Right = right; public enum Op diff --git a/compiler/Compiler/Program.cs b/compiler/Compiler/Program.cs index 7ca2a8c..93bb8d5 100644 --- a/compiler/Compiler/Program.cs +++ b/compiler/Compiler/Program.cs @@ -1,6 +1,11 @@ using Compiler; const string contents = """ + struct person { + age: i32 + name: string + } + func main(): i32 { let x: i32 = 23 x = 24 @@ -20,10 +25,12 @@ const string contents = """ x = i } - do_something("test") + let me: person = struct person { age = 21 name = "Oliver" } + + do_something(me.name) return x } - + func do_something(text: string): void { } """; @@ -32,4 +39,4 @@ var tokens = Tokenizer.Tokenize(contents); var nodes = Parser.Parse(tokens); var output = Generator.Emit(nodes); -File.WriteAllText("C:/Users/oliste/repos/nub-lang/compiler/Compiler/out.c", output); +File.WriteAllText("C:/Users/oliste/repos/nub-lang/compiler/Compiler/out.c", output); \ No newline at end of file diff --git a/compiler/Compiler/Tokenizer.cs b/compiler/Compiler/Tokenizer.cs index 6403ca4..831ca6a 100644 --- a/compiler/Compiler/Tokenizer.cs +++ b/compiler/Compiler/Tokenizer.cs @@ -165,6 +165,11 @@ public sealed class Tokenizer(string contents) Consume(); return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Comma); } + case '.': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period); + } case ':': { Consume(); @@ -322,6 +327,7 @@ public sealed class Tokenizer(string contents) return value switch { "func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func), + "struct" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Struct), "let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let), "if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If), "else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else), @@ -415,6 +421,7 @@ public enum Symbol OpenParen, CloseParen, Comma, + Period, Colon, Caret, Bang, @@ -451,6 +458,7 @@ public sealed class TokenSymbol(int line, int column, int length, Symbol symbol) public enum Keyword { Func, + Struct, Let, If, Else,