This commit is contained in:
Michael Schimmel
2025-09-04 17:33:10 +02:00
parent 9c90a92b04
commit faa447fd91
7 changed files with 411 additions and 378 deletions
+34 -51
View File
@@ -8,6 +8,7 @@ uses
System.Generics.Collections,
Myc.Data.Scalar,
Myc.Ast.Nodes,
Myc.Ast.Scope,
Myc.Ast;
type
@@ -80,7 +81,6 @@ uses
System.TypInfo,
Myc.Data.Decimal,
Myc.Data.Series,
Myc.Ast.Scope,
Myc.Ast.Printer,
Myc.Data.Scalar.JSON;
@@ -92,13 +92,16 @@ type
TClosureValue = class(TInterfacedObject, IEvaluatorClosure)
private
FBody: IExpressionNode;
FParameters: TArray<IIdentifierNode>;
FClosureScope: IExecutionScope;
FLambdaNode: ILambdaExpressionNode;
function GetBody: IExpressionNode;
function GetParameters: TArray<IIdentifierNode>;
function GetClosureScope: IExecutionScope;
function GetLambdaNode: ILambdaExpressionNode;
public
constructor Create(ABody: IExpressionNode; const AParameters: TArray<IIdentifierNode>; const AClosureScope: IExecutionScope);
constructor Create(const ALambdaNode: ILambdaExpressionNode; const AClosureScope: IExecutionScope);
property ClosureScope: IExecutionScope read GetClosureScope;
property LambdaNode: ILambdaExpressionNode read GetLambdaNode;
end;
// Concrete implementation of IEvaluatorClosure for native Delphi functions.
@@ -203,11 +206,11 @@ end;
{ TClosureValue }
constructor TClosureValue.Create(ABody: IExpressionNode; const AParameters: TArray<IIdentifierNode>; const AClosureScope: IExecutionScope);
constructor TClosureValue.Create(const ALambdaNode: ILambdaExpressionNode; const AClosureScope: IExecutionScope);
begin
inherited Create;
FBody := ABody;
FParameters := AParameters;
FLambdaNode := ALambdaNode;
FBody := ALambdaNode.Body;
FClosureScope := AClosureScope;
end;
@@ -221,9 +224,14 @@ begin
Result := FClosureScope;
end;
function TClosureValue.GetLambdaNode: ILambdaExpressionNode;
begin
Result := FLambdaNode;
end;
function TClosureValue.GetParameters: TArray<IIdentifierNode>;
begin
Result := FParameters;
Result := FLambdaNode.Parameters;
end;
{ TNativeClosure }
@@ -284,14 +292,9 @@ function TEvaluatorVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): T
var
itemValue, lookbackValue, seriesVar: TAstValue;
lookback: Int64;
depth, index: Integer;
begin
// The target series must have been resolved by the binder.
if not Node.Series.Resolve(depth, index) then
raise EArgumentException
.CreateFmt('Identifier could not be resolved: "%s". This should have been caught by the binder.', [Node.Series.Name]);
seriesVar := FScope.GetValue(depth, index);
seriesVar := FScope.GetValue(Node.Series.Resolve);
itemValue := Node.Value.Accept(Self);
lookback := -1;
@@ -340,18 +343,9 @@ begin
end;
function TEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TAstValue;
var
value: TAstValue;
depth, index: Integer;
begin
value := Node.Value.Accept(Self);
if not Node.Identifier.Resolve(depth, index) then
raise EArgumentException
.CreateFmt('Identifier could not be resolved: "%s". This should have been caught by the binder.', [Node.Identifier.Name]);
FScope.AssignValue(depth, index, value);
Result := value;
Result := Node.Value.Accept(Self);
FScope.AssignValue(Node.Identifier.Resolve, Result);
end;
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TAstValue;
@@ -383,15 +377,8 @@ begin
end;
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TAstValue;
var
depth, index: Integer;
begin
if Node.Resolve(depth, index) then
Result := FScope.GetValue(depth, index)
else
// This should not happen if the binder ran successfully.
raise EArgumentException
.CreateFmt('Identifier could not be resolved: "%s". This should have been caught by the binder.', [Node.Name]);
Result := FScope.GetValue(Node.Resolve)
end;
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TAstValue;
@@ -488,16 +475,14 @@ begin
else
value := TAstValue.Void;
FScope.Define(Node.Identifier.Name, value);
FScope.AssignValue(Node.Identifier.Resolve, value);
Result := value;
end;
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
var
closureImpl: IEvaluatorClosure;
begin
closureImpl := TClosureValue.Create(Node.Body, Node.Parameters, FScope);
Result := TAstValue.FromClosure(closureImpl);
Result := TAstValue.FromClosure(TClosureValue.Create(Node, FScope));
end;
function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TAstValue;
@@ -505,9 +490,9 @@ var
calleeValue: TAstValue;
closure: IEvaluatorClosure;
i: Integer;
argValues: TArray<TAstValue>;
callScope: IExecutionScope;
innerVisitor: IAstVisitor;
descriptor: IScopeDescriptor;
begin
calleeValue := Node.Callee.Accept(Self);
@@ -518,11 +503,10 @@ begin
if closure is TNativeClosure then
begin
var argValues: TArray<TAstValue>;
SetLength(argValues, Node.Arguments.Count);
for i := 0 to Node.Arguments.Count - 1 do
begin
argValues[i] := Node.Arguments[i].Accept(Self);
end;
Result := (closure as TNativeClosure).Method(argValues);
end
else if closure is TClosureValue then
@@ -531,20 +515,17 @@ begin
raise EArgumentException
.CreateFmt('Argument count mismatch: expected %d, got %d', [Length(closure.Parameters), Node.Arguments.Count]);
callScope := TExecutionScope.Create(closure.ClosureScope);
descriptor := (closure as TClosureValue).LambdaNode.ScopeDescriptor;
if not Assigned(descriptor) then
raise EParserError.Create('Lambda has no scope descriptor. Did the binder run?');
// The order of definitions must exactly match the binder's order.
callScope := descriptor.Instantiate(closure.ClosureScope);
callScope.SetValueByIndex(0, calleeValue);
// Define 'Self' first.
callScope.Define('Self', calleeValue);
// 2. Then, define the parameters.
for i := 0 to Node.Arguments.Count - 1 do
begin
var argValue := Node.Arguments[i].Accept(Self);
callScope.Define(closure.Parameters[i].Name, argValue);
end;
callScope.SetValueByIndex(closure.Parameters[i].Resolve.SlotIndex, Node.Arguments[i].Accept(Self));
// 3. Führe den Körper der Funktion im neuen Scope aus.
innerVisitor := Self.CreateVisitorForScope(callScope);
Result := closure.Body.Accept(innerVisitor);
end
@@ -914,6 +895,7 @@ begin
AppendMultiline((pp as TPrettyPrintVisitor).GetResult);
ShowScope;
Result := inherited VisitLambdaExpression(Node);
finally
Unindent;
@@ -926,6 +908,7 @@ begin
AppendLine('FunctionCall{');
Indent;
try
ShowScope;
Result := inherited VisitFunctionCall(Node);
finally
Unindent;
@@ -938,8 +921,8 @@ begin
AppendLine('Block{');
Indent;
try
ShowScope;
Result := inherited VisitBlockExpression(Node);
ShowScope;
finally
Unindent;
end;