Binder refactoring, Monster refactoring
This commit is contained in:
@@ -11,8 +11,7 @@ uses
|
||||
Myc.Ast.Visitor,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Types,
|
||||
Myc.Ast,
|
||||
Myc.Ast.Binding.Nodes;
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
IAstTCO = interface(IAstVisitor)
|
||||
@@ -21,22 +20,27 @@ type
|
||||
|
||||
// This transformer runs *after* the Lowerer (Phase 4).
|
||||
// Its sole responsibility is to identify tail calls (TCO)
|
||||
// and set the `IsTailCall` flag on TBoundFunctionCallNode.
|
||||
// and set the `IsTailCall` flag on TFunctionCallNode.
|
||||
TAstTCO = class(TAstTransformer, IAstTCO)
|
||||
private
|
||||
FIsTailStack: TStack<Boolean>;
|
||||
FNextIsTail: Boolean;
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
function Accept(const Node: IAstNode): IAstNode; override;
|
||||
// Overrides for TCO propagation
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
|
||||
|
||||
// Operands are never in tail position.
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode; override;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode; override;
|
||||
|
||||
// The core TCO logic
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
@@ -70,140 +74,169 @@ end;
|
||||
|
||||
function TAstTCO.Execute(const RootNode: IAstNode): IAstNode;
|
||||
begin
|
||||
var transformedValue := Accept(RootNode);
|
||||
if transformedValue.IsVoid then
|
||||
Result := TAst.Block([])
|
||||
else
|
||||
Result := transformedValue.AsIntf<IAstNode>;
|
||||
Result := Accept(RootNode); // Use IAstNode-returning Accept
|
||||
if not Assigned(Result) then
|
||||
Result := TAst.Block([]);
|
||||
end;
|
||||
|
||||
function TAstTCO.Accept(const Node: IAstNode): TDataValue;
|
||||
function TAstTCO.Accept(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
if (not Assigned(Node)) or Done then
|
||||
if (not Assigned(Node)) then
|
||||
begin
|
||||
Result := nil;
|
||||
exit;
|
||||
end;
|
||||
|
||||
FIsTailStack.Push(FNextIsTail);
|
||||
try
|
||||
// Call inherited Accept, which handles the data unwrapping
|
||||
Result := inherited Accept(Node);
|
||||
finally
|
||||
FNextIsTail := FIsTailStack.Pop;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
function TAstTCO.VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode;
|
||||
begin
|
||||
// Operands are never in tail position.
|
||||
FNextIsTail := False;
|
||||
|
||||
// Call inherited, which will visit Left and Right with FNextIsTail = False
|
||||
Result := inherited VisitBinaryExpression(Node);
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode;
|
||||
begin
|
||||
// Operand is never in tail position.
|
||||
FNextIsTail := False;
|
||||
|
||||
// Call inherited, which will visit Right with FNextIsTail = False
|
||||
Result := inherited VisitUnaryExpression(Node);
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
||||
var
|
||||
i: Integer;
|
||||
isContextTail: Boolean;
|
||||
N: TBlockExpressionNode;
|
||||
begin
|
||||
N := (Node as TBlockExpressionNode);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
for i := 0 to High(Node.Expressions) do
|
||||
|
||||
// We must manually iterate here to set FNextIsTail for each child
|
||||
for i := 0 to High(N.Expressions) do
|
||||
begin
|
||||
// Only the last expression in a block is in tail position
|
||||
FNextIsTail := isContextTail and (i = High(Node.Expressions));
|
||||
Accept(Node.Expressions[i]);
|
||||
FNextIsTail := isContextTail and (i = High(N.Expressions));
|
||||
N.Expressions[i] := Accept(N.Expressions[i]);
|
||||
end;
|
||||
Result := TDataValue.FromIntf<IBlockExpressionNode>(Node);
|
||||
Result := N;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
|
||||
var
|
||||
isContextTail: Boolean;
|
||||
N: TIfExpressionNode;
|
||||
begin
|
||||
N := (Node as TIfExpressionNode);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
// Condition is never in tail position
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Condition);
|
||||
N.Condition := Accept(N.Condition);
|
||||
|
||||
// Then/Else branches ARE in tail position if the IfExpr is
|
||||
FNextIsTail := isContextTail;
|
||||
Accept(Node.ThenBranch);
|
||||
if Assigned(Node.ElseBranch) then
|
||||
Accept(Node.ElseBranch);
|
||||
N.ThenBranch := Accept(N.ThenBranch);
|
||||
N.ElseBranch := Accept(N.ElseBranch);
|
||||
|
||||
Result := TDataValue.FromIntf<IIfExpressionNode>(Node);
|
||||
Result := N;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
function TAstTCO.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
|
||||
var
|
||||
isContextTail: Boolean;
|
||||
N: TTernaryExpressionNode;
|
||||
begin
|
||||
N := (Node as TTernaryExpressionNode);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
// Condition is never in tail position
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Condition);
|
||||
N.Condition := Accept(N.Condition);
|
||||
|
||||
// Then/Else branches ARE in tail position if the TernaryExpr is
|
||||
FNextIsTail := isContextTail;
|
||||
Accept(Node.ThenBranch);
|
||||
Accept(Node.ElseBranch);
|
||||
N.ThenBranch := Accept(N.ThenBranch);
|
||||
N.ElseBranch := Accept(N.ElseBranch);
|
||||
|
||||
Result := TDataValue.FromIntf<ITernaryExpressionNode>(Node);
|
||||
Result := N;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
var
|
||||
N: TLambdaExpressionNode;
|
||||
begin
|
||||
// Parameters are never in tail position
|
||||
FNextIsTail := False;
|
||||
AcceptNodes<IIdentifierNode>(Node.Parameters);
|
||||
N := (Node as TLambdaExpressionNode);
|
||||
|
||||
// The body of a lambda is *always* a tail position (relative to the lambda)
|
||||
FNextIsTail := True;
|
||||
Accept(Node.Body);
|
||||
N.Body := Accept(N.Body);
|
||||
|
||||
Result := TDataValue.FromIntf<ILambdaExpressionNode>(Node);
|
||||
Result := N;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
function TAstTCO.VisitRecurNode(const Node: IRecurNode): IAstNode;
|
||||
var
|
||||
N: TRecurNode;
|
||||
begin
|
||||
if not FIsTailStack.Peek then
|
||||
raise Exception.Create('''recur'' can only be used in a tail position.');
|
||||
|
||||
N := (Node as TRecurNode);
|
||||
|
||||
// Arguments are not in tail position
|
||||
FNextIsTail := False;
|
||||
AcceptNodes<IAstNode>(Node.Arguments);
|
||||
N.Arguments := AcceptNodes<IAstNode>(N.Arguments, function(Node: IAstNode): IAstNode begin exit(Node) end);
|
||||
|
||||
Result := TDataValue.FromIntf<IRecurNode>(Node);
|
||||
Result := N;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
|
||||
var
|
||||
N: TMacroExpansionNode;
|
||||
begin
|
||||
N := (Node as TMacroExpansionNode);
|
||||
|
||||
// Propagate tail call status to the expanded body
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
Accept(Node.ExpandedBody);
|
||||
N.ExpandedBody := Accept(N.ExpandedBody);
|
||||
|
||||
// Also visit the original call nodes, though they are not in tail pos
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Callee);
|
||||
AcceptNodes<IAstNode>(Node.Arguments);
|
||||
N.Callee := Accept(N.Callee);
|
||||
N.Arguments := AcceptNodes<IAstNode>(N.Arguments, function(Node: IAstNode): IAstNode begin exit(Node) end);
|
||||
|
||||
Result := TDataValue.FromIntf<IMacroExpansionNode>(Node);
|
||||
Result := N;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
var
|
||||
callee: IAstNode;
|
||||
args: TArray<IAstNode>;
|
||||
isTailCall: Boolean;
|
||||
N: TFunctionCallNode;
|
||||
begin
|
||||
isTailCall := FIsTailStack.Peek;
|
||||
N := (Node as TFunctionCallNode);
|
||||
|
||||
// Arguments are not in tail position
|
||||
FNextIsTail := False;
|
||||
callee := Accept(Node.Callee).AsIntf<IAstNode>;
|
||||
args := AcceptNodes<IAstNode>(Node.Arguments);
|
||||
N.Callee := Accept(N.Callee);
|
||||
N.Arguments := AcceptNodes<IAstNode>(N.Arguments, function(Node: IAstNode): IAstNode begin exit(Node) end);
|
||||
|
||||
// If TCO status changed, we MUST rebuild the node.
|
||||
var boundNode := (Node as TBoundFunctionCallNode);
|
||||
if (boundNode.IsTailCall <> isTailCall) or (callee <> Node.Callee) or (args <> Node.Arguments) then
|
||||
begin
|
||||
var newBoundCall := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall);
|
||||
(newBoundCall as TAstNode).StaticType := (Node as TAstNode).StaticType;
|
||||
Result := TDataValue.FromIntf<IFunctionCallNode>(newBoundCall);
|
||||
end
|
||||
else
|
||||
Result := TDataValue.FromIntf<IFunctionCallNode>(Node); // No change
|
||||
// Mutate the node with the TCO status
|
||||
N.IsTailCall := isTailCall;
|
||||
|
||||
Result := N;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user