Ast list nodes

This commit is contained in:
Michael Schimmel
2025-11-29 18:59:16 +01:00
parent 250f950a68
commit 851f56c63f
24 changed files with 1819 additions and 863 deletions
+47 -27
View File
@@ -99,26 +99,42 @@ function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNod
var
isContextTail: Boolean;
newExprs: TArray<IAstNode>;
exprsList: IExpressionList;
i: Integer;
item, newItem: IAstNode;
hasChanged: Boolean;
begin
isContextTail := FIsTailStack.Peek;
exprsList := Node.Expressions;
var nTail := High(Node.Expressions);
newExprs :=
AcceptNodes(
Node.Expressions,
function(idx: Integer; Node: IAstNode): IAstNode
begin
// Only the last expression in the block inherits the tail position status
FNextIsTail := isContextTail and (idx = nTail);
Result := Accept(Node);
end
);
SetLength(newExprs, exprsList.Count);
hasChanged := False;
if newExprs = Node.Expressions then
var nTail := exprsList.Count - 1;
for i := 0 to nTail do
begin
item := exprsList[i];
// Only the last expression in the block inherits the tail position status
FNextIsTail := isContextTail and (i = nTail);
newItem := Accept(item);
newExprs[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
begin
// Wrap array in List container
var newList := TExpressionList.Create(newExprs, Node.Expressions.Identity);
// CoW: Reuse Identity
Result := TAst.Block(Node.Identity, newExprs, Node.StaticType);
Result := TAst.Block(Node.Identity, newList, Node.StaticType);
end;
end;
function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
@@ -169,12 +185,13 @@ end;
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
newParams: TArray<IIdentifierNode>;
newParams: IParameterList;
newBody: IAstNode;
begin
// Parameters are not in tail position
FNextIsTail := False;
newParams := AcceptParameters(Node.Parameters);
// Visit list node to handle potential transformations in children
newParams := Accept(Node.Parameters).AsParameterList;
// The body of a lambda is *always* a tail position (relative to the lambda execution)
FNextIsTail := True;
@@ -185,8 +202,7 @@ begin
else
begin
// Rebuild using factory.
// IMPORTANT: Pass Layout AND Descriptor (TCO runs after TypeChecker).
// CoW: Reuse Identity
// CoW: Reuse Identity. Factory expects IParameterList now.
Result :=
TAst.LambdaExpr(
Node.Identity,
@@ -204,20 +220,21 @@ end;
function TAstTCO.VisitRecurNode(const Node: IRecurNode): IAstNode;
var
newArgs: TArray<IAstNode>;
newArgsList: IArgumentList;
begin
if not FIsTailStack.Peek then
raise EOptimizerException.Create('''recur'' can only be used in a tail position.');
// Arguments are not in tail position
FNextIsTail := False;
newArgs := AcceptNodes(Node.Arguments);
// Visit the list node. Accept() ensures children are visited via TAstTransformer.VisitArgumentList
newArgsList := Accept(Node.Arguments).AsArgumentList;
if newArgs = Node.Arguments then
if newArgsList = Node.Arguments then
Result := Node
else
// CoW: Reuse Identity
Result := TAst.Recur(Node.Identity, newArgs, Node.StaticType);
// CoW: Reuse Identity. Factory expects IArgumentList.
Result := TAst.Recur(Node.Identity, newArgsList, Node.StaticType);
end;
function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
@@ -239,29 +256,32 @@ function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var
isTailCall: Boolean;
newCallee: IAstNode;
newArgs: TArray<IAstNode>;
newArgsList: IArgumentList;
begin
isTailCall := FIsTailStack.Peek;
// Callee/Arguments are not in tail position
FNextIsTail := False;
newCallee := Accept(Node.Callee);
newArgs := AcceptNodes(Node.Arguments);
// Visit the arguments list
newArgsList := Accept(Node.Arguments).AsArgumentList;
// CoW check: Create a new node only if children changed OR IsTailCall needs update
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) and (isTailCall = Node.IsTailCall) then
if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) and (isTailCall = Node.IsTailCall) then
begin
Result := Node;
exit;
end;
// Use factory to create new node, passing the *new* TCO status
// CoW: Reuse Identity
// CoW: Reuse Identity. Factory expects IArgumentList.
Result :=
TAst.FunctionCall(
Node.Identity,
newCallee,
newArgs,
newArgsList,
Node.StaticType,
isTailCall,
Node.StaticTarget, // Preserve the static target from Specializer