...
This commit is contained in:
@@ -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 = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -23,5 +23,3 @@ 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);
|
|
||||||
@@ -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;
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user