From 1fb88f207377ce128fab078871d9e18664843103 Mon Sep 17 00:00:00 2001 From: nub31 Date: Wed, 22 Oct 2025 18:32:45 +0200 Subject: [PATCH] for in syntax --- compiler/NubLang/Ast/Node.cs | 4 + compiler/NubLang/Ast/TypeChecker.cs | 48 +++++++++- compiler/NubLang/Generation/Generator.cs | 106 ++++++++++++++++++----- compiler/NubLang/Syntax/Parser.cs | 19 ++++ compiler/NubLang/Syntax/Syntax.cs | 2 + compiler/NubLang/Syntax/Token.cs | 2 + compiler/NubLang/Syntax/Tokenizer.cs | 2 + examples/hello-world/main.nub | 14 ++- 8 files changed, 172 insertions(+), 25 deletions(-) diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs index 685e303..6389cf1 100644 --- a/compiler/NubLang/Ast/Node.cs +++ b/compiler/NubLang/Ast/Node.cs @@ -40,6 +40,10 @@ public record BreakNode(List Tokens) : TerminalStatementNode(Tokens); public record WhileNode(List Tokens, ExpressionNode Condition, BlockNode Body) : StatementNode(Tokens); +public record ForSliceNode(List Tokens, string ElementName, string? IndexName, ExpressionNode Target, BlockNode Body) : StatementNode(Tokens); + +public record ForConstArrayNode(List Tokens, string ElementName, string? IndexName, ExpressionNode Target, BlockNode Body) : StatementNode(Tokens); + public record DeferNode(List Tokens, StatementNode Statement) : StatementNode(Tokens); #endregion diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs index 2e35fd8..37aebd0 100644 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ b/compiler/NubLang/Ast/TypeChecker.cs @@ -234,6 +234,51 @@ public sealed class TypeChecker return new WhileNode(statement.Tokens, condition, body); } + private StatementNode CheckFor(ForSyntax forSyntax) + { + var target = CheckExpression(forSyntax.Target); + + + switch (target.Type) + { + case NubSliceType sliceType: + { + using (BeginScope()) + { + Scope.DeclareVariable(new Variable(forSyntax.ElementName, sliceType.ElementType)); + if (forSyntax.IndexName != null) + { + Scope.DeclareVariable(new Variable(forSyntax.IndexName, new NubIntType(false, 64))); + } + + var body = CheckBlock(forSyntax.Body); + return new ForSliceNode(forSyntax.Tokens, forSyntax.ElementName, forSyntax.IndexName, target, body); + } + } + case NubConstArrayType constArrayType: + { + using (BeginScope()) + { + Scope.DeclareVariable(new Variable(forSyntax.ElementName, constArrayType.ElementType)); + if (forSyntax.IndexName != null) + { + Scope.DeclareVariable(new Variable(forSyntax.IndexName, new NubIntType(false, 64))); + } + + var body = CheckBlock(forSyntax.Body); + return new ForConstArrayNode(forSyntax.Tokens, forSyntax.ElementName, forSyntax.IndexName, target, body); + } + } + default: + { + throw new TypeCheckerException(Diagnostic + .Error($"For statement not supported for type {target.Type}") + .At(forSyntax) + .Build()); + } + } + } + private FuncPrototypeNode CheckFuncPrototype(FuncPrototypeSyntax statement) { var parameters = new List(); @@ -332,8 +377,8 @@ public sealed class TypeChecker private ArrayInitializerNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? _) { var elementType = ResolveType(expression.ElementType); - var type = new NubArrayType(elementType); var capacity = CheckExpression(expression.Capacity); + var type = new NubArrayType(elementType); return new ArrayInitializerNode(expression.Tokens, type, capacity, elementType); } @@ -811,6 +856,7 @@ public sealed class TypeChecker VariableDeclarationSyntax varDeclStmt => CheckVariableDeclaration(varDeclStmt), WhileSyntax whileStmt => CheckWhile(whileStmt), DeferSyntax defer => new DeferNode(statement.Tokens, CheckStatement(defer.Statement)), + ForSyntax forSyntax => CheckFor(forSyntax), _ => throw new ArgumentOutOfRangeException(nameof(statement)) }; } diff --git a/compiler/NubLang/Generation/Generator.cs b/compiler/NubLang/Generation/Generator.cs index c61352f..1b1575f 100644 --- a/compiler/NubLang/Generation/Generator.cs +++ b/compiler/NubLang/Generation/Generator.cs @@ -95,7 +95,13 @@ public class Generator var name = FuncName(funcNode.Module, funcNode.Name, funcNode.Prototype.ExternSymbol); _writer.WriteLine($"{CType.Create(funcNode.Prototype.ReturnType, name)}({parameters})"); - EmitBlock(funcNode.Body); + _writer.WriteLine("{"); + using (_writer.Indent()) + { + EmitBlock(funcNode.Body); + } + + _writer.WriteLine("}"); _writer.WriteLine(); } @@ -111,7 +117,13 @@ public class Generator EmitAssignment(assignmentNode); break; case BlockNode blockNode: - EmitBlock(blockNode); + _writer.WriteLine("{"); + using (_writer.Indent()) + { + EmitBlock(blockNode); + } + + _writer.WriteLine("{"); break; case BreakNode breakNode: EmitBreak(breakNode); @@ -122,6 +134,12 @@ public class Generator case DeferNode deferNode: EmitDefer(deferNode); break; + case ForConstArrayNode forConstArrayNode: + EmitForConstArray(forConstArrayNode); + break; + case ForSliceNode forSliceNode: + EmitForSlice(forSliceNode); + break; case IfNode ifNode: EmitIf(ifNode); break; @@ -174,18 +192,64 @@ public class Generator _deferStack.Peek().Add(deferNode); } + private void EmitForSlice(ForSliceNode forSliceNode) + { + var targetType = (NubSliceType)forSliceNode.Target.Type; + var target = EmitExpression(forSliceNode.Target); + var indexName = forSliceNode.IndexName ?? NewTmp(); + + _writer.WriteLine($"for (size_t {indexName} = 0; {indexName} < {target}.length; ++{indexName})"); + _writer.WriteLine("{"); + using (_writer.Indent()) + { + _writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementName)} = {target}.data[{indexName}];"); + EmitBlock(forSliceNode.Body); + } + + _writer.WriteLine("}"); + } + + private void EmitForConstArray(ForConstArrayNode forConstArrayNode) + { + var targetType = (NubConstArrayType)forConstArrayNode.Target.Type; + var target = EmitExpression(forConstArrayNode.Target); + var indexName = forConstArrayNode.IndexName ?? NewTmp(); + + _writer.WriteLine($"for (size_t {indexName} = 0; {indexName} < {targetType.Size}; ++{indexName})"); + _writer.WriteLine("{"); + using (_writer.Indent()) + { + _writer.WriteLine($"{CType.Create(targetType.ElementType, forConstArrayNode.ElementName)} = {target}[{indexName}];"); + EmitBlock(forConstArrayNode.Body); + } + + _writer.WriteLine("}"); + } + private void EmitIf(IfNode ifNode, bool elseIf = false) { var condition = EmitExpression(ifNode.Condition); _writer.WriteLine($"{(elseIf ? "else " : "")}if ({condition})"); - EmitBlock(ifNode.Body); + _writer.WriteLine("{"); + using (_writer.Indent()) + { + EmitBlock(ifNode.Body); + } + + _writer.WriteLine("}"); ifNode.Else?.Match ( elseIfNode => EmitIf(elseIfNode, true), elseNode => { _writer.WriteLine("else"); - EmitBlock(elseNode); + _writer.WriteLine("{"); + using (_writer.Indent()) + { + EmitBlock(elseNode); + } + + _writer.WriteLine("}"); } ); } @@ -251,7 +315,13 @@ public class Generator { var condition = EmitExpression(whileNode.Condition); _writer.WriteLine($"while ({condition})"); - EmitBlock(whileNode.Body); + _writer.WriteLine("{"); + using (_writer.Indent()) + { + EmitBlock(whileNode.Body); + } + + _writer.WriteLine("}"); } private string EmitExpression(ExpressionNode expressionNode) @@ -530,25 +600,17 @@ public class Generator private void EmitBlock(BlockNode blockNode) { - EmitLine(blockNode.Tokens.FirstOrDefault()); - _writer.WriteLine("{"); - using (_writer.Indent()) + _deferStack.Push([]); + + foreach (var statementNode in blockNode.Statements) { - _deferStack.Push([]); - - foreach (var statementNode in blockNode.Statements) - { - EmitStatement(statementNode); - } - - var blockDefers = _deferStack.Pop(); - for (var i = blockDefers.Count - 1; i >= 0; i--) - { - EmitStatement(blockDefers[i].Statement); - } + EmitStatement(statementNode); } - EmitLine(blockNode.Tokens.LastOrDefault()); - _writer.WriteLine("}"); + var blockDefers = _deferStack.Pop(); + for (var i = blockDefers.Count - 1; i >= 0; i--) + { + EmitStatement(blockDefers[i].Statement); + } } } \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index e54bfca..a0f8f95 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -241,6 +241,8 @@ public sealed class Parser return ParseIf(startIndex); case Symbol.While: return ParseWhile(startIndex); + case Symbol.For: + return ParseFor(startIndex); case Symbol.Let: return ParseVariableDeclaration(startIndex); case Symbol.Defer: @@ -330,6 +332,23 @@ public sealed class Parser return new WhileSyntax(GetTokens(startIndex), condition, body); } + private ForSyntax ParseFor(int startIndex) + { + var itemName = ExpectIdentifier().Value; + string? indexName = null; + + if (TryExpectSymbol(Symbol.Comma)) + { + indexName = ExpectIdentifier().Value; + } + + ExpectSymbol(Symbol.In); + var target = ParseExpression(); + var body = ParseBlock(); + + return new ForSyntax(GetTokens(startIndex), itemName, indexName, target, body); + } + private ExpressionSyntax ParseExpression(int precedence = 0) { var startIndex = _tokenIndex; diff --git a/compiler/NubLang/Syntax/Syntax.cs b/compiler/NubLang/Syntax/Syntax.cs index 40e28af..8781644 100644 --- a/compiler/NubLang/Syntax/Syntax.cs +++ b/compiler/NubLang/Syntax/Syntax.cs @@ -74,6 +74,8 @@ public record DeferSyntax(List Tokens, StatementSyntax Statement) : State public record WhileSyntax(List Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); +public record ForSyntax(List Tokens, string ElementName, string? IndexName, ExpressionSyntax Target, BlockSyntax Body) : StatementSyntax(Tokens); + #endregion #region Expressions diff --git a/compiler/NubLang/Syntax/Token.cs b/compiler/NubLang/Syntax/Token.cs index 9572297..66f0980 100644 --- a/compiler/NubLang/Syntax/Token.cs +++ b/compiler/NubLang/Syntax/Token.cs @@ -8,6 +8,8 @@ public enum Symbol If, Else, While, + For, + In, Break, Continue, Return, diff --git a/compiler/NubLang/Syntax/Tokenizer.cs b/compiler/NubLang/Syntax/Tokenizer.cs index 166fede..565ab43 100644 --- a/compiler/NubLang/Syntax/Tokenizer.cs +++ b/compiler/NubLang/Syntax/Tokenizer.cs @@ -10,6 +10,8 @@ public sealed class Tokenizer ["if"] = Symbol.If, ["else"] = Symbol.Else, ["while"] = Symbol.While, + ["for"] = Symbol.For, + ["in"] = Symbol.In, ["break"] = Symbol.Break, ["continue"] = Symbol.Continue, ["return"] = Symbol.Return, diff --git a/examples/hello-world/main.nub b/examples/hello-world/main.nub index 3b48606..d63504f 100644 --- a/examples/hello-world/main.nub +++ b/examples/hello-world/main.nub @@ -2,9 +2,19 @@ module "main" extern "puts" func puts(text: cstring) +struct Test +{ + data: [23]cstring +} + extern "main" func main(argc: i64, argv: [?]cstring): i64 { - defer puts("Bye cruel world!") - puts("Hello world!") + let names = struct Test {} + + for name, i in names.data + { + puts(name) + } + return 0 } \ No newline at end of file