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

@@ -487,6 +487,9 @@ public class QBEGenerator
case AssignmentNode assignment: case AssignmentNode assignment:
EmitCopyInto(assignment.Value, EmitAddressOf(assignment.Target)); EmitCopyInto(assignment.Value, EmitAddressOf(assignment.Target));
break; break;
case BlockNode block:
EmitBlock(block);
break;
case BreakNode: case BreakNode:
EmitScopeCleanup(); EmitScopeCleanup();
_writer.Indented($"jmp {_breakLabels.Peek()}"); _writer.Indented($"jmp {_breakLabels.Peek()}");
@@ -510,15 +513,9 @@ public class QBEGenerator
case VariableDeclarationNode variableDeclaration: case VariableDeclarationNode variableDeclaration:
EmitVariableDeclaration(variableDeclaration); EmitVariableDeclaration(variableDeclaration);
break; break;
case DeferNode defer:
Scope.DeferredStatements.Push(defer.Statement);
break;
case WhileNode whileStatement: case WhileNode whileStatement:
EmitWhile(whileStatement); EmitWhile(whileStatement);
break; break;
case ForArrayNode forArray:
EmitForArray(forArray);
break;
default: default:
throw new ArgumentOutOfRangeException(nameof(statement)); throw new ArgumentOutOfRangeException(nameof(statement));
} }
@@ -526,11 +523,6 @@ public class QBEGenerator
private void EmitScopeCleanup() private void EmitScopeCleanup()
{ {
while (Scope.DeferredStatements.TryPop(out var deferredStatement))
{
EmitStatement(deferredStatement);
}
while (Scope.Variables.TryPop(out var variable)) while (Scope.Variables.TryPop(out var variable))
{ {
if (variable.Type is NubStructType structType) if (variable.Type is NubStructType structType)
@@ -613,54 +605,6 @@ public class QBEGenerator
_breakLabels.Pop(); _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) private string EmitExpression(ExpressionNode expr)
{ {
return expr switch return expr switch
@@ -1338,7 +1282,6 @@ public class QBEGenerator
public class Scope public class Scope
{ {
public readonly Stack<StatementNode> DeferredStatements = [];
public readonly Stack<Variable> Variables = []; public readonly Stack<Variable> Variables = [];
} }

View File

@@ -241,8 +241,6 @@ public sealed class Parser
return ParseIf(startIndex); return ParseIf(startIndex);
case Symbol.While: case Symbol.While:
return ParseWhile(startIndex); return ParseWhile(startIndex);
case Symbol.For:
return ParseFor(startIndex);
case Symbol.Let: case Symbol.Let:
return ParseVariableDeclaration(startIndex); return ParseVariableDeclaration(startIndex);
case Symbol.Defer: case Symbol.Defer:
@@ -332,23 +330,6 @@ public sealed class Parser
return new WhileSyntax(GetTokens(startIndex), condition, body); 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) private ExpressionSyntax ParseExpression(int precedence = 0)
{ {
var startIndex = _tokenIndex; var startIndex = _tokenIndex;

View File

@@ -22,6 +22,4 @@ public record BreakSyntax(IEnumerable<Token> Tokens) : StatementSyntax(Tokens);
public record DeferSyntax(IEnumerable<Token> Tokens, StatementSyntax Statement) : StatementSyntax(Tokens); public record DeferSyntax(IEnumerable<Token> Tokens, StatementSyntax Statement) : StatementSyntax(Tokens);
public record WhileSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); public record WhileSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens);
public record ForSyntax(IEnumerable<Token> Tokens, string ElementIdent, string? IndexIdent, ExpressionSyntax Target, BlockSyntax Body) : StatementSyntax(Tokens);

View File

@@ -22,8 +22,4 @@ public record ContinueNode : TerminalStatementNode;
public record BreakNode : TerminalStatementNode; public record BreakNode : TerminalStatementNode;
public record DeferNode(StatementNode Statement) : StatementNode; public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode;
public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode;
public record ForArrayNode(NubArrayType ArrayType, string ElementIdent, string? IndexIdent, ExpressionNode Target, BlockNode Body) : StatementNode;

View File

@@ -1,4 +1,5 @@
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using NubLang.Diagnostics; using NubLang.Diagnostics;
using NubLang.Modules; using NubLang.Modules;
@@ -125,7 +126,7 @@ public sealed class TypeChecker
} }
_funcReturnTypes.Push(ResolveType(function.Signature.ReturnType)); _funcReturnTypes.Push(ResolveType(function.Signature.ReturnType));
var body = CheckBlock(function.Body, CurrentScope); var body = CheckBlock(function.Body);
_funcReturnTypes.Pop(); _funcReturnTypes.Pop();
return new StructFuncNode(function.Name, function.Hook, CheckFuncSignature(function.Signature), body); return new StructFuncNode(function.Name, function.Hook, CheckFuncSignature(function.Signature), body);
} }
@@ -143,10 +144,9 @@ public sealed class TypeChecker
private FuncNode CheckFuncDefinition(FuncSyntax node) private FuncNode CheckFuncDefinition(FuncSyntax node)
{ {
var scope = new Scope();
foreach (var parameter in node.Signature.Parameters) 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); var signature = CheckFuncSignature(node.Signature);
@@ -156,7 +156,7 @@ public sealed class TypeChecker
{ {
_funcReturnTypes.Push(signature.ReturnType); _funcReturnTypes.Push(signature.ReturnType);
body = CheckBlock(node.Body, scope); body = CheckBlock(node.Body);
if (!AlwaysReturns(body)) if (!AlwaysReturns(body))
{ {
@@ -179,24 +179,6 @@ public sealed class TypeChecker
return new FuncNode(CurrentModule, node.Name, node.ExternSymbol, signature, body); 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) private AssignmentNode CheckAssignment(AssignmentSyntax statement)
{ {
var target = CheckExpression(statement.Target); var target = CheckExpression(statement.Target);
@@ -272,11 +254,6 @@ public sealed class TypeChecker
return new VariableDeclarationNode(statement.Name, Optional.OfNullable(assignmentNode), type); 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) private WhileNode CheckWhile(WhileSyntax statement)
{ {
var condition = CheckExpression(statement.Condition, new NubBoolType()); var condition = CheckExpression(statement.Condition, new NubBoolType());
@@ -284,31 +261,6 @@ public sealed class TypeChecker
return new WhileNode(condition, body); 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) private FuncSignatureNode CheckFuncSignature(FuncSignatureSyntax statement)
{ {
var parameters = new List<FuncParameterNode>(); var parameters = new List<FuncParameterNode>();
@@ -802,42 +754,98 @@ public sealed class TypeChecker
return new StructInitializerNode(structType, initializers); return new StructInitializerNode(structType, initializers);
} }
private BlockNode CheckBlock(BlockSyntax node, Scope? scope = null) private BlockNode CheckBlock(BlockSyntax node)
{ {
var statements = new List<StatementNode>(); var statements = new List<StatementNode>();
_scopes.Push(scope ?? CurrentScope.SubScope());
var reachable = true; var reachable = true;
var warnedUnreachable = false; var warnedUnreachable = false;
BeginScope(false);
foreach (var statement in node.Statements) foreach (var statement in node.Statements)
{ {
var checkedStatement = CheckStatement(statement); if (statement is DeferSyntax deferStmt)
if (reachable)
{ {
statements.Add(checkedStatement); CurrentScope.PushDefer(deferStmt);
if (checkedStatement is TerminalStatementNode)
{
reachable = false;
}
} }
else else
{ {
if (!warnedUnreachable) var checkedStatement = CheckStatement(statement);
if (reachable)
{ {
Diagnostics.Add(Diagnostic.Warning("Statement is unreachable").At(statement).Build()); if (checkedStatement is TerminalStatementNode)
warnedUnreachable = true; {
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); 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) private bool AlwaysReturns(StatementNode statement)
{ {
switch (statement) switch (statement)
@@ -949,6 +957,7 @@ public record Variable(string Name, NubType Type, VariableKind Kind);
public class Scope(Scope? parent = null) public class Scope(Scope? parent = null)
{ {
private readonly Stack<DeferSyntax> _defers = [];
private readonly List<Variable> _variables = []; private readonly List<Variable> _variables = [];
public Variable? LookupVariable(string name) public Variable? LookupVariable(string name)
@@ -967,6 +976,16 @@ public class Scope(Scope? parent = null)
_variables.Add(variable); _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() public Scope SubScope()
{ {
return new Scope(this); return new Scope(this);

View File

@@ -12,9 +12,7 @@ struct Human
extern "main" func main(args: []cstring): i64 extern "main" func main(args: []cstring): i64
{ {
let x: ref<Human> = {} let x: ref<Human> = {}
test(x) test(x)
return 0 return 0
} }