...
This commit is contained in:
@@ -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<StatementNode> DeferredStatements = [];
|
||||
public readonly Stack<Variable> Variables = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -22,6 +22,4 @@ public record BreakSyntax(IEnumerable<Token> Tokens) : 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 ForSyntax(IEnumerable<Token> Tokens, string ElementIdent, string? IndexIdent, ExpressionSyntax Target, BlockSyntax Body) : StatementSyntax(Tokens);
|
||||
public record WhileSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens);
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user