Generic Visitor Pattern

This commit is contained in:
Michael Schimmel
2026-01-02 13:29:33 +01:00
parent b7d0222ec2
commit 2395cb1e70
3 changed files with 322 additions and 187 deletions
+99 -84
View File
@@ -10,56 +10,59 @@ uses
Myc.Data.Value, Myc.Data.Value,
Myc.Data.Keyword, Myc.Data.Keyword,
Myc.Ast.Nodes, Myc.Ast.Nodes,
Myc.Ast.Scope; Myc.Ast.Scope,
Myc.Ast.Visitor;
type type
// Exception for runtime errors during script evaluation // Exception for runtime errors during script evaluation
EEvaluatorException = class(EAstException); EEvaluatorException = class(EAstException);
// The standard AST evaluator for production use. // The standard AST evaluator for production use.
TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor, IEvaluatorVisitor) TEvaluatorVisitor = class(TAstVisitor<TDataValue>, IEvaluatorVisitor)
private private
FScope: IExecutionScope; FScope: IExecutionScope;
protected protected
// IAstVisitor methods made virtual for TDebugEvaluatorVisitor to override // Setup the registry-based handlers
function VisitConstant(const Node: IConstantNode): TDataValue; virtual; procedure SetupHandlers; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual;
function VisitKeyword(const Node: IKeywordNode): TDataValue; virtual;
// List Visitors // --- Core Logic Implementation (Legacy Virtual Overrides) ---
function VisitParameterList(const Node: IParameterList): TDataValue; virtual; // These are kept as virtual methods so TDebugEvaluatorVisitor can still override them.
function VisitArgumentList(const Node: IArgumentList): TDataValue; virtual; function VisitConstant(const Node: IConstantNode): TDataValue; override;
function VisitExpressionList(const Node: IExpressionList): TDataValue; virtual; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitRecordFieldList(const Node: IRecordFieldList): TDataValue; virtual; function VisitKeyword(const Node: IKeywordNode): TDataValue; override;
function VisitRecordField(const Node: IRecordFieldNode): TDataValue; virtual;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual; function VisitParameterList(const Node: IParameterList): TDataValue; override;
function VisitCondExpression(const Node: ICondExpressionNode): TDataValue; virtual; function VisitArgumentList(const Node: IArgumentList): TDataValue; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual; function VisitExpressionList(const Node: IExpressionList): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; virtual; function VisitRecordFieldList(const Node: IRecordFieldList): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; virtual; function VisitRecordField(const Node: IRecordFieldNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; virtual;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; virtual;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; virtual;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual;
function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; virtual;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual;
function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual;
function VisitNop(const Node: INopNode): TDataValue; virtual;
// Pipe Support function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
function VisitPipeInput(const Node: IPipeInputNode): TDataValue; virtual; function VisitCondExpression(const Node: ICondExpressionNode): TDataValue; override;
function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; virtual; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitPipeInputList(const Node: IPipeInputList): TDataValue; virtual; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitPipe(const Node: IPipeNode): TDataValue; virtual; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
function VisitNop(const Node: INopNode): TDataValue; override;
function VisitPipeInput(const Node: IPipeInputNode): TDataValue; override;
function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; override;
function VisitPipeInputList(const Node: IPipeInputList): TDataValue; override;
function VisitPipe(const Node: IPipeNode): TDataValue; override;
function IsTruthy(const AValue: TDataValue): Boolean; inline; function IsTruthy(const AValue: TDataValue): Boolean; inline;
@@ -108,9 +111,21 @@ end;
constructor TEvaluatorVisitor.Create(const AScope: IExecutionScope); constructor TEvaluatorVisitor.Create(const AScope: IExecutionScope);
begin begin
FScope := AScope;
// Base constructor calls SetupHandlers automatically
inherited Create; inherited Create;
Assert(Assigned(AScope)); Assert(Assigned(AScope));
FScope := AScope; end;
procedure TEvaluatorVisitor.SetupHandlers;
begin
// The base SetupHandlers registers delegates that call the virtual methods (VisitConstant, etc.).
// Since TEvaluatorVisitor still implements its logic by overriding these virtual methods (for now),
// we just call inherited.
//
// In Phase 2/3 of refactoring, we would move the logic from VisitConstant directly into
// anonymous methods or separate handler functions registered here, bypassing the virtual table.
inherited;
end; end;
function TEvaluatorVisitor.Execute(const RootNode: IAstNode): TDataValue; function TEvaluatorVisitor.Execute(const RootNode: IAstNode): TDataValue;
@@ -119,7 +134,8 @@ begin
exit(TDataValue.Void); exit(TDataValue.Void);
try try
Result := RootNode.Accept(Self); // Call the central dispatch method from TAstVisitor<T>
Result := Visit(RootNode);
HandleTCO(Result); HandleTCO(Result);
except except
on E: EAstException do on E: EAstException do
@@ -171,6 +187,23 @@ begin
end; end;
end; end;
// --- Visits Implementation ---
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
begin
Result := Node.Value;
end;
function TEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue;
begin
Result := TDataValue(TScalar.FromKeyword(Node.Value));
end;
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
Result := FScope[Node.Address];
end;
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
var var
capturedCells: TArray<IValueCell>; capturedCells: TArray<IValueCell>;
@@ -232,8 +265,9 @@ begin
end; end;
// Create a visitor with the new scope and execute the lambda's body. // Create a visitor with the new scope and execute the lambda's body.
// Use the generic Visit method for dispatch
bodyVisitor := visitorFactory(lambdaScope); bodyVisitor := visitorFactory(lambdaScope);
Result := cNode.Body.Accept(bodyVisitor); Result := bodyVisitor.Visit(cNode.Body);
end; end;
Result := TDataValue(closure); Result := TDataValue(closure);
@@ -272,7 +306,7 @@ begin
argList := Node.Arguments; argList := Node.Arguments;
SetLength(argValues, argList.Count); SetLength(argValues, argList.Count);
for i := 0 to argList.Count - 1 do for i := 0 to argList.Count - 1 do
argValues[i] := argList[i].Accept(Self); argValues[i] := Visit(argList[i]); // Use central dispatch
// Call static target. Exceptions here are caught by Execute/HandleTCO. // Call static target. Exceptions here are caught by Execute/HandleTCO.
Result := Node.StaticTarget(argValues); Result := Node.StaticTarget(argValues);
@@ -281,14 +315,14 @@ begin
else else
begin begin
// --- Dynamic Path (Default) --- // --- Dynamic Path (Default) ---
calleeValue := Node.Callee.Accept(Self); calleeValue := Visit(Node.Callee);
if calleeValue.Kind <> vkMethod then if calleeValue.Kind <> vkMethod then
raise EEvaluatorException.Create('Expression is not invokable in this context.'); raise EEvaluatorException.Create('Expression is not invokable in this context.');
argList := Node.Arguments; argList := Node.Arguments;
SetLength(argValues, argList.Count); SetLength(argValues, argList.Count);
for i := 0 to argList.Count - 1 do for i := 0 to argList.Count - 1 do
argValues[i] := argList[i].Accept(Self); argValues[i] := Visit(argList[i]);
if Node.IsTailCall then if Node.IsTailCall then
begin begin
@@ -304,7 +338,7 @@ end;
function TEvaluatorVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; function TEvaluatorVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
begin begin
Result := Node.ExpandedBody.Accept(Self); Result := Visit(Node.ExpandedBody);
end; end;
function TEvaluatorVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue; function TEvaluatorVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
@@ -316,7 +350,7 @@ var
begin begin
SetLength(argValues, Node.Arguments.Count); SetLength(argValues, Node.Arguments.Count);
for i := 0 to Node.Arguments.Count - 1 do for i := 0 to Node.Arguments.Count - 1 do
argValues[i] := Node.Arguments[i].Accept(Self); argValues[i] := Visit(Node.Arguments[i]);
calleeAddress.Kind := akLocalOrParent; calleeAddress.Kind := akLocalOrParent;
calleeAddress.ScopeDepth := 0; calleeAddress.ScopeDepth := 0;
@@ -332,12 +366,12 @@ var
lookback: Int64; lookback: Int64;
begin begin
seriesVar := FScope[Node.Series.Address]; seriesVar := FScope[Node.Series.Address];
itemValue := Node.Value.Accept(Self); itemValue := Visit(Node.Value);
lookback := -1; lookback := -1;
if Assigned(Node.Lookback) then if Assigned(Node.Lookback) then
begin begin
lookbackValue := Node.Lookback.Accept(Self); lookbackValue := Visit(Node.Lookback);
if (lookbackValue.Kind <> vkScalar) or (lookbackValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then if (lookbackValue.Kind <> vkScalar) or (lookbackValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then
raise EEvaluatorException.Create('Lookback parameter must be an integer.'); raise EEvaluatorException.Create('Lookback parameter must be an integer.');
lookback := lookbackValue.AsScalar.Value.AsInt64; lookback := lookbackValue.AsScalar.Value.AsInt64;
@@ -362,20 +396,10 @@ begin
if Node.Target.Kind <> akIdentifier then if Node.Target.Kind <> akIdentifier then
raise EEvaluatorException.Create('Runtime Error: Assignment target must be an identifier.'); raise EEvaluatorException.Create('Runtime Error: Assignment target must be an identifier.');
Result := Node.Value.Accept(Self); Result := Visit(Node.Value);
FScope[Node.Target.AsIdentifier.Address] := Result; FScope[Node.Target.AsIdentifier.Address] := Result;
end; end;
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
begin
Result := Node.Value;
end;
function TEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue;
begin
Result := TDataValue(TScalar.FromKeyword(Node.Value));
end;
function TEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function TEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
var var
def: string; def: string;
@@ -403,11 +427,6 @@ begin
end; end;
end; end;
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
Result := FScope[Node.Address];
end;
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
var var
baseValue, indexValue: TDataValue; baseValue, indexValue: TDataValue;
@@ -420,12 +439,12 @@ var
memberSeries: ISeries; memberSeries: ISeries;
scalarValue: TScalar; scalarValue: TScalar;
begin begin
baseValue := Node.Base.Accept(Self); baseValue := Visit(Node.Base);
if baseValue.IsVoid then if baseValue.IsVoid then
exit(TDataValue.Void); exit(TDataValue.Void);
indexValue := Node.Index.Accept(Self); indexValue := Visit(Node.Index);
if (indexValue.Kind <> vkScalar) or (indexValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then if (indexValue.Kind <> vkScalar) or (indexValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then
raise EEvaluatorException.Create('Indexer `[]` requires an integer argument.'); raise EEvaluatorException.Create('Indexer `[]` requires an integer argument.');
@@ -471,7 +490,7 @@ function TEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDa
var var
baseValue: TDataValue; baseValue: TDataValue;
begin begin
baseValue := Node.Base.Accept(Self); baseValue := Visit(Node.Base);
if baseValue.IsVoid then if baseValue.IsVoid then
exit(TDataValue.Void); exit(TDataValue.Void);
@@ -498,7 +517,7 @@ begin
for i := 0 to Node.Fields.Count - 1 do for i := 0 to Node.Fields.Count - 1 do
begin begin
var field := Node.Fields[i]; var field := Node.Fields[i];
genFields[i] := TPair<IKeyword, TDataValue>.Create(field.Key.Value, field.Value.Accept(Self)); genFields[i] := TPair<IKeyword, TDataValue>.Create(field.Key.Value, Visit(field.Value));
end; end;
Result := TDataValue.FromGenericRecord(TKeywordMapping<TDataValue>.Create(genFields)); Result := TDataValue.FromGenericRecord(TKeywordMapping<TDataValue>.Create(genFields));
@@ -511,7 +530,7 @@ begin
for i := 0 to Node.Fields.Count - 1 do for i := 0 to Node.Fields.Count - 1 do
begin begin
var field := Node.Fields[i]; var field := Node.Fields[i];
var valData := field.Value.Accept(Self); var valData := Visit(field.Value);
values[i] := valData.AsScalar.Value; values[i] := valData.AsScalar.Value;
end; end;
@@ -529,7 +548,7 @@ begin
raise EEvaluatorException.Create('Runtime Error: Variable declaration target must be an identifier.'); raise EEvaluatorException.Create('Runtime Error: Variable declaration target must be an identifier.');
if Assigned(Node.Initializer) then if Assigned(Node.Initializer) then
Result := Node.Initializer.Accept(Self) Result := Visit(Node.Initializer)
else else
Result := TDataValue.Void; Result := TDataValue.Void;
@@ -548,10 +567,10 @@ end;
function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin begin
if IsTruthy(Node.Condition.Accept(Self)) then if IsTruthy(Visit(Node.Condition)) then
Result := Node.ThenBranch.Accept(Self) Result := Visit(Node.ThenBranch)
else if Assigned(Node.ElseBranch) then else if Assigned(Node.ElseBranch) then
Result := Node.ElseBranch.Accept(Self) Result := Visit(Node.ElseBranch)
else else
Result := TDataValue.Void; Result := TDataValue.Void;
end; end;
@@ -562,20 +581,20 @@ var
begin begin
for pair in Node.Pairs do for pair in Node.Pairs do
begin begin
if IsTruthy(pair.Condition.Accept(Self)) then if IsTruthy(Visit(pair.Condition)) then
begin begin
Result := pair.Branch.Accept(Self); Result := Visit(pair.Branch);
Exit; Exit;
end; end;
end; end;
// Else branch is mandatory in AST (though can be Nop/Void in source if omitted and normalized) // Else branch is mandatory in AST (though can be Nop/Void in source if omitted and normalized)
Result := Node.ElseBranch.Accept(Self); Result := Visit(Node.ElseBranch);
end; end;
function TEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function TEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
begin begin
// Delegate to ExpressionList visitor // Delegate to ExpressionList visitor via central Visit dispatch (default handler maps to VisitExpressionList)
Result := Node.Expressions.Accept(Self); Result := Visit(Node.Expressions);
end; end;
function TEvaluatorVisitor.VisitExpressionList(const Node: IExpressionList): TDataValue; function TEvaluatorVisitor.VisitExpressionList(const Node: IExpressionList): TDataValue;
@@ -584,7 +603,7 @@ var
begin begin
Result := TDataValue.Void; Result := TDataValue.Void;
for i := 0 to Node.Count - 1 do for i := 0 to Node.Count - 1 do
Result := Node[i].Accept(Self); Result := Visit(Node[i]);
end; end;
function TEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue; function TEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue;
@@ -610,7 +629,7 @@ begin
Result := TDataValue(TScalar.FromInt64(len)); Result := TDataValue(TScalar.FromInt64(len));
end; end;
// --- List Visitor Stubs (Mostly Unused directly, but required by Interface) --- // --- List Visitor Stubs (Default to Void if not implemented specifically) ---
function TEvaluatorVisitor.VisitParameterList(const Node: IParameterList): TDataValue; function TEvaluatorVisitor.VisitParameterList(const Node: IParameterList): TDataValue;
begin begin
@@ -619,20 +638,16 @@ end;
function TEvaluatorVisitor.VisitArgumentList(const Node: IArgumentList): TDataValue; function TEvaluatorVisitor.VisitArgumentList(const Node: IArgumentList): TDataValue;
begin begin
// Could evaluate arguments here and return an array-wrapped TDataValue,
// but VisitFunctionCall handles iteration manually for efficiency.
Result := TDataValue.Void; Result := TDataValue.Void;
end; end;
function TEvaluatorVisitor.VisitRecordFieldList(const Node: IRecordFieldList): TDataValue; function TEvaluatorVisitor.VisitRecordFieldList(const Node: IRecordFieldList): TDataValue;
begin begin
// Handled by VisitRecordLiteral
Result := TDataValue.Void; Result := TDataValue.Void;
end; end;
function TEvaluatorVisitor.VisitRecordField(const Node: IRecordFieldNode): TDataValue; function TEvaluatorVisitor.VisitRecordField(const Node: IRecordFieldNode): TDataValue;
begin begin
// Handled by manual iteration in VisitRecordLiteral
Result := TDataValue.Void; Result := TDataValue.Void;
end; end;
@@ -711,7 +726,7 @@ begin
outputDef := Node.StaticType.Definition; outputDef := Node.StaticType.Definition;
// 3. Compile Lambda // 3. Compile Lambda
lambdaFunc := Node.Transformation.Accept(Self).AsMethod(); lambdaFunc := Visit(Node.Transformation).AsMethod();
// 4. Create Adapter // 4. Create Adapter
pipeAdapter := pipeAdapter :=
+17
View File
@@ -64,6 +64,12 @@ type
property Errors: TArray<TCompilerError> read FErrors; property Errors: TArray<TCompilerError> read FErrors;
end; end;
// Exception for the new visitor registry dispatch
ENoHandlerException = class(EAstException)
public
constructor Create(const KindName: string);
end;
TCompiledFunction = record TCompiledFunction = record
public public
Func: TDataValue.TFunc; Func: TDataValue.TFunc;
@@ -507,6 +513,10 @@ type
end; end;
IAstVisitor = interface IAstVisitor = interface
// --- Central Dispatch Method (New) ---
function Visit(const Node: IAstNode): TDataValue;
// --- Specific Methods (Legacy / Hybrid Support) ---
function VisitConstant(const Node: IConstantNode): TDataValue; function VisitConstant(const Node: IConstantNode): TDataValue;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; function VisitIdentifier(const Node: IIdentifierNode): TDataValue;
function VisitKeyword(const Node: IKeywordNode): TDataValue; function VisitKeyword(const Node: IKeywordNode): TDataValue;
@@ -1268,6 +1278,13 @@ begin
end; end;
end; end;
{ ENoHandlerException }
constructor ENoHandlerException.Create(const KindName: string);
begin
inherited CreateFmt('No visitor handler registered for AST node kind: %s', [KindName]);
end;
{ TAstNode } { TAstNode }
constructor TAstNode.Create(const AIdentity: IAstIdentity); constructor TAstNode.Create(const AIdentity: IAstIdentity);
+206 -103
View File
@@ -12,9 +12,20 @@ uses
Myc.Ast.Identities; Myc.Ast.Identities;
type type
TAstVisitor<T> = class;
// Delegate for handling a specific node kind within the visitor context
TNodeHandler<T> = reference to function(const Node: IAstNode): T;
TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor) TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor)
strict private strict private
// The registry for O(1) dispatch based on TAstNodeKind
FHandlers: array[TAstNodeKind] of TNodeHandler<T>;
// IAstVisitor explicit implementation (bridge methods) // IAstVisitor explicit implementation (bridge methods)
function IAstVisitor.Visit = VisitBridge;
// Legacy bridge methods for IAstVisitor interface compliance
function IAstVisitor.VisitConstant = DoVisitConstant; function IAstVisitor.VisitConstant = DoVisitConstant;
function IAstVisitor.VisitIdentifier = DoVisitIdentifier; function IAstVisitor.VisitIdentifier = DoVisitIdentifier;
function IAstVisitor.VisitKeyword = DoVisitKeyword; function IAstVisitor.VisitKeyword = DoVisitKeyword;
@@ -46,23 +57,21 @@ type
function IAstVisitor.VisitRecurNode = DoVisitRecurNode; function IAstVisitor.VisitRecurNode = DoVisitRecurNode;
function IAstVisitor.VisitNop = DoVisitNop; function IAstVisitor.VisitNop = DoVisitNop;
// Pipe Support
function IAstVisitor.VisitPipeInput = DoVisitPipeInput; function IAstVisitor.VisitPipeInput = DoVisitPipeInput;
function IAstVisitor.VisitPipeSelectorList = DoVisitPipeSelectorList; function IAstVisitor.VisitPipeSelectorList = DoVisitPipeSelectorList;
function IAstVisitor.VisitPipeInputList = DoVisitPipeInputList; function IAstVisitor.VisitPipeInputList = DoVisitPipeInputList;
function IAstVisitor.VisitPipe = DoVisitPipe; function IAstVisitor.VisitPipe = DoVisitPipe;
// Private bridge method implementations // Private helpers to wrap T into TDataValue for the non-generic interface
function VisitBridge(const Node: IAstNode): TDataValue;
function DoVisitConstant(const Node: IConstantNode): TDataValue; function DoVisitConstant(const Node: IConstantNode): TDataValue;
function DoVisitIdentifier(const Node: IIdentifierNode): TDataValue; function DoVisitIdentifier(const Node: IIdentifierNode): TDataValue;
function DoVisitKeyword(const Node: IKeywordNode): TDataValue; function DoVisitKeyword(const Node: IKeywordNode): TDataValue;
function DoVisitParameterList(const Node: IParameterList): TDataValue; function DoVisitParameterList(const Node: IParameterList): TDataValue;
function DoVisitArgumentList(const Node: IArgumentList): TDataValue; function DoVisitArgumentList(const Node: IArgumentList): TDataValue;
function DoVisitExpressionList(const Node: IExpressionList): TDataValue; function DoVisitExpressionList(const Node: IExpressionList): TDataValue;
function DoVisitRecordFieldList(const Node: IRecordFieldList): TDataValue; function DoVisitRecordFieldList(const Node: IRecordFieldList): TDataValue;
function DoVisitRecordField(const Node: IRecordFieldNode): TDataValue; function DoVisitRecordField(const Node: IRecordFieldNode): TDataValue;
function DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue; function DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue;
function DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue; function DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue;
function DoVisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function DoVisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
@@ -83,17 +92,26 @@ type
function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
function DoVisitRecurNode(const Node: IRecurNode): TDataValue; function DoVisitRecurNode(const Node: IRecurNode): TDataValue;
function DoVisitNop(const Node: INopNode): TDataValue; function DoVisitNop(const Node: INopNode): TDataValue;
function DoVisitPipeInput(const Node: IPipeInputNode): TDataValue; function DoVisitPipeInput(const Node: IPipeInputNode): TDataValue;
function DoVisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; function DoVisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue;
function DoVisitPipeInputList(const Node: IPipeInputList): TDataValue; function DoVisitPipeInputList(const Node: IPipeInputList): TDataValue;
function DoVisitPipe(const Node: IPipeNode): TDataValue; function DoVisitPipe(const Node: IPipeNode): TDataValue;
protected protected
// Visit a node. // Registers a handler for a specific node kind.
function Accept(const Node: IAstNode): T; virtual; // Subclasses can call this in their constructor/SetupHandlers to override behavior without implementing VisitXYZ.
procedure RegisterHandler(Kind: TAstNodeKind; Handler: TNodeHandler<T>);
// Default visitor functions implemented analogous to TAstVisitor // Initial setup of the dispatch table.
// The base implementation maps all kinds to the legacy VisitXYZ virtual methods.
procedure SetupHandlers; virtual;
// The central dispatch method. Looks up the handler in the registry.
function Visit(const Node: IAstNode): T; virtual;
// Legacy Virtual Methods (kept for backward compatibility and default mapping)
// Subclasses should eventually move to RegisterHandler, but overriding these still works
// because SetupHandlers maps them by default.
function VisitConstant(const Node: IConstantNode): T; virtual; function VisitConstant(const Node: IConstantNode): T; virtual;
function VisitIdentifier(const Node: IIdentifierNode): T; virtual; function VisitIdentifier(const Node: IIdentifierNode): T; virtual;
function VisitKeyword(const Node: IKeywordNode): T; virtual; function VisitKeyword(const Node: IKeywordNode): T; virtual;
@@ -129,8 +147,17 @@ type
function VisitPipeSelectorList(const Node: IPipeSelectorList): T; virtual; function VisitPipeSelectorList(const Node: IPipeSelectorList): T; virtual;
function VisitPipeInputList(const Node: IPipeInputList): T; virtual; function VisitPipeInputList(const Node: IPipeInputList): T; virtual;
function VisitPipe(const Node: IPipeNode): T; virtual; function VisitPipe(const Node: IPipeNode): T; virtual;
// Bridge to support calling Accept on node directly (double dispatch pattern)
// In the new architecture, we prefer calling Visit(Node) directly.
function Accept(const Node: IAstNode): T; virtual;
public
constructor Create;
end; end;
// Helper for transformation visitors (returns IAstNode)
// Overrides legacy methods to provide default recursive reconstruction behavior.
TAstTransformer = class abstract(TAstVisitor<IAstNode>) TAstTransformer = class abstract(TAstVisitor<IAstNode>)
protected protected
function VisitConstant(const Node: IConstantNode): IAstNode; override; function VisitConstant(const Node: IConstantNode): IAstNode; override;
@@ -179,12 +206,91 @@ implementation
{ TAstVisitor<T> } { TAstVisitor<T> }
constructor TAstVisitor<T>.Create;
begin
inherited Create;
SetupHandlers;
end;
procedure TAstVisitor<T>.RegisterHandler(Kind: TAstNodeKind; Handler: TNodeHandler<T>);
begin
FHandlers[Kind] := Handler;
end;
procedure TAstVisitor<T>.SetupHandlers;
begin
// Map all kinds to the legacy virtual methods.
// This provides the compatibility layer for existing subclasses.
// In Phase 2, subclasses will register their own handlers directly here.
// Core
FHandlers[akConstant] := function(const Node: IAstNode): T begin Result := VisitConstant(Node.AsConstant); end;
FHandlers[akIdentifier] := function(const Node: IAstNode): T begin Result := VisitIdentifier(Node.AsIdentifier); end;
FHandlers[akKeyword] := function(const Node: IAstNode): T begin Result := VisitKeyword(Node.AsKeyword); end;
// Lists
FHandlers[akParameterList] := function(const Node: IAstNode): T begin Result := VisitParameterList(Node.AsParameterList); end;
FHandlers[akArgumentList] := function(const Node: IAstNode): T begin Result := VisitArgumentList(Node.AsArgumentList); end;
FHandlers[akExpressionList] := function(const Node: IAstNode): T begin Result := VisitExpressionList(Node.AsExpressionList); end;
FHandlers[akRecordFieldList] := function(const Node: IAstNode): T begin Result := VisitRecordFieldList(Node.AsRecordFieldList); end;
FHandlers[akRecordField] := function(const Node: IAstNode): T begin Result := VisitRecordField(Node.AsRecordField); end;
// Structural
FHandlers[akIfExpression] := function(const Node: IAstNode): T begin Result := VisitIfExpression(Node.AsIfExpression); end;
FHandlers[akCondExpression] := function(const Node: IAstNode): T begin Result := VisitCondExpression(Node.AsCondExpression); end;
FHandlers[akLambdaExpression] := function(const Node: IAstNode): T begin Result := VisitLambdaExpression(Node.AsLambdaExpression); end;
FHandlers[akFunctionCall] := function(const Node: IAstNode): T begin Result := VisitFunctionCall(Node.AsFunctionCall); end;
FHandlers[akMacroExpansion] := function(const Node: IAstNode): T begin Result := VisitMacroExpansionNode(Node.AsMacroExpansion); end;
FHandlers[akBlockExpression] := function(const Node: IAstNode): T begin Result := VisitBlockExpression(Node.AsBlockExpression); end;
FHandlers[akVariableDeclaration] :=
function(const Node: IAstNode): T begin Result := VisitVariableDeclaration(Node.AsVariableDeclaration); end;
FHandlers[akAssignment] := function(const Node: IAstNode): T begin Result := VisitAssignment(Node.AsAssignment); end;
FHandlers[akMacroDefinition] := function(const Node: IAstNode): T begin Result := VisitMacroDefinition(Node.AsMacroDefinition); end;
FHandlers[akQuasiquote] := function(const Node: IAstNode): T begin Result := VisitQuasiquote(Node.AsQuasiquote); end;
FHandlers[akUnquote] := function(const Node: IAstNode): T begin Result := VisitUnquote(Node.AsUnquote); end;
FHandlers[akUnquoteSplicing] := function(const Node: IAstNode): T begin Result := VisitUnquoteSplicing(Node.AsUnquoteSplicing); end;
FHandlers[akIndexer] := function(const Node: IAstNode): T begin Result := VisitIndexer(Node.AsIndexer); end;
FHandlers[akMemberAccess] := function(const Node: IAstNode): T begin Result := VisitMemberAccess(Node.AsMemberAccess); end;
FHandlers[akRecordLiteral] := function(const Node: IAstNode): T begin Result := VisitRecordLiteral(Node.AsRecordLiteral); end;
FHandlers[akCreateSeries] := function(const Node: IAstNode): T begin Result := VisitCreateSeries(Node.AsCreateSeries); end;
FHandlers[akAddSeriesItem] := function(const Node: IAstNode): T begin Result := VisitAddSeriesItem(Node.AsAddSeriesItem); end;
FHandlers[akSeriesLength] := function(const Node: IAstNode): T begin Result := VisitSeriesLength(Node.AsSeriesLength); end;
FHandlers[akRecur] := function(const Node: IAstNode): T begin Result := VisitRecurNode(Node.AsRecur); end;
FHandlers[akNop] := function(const Node: IAstNode): T begin Result := VisitNop(Node.AsNop); end;
// Pipes
FHandlers[akPipeInput] := function(const Node: IAstNode): T begin Result := VisitPipeInput(Node.AsPipeInput); end;
FHandlers[akPipeSelectorList] := function(const Node: IAstNode): T begin Result := VisitPipeSelectorList(Node.AsPipeSelectorList); end;
FHandlers[akPipeInputList] := function(const Node: IAstNode): T begin Result := VisitPipeInputList(Node.AsPipeInputList); end;
FHandlers[akPipe] := function(const Node: IAstNode): T begin Result := VisitPipe(Node.AsPipe); end;
end;
function TAstVisitor<T>.Visit(const Node: IAstNode): T;
var
handler: TNodeHandler<T>;
begin
if not Assigned(Node) then
Exit(Default(T));
// O(1) Lookup
handler := FHandlers[Node.Kind];
if Assigned(handler) then
Result := handler(Node)
else
raise ENoHandlerException.Create(Node.Kind.ToString);
end;
function TAstVisitor<T>.Accept(const Node: IAstNode): T; function TAstVisitor<T>.Accept(const Node: IAstNode): T;
begin begin
if Assigned(Node) then Result := Visit(Node);
Result := Node.Accept(Self).AsGeneric<T> end;
else
Result := Default(T); // --- Interface Bridge Methods (IAstVisitor -> TDataValue) ---
function TAstVisitor<T>.VisitBridge(const Node: IAstNode): TDataValue;
begin
Result := TDataValue.FromGeneric<T>(Visit(Node));
end; end;
function TAstVisitor<T>.DoVisitConstant(const Node: IConstantNode): TDataValue; function TAstVisitor<T>.DoVisitConstant(const Node: IConstantNode): TDataValue;
@@ -199,7 +305,6 @@ function TAstVisitor<T>.DoVisitKeyword(const Node: IKeywordNode): TDataValue;
begin begin
Result := TDataValue.FromGeneric<T>(VisitKeyword(Node)); Result := TDataValue.FromGeneric<T>(VisitKeyword(Node));
end; end;
function TAstVisitor<T>.DoVisitParameterList(const Node: IParameterList): TDataValue; function TAstVisitor<T>.DoVisitParameterList(const Node: IParameterList): TDataValue;
begin begin
Result := TDataValue.FromGeneric<T>(VisitParameterList(Node)); Result := TDataValue.FromGeneric<T>(VisitParameterList(Node));
@@ -220,7 +325,6 @@ function TAstVisitor<T>.DoVisitRecordField(const Node: IRecordFieldNode): TDataV
begin begin
Result := TDataValue.FromGeneric<T>(VisitRecordField(Node)); Result := TDataValue.FromGeneric<T>(VisitRecordField(Node));
end; end;
function TAstVisitor<T>.DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TAstVisitor<T>.DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin begin
Result := TDataValue.FromGeneric<T>(VisitIfExpression(Node)); Result := TDataValue.FromGeneric<T>(VisitIfExpression(Node));
@@ -301,7 +405,6 @@ function TAstVisitor<T>.DoVisitNop(const Node: INopNode): TDataValue;
begin begin
Result := TDataValue.FromGeneric<T>(VisitNop(Node)); Result := TDataValue.FromGeneric<T>(VisitNop(Node));
end; end;
function TAstVisitor<T>.DoVisitPipeInput(const Node: IPipeInputNode): TDataValue; function TAstVisitor<T>.DoVisitPipeInput(const Node: IPipeInputNode): TDataValue;
begin begin
Result := TDataValue.FromGeneric<T>(VisitPipeInput(Node)); Result := TDataValue.FromGeneric<T>(VisitPipeInput(Node));
@@ -319,7 +422,7 @@ begin
Result := TDataValue.FromGeneric<T>(VisitPipe(Node)); Result := TDataValue.FromGeneric<T>(VisitPipe(Node));
end; end;
// --- Default implementations (analogous to TAstVisitor) --- // --- Default implementations (analogous to original abstract class stubs) ---
function TAstVisitor<T>.VisitConstant(const Node: IConstantNode): T; function TAstVisitor<T>.VisitConstant(const Node: IConstantNode): T;
begin begin
@@ -340,43 +443,43 @@ function TAstVisitor<T>.VisitParameterList(const Node: IParameterList): T;
begin begin
Result := Default(T); Result := Default(T);
for var Item in Node do for var Item in Node do
Accept(Item); Visit(Item);
end; end;
function TAstVisitor<T>.VisitArgumentList(const Node: IArgumentList): T; function TAstVisitor<T>.VisitArgumentList(const Node: IArgumentList): T;
begin begin
Result := Default(T); Result := Default(T);
for var Item in Node do for var Item in Node do
Accept(Item); Visit(Item);
end; end;
function TAstVisitor<T>.VisitExpressionList(const Node: IExpressionList): T; function TAstVisitor<T>.VisitExpressionList(const Node: IExpressionList): T;
begin begin
Result := Default(T); Result := Default(T);
for var Item in Node do for var Item in Node do
Accept(Item); Visit(Item);
end; end;
function TAstVisitor<T>.VisitRecordFieldList(const Node: IRecordFieldList): T; function TAstVisitor<T>.VisitRecordFieldList(const Node: IRecordFieldList): T;
begin begin
Result := Default(T); Result := Default(T);
for var Item in Node do for var Item in Node do
Accept(Item); Visit(Item);
end; end;
function TAstVisitor<T>.VisitRecordField(const Node: IRecordFieldNode): T; function TAstVisitor<T>.VisitRecordField(const Node: IRecordFieldNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Key); Visit(Node.Key);
Accept(Node.Value); Visit(Node.Value);
end; end;
function TAstVisitor<T>.VisitIfExpression(const Node: IIfExpressionNode): T; function TAstVisitor<T>.VisitIfExpression(const Node: IIfExpressionNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Condition); Visit(Node.Condition);
Accept(Node.ThenBranch); Visit(Node.ThenBranch);
Accept(Node.ElseBranch); Visit(Node.ElseBranch);
end; end;
function TAstVisitor<T>.VisitCondExpression(const Node: ICondExpressionNode): T; function TAstVisitor<T>.VisitCondExpression(const Node: ICondExpressionNode): T;
@@ -386,97 +489,97 @@ begin
Result := Default(T); Result := Default(T);
for pair in Node.Pairs do for pair in Node.Pairs do
begin begin
Accept(pair.Condition); Visit(pair.Condition);
Accept(pair.Branch); Visit(pair.Branch);
end; end;
Accept(Node.ElseBranch); Visit(Node.ElseBranch);
end; end;
function TAstVisitor<T>.VisitLambdaExpression(const Node: ILambdaExpressionNode): T; function TAstVisitor<T>.VisitLambdaExpression(const Node: ILambdaExpressionNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Parameters); Visit(Node.Parameters);
Accept(Node.Body); Visit(Node.Body);
end; end;
function TAstVisitor<T>.VisitFunctionCall(const Node: IFunctionCallNode): T; function TAstVisitor<T>.VisitFunctionCall(const Node: IFunctionCallNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Callee); Visit(Node.Callee);
Accept(Node.Arguments); Visit(Node.Arguments);
end; end;
function TAstVisitor<T>.VisitMacroExpansionNode(const Node: IMacroExpansionNode): T; function TAstVisitor<T>.VisitMacroExpansionNode(const Node: IMacroExpansionNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.CallNode); Visit(Node.CallNode);
Accept(Node.ExpandedBody); Visit(Node.ExpandedBody);
end; end;
function TAstVisitor<T>.VisitBlockExpression(const Node: IBlockExpressionNode): T; function TAstVisitor<T>.VisitBlockExpression(const Node: IBlockExpressionNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Expressions); Visit(Node.Expressions);
end; end;
function TAstVisitor<T>.VisitVariableDeclaration(const Node: IVariableDeclarationNode): T; function TAstVisitor<T>.VisitVariableDeclaration(const Node: IVariableDeclarationNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Target); Visit(Node.Target);
Accept(Node.Initializer); Visit(Node.Initializer);
end; end;
function TAstVisitor<T>.VisitAssignment(const Node: IAssignmentNode): T; function TAstVisitor<T>.VisitAssignment(const Node: IAssignmentNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Target); Visit(Node.Target);
Accept(Node.Value); Visit(Node.Value);
end; end;
function TAstVisitor<T>.VisitMacroDefinition(const Node: IMacroDefinitionNode): T; function TAstVisitor<T>.VisitMacroDefinition(const Node: IMacroDefinitionNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Name); Visit(Node.Name);
Accept(Node.Parameters); Visit(Node.Parameters);
Accept(Node.Body); Visit(Node.Body);
end; end;
function TAstVisitor<T>.VisitQuasiquote(const Node: IQuasiquoteNode): T; function TAstVisitor<T>.VisitQuasiquote(const Node: IQuasiquoteNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Expression); Visit(Node.Expression);
end; end;
function TAstVisitor<T>.VisitUnquote(const Node: IUnquoteNode): T; function TAstVisitor<T>.VisitUnquote(const Node: IUnquoteNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Expression); Visit(Node.Expression);
end; end;
function TAstVisitor<T>.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T; function TAstVisitor<T>.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Expression); Visit(Node.Expression);
end; end;
function TAstVisitor<T>.VisitIndexer(const Node: IIndexerNode): T; function TAstVisitor<T>.VisitIndexer(const Node: IIndexerNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Base); Visit(Node.Base);
Accept(Node.Index); Visit(Node.Index);
end; end;
function TAstVisitor<T>.VisitMemberAccess(const Node: IMemberAccessNode): T; function TAstVisitor<T>.VisitMemberAccess(const Node: IMemberAccessNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Base); Visit(Node.Base);
Accept(Node.Member); Visit(Node.Member);
end; end;
function TAstVisitor<T>.VisitRecordLiteral(const Node: IRecordLiteralNode): T; function TAstVisitor<T>.VisitRecordLiteral(const Node: IRecordLiteralNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Fields); Visit(Node.Fields);
end; end;
function TAstVisitor<T>.VisitCreateSeries(const Node: ICreateSeriesNode): T; function TAstVisitor<T>.VisitCreateSeries(const Node: ICreateSeriesNode): T;
@@ -487,21 +590,21 @@ end;
function TAstVisitor<T>.VisitAddSeriesItem(const Node: IAddSeriesItemNode): T; function TAstVisitor<T>.VisitAddSeriesItem(const Node: IAddSeriesItemNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Series); Visit(Node.Series);
Accept(Node.Value); Visit(Node.Value);
Accept(Node.Lookback); Visit(Node.Lookback);
end; end;
function TAstVisitor<T>.VisitSeriesLength(const Node: ISeriesLengthNode): T; function TAstVisitor<T>.VisitSeriesLength(const Node: ISeriesLengthNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Series); Visit(Node.Series);
end; end;
function TAstVisitor<T>.VisitRecurNode(const Node: IRecurNode): T; function TAstVisitor<T>.VisitRecurNode(const Node: IRecurNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Arguments); Visit(Node.Arguments);
end; end;
function TAstVisitor<T>.VisitNop(const Node: INopNode): T; function TAstVisitor<T>.VisitNop(const Node: INopNode): T;
@@ -512,29 +615,29 @@ end;
function TAstVisitor<T>.VisitPipeInput(const Node: IPipeInputNode): T; function TAstVisitor<T>.VisitPipeInput(const Node: IPipeInputNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.StreamSource); Visit(Node.StreamSource);
Accept(Node.Selectors); // Now visits SelectorList Visit(Node.Selectors);
end; end;
function TAstVisitor<T>.VisitPipeSelectorList(const Node: IPipeSelectorList): T; function TAstVisitor<T>.VisitPipeSelectorList(const Node: IPipeSelectorList): T;
begin begin
Result := Default(T); Result := Default(T);
for var Item in Node do for var Item in Node do
Accept(Item); Visit(Item);
end; end;
function TAstVisitor<T>.VisitPipeInputList(const Node: IPipeInputList): T; function TAstVisitor<T>.VisitPipeInputList(const Node: IPipeInputList): T;
begin begin
Result := Default(T); Result := Default(T);
for var Item in Node do for var Item in Node do
Accept(Item); Visit(Item);
end; end;
function TAstVisitor<T>.VisitPipe(const Node: IPipeNode): T; function TAstVisitor<T>.VisitPipe(const Node: IPipeNode): T;
begin begin
Result := Default(T); Result := Default(T);
Accept(Node.Inputs); Visit(Node.Inputs);
Accept(Node.Transformation); Visit(Node.Transformation);
end; end;
{ TAstTransformer } { TAstTransformer }
@@ -580,7 +683,7 @@ begin
for i := 0 to Node.Count - 1 do for i := 0 to Node.Count - 1 do
begin begin
item := Node[i]; item := Node[i];
newItem := Accept(item).AsIdentifier; newItem := Visit(item).AsIdentifier;
newItems[i] := newItem; newItems[i] := newItem;
if item <> newItem then if item <> newItem then
hasChanged := True; hasChanged := True;
@@ -605,7 +708,7 @@ begin
for i := 0 to Node.Count - 1 do for i := 0 to Node.Count - 1 do
begin begin
item := Node[i]; item := Node[i];
newItem := Accept(item); newItem := Visit(item);
newItems[i] := newItem; newItems[i] := newItem;
if item <> newItem then if item <> newItem then
hasChanged := True; hasChanged := True;
@@ -629,7 +732,7 @@ begin
for i := 0 to Node.Count - 1 do for i := 0 to Node.Count - 1 do
begin begin
item := Node[i]; item := Node[i];
newItem := Accept(item); newItem := Visit(item);
newItems[i] := newItem; newItems[i] := newItem;
if item <> newItem then if item <> newItem then
hasChanged := True; hasChanged := True;
@@ -653,8 +756,8 @@ begin
for i := 0 to Node.Count - 1 do for i := 0 to Node.Count - 1 do
begin begin
item := Node[i]; item := Node[i];
// Ensure we transform the field node itself via Accept to trigger VisitRecordField // Ensure we transform the field node itself via Visit to trigger VisitRecordField
newItem := Accept(item).AsRecordField; newItem := Visit(item).AsRecordField;
newItems[i] := newItem; newItems[i] := newItem;
if item <> newItem then if item <> newItem then
hasChanged := True; hasChanged := True;
@@ -671,8 +774,8 @@ var
newKey: IKeywordNode; newKey: IKeywordNode;
newValue: IAstNode; newValue: IAstNode;
begin begin
newKey := Accept(Node.Key).AsKeyword; newKey := Visit(Node.Key).AsKeyword;
newValue := Accept(Node.Value); newValue := Visit(Node.Value);
if (newKey = Node.Key) and (newValue = Node.Value) then if (newKey = Node.Key) and (newValue = Node.Value) then
Result := Node Result := Node
@@ -686,9 +789,9 @@ function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstN
var var
newCond, newThen, newElse: IAstNode; newCond, newThen, newElse: IAstNode;
begin begin
newCond := Accept(Node.Condition); newCond := Visit(Node.Condition);
newThen := Accept(Node.ThenBranch); newThen := Visit(Node.ThenBranch);
newElse := Accept(Node.ElseBranch); newElse := Visit(Node.ElseBranch);
if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
Result := Node Result := Node
@@ -709,15 +812,15 @@ begin
for i := 0 to High(Node.Pairs) do for i := 0 to High(Node.Pairs) do
begin begin
newCond := Accept(Node.Pairs[i].Condition); newCond := Visit(Node.Pairs[i].Condition);
newBranch := Accept(Node.Pairs[i].Branch); newBranch := Visit(Node.Pairs[i].Branch);
newPairs[i] := TCondPair.Create(newCond, newBranch); newPairs[i] := TCondPair.Create(newCond, newBranch);
if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then
hasChanged := True; hasChanged := True;
end; end;
newElse := Accept(Node.ElseBranch); // Accept handles nil newElse := Visit(Node.ElseBranch); // Visit handles nil
if newElse <> Node.ElseBranch then if newElse <> Node.ElseBranch then
hasChanged := True; hasChanged := True;
@@ -732,8 +835,8 @@ var
newParams: IParameterList; newParams: IParameterList;
newBody: IAstNode; newBody: IAstNode;
begin begin
newParams := Accept(Node.Parameters).AsParameterList; newParams := Visit(Node.Parameters).AsParameterList;
newBody := Accept(Node.Body); newBody := Visit(Node.Body);
if (newParams = Node.Parameters) and (newBody = Node.Body) then if (newParams = Node.Parameters) and (newBody = Node.Body) then
Result := Node Result := Node
@@ -761,8 +864,8 @@ var
newCallee: IAstNode; newCallee: IAstNode;
newArgs: IArgumentList; newArgs: IArgumentList;
begin begin
newCallee := Accept(Node.Callee); newCallee := Visit(Node.Callee);
newArgs := Accept(Node.Arguments).AsArgumentList; newArgs := Visit(Node.Arguments).AsArgumentList;
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
Result := Node Result := Node
@@ -778,7 +881,7 @@ function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode
var var
newBody: IAstNode; newBody: IAstNode;
begin begin
newBody := Accept(Node.ExpandedBody); newBody := Visit(Node.ExpandedBody);
if newBody = Node.ExpandedBody then if newBody = Node.ExpandedBody then
exit(Node); exit(Node);
@@ -791,7 +894,7 @@ function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode):
var var
newExprs: IExpressionList; newExprs: IExpressionList;
begin begin
newExprs := Accept(Node.Expressions).AsExpressionList; newExprs := Visit(Node.Expressions).AsExpressionList;
if newExprs = Node.Expressions then if newExprs = Node.Expressions then
Result := Node Result := Node
@@ -805,8 +908,8 @@ var
newTarget: IAstNode; newTarget: IAstNode;
newInit: IAstNode; newInit: IAstNode;
begin begin
newTarget := Accept(Node.Target); newTarget := Visit(Node.Target);
newInit := Accept(Node.Initializer); newInit := Visit(Node.Initializer);
if (newTarget = Node.Target) and (newInit = Node.Initializer) then if (newTarget = Node.Target) and (newInit = Node.Initializer) then
Result := Node Result := Node
@@ -821,8 +924,8 @@ var
newTarget: IAstNode; newTarget: IAstNode;
newValue: IAstNode; newValue: IAstNode;
begin begin
newTarget := Accept(Node.Target); newTarget := Visit(Node.Target);
newValue := Accept(Node.Value); newValue := Visit(Node.Value);
if (newTarget = Node.Target) and (newValue = Node.Value) then if (newTarget = Node.Target) and (newValue = Node.Value) then
Result := Node Result := Node
@@ -841,7 +944,7 @@ function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
var var
newExpr: IAstNode; newExpr: IAstNode;
begin begin
newExpr := Accept(Node.Expression); newExpr := Visit(Node.Expression);
if newExpr = Node.Expression then if newExpr = Node.Expression then
Result := Node Result := Node
else else
@@ -852,7 +955,7 @@ function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode;
var var
newExpr: IAstNode; newExpr: IAstNode;
begin begin
newExpr := Accept(Node.Expression); newExpr := Visit(Node.Expression);
if newExpr = Node.Expression then if newExpr = Node.Expression then
Result := Node Result := Node
else else
@@ -863,7 +966,7 @@ function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode):
var var
newExpr: IAstNode; newExpr: IAstNode;
begin begin
newExpr := Accept(Node.Expression); newExpr := Visit(Node.Expression);
if (newExpr = Node.Expression) then if (newExpr = Node.Expression) then
Result := Node Result := Node
@@ -875,8 +978,8 @@ function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode;
var var
newBase, newIndex: IAstNode; newBase, newIndex: IAstNode;
begin begin
newBase := Accept(Node.Base); newBase := Visit(Node.Base);
newIndex := Accept(Node.Index); newIndex := Visit(Node.Index);
if (newBase = Node.Base) and (newIndex = Node.Index) then if (newBase = Node.Base) and (newIndex = Node.Index) then
Result := Node Result := Node
@@ -889,8 +992,8 @@ var
newBase: IAstNode; newBase: IAstNode;
newMember: IKeywordNode; newMember: IKeywordNode;
begin begin
newBase := Accept(Node.Base); newBase := Visit(Node.Base);
newMember := Accept(Node.Member).AsKeyword; newMember := Visit(Node.Member).AsKeyword;
if (newBase = Node.Base) and (newMember = Node.Member) then if (newBase = Node.Base) and (newMember = Node.Member) then
Result := Node Result := Node
@@ -902,7 +1005,7 @@ function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAs
var var
newFields: IRecordFieldList; newFields: IRecordFieldList;
begin begin
newFields := Accept(Node.Fields).AsRecordFieldList; newFields := Visit(Node.Fields).AsRecordFieldList;
if newFields = Node.Fields then if newFields = Node.Fields then
Result := Node Result := Node
@@ -917,9 +1020,9 @@ var
newSeries: IIdentifierNode; newSeries: IIdentifierNode;
newValue, newLookback: IAstNode; newValue, newLookback: IAstNode;
begin begin
newSeries := Accept(Node.Series).AsIdentifier; newSeries := Visit(Node.Series).AsIdentifier;
newValue := Accept(Node.Value); newValue := Visit(Node.Value);
newLookback := Accept(Node.Lookback); newLookback := Visit(Node.Lookback);
if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then
Result := Node Result := Node
@@ -931,7 +1034,7 @@ function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstN
var var
newSeries: IIdentifierNode; newSeries: IIdentifierNode;
begin begin
newSeries := Accept(Node.Series).AsIdentifier; newSeries := Visit(Node.Series).AsIdentifier;
if newSeries = Node.Series then if newSeries = Node.Series then
Result := Node Result := Node
@@ -943,7 +1046,7 @@ function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode;
var var
newArgs: IArgumentList; newArgs: IArgumentList;
begin begin
newArgs := Accept(Node.Arguments).AsArgumentList; newArgs := Visit(Node.Arguments).AsArgumentList;
if newArgs = Node.Arguments then if newArgs = Node.Arguments then
Result := Node Result := Node
@@ -956,8 +1059,8 @@ var
newSource: IIdentifierNode; newSource: IIdentifierNode;
newSels: IPipeSelectorList; newSels: IPipeSelectorList;
begin begin
newSource := Accept(Node.StreamSource).AsIdentifier; newSource := Visit(Node.StreamSource).AsIdentifier;
newSels := Accept(Node.Selectors).AsPipeSelectorList; newSels := Visit(Node.Selectors).AsPipeSelectorList;
if (newSource = Node.StreamSource) and (newSels = Node.Selectors) then if (newSource = Node.StreamSource) and (newSels = Node.Selectors) then
Result := Node Result := Node
@@ -977,7 +1080,7 @@ begin
for i := 0 to Node.Count - 1 do for i := 0 to Node.Count - 1 do
begin begin
item := Node[i]; item := Node[i];
newItem := Accept(item).AsKeyword; newItem := Visit(item).AsKeyword;
newItems[i] := newItem; newItems[i] := newItem;
if item <> newItem then if item <> newItem then
hasChanged := True; hasChanged := True;
@@ -1002,7 +1105,7 @@ begin
for i := 0 to Node.Count - 1 do for i := 0 to Node.Count - 1 do
begin begin
item := Node[i]; item := Node[i];
newItem := Accept(item).AsPipeInput; // Explicit cast via new helper newItem := Visit(item).AsPipeInput; // Explicit cast via new helper
newItems[i] := newItem; newItems[i] := newItem;
if item <> newItem then if item <> newItem then
hasChanged := True; hasChanged := True;
@@ -1021,8 +1124,8 @@ var
newInputs: IPipeInputList; newInputs: IPipeInputList;
newTrans: ILambdaExpressionNode; newTrans: ILambdaExpressionNode;
begin begin
newInputs := Accept(Node.Inputs).AsPipeInputList; newInputs := Visit(Node.Inputs).AsPipeInputList;
newTrans := Accept(Node.Transformation).AsLambdaExpression; newTrans := Visit(Node.Transformation).AsLambdaExpression;
if (newInputs = Node.Inputs) and (newTrans = Node.Transformation) then if (newInputs = Node.Inputs) and (newTrans = Node.Transformation) then
Result := Node Result := Node