1st full macro expander

This commit is contained in:
Michael Schimmel
2025-10-03 19:46:30 +02:00
parent bb0ecda6be
commit d9219474e0
16 changed files with 700 additions and 323 deletions
+29 -20
View File
@@ -8,21 +8,23 @@ uses
System.Generics.Collections,
Myc.Ast.Nodes,
Myc.Ast.Visitor,
Myc.Ast.Scope;
Myc.Ast.Scope,
Myc.Data.Value;
type
// This visitor analyzes the AST to find all variables that need to be "lifted" or "boxed"
// because they are captured by a nested lambda.
TUpvalueAnalyzer = class(TAstTraverser)
TUpvalueAnalyzer = class(TAstTransformer)
private
FBoxedDeclarations: THashSet<IVariableDeclarationNode>;
FCurrentScope: IScopeDescriptor;
FDeclarationMap: TDictionary<IScopeDescriptor, TDictionary<string, IVariableDeclarationNode>>;
procedure MarkDeclarationForBoxing(const AName: string);
protected
function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; override;
function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; override;
function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; override;
// Overridden Visit methods to perform analysis during traversal.
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
public
constructor Create(const AParent: IScopeDescriptor);
destructor Destroy; override;
@@ -96,24 +98,26 @@ begin
end;
end;
function TUpvalueAnalyzer.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode;
function TUpvalueAnalyzer.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
var
address: TResolvedAddress;
begin
Result := Node;
if not Assigned(FCurrentScope) then
exit;
address := FCurrentScope.FindSymbol(Node.Name);
if (address.Kind = akLocalOrParent) and (address.ScopeDepth > 0) then
if Assigned(FCurrentScope) then
begin
// This is an upvalue. Mark its original declaration for boxing.
MarkDeclarationForBoxing(Node.Name);
address := FCurrentScope.FindSymbol(Node.Name);
if (address.Kind = akLocalOrParent) and (address.ScopeDepth > 0) then
begin
// This is an upvalue. Mark its original declaration for boxing.
MarkDeclarationForBoxing(Node.Name);
end;
end;
// As a traverser, return the original node wrapped in a TDataValue.
Result := TDataValue.FromIntf<IIdentifierNode>(Node);
end;
function TUpvalueAnalyzer.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode;
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
begin
// A lambda creates a new lexical scope, inheriting from the current one.
FCurrentScope := TScope.CreateDescriptor(FCurrentScope);
@@ -124,19 +128,21 @@ begin
// Traverse the lambda body within the new scope context.
Node.Body.Accept(Self);
Result := Node; // We do not transform, just analyze.
// We do not transform, just analyze. Return the original node wrapped.
Result := TDataValue.FromIntf<ILambdaExpressionNode>(Node);
finally
// Restore the parent scope after leaving the lambda.
FCurrentScope := FCurrentScope.Parent;
end;
end;
function TUpvalueAnalyzer.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode;
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
scopeDeclarations: TDictionary<string, IVariableDeclarationNode>;
begin
Result := Node;
// Traverse the initializer first. It's evaluated in the current scope
// before the new variable is defined.
if Assigned(Node.Initializer) then
Node.Initializer.Accept(Self);
@@ -150,6 +156,9 @@ begin
FDeclarationMap.Add(FCurrentScope, scopeDeclarations);
end;
scopeDeclarations.Add(Node.Identifier.Name, Node);
// As a traverser, return the original node wrapped in a TDataValue.
Result := TDataValue.FromIntf<IVariableDeclarationNode>(Node);
end;
end.