Imutability for macro nodes
This commit is contained in:
@@ -116,79 +116,109 @@ end;
|
||||
|
||||
function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
||||
var
|
||||
i: Integer;
|
||||
isContextTail: Boolean;
|
||||
N: TBlockExpressionNode;
|
||||
newExprs: TArray<IAstNode>; // For CoW
|
||||
begin
|
||||
N := (Node as TBlockExpressionNode);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
// 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(N.Expressions));
|
||||
N.Expressions[i] := Accept(N.Expressions[i]);
|
||||
end;
|
||||
Result := N;
|
||||
// Call the base implementation which handles CoW
|
||||
var nTail := High(Node.Expressions);
|
||||
newExprs :=
|
||||
AcceptNodes(
|
||||
Node.Expressions,
|
||||
function(idx: Integer; Node: IAstNode): IAstNode
|
||||
begin
|
||||
FNextIsTail := isContextTail and (idx = nTail);
|
||||
Result := Accept(Node);
|
||||
end
|
||||
);
|
||||
|
||||
if newExprs = Node.Expressions then
|
||||
Result := Node
|
||||
else
|
||||
Result := TBlockExpressionNode.Create(newExprs, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
|
||||
var
|
||||
isContextTail: Boolean;
|
||||
N: TIfExpressionNode;
|
||||
newCond, newThen, newElse: IAstNode;
|
||||
begin
|
||||
N := (Node as TIfExpressionNode);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
// Condition is never in tail position
|
||||
FNextIsTail := False;
|
||||
N.Condition := Accept(N.Condition);
|
||||
newCond := Accept(N.Condition);
|
||||
|
||||
// Then/Else branches ARE in tail position if the IfExpr is
|
||||
FNextIsTail := isContextTail;
|
||||
N.ThenBranch := Accept(N.ThenBranch);
|
||||
N.ElseBranch := Accept(N.ElseBranch);
|
||||
newThen := Accept(N.ThenBranch);
|
||||
newElse := Accept(N.ElseBranch);
|
||||
|
||||
Result := N;
|
||||
if (newCond = N.Condition) and (newThen = N.ThenBranch) and (newElse = N.ElseBranch) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TIfExpressionNode.Create(newCond, newThen, newElse, N.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
|
||||
var
|
||||
isContextTail: Boolean;
|
||||
N: TTernaryExpressionNode;
|
||||
newCond, newThen, newElse: IAstNode;
|
||||
begin
|
||||
N := (Node as TTernaryExpressionNode);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
// Condition is never in tail position
|
||||
FNextIsTail := False;
|
||||
N.Condition := Accept(N.Condition);
|
||||
newCond := Accept(N.Condition);
|
||||
|
||||
// Then/Else branches ARE in tail position if the TernaryExpr is
|
||||
FNextIsTail := isContextTail;
|
||||
N.ThenBranch := Accept(N.ThenBranch);
|
||||
N.ElseBranch := Accept(N.ElseBranch);
|
||||
newThen := Accept(N.ThenBranch);
|
||||
newElse := Accept(N.ElseBranch);
|
||||
|
||||
Result := N;
|
||||
if (newCond = N.Condition) and (newThen = N.ThenBranch) and (newElse = N.ElseBranch) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TTernaryExpressionNode.Create(newCond, newThen, newElse, N.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
var
|
||||
N: TLambdaExpressionNode;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
begin
|
||||
N := (Node as TLambdaExpressionNode);
|
||||
|
||||
// Parameters are not in tail position (handled by AcceptParameters)
|
||||
FNextIsTail := False;
|
||||
newParams := AcceptParameters(N.Parameters);
|
||||
|
||||
// The body of a lambda is *always* a tail position (relative to the lambda)
|
||||
FNextIsTail := True;
|
||||
N.Body := Accept(N.Body);
|
||||
newBody := Accept(N.Body);
|
||||
|
||||
Result := N;
|
||||
if (newParams = N.Parameters) and (newBody = N.Body) then
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
Result := TLambdaExpressionNode.Create(newParams, newBody, N.StaticType);
|
||||
// Copy runtime properties
|
||||
(Result as TLambdaExpressionNode).ScopeDescriptor := N.ScopeDescriptor;
|
||||
(Result as TLambdaExpressionNode).Upvalues := N.Upvalues;
|
||||
(Result as TLambdaExpressionNode).HasNestedLambdas := N.HasNestedLambdas;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitRecurNode(const Node: IRecurNode): IAstNode;
|
||||
var
|
||||
N: TRecurNode;
|
||||
newArgs: TArray<IAstNode>;
|
||||
begin
|
||||
if not FIsTailStack.Peek then
|
||||
raise Exception.Create('''recur'' can only be used in a tail position.');
|
||||
@@ -197,41 +227,56 @@ begin
|
||||
|
||||
// Arguments are not in tail position
|
||||
FNextIsTail := False;
|
||||
N.Arguments := AcceptNodes(N.Arguments);
|
||||
newArgs := AcceptNodes(N.Arguments);
|
||||
|
||||
Result := N;
|
||||
if newArgs = N.Arguments then
|
||||
Result := Node
|
||||
else
|
||||
Result := TRecurNode.Create(newArgs, N.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
|
||||
var
|
||||
N: TMacroExpansionNode;
|
||||
newBody: IAstNode;
|
||||
begin
|
||||
N := (Node as TMacroExpansionNode);
|
||||
|
||||
// Propagate tail call status to the expanded body
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
N.ExpandedBody := Accept(N.ExpandedBody);
|
||||
newBody := Accept(N.ExpandedBody);
|
||||
|
||||
Result := N;
|
||||
if newBody = N.ExpandedBody then
|
||||
Result := Node
|
||||
else
|
||||
// Rebuild, preserving the original CallNode metadata
|
||||
Result := TMacroExpansionNode.Create(N.CallNode, newBody, newBody.AsTypedNode.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
var
|
||||
isTailCall: Boolean;
|
||||
N: TFunctionCallNode;
|
||||
newCallee: IAstNode;
|
||||
newArgs: TArray<IAstNode>;
|
||||
begin
|
||||
isTailCall := FIsTailStack.Peek;
|
||||
N := (Node as TFunctionCallNode);
|
||||
|
||||
// Arguments are not in tail position
|
||||
FNextIsTail := False;
|
||||
N.Callee := Accept(N.Callee);
|
||||
N.Arguments := AcceptNodes(N.Arguments);
|
||||
newCallee := Accept(N.Callee);
|
||||
newArgs := AcceptNodes(N.Arguments);
|
||||
|
||||
// Mutate the node with the TCO status
|
||||
N.IsTailCall := isTailCall;
|
||||
|
||||
Result := N;
|
||||
// CoW check: Create a new node only if children changed OR IsTailCall needs update
|
||||
if (newCallee = N.Callee) and (newArgs = N.Arguments) and (isTailCall = N.IsTailCall) then
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
Result := TFunctionCallNode.Create(newCallee, newArgs, N.StaticType);
|
||||
// Mutate the *new* node with the TCO status
|
||||
(Result as TFunctionCallNode).IsTailCall := isTailCall;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user