Closure upvalues
This commit is contained in:
@@ -88,19 +88,25 @@ type
|
||||
// The signature for a native Delphi function callable from the script.
|
||||
TNativeFunction = function(const Args: TArray<TAstValue>): TAstValue;
|
||||
|
||||
// Concrete implementation of the TAstValue.IClosure interface for script-defined functions.
|
||||
TClosureValue = class(TInterfacedObject, TAstValue.IClosure)
|
||||
private
|
||||
FBody: IExpressionNode;
|
||||
FClosureScope: IExecutionScope;
|
||||
FLambdaNode: ILambdaExpressionNode;
|
||||
FUpvalues: TArray<IValueCell>;
|
||||
function GetBody: IExpressionNode;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetClosureScope: IExecutionScope;
|
||||
function GetUpvalues: TArray<IValueCell>;
|
||||
public
|
||||
constructor Create(const ALambdaNode: ILambdaExpressionNode; const AClosureScope: IExecutionScope);
|
||||
constructor Create(
|
||||
const ALambdaNode: ILambdaExpressionNode;
|
||||
const AClosureScope: IExecutionScope;
|
||||
const AUpvalues: TArray<IValueCell>
|
||||
);
|
||||
property ClosureScope: IExecutionScope read GetClosureScope;
|
||||
property LambdaNode: ILambdaExpressionNode read FLambdaNode;
|
||||
property Upvalues: TArray<IValueCell> read FUpvalues;
|
||||
end;
|
||||
|
||||
// Concrete implementation of TAstValue.IClosure for native Delphi functions.
|
||||
@@ -110,6 +116,7 @@ type
|
||||
function GetBody: IExpressionNode;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetClosureScope: IExecutionScope;
|
||||
function GetUpvalues: TArray<IValueCell>;
|
||||
public
|
||||
constructor Create(const AMethod: TNativeFunction);
|
||||
property Method: TNativeFunction read FMethod;
|
||||
@@ -205,12 +212,17 @@ end;
|
||||
|
||||
{ TClosureValue }
|
||||
|
||||
constructor TClosureValue.Create(const ALambdaNode: ILambdaExpressionNode; const AClosureScope: IExecutionScope);
|
||||
constructor TClosureValue.Create(
|
||||
const ALambdaNode: ILambdaExpressionNode;
|
||||
const AClosureScope: IExecutionScope;
|
||||
const AUpvalues: TArray<IValueCell>
|
||||
);
|
||||
begin
|
||||
inherited Create;
|
||||
FLambdaNode := ALambdaNode;
|
||||
FBody := ALambdaNode.Body;
|
||||
FClosureScope := AClosureScope;
|
||||
FUpvalues := AUpvalues; // Store the captured cells
|
||||
end;
|
||||
|
||||
function TClosureValue.GetBody: IExpressionNode;
|
||||
@@ -228,6 +240,11 @@ begin
|
||||
Result := FLambdaNode.Parameters;
|
||||
end;
|
||||
|
||||
function TClosureValue.GetUpvalues: TArray<IValueCell>;
|
||||
begin
|
||||
Result := FUpvalues;
|
||||
end;
|
||||
|
||||
{ TNativeClosure }
|
||||
|
||||
constructor TNativeClosure.Create(const AMethod: TNativeFunction);
|
||||
@@ -251,6 +268,10 @@ begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TNativeClosure.GetUpvalues: TArray<IValueCell>;
|
||||
begin
|
||||
end;
|
||||
|
||||
{ TEvaluatorVisitor }
|
||||
|
||||
constructor TEvaluatorVisitor.Create(const AScope: IExecutionScope);
|
||||
@@ -475,8 +496,27 @@ begin
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
|
||||
var
|
||||
capturedCells: TArray<IValueCell>;
|
||||
i: Integer;
|
||||
sourceAddr: TResolvedAddress;
|
||||
sourceAddresses: TArray<TResolvedAddress>;
|
||||
begin
|
||||
Result := TAstValue.FromClosure(TClosureValue.Create(Node, FScope));
|
||||
// The binder has identified the upvalues. Capture the cells from the current scope
|
||||
// using the original source addresses provided by the binder.
|
||||
sourceAddresses := Node.UpvalueSourceAddresses;
|
||||
SetLength(capturedCells, Length(sourceAddresses));
|
||||
for i := 0 to High(sourceAddresses) do
|
||||
begin
|
||||
sourceAddr := sourceAddresses[i];
|
||||
// The binder gives the depth relative to the lambda's
|
||||
// inner scope. The evaluator is in the outer scope, so we adjust the
|
||||
// depth by -1 to find the cell in the current context.
|
||||
dec(sourceAddr.ScopeDepth);
|
||||
capturedCells[i] := FScope.GetCell(sourceAddr);
|
||||
end;
|
||||
|
||||
Result := TAstValue.FromClosure(TClosureValue.Create(Node, FScope, capturedCells));
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TAstValue;
|
||||
@@ -510,17 +550,21 @@ begin
|
||||
if not Assigned(descriptor) then
|
||||
raise EParserError.Create('Lambda has no scope descriptor. Did the binder run?');
|
||||
|
||||
var callScope := descriptor.CreateScope(closure.ClosureScope);
|
||||
// Create the execution scope for the call, providing the captured upvalues.
|
||||
var callScope := TExecutionScope.Create(closure.ClosureScope, descriptor, (closure as TClosureValue).Upvalues) as IExecutionScope;
|
||||
|
||||
var adr: TResolvedAddress;
|
||||
adr.Kind := akLocalOrParent;
|
||||
adr.ScopeDepth := 0;
|
||||
|
||||
// Slot 0 is 'Self'.
|
||||
adr.SlotIndex := 0;
|
||||
callScope[adr] := calleeValue;
|
||||
callScope.Values[adr] := calleeValue;
|
||||
|
||||
for i := 0 to Node.Arguments.Count - 1 do
|
||||
begin
|
||||
adr.SlotIndex := closure.Parameters[i].Address.SlotIndex;
|
||||
callScope[adr] := Node.Arguments[i].Accept(Self);
|
||||
callScope.Values[adr] := Node.Arguments[i].Accept(Self);
|
||||
end;
|
||||
|
||||
Result := closure.Body.Accept(CreateVisitorForScope(callScope));
|
||||
|
||||
Reference in New Issue
Block a user