Generic Visitors

This commit is contained in:
Michael Schimmel
2026-01-03 19:14:18 +01:00
parent db74b83e11
commit 22674b962b
27 changed files with 3573 additions and 3547 deletions
+36 -14
View File
@@ -40,11 +40,16 @@ type
FCurrentScope: TAnalysisScope;
procedure MarkDeclarationForBoxing(const AName: string);
strict private
// Analysis Handlers (IAstNode signature)
function VisitLambdaExpression(const Node: IAstNode): IAstNode;
function VisitIdentifier(const Node: IAstNode): IAstNode;
function VisitVariableDeclaration(const Node: IAstNode): IAstNode;
protected
// Overridden Visit methods to perform analysis during traversal.
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
procedure SetupHandlers; override;
public
constructor Create;
destructor Destroy; override;
@@ -126,6 +131,16 @@ begin
inherited Destroy;
end;
procedure TUpvalueAnalyzer.SetupHandlers;
begin
inherited SetupHandlers; // Load default transformer logic
// Override specific handlers for analysis
Register(akLambdaExpression, VisitLambdaExpression);
Register(akIdentifier, VisitIdentifier);
Register(akVariableDeclaration, VisitVariableDeclaration);
end;
function TUpvalueAnalyzer.Execute(const ARootNode: IAstNode): IAstNode;
begin
Result := Accept(ARootNode);
@@ -167,32 +182,35 @@ begin
end;
end;
function TUpvalueAnalyzer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
function TUpvalueAnalyzer.VisitIdentifier(const Node: IAstNode): IAstNode;
begin
// Check if this identifier refers to a variable from an outer scope
MarkDeclarationForBoxing(Node.Name);
MarkDeclarationForBoxing(Node.AsIdentifier.Name);
// Return original node (Analysis pass only)
Result := Node;
end;
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
i: Integer;
begin
L := Node.AsLambdaExpression;
// 1. Enter new analysis scope
FCurrentScope := TAnalysisScope.Create(FCurrentScope);
try
// 2. Register parameters (they mask outer variables)
// We pass 'nil' as the node because we currently don't box parameters,
// but we must ensure Resolve() finds them so we don't accidentally box a shadowed variable.
for i := 0 to Node.Parameters.Count - 1 do
for i := 0 to L.Parameters.Count - 1 do
begin
FCurrentScope.Define(Node.Parameters[i].Name, nil);
FCurrentScope.Define(L.Parameters[i].Name, nil);
end;
// 3. Visit Body
Accept(Node.Body); // Recursive call
Accept(L.Body); // Recursive call
// Rebuild if needed (default CoW behavior)
Result := Node;
@@ -204,15 +222,19 @@ begin
end;
end;
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
var
V: IVariableDeclarationNode;
begin
V := Node.AsVariableDeclaration;
// 1. Visit initializer first (it executes in the CURRENT scope)
if Assigned(Node.Initializer) then
Accept(Node.Initializer);
if Assigned(V.Initializer) then
Accept(V.Initializer);
// 2. Define the variable in the CURRENT scope
// Store the Node reference so we can add it to FBoxedDeclarations if captured.
FCurrentScope.Define(Node.Target.AsIdentifier.Name, Node);
FCurrentScope.Define(V.Target.AsIdentifier.Name, V);
Result := Node;
end;