This commit is contained in:
nub31
2025-09-20 19:53:40 +02:00
parent 6e403a9884
commit b5c9fa2303
6 changed files with 91 additions and 156 deletions

View File

@@ -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;
public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode;

View File

@@ -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<FuncParameterNode>();
@@ -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<StatementNode>();
_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<DeferSyntax> _defers = [];
private readonly List<Variable> _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);