From c9e34ae7e2044f7f68bf33aff0bdbed4eafb1b72 Mon Sep 17 00:00:00 2001 From: nub31 Date: Tue, 16 Sep 2025 16:02:08 +0200 Subject: [PATCH] ... --- .../NubLang/Generation/QBE/QBEGenerator.cs | 59 ++++++++++++++++++- compiler/NubLang/Parsing/Parser.cs | 23 +++++++- .../NubLang/Parsing/Syntax/StatementSyntax.cs | 4 +- compiler/NubLang/Tokenization/Token.cs | 1 + compiler/NubLang/Tokenization/Tokenizer.cs | 1 + .../TypeChecking/Node/StatementNode.cs | 4 +- compiler/NubLang/TypeChecking/TypeChecker.cs | 49 +++++++++++---- example/src/main.nub | 25 +++----- 8 files changed, 130 insertions(+), 36 deletions(-) diff --git a/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 184961d..d7c19ae 100644 --- a/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -528,6 +528,9 @@ public class QBEGenerator case WhileNode whileStatement: EmitWhile(whileStatement); break; + case ForArrayNode forArray: + EmitForArray(forArray); + break; default: throw new ArgumentOutOfRangeException(nameof(statement)); } @@ -615,6 +618,54 @@ public class QBEGenerator _breakLabels.Pop(); } + // todo(nub31): Implement index ident + private void EmitForArray(ForArrayNode forArray) + { + var target = EmitExpression(forArray.Target); + + var arrayStart = TmpName(); + _writer.Indented($"{arrayStart} =l add {target}, 8"); + + var size = TmpName(); + _writer.Indented($"{size} =l loadl {target}"); + + var count = TmpName(); + _writer.Indented($"{count} =l copy 0"); + + var loopLabel = LabelName(); + _writer.WriteLine(loopLabel); + + var condition = TmpName(); + _writer.Indented($"{condition} =w cultl {count}, {size}"); + + var continueLabel = LabelName(); + var endLabel = LabelName(); + _writer.Indented($"jnz {condition}, {continueLabel}, {endLabel}"); + _writer.WriteLine(continueLabel); + + var arrayOffset = TmpName(); + _writer.Indented($"{arrayOffset} =l mul {count}, {SizeOf(forArray.ArrayType.ElementType)}"); + var elementAddress = TmpName(); + _writer.Indented($"{elementAddress} =l add {arrayStart}, {arrayOffset}"); + + if (forArray.ArrayType.ElementType is StructTypeNode) + { + _writer.Indented($"%{forArray.ElementIdent} =l copy {elementAddress}"); + } + else + { + var element = EmitLoad(forArray.ArrayType.ElementType, elementAddress); + _writer.Indented($"%{forArray.ElementIdent} {QBEAssign(forArray.ArrayType.ElementType)} copy {element}"); + } + + EmitBlock(forArray.Body); + + _writer.Indented($"{count} =l add {count}, 1"); + _writer.Indented($"jmp {loopLabel}"); + + _writer.WriteLine(endLabel); + } + private string EmitExpression(ExpressionNode expression) { return expression switch @@ -647,10 +698,12 @@ public class QBEGenerator private string EmitVariableIdentifier(VariableIdentifierNode variableIdent) { var address = EmitAddressOfVariableIdent(variableIdent); + if (variableIdent.Type is StructTypeNode) + { + return address; + } - return variableIdent.Type.IsSimpleType(out _, out _) - ? EmitLoad(variableIdent.Type, address) - : address; + return EmitLoad(variableIdent.Type, address); } private string EmitParameterFuncIdentifier(FuncParameterIdentifierNode funcParameterIdent) diff --git a/compiler/NubLang/Parsing/Parser.cs b/compiler/NubLang/Parsing/Parser.cs index be20d21..913ed87 100644 --- a/compiler/NubLang/Parsing/Parser.cs +++ b/compiler/NubLang/Parsing/Parser.cs @@ -156,7 +156,6 @@ public sealed class Parser BlockSyntax? body = null; if (CurrentToken is SymbolToken { Symbol: Symbol.OpenBrace }) { - Next(); body = ParseBlock(); } @@ -216,6 +215,8 @@ public sealed class Parser return ParseIf(); case Symbol.While: return ParseWhile(); + case Symbol.For: + return ParseFor(); case Symbol.Let: return ParseVariableDeclaration(); case Symbol.Break: @@ -319,6 +320,25 @@ public sealed class Parser return new WhileSyntax(GetTokens(startIndex), condition, body); } + private ForSyntax ParseFor() + { + var startIndex = _tokenIndex; + ExpectSymbol(Symbol.For); + var elementIdent = ExpectIdentifier().Value; + + string? indexIndent = null; + if (TryExpectSymbol(Symbol.Comma)) + { + indexIndent = ExpectIdentifier().Value; + } + + ExpectSymbol(Symbol.In); + var target = ParseExpression(); + var body = ParseBlock(); + + return new ForSyntax(GetTokens(startIndex), elementIdent, indexIndent, target, body); + } + private ExpressionSyntax ParseExpression(int precedence = 0) { var startIndex = _tokenIndex; @@ -595,6 +615,7 @@ public sealed class Parser { var startIndex = _tokenIndex; List statements = []; + ExpectSymbol(Symbol.OpenBrace); while (!TryExpectSymbol(Symbol.CloseBrace)) { try diff --git a/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs b/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs index acf1d8c..a3e421d 100644 --- a/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs +++ b/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs @@ -18,4 +18,6 @@ public record ContinueSyntax(IEnumerable Tokens) : StatementSyntax(Tokens public record BreakSyntax(IEnumerable Tokens) : StatementSyntax(Tokens); -public record WhileSyntax(IEnumerable Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); \ No newline at end of file +public record WhileSyntax(IEnumerable Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); + +public record ForSyntax(IEnumerable Tokens, string ElementIdent, string? IndexIdent, ExpressionSyntax Target, BlockSyntax Body) : StatementSyntax(Tokens); \ No newline at end of file diff --git a/compiler/NubLang/Tokenization/Token.cs b/compiler/NubLang/Tokenization/Token.cs index ec9709b..764c528 100644 --- a/compiler/NubLang/Tokenization/Token.cs +++ b/compiler/NubLang/Tokenization/Token.cs @@ -68,6 +68,7 @@ public enum Symbol Let, Calls, For, + In, Extern, Semi, Percent, diff --git a/compiler/NubLang/Tokenization/Tokenizer.cs b/compiler/NubLang/Tokenization/Tokenizer.cs index 33ff5f1..7478a5d 100644 --- a/compiler/NubLang/Tokenization/Tokenizer.cs +++ b/compiler/NubLang/Tokenization/Tokenizer.cs @@ -18,6 +18,7 @@ public sealed class Tokenizer ["let"] = Symbol.Let, ["calls"] = Symbol.Calls, ["for"] = Symbol.For, + ["in"] = Symbol.In, ["extern"] = Symbol.Extern, ["module"] = Symbol.Module, ["export"] = Symbol.Export, diff --git a/compiler/NubLang/TypeChecking/Node/StatementNode.cs b/compiler/NubLang/TypeChecking/Node/StatementNode.cs index bd58462..934a7d1 100644 --- a/compiler/NubLang/TypeChecking/Node/StatementNode.cs +++ b/compiler/NubLang/TypeChecking/Node/StatementNode.cs @@ -16,4 +16,6 @@ public record ContinueNode : StatementNode; public record BreakNode : StatementNode; -public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode; \ No newline at end of file +public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode; + +public record ForArrayNode(ArrayTypeNode ArrayType, string ElementIdent, string? IndexIdent, ExpressionNode Target, BlockNode Body) : StatementNode; \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/TypeChecker.cs b/compiler/NubLang/TypeChecking/TypeChecker.cs index b1a7300..1518ffa 100644 --- a/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -14,9 +14,6 @@ public sealed class TypeChecker private readonly Stack _scopes = []; private readonly Stack _funcReturnTypes = []; - private readonly List _diagnostics = []; - private readonly List _referencedStructTypes = []; - private readonly List _definitions = []; private Scope Scope => _scopes.Peek(); @@ -29,27 +26,27 @@ public sealed class TypeChecker .ToDictionary(); } - public List Definitions => _definitions; - public List Diagnostics => _diagnostics; - public List ReferencedStructTypes => _referencedStructTypes; + public List Definitions { get; } = []; + public List Diagnostics { get; } = []; + public List ReferencedStructTypes { get; } = []; public void Check() { _scopes.Clear(); _funcReturnTypes.Clear(); - _diagnostics.Clear(); - _referencedStructTypes.Clear(); - _definitions.Clear(); + Diagnostics.Clear(); + Definitions.Clear(); + ReferencedStructTypes.Clear(); foreach (var definition in _syntaxTree.Definitions) { try { - _definitions.Add(CheckDefinition(definition)); + Definitions.Add(CheckDefinition(definition)); } catch (TypeCheckerException e) { - _diagnostics.Add(e.Diagnostic); + Diagnostics.Add(e.Diagnostic); } } } @@ -143,6 +140,7 @@ public sealed class TypeChecker StatementExpressionSyntax statement => CheckStatementExpression(statement), VariableDeclarationSyntax statement => CheckVariableDeclaration(statement), WhileSyntax statement => CheckWhile(statement), + ForSyntax statement => CheckFor(statement), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } @@ -222,6 +220,31 @@ public sealed class TypeChecker return new WhileNode(condition, body); } + private StatementNode CheckFor(ForSyntax statement) + { + var target = CheckExpression(statement.Target); + + switch (target.Type) + { + case ArrayTypeNode arrayType: + { + var scope = Scope.SubScope(); + scope.Declare(new Identifier(statement.ElementIdent, arrayType.ElementType, IdentifierKind.FunctionParameter)); + if (statement.IndexIdent != null) + { + scope.Declare(new Identifier(statement.ElementIdent, new IntTypeNode(true, 64), IdentifierKind.FunctionParameter)); + } + + var body = CheckBlock(statement.Body, scope); + return new ForArrayNode(arrayType, statement.ElementIdent, statement.IndexIdent, target, body); + } + default: + { + throw new TypeCheckerException(Diagnostic.Error($"Type {target.Type} is not an iterable target").Build()); + } + } + } + private FuncSignatureNode CheckFuncSignature(FuncSignatureSyntax statement) { var parameters = new List(); @@ -659,7 +682,7 @@ public sealed class TypeChecker var typeField = structType.Fields.FirstOrDefault(x => x.Name == initializer.Key); if (typeField == null) { - _diagnostics.AddRange(Diagnostic + Diagnostics.AddRange(Diagnostic .Error($"Struct {structType.Name} does not have a field named {initializer.Key}") .At(initializer.Value) .Build()); @@ -764,7 +787,7 @@ public sealed class TypeChecker result.Functions.Add(new StructTypeFunc(function.Name, type)); } - _referencedStructTypes.Add(result); + ReferencedStructTypes.Add(result); return result; } diff --git a/example/src/main.nub b/example/src/main.nub index 32b1724..c091174 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -2,26 +2,17 @@ module "main" extern "puts" func puts(text: cstring) -struct Name -{ - first: cstring - last: cstring -} - -struct Human -{ - name: Name -} - extern "main" func main(args: []cstring): i64 { - let x: Human = { - name = { - first = "oliver" - last = "stene" - } + let x = [2]cstring + + x[0] = "test1" + x[1] = "test2" + + for u in x + { + puts(u) } - puts(x.name.last) return 0 }