Generic Visitors

This commit is contained in:
Michael Schimmel
2026-01-04 11:47:00 +01:00
parent 22674b962b
commit f1733c41a0
2 changed files with 18 additions and 90 deletions
+16 -86
View File
@@ -24,26 +24,18 @@ type
function Visit(const Node: IAstNode): TDataValue; virtual;
function CreateVisitorFactory: TEvaluatorFactory; virtual;
// Nur noch die für die Ausführung relevanten Methoden
function VisitConstant(const N: IConstantNode): TDataValue; virtual;
function VisitIdentifier(const N: IIdentifierNode): TDataValue; virtual;
function VisitKeyword(const N: IKeywordNode): TDataValue; virtual;
function VisitParameterList(const N: IParameterList): TDataValue; virtual;
function VisitArgumentList(const N: IArgumentList): TDataValue; virtual;
function VisitExpressionList(const N: IExpressionList): TDataValue; virtual;
function VisitRecordFieldList(const N: IRecordFieldList): TDataValue; virtual;
function VisitRecordField(const N: IRecordFieldNode): TDataValue; virtual;
function VisitIfExpression(const N: IIfExpressionNode): TDataValue; virtual;
function VisitCondExpression(const N: ICondExpressionNode): TDataValue; virtual;
function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; virtual;
function VisitFunctionCall(const N: IFunctionCallNode): TDataValue; virtual;
function VisitMacroExpansionNode(const N: IMacroExpansionNode): TDataValue; virtual;
function VisitBlockExpression(const N: IBlockExpressionNode): TDataValue; virtual;
function VisitExpressionList(const N: IExpressionList): TDataValue; virtual;
function VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue; virtual;
function VisitAssignment(const N: IAssignmentNode): TDataValue; virtual;
function VisitMacroDefinition(const N: IMacroDefinitionNode): TDataValue; virtual;
function VisitQuasiquote(const N: IQuasiquoteNode): TDataValue; virtual;
function VisitUnquote(const N: IUnquoteNode): TDataValue; virtual;
function VisitUnquoteSplicing(const N: IUnquoteSplicingNode): TDataValue; virtual;
function VisitIndexer(const N: IIndexerNode): TDataValue; virtual;
function VisitMemberAccess(const N: IMemberAccessNode): TDataValue; virtual;
function VisitRecordLiteral(const N: IRecordLiteralNode): TDataValue; virtual;
@@ -51,10 +43,6 @@ type
function VisitAddSeriesItem(const N: IAddSeriesItemNode): TDataValue; virtual;
function VisitSeriesLength(const N: ISeriesLengthNode): TDataValue; virtual;
function VisitRecurNode(const N: IRecurNode): TDataValue; virtual;
function VisitNop(const N: INopNode): TDataValue; virtual;
function VisitPipeInput(const N: IPipeInputNode): TDataValue; virtual;
function VisitPipeSelectorList(const N: IPipeSelectorList): TDataValue; virtual;
function VisitPipeInputList(const N: IPipeInputList): TDataValue; virtual;
function VisitPipe(const N: IPipeNode): TDataValue; virtual;
function IsTruthy(const AValue: TDataValue): Boolean; inline;
@@ -100,6 +88,7 @@ end;
function TEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
begin
// Der Hot-Path: Direkter Dispatch ohne Umweg über ungenutzte Methoden.
case Node.Kind of
akConstant: Result := VisitConstant(Node.AsConstant);
akIdentifier: Result := VisitIdentifier(Node.AsIdentifier);
@@ -108,8 +97,8 @@ begin
akCondExpression: Result := VisitCondExpression(Node.AsCondExpression);
akLambdaExpression: Result := VisitLambdaExpression(Node.AsLambdaExpression);
akFunctionCall: Result := VisitFunctionCall(Node.AsFunctionCall);
akMacroExpansion: Result := VisitMacroExpansionNode(Node.AsMacroExpansion);
akBlockExpression: Result := VisitBlockExpression(Node.AsBlockExpression);
akExpressionList: Result := VisitExpressionList(Node.AsExpressionList);
akVariableDeclaration: Result := VisitVariableDeclaration(Node.AsVariableDeclaration);
akAssignment: Result := VisitAssignment(Node.AsAssignment);
akIndexer: Result := VisitIndexer(Node.AsIndexer);
@@ -119,9 +108,9 @@ begin
akAddSeriesItem: Result := VisitAddSeriesItem(Node.AsAddSeriesItem);
akSeriesLength: Result := VisitSeriesLength(Node.AsSeriesLength);
akRecur: Result := VisitRecurNode(Node.AsRecur);
akNop: Result := VisitNop(Node.AsNop);
akPipe: Result := VisitPipe(Node.AsPipe);
akExpressionList: Result := VisitExpressionList(Node.AsExpressionList);
akMacroExpansion: Result := Visit(Node.AsMacroExpansion.ExpandedBody);
akNop: Result := TDataValue.Void;
else
Result := TDataValue.Void;
end;
@@ -129,7 +118,7 @@ end;
class procedure TEvaluatorVisitor.HandleTCO(var ResultValue: TDataValue);
begin
while ResultValue.Kind = vkGeneric do
while (ResultValue.Kind = vkGeneric) do
begin
var thunk := ResultValue.AsGeneric<TThunk>;
var callee := thunk.Callee.AsMethod();
@@ -169,8 +158,6 @@ begin
Result := function(const AScope: IExecutionScope): IEvaluatorVisitor begin Result := TEvaluatorVisitor.Create(AScope); end;
end;
// --- Implementations ---
function TEvaluatorVisitor.VisitConstant(const N: IConstantNode): TDataValue;
begin
Result := N.Value;
@@ -222,7 +209,7 @@ begin
raise EEvaluatorException.Create('Arg mismatch');
lambdaScope := TScope.CreateScope(closureScope, descriptor, capturedCells);
// Self-reference for recursion
// Self-reference for recursion (Slot 0)
lambdaScope.SetValues(TResolvedAddress.Create(akLocalOrParent, 0, 0), TDataValue(closure));
for k := 0 to params.Count - 1 do
@@ -248,13 +235,14 @@ begin
Result := N.StaticTarget(argValues);
if not N.IsTailCall then
HandleTCO(Result); // Flushes stack if not tail
HandleTCO(Result);
end
else
begin
calleeValue := Visit(N.Callee);
if calleeValue.Kind <> vkMethod then
if (calleeValue.Kind <> vkMethod) then
raise EEvaluatorException.Create('Not a function');
SetLength(argValues, N.Arguments.Count);
for i := 0 to N.Arguments.Count - 1 do
argValues[i] := Visit(N.Arguments[i]);
@@ -320,17 +308,12 @@ function TEvaluatorVisitor.VisitVariableDeclaration(const N: IVariableDeclaratio
var
ident: IIdentifierNode;
begin
// 1. Initialisator auswerten
if Assigned(N.Initializer) then
Result := Visit(N.Initializer)
else
Result := TDataValue.Void;
// 2. Das Target-Identifier-Interface extrahieren
// N.Target ist IAstNode, wir brauchen IIdentifierNode für die Adresse
ident := N.Target.AsIdentifier;
// 3. Im Scope speichern
if N.IsBoxed then
FScope.DefineBoxed(ident.Address.SlotIndex, Result)
else
@@ -351,9 +334,9 @@ begin
if base.IsVoid then
exit(TDataValue.Void);
idx := Visit(N.Index);
if base.Kind = vkSeries then
if (base.Kind = vkSeries) then
Result := TDataValue(base.AsSeries.Items[Integer(idx.AsScalar.Value.AsInt64)])
else if base.Kind = vkRecordSeries then
else if (base.Kind = vkRecordSeries) then
begin
var rs := base.AsRecordSeries;
var vals: TArray<TScalar.TValue>;
@@ -416,7 +399,7 @@ end;
function TEvaluatorVisitor.VisitAddSeriesItem(const N: IAddSeriesItemNode): TDataValue;
begin
var lb := -1;
var lb: Int64 := -1;
if Assigned(N.Lookback) then
lb := Visit(N.Lookback).AsScalar.Value.AsInt64;
FScope[N.Series.Address].AsRecordSeries.Add(Visit(N.Value).AsScalarRecord, lb);
@@ -448,7 +431,7 @@ begin
begin
inputNode := N.Inputs[i];
inputVal := FScope[inputNode.StreamSource.Address];
if inputVal.Kind <> vkStream then
if (inputVal.Kind <> vkStream) then
raise EEvaluatorException.Create('Stream expected');
sources[i] := inputVal.AsStream;
sourceSeries := sources[i].Series;
@@ -473,7 +456,7 @@ begin
begin
SetLength(args, Length(S));
for k := 0 to High(S) do
args[k] := TDataValue(S[k].Items[0]);
args[k] := S[k].Items[0];
res := lambdaFunc(args);
if res.IsVoid then
exit(False);
@@ -485,57 +468,4 @@ begin
Result := TDataValue.FromStream(TPipeStream.Create(config, outputDef, sources, pipeAdapter));
end;
function TEvaluatorVisitor.VisitMacroDefinition(const N: IMacroDefinitionNode): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitQuasiquote(const N: IQuasiquoteNode): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitUnquote(const N: IUnquoteNode): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitUnquoteSplicing(const N: IUnquoteSplicingNode): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitNop(const N: INopNode): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitParameterList(const N: IParameterList): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitArgumentList(const N: IArgumentList): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitRecordFieldList(const N: IRecordFieldList): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitRecordField(const N: IRecordFieldNode): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitPipeInput(const N: IPipeInputNode): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitPipeSelectorList(const N: IPipeSelectorList): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitPipeInputList(const N: IPipeInputList): TDataValue;
begin
Result := TDataValue.Void;
end;
function TEvaluatorVisitor.VisitMacroExpansionNode(const N: IMacroExpansionNode): TDataValue;
begin
Result := Visit(N.ExpandedBody);
end;
end.
+2 -4
View File
@@ -14,8 +14,6 @@ uses
type
TAstVisitor<T> = class;
// Handler signature is now strictly based on IAstNode to ensure binary compatibility
// with the dispatch array.
TNodeHandler<T> = reference to function(const Node: IAstNode): T;
TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor)
@@ -32,7 +30,7 @@ type
protected
// --- Registration ---
// Register a handler. No generic overload anymore.
// Register a handler.
procedure Register(Kind: TAstNodeKind; const Handler: TNodeHandler<T>); virtual;
// Subclasses implement this to call Register()
@@ -49,7 +47,7 @@ type
end;
// Helper for transformation visitors (returns IAstNode)
// Implements recursive reconstruction: If a child changes, a new node is returned.
// Implements recursive reconstruction: If a child changes, a new node is returned, but node.Identity stays the same.
TAstTransformer = class abstract(TAstVisitor<IAstNode>)
protected
// Leaves