From b5c9fa2303d6c0f67bad4f737b2551787129f48c Mon Sep 17 00:00:00 2001 From: nub31 Date: Sat, 20 Sep 2025 19:53:40 +0200 Subject: [PATCH] ... --- .../NubLang/Generation/QBE/QBEGenerator.cs | 63 +------- compiler/NubLang/Parsing/Parser.cs | 19 --- .../NubLang/Parsing/Syntax/StatementSyntax.cs | 4 +- .../TypeChecking/Node/StatementNode.cs | 6 +- compiler/NubLang/TypeChecking/TypeChecker.cs | 153 ++++++++++-------- example/src/main.nub | 2 - 6 files changed, 91 insertions(+), 156 deletions(-) diff --git a/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 87c964f..32fa854 100644 --- a/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -487,6 +487,9 @@ public class QBEGenerator case AssignmentNode assignment: EmitCopyInto(assignment.Value, EmitAddressOf(assignment.Target)); break; + case BlockNode block: + EmitBlock(block); + break; case BreakNode: EmitScopeCleanup(); _writer.Indented($"jmp {_breakLabels.Peek()}"); @@ -510,15 +513,9 @@ public class QBEGenerator case VariableDeclarationNode variableDeclaration: EmitVariableDeclaration(variableDeclaration); break; - case DeferNode defer: - Scope.DeferredStatements.Push(defer.Statement); - break; case WhileNode whileStatement: EmitWhile(whileStatement); break; - case ForArrayNode forArray: - EmitForArray(forArray); - break; default: throw new ArgumentOutOfRangeException(nameof(statement)); } @@ -526,11 +523,6 @@ public class QBEGenerator private void EmitScopeCleanup() { - while (Scope.DeferredStatements.TryPop(out var deferredStatement)) - { - EmitStatement(deferredStatement); - } - while (Scope.Variables.TryPop(out var variable)) { if (variable.Type is NubStructType structType) @@ -613,54 +605,6 @@ 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 NubStructType) - { - _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 expr) { return expr switch @@ -1338,7 +1282,6 @@ public class QBEGenerator public class Scope { - public readonly Stack DeferredStatements = []; public readonly Stack Variables = []; } diff --git a/compiler/NubLang/Parsing/Parser.cs b/compiler/NubLang/Parsing/Parser.cs index d0dae35..5a4c911 100644 --- a/compiler/NubLang/Parsing/Parser.cs +++ b/compiler/NubLang/Parsing/Parser.cs @@ -241,8 +241,6 @@ 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: @@ -332,23 +330,6 @@ public sealed class Parser return new WhileSyntax(GetTokens(startIndex), condition, body); } - private ForSyntax ParseFor(int startIndex) - { - 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; diff --git a/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs b/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs index a663ab9..b13f116 100644 --- a/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs +++ b/compiler/NubLang/Parsing/Syntax/StatementSyntax.cs @@ -22,6 +22,4 @@ public record BreakSyntax(IEnumerable Tokens) : StatementSyntax(Tokens); public record DeferSyntax(IEnumerable Tokens, StatementSyntax Statement) : StatementSyntax(Tokens); -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 +public record WhileSyntax(IEnumerable Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/Node/StatementNode.cs b/compiler/NubLang/TypeChecking/Node/StatementNode.cs index 92d2b8a..363e7c0 100644 --- a/compiler/NubLang/TypeChecking/Node/StatementNode.cs +++ b/compiler/NubLang/TypeChecking/Node/StatementNode.cs @@ -22,8 +22,4 @@ public record ContinueNode : TerminalStatementNode; public record BreakNode : TerminalStatementNode; -public record DeferNode(StatementNode Statement) : StatementNode; - -public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode; - -public record ForArrayNode(NubArrayType ArrayType, string ElementIdent, string? IndexIdent, ExpressionNode Target, BlockNode Body) : StatementNode; \ No newline at end of file +public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode; \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/TypeChecker.cs b/compiler/NubLang/TypeChecking/TypeChecker.cs index 3f02fb4..d7961a6 100644 --- a/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using NubLang.Diagnostics; using NubLang.Modules; @@ -125,7 +126,7 @@ public sealed class TypeChecker } _funcReturnTypes.Push(ResolveType(function.Signature.ReturnType)); - var body = CheckBlock(function.Body, CurrentScope); + var body = CheckBlock(function.Body); _funcReturnTypes.Pop(); return new StructFuncNode(function.Name, function.Hook, CheckFuncSignature(function.Signature), body); } @@ -143,10 +144,9 @@ public sealed class TypeChecker private FuncNode CheckFuncDefinition(FuncSyntax node) { - var scope = new Scope(); foreach (var parameter in node.Signature.Parameters) { - scope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue)); + CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue)); } var signature = CheckFuncSignature(node.Signature); @@ -156,7 +156,7 @@ public sealed class TypeChecker { _funcReturnTypes.Push(signature.ReturnType); - body = CheckBlock(node.Body, scope); + body = CheckBlock(node.Body); if (!AlwaysReturns(body)) { @@ -179,24 +179,6 @@ public sealed class TypeChecker return new FuncNode(CurrentModule, node.Name, node.ExternSymbol, signature, body); } - private StatementNode CheckStatement(StatementSyntax node) - { - return node switch - { - AssignmentSyntax statement => CheckAssignment(statement), - BreakSyntax => new BreakNode(), - ContinueSyntax => new ContinueNode(), - IfSyntax statement => CheckIf(statement), - ReturnSyntax statement => CheckReturn(statement), - StatementExpressionSyntax statement => CheckStatementExpression(statement), - VariableDeclarationSyntax statement => CheckVariableDeclaration(statement), - DeferSyntax statement => CheckDefer(statement), - WhileSyntax statement => CheckWhile(statement), - ForSyntax statement => CheckFor(statement), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - } - private AssignmentNode CheckAssignment(AssignmentSyntax statement) { var target = CheckExpression(statement.Target); @@ -272,11 +254,6 @@ public sealed class TypeChecker return new VariableDeclarationNode(statement.Name, Optional.OfNullable(assignmentNode), type); } - private DeferNode CheckDefer(DeferSyntax statement) - { - return new DeferNode(CheckStatement(statement.Statement)); - } - private WhileNode CheckWhile(WhileSyntax statement) { var condition = CheckExpression(statement.Condition, new NubBoolType()); @@ -284,31 +261,6 @@ public sealed class TypeChecker return new WhileNode(condition, body); } - private StatementNode CheckFor(ForSyntax statement) - { - var target = CheckExpression(statement.Target); - - switch (target.Type) - { - case NubArrayType arrayType: - { - var scope = CurrentScope.SubScope(); - scope.DeclareVariable(new Variable(statement.ElementIdent, arrayType.ElementType, VariableKind.RValue)); - if (statement.IndexIdent != null) - { - scope.DeclareVariable(new Variable(statement.ElementIdent, new NubIntType(true, 64), VariableKind.RValue)); - } - - 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").At(statement.Target).Build()); - } - } - } - private FuncSignatureNode CheckFuncSignature(FuncSignatureSyntax statement) { var parameters = new List(); @@ -802,42 +754,98 @@ public sealed class TypeChecker return new StructInitializerNode(structType, initializers); } - private BlockNode CheckBlock(BlockSyntax node, Scope? scope = null) + private BlockNode CheckBlock(BlockSyntax node) { var statements = new List(); - _scopes.Push(scope ?? CurrentScope.SubScope()); - var reachable = true; var warnedUnreachable = false; + BeginScope(false); + foreach (var statement in node.Statements) { - var checkedStatement = CheckStatement(statement); - - if (reachable) + if (statement is DeferSyntax deferStmt) { - statements.Add(checkedStatement); - if (checkedStatement is TerminalStatementNode) - { - reachable = false; - } + CurrentScope.PushDefer(deferStmt); } else { - if (!warnedUnreachable) + var checkedStatement = CheckStatement(statement); + + if (reachable) { - Diagnostics.Add(Diagnostic.Warning("Statement is unreachable").At(statement).Build()); - warnedUnreachable = true; + if (checkedStatement is TerminalStatementNode) + { + while (CurrentScope.TryPopDefer(out var defer)) + { + var checkedDeferredStatement = CheckStatement(defer.Statement); + if (checkedDeferredStatement is TerminalStatementNode) + { + throw new TypeCheckerException(Diagnostic + .Error("Cannot defer terminal statements") + .WithHelp("Avoid using defer with terminal statements such as 'return', 'break', 'continue' etc.") + .At(defer) + .Build()); + } + + statements.Add(checkedDeferredStatement); + } + + reachable = false; + } + + statements.Add(checkedStatement); + } + else + { + if (!warnedUnreachable) + { + Diagnostics.Add(Diagnostic.Warning("Statement is unreachable").At(statement).Build()); + warnedUnreachable = true; + } } } } - _scopes.Pop(); + while (CurrentScope.TryPopDefer(out var defer)) + { + var checkedDeferredStatement = CheckStatement(defer.Statement); + if (checkedDeferredStatement is TerminalStatementNode) + { + throw new TypeCheckerException(Diagnostic + .Error("Cannot defer terminal statements") + .WithHelp("Avoid using defer with terminal statements such as 'return', 'break', 'continue' etc.") + .At(defer) + .Build()); + } + + statements.Add(checkedDeferredStatement); + } + + EndScope(); return new BlockNode(statements); } + private StatementNode CheckStatement(StatementSyntax statement) + { + return statement switch + { + AssignmentSyntax assignmentStmt => CheckAssignment(assignmentStmt), + BlockSyntax blockStmt => CheckBlock(blockStmt), + BreakSyntax => new BreakNode(), + ContinueSyntax => new ContinueNode(), + IfSyntax ifStmt => CheckIf(ifStmt), + ReturnSyntax retStmt => CheckReturn(retStmt), + StatementExpressionSyntax stmtExpr => CheckStatementExpression(stmtExpr), + VariableDeclarationSyntax varDeclStmt => CheckVariableDeclaration(varDeclStmt), + WhileSyntax whileStmt => CheckWhile(whileStmt), + DeferSyntax => throw new InvalidOperationException($"Compiler bug: defer statement should not have reached {nameof(CheckStatement)}"), + _ => throw new ArgumentOutOfRangeException(nameof(statement)) + }; + } + private bool AlwaysReturns(StatementNode statement) { switch (statement) @@ -949,6 +957,7 @@ public record Variable(string Name, NubType Type, VariableKind Kind); public class Scope(Scope? parent = null) { + private readonly Stack _defers = []; private readonly List _variables = []; public Variable? LookupVariable(string name) @@ -967,6 +976,16 @@ public class Scope(Scope? parent = null) _variables.Add(variable); } + public void PushDefer(DeferSyntax defer) + { + _defers.Push(defer); + } + + public bool TryPopDefer([NotNullWhen(true)] out DeferSyntax? defer) + { + return _defers.TryPop(out defer); + } + public Scope SubScope() { return new Scope(this); diff --git a/example/src/main.nub b/example/src/main.nub index 024d0a9..e817e04 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -12,9 +12,7 @@ struct Human extern "main" func main(args: []cstring): i64 { let x: ref = {} - test(x) - return 0 }