Files
MycLib/Src/AST/Myc.Ast.Visitor.pas
T
Michael Schimmel db74b83e11 Generic Visitors
2026-01-02 14:29:16 +01:00

1005 lines
34 KiB
ObjectPascal

unit Myc.Ast.Visitor;
interface
uses
System.SysUtils,
System.Generics.Collections,
Myc.Data.Value,
Myc.Ast,
Myc.Ast.Types,
Myc.Ast.Nodes,
Myc.Ast.Identities;
type
TAstVisitor<T> = class;
// Delegate for handling a specific node kind within the visitor context.
// Receives the visitor instance explicitly to allow for stateless handlers or external logic.
TNodeHandler<T> = reference to function(const Visitor: TAstVisitor<T>; const Node: IAstNode): T;
// Callback to register a handler for a specific node kind.
TRegisterHandler<T> = reference to procedure(Kind: TAstNodeKind; const Handler: TNodeHandler<T>);
TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor)
strict private
// The registry for O(1) dispatch based on TAstNodeKind
FHandlers: array[TAstNodeKind] of TNodeHandler<T>;
// IAstVisitor explicit implementation (bridge methods)
function IAstVisitor.Visit = VisitBridge;
// Private helpers to wrap T into TDataValue for the non-generic interface
function VisitBridge(const Node: IAstNode): TDataValue;
protected
// Initial setup of the dispatch table.
// The base implementation maps all kinds to the legacy VisitXYZ virtual methods.
procedure SetupHandlers(const Register: TRegisterHandler<T>); 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 VisitIdentifier(const Node: IIdentifierNode): T; virtual;
function VisitKeyword(const Node: IKeywordNode): T; virtual;
function VisitParameterList(const Node: IParameterList): T; virtual;
function VisitArgumentList(const Node: IArgumentList): T; virtual;
function VisitExpressionList(const Node: IExpressionList): T; virtual;
function VisitRecordFieldList(const Node: IRecordFieldList): T; virtual;
function VisitRecordField(const Node: IRecordFieldNode): T; virtual;
function VisitIfExpression(const Node: IIfExpressionNode): T; virtual;
function VisitCondExpression(const Node: ICondExpressionNode): T; virtual;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): T; virtual;
function VisitFunctionCall(const Node: IFunctionCallNode): T; virtual;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): T; virtual;
function VisitBlockExpression(const Node: IBlockExpressionNode): T; virtual;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): T; virtual;
function VisitAssignment(const Node: IAssignmentNode): T; virtual;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): T; virtual;
function VisitQuasiquote(const Node: IQuasiquoteNode): T; virtual;
function VisitUnquote(const Node: IUnquoteNode): T; virtual;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T; virtual;
function VisitIndexer(const Node: IIndexerNode): T; virtual;
function VisitMemberAccess(const Node: IMemberAccessNode): T; virtual;
function VisitRecordLiteral(const Node: IRecordLiteralNode): T; virtual;
function VisitCreateSeries(const Node: ICreateSeriesNode): T; virtual;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): T; virtual;
function VisitSeriesLength(const Node: ISeriesLengthNode): T; virtual;
function VisitRecurNode(const Node: IRecurNode): T; virtual;
function VisitNop(const Node: INopNode): T; virtual;
function VisitPipeInput(const Node: IPipeInputNode): T; virtual;
function VisitPipeSelectorList(const Node: IPipeSelectorList): T; virtual;
function VisitPipeInputList(const Node: IPipeInputList): 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
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
end;
// Helper for transformation visitors (returns IAstNode)
// Overrides legacy methods to provide default recursive reconstruction behavior.
TAstTransformer = class abstract(TAstVisitor<IAstNode>)
protected
function VisitConstant(const Node: IConstantNode): IAstNode; override;
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
function VisitParameterList(const Node: IParameterList): IAstNode; override;
function VisitArgumentList(const Node: IArgumentList): IAstNode; override;
function VisitExpressionList(const Node: IExpressionList): IAstNode; override;
function VisitRecordFieldList(const Node: IRecordFieldList): IAstNode; override;
function VisitRecordField(const Node: IRecordFieldNode): IAstNode; override;
function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override;
function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
function VisitNop(const Node: INopNode): IAstNode; override;
function VisitPipeInput(const Node: IPipeInputNode): IAstNode; override;
function VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode; override;
function VisitPipeInputList(const Node: IPipeInputList): IAstNode; override;
function VisitPipe(const Node: IPipeNode): IAstNode; override;
end;
TVoid = record
end;
TAstVisitor = class(TAstVisitor<TVoid>);
implementation
{ TAstVisitor<T> }
procedure TAstVisitor<T>.AfterConstruction;
begin
inherited;
// We pass explicit anonymous methods that use the 'Visitor' argument.
// This ensures that even if handlers are moved out of context later, they operate on the passed visitor instance.
SetupHandlers(procedure(Kind: TAstNodeKind; const Handler: TNodeHandler<T>) begin FHandlers[Kind] := Handler; end);
end;
procedure TAstVisitor<T>.BeforeDestruction;
begin
for var i := Low(TAstNodeKind) to High(TAstNodeKind) do
FHandlers[i] := nil;
inherited;
end;
procedure TAstVisitor<T>.SetupHandlers(const Register: TRegisterHandler<T>);
begin
// Map all kinds to the legacy virtual methods using the passed Visitor instance (V).
// Core
Register(akConstant, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitConstant(N.AsConstant); end);
Register(akIdentifier, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitIdentifier(N.AsIdentifier); end);
Register(akKeyword, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitKeyword(N.AsKeyword); end);
// Lists
Register(
akParameterList,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitParameterList(N.AsParameterList); end
);
Register(
akArgumentList,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitArgumentList(N.AsArgumentList); end
);
Register(
akExpressionList,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitExpressionList(N.AsExpressionList); end
);
Register(
akRecordFieldList,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitRecordFieldList(N.AsRecordFieldList); end
);
Register(
akRecordField,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitRecordField(N.AsRecordField); end
);
// Structural
Register(
akIfExpression,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitIfExpression(N.AsIfExpression); end
);
Register(
akCondExpression,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitCondExpression(N.AsCondExpression); end
);
Register(
akLambdaExpression,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitLambdaExpression(N.AsLambdaExpression); end
);
Register(
akFunctionCall,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitFunctionCall(N.AsFunctionCall); end
);
Register(
akMacroExpansion,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitMacroExpansionNode(N.AsMacroExpansion); end
);
Register(
akBlockExpression,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitBlockExpression(N.AsBlockExpression); end
);
Register(
akVariableDeclaration,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitVariableDeclaration(N.AsVariableDeclaration); end
);
Register(akAssignment, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitAssignment(N.AsAssignment); end);
Register(
akMacroDefinition,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitMacroDefinition(N.AsMacroDefinition); end
);
Register(akQuasiquote, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitQuasiquote(N.AsQuasiquote); end);
Register(akUnquote, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitUnquote(N.AsUnquote); end);
Register(
akUnquoteSplicing,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitUnquoteSplicing(N.AsUnquoteSplicing); end
);
Register(akIndexer, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitIndexer(N.AsIndexer); end);
Register(
akMemberAccess,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitMemberAccess(N.AsMemberAccess); end
);
Register(
akRecordLiteral,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitRecordLiteral(N.AsRecordLiteral); end
);
Register(
akCreateSeries,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitCreateSeries(N.AsCreateSeries); end
);
Register(
akAddSeriesItem,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitAddSeriesItem(N.AsAddSeriesItem); end
);
Register(
akSeriesLength,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitSeriesLength(N.AsSeriesLength); end
);
Register(akRecur, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitRecurNode(N.AsRecur); end);
Register(akNop, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitNop(N.AsNop); end);
// Pipes
Register(akPipeInput, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitPipeInput(N.AsPipeInput); end);
Register(
akPipeSelectorList,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitPipeSelectorList(N.AsPipeSelectorList); end
);
Register(
akPipeInputList,
function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitPipeInputList(N.AsPipeInputList); end
);
Register(akPipe, function(const V: TAstVisitor<T>; const N: IAstNode): T begin Result := V.VisitPipe(N.AsPipe); end);
end;
function TAstVisitor<T>.Visit(const Node: IAstNode): T;
var
[unsafe]
handler: TNodeHandler<T>;
begin
if not Assigned(Node) then
Exit(Default(T));
// O(1) Lookup
handler := TNodeHandler<T>(FHandlers[Node.Kind]);
if Assigned(handler) then
Result := handler(Self, Node)
else
raise ENoHandlerException.Create(Node.Kind.ToString);
end;
function TAstVisitor<T>.Accept(const Node: IAstNode): T;
begin
Result := Visit(Node);
end;
// --- Interface Bridge Methods (IAstVisitor -> TDataValue) ---
function TAstVisitor<T>.VisitBridge(const Node: IAstNode): TDataValue;
begin
Result := TDataValue.FromGeneric<T>(Visit(Node));
end;
// --- Default implementations (analogous to original abstract class stubs) ---
function TAstVisitor<T>.VisitConstant(const Node: IConstantNode): T;
begin
Result := Default(T);
end;
function TAstVisitor<T>.VisitIdentifier(const Node: IIdentifierNode): T;
begin
Result := Default(T);
end;
function TAstVisitor<T>.VisitKeyword(const Node: IKeywordNode): T;
begin
Result := Default(T);
end;
function TAstVisitor<T>.VisitParameterList(const Node: IParameterList): T;
begin
Result := Default(T);
for var Item in Node do
Visit(Item);
end;
function TAstVisitor<T>.VisitArgumentList(const Node: IArgumentList): T;
begin
Result := Default(T);
for var Item in Node do
Visit(Item);
end;
function TAstVisitor<T>.VisitExpressionList(const Node: IExpressionList): T;
begin
Result := Default(T);
for var Item in Node do
Visit(Item);
end;
function TAstVisitor<T>.VisitRecordFieldList(const Node: IRecordFieldList): T;
begin
Result := Default(T);
for var Item in Node do
Visit(Item);
end;
function TAstVisitor<T>.VisitRecordField(const Node: IRecordFieldNode): T;
begin
Result := Default(T);
Visit(Node.Key);
Visit(Node.Value);
end;
function TAstVisitor<T>.VisitIfExpression(const Node: IIfExpressionNode): T;
begin
Result := Default(T);
Visit(Node.Condition);
Visit(Node.ThenBranch);
Visit(Node.ElseBranch);
end;
function TAstVisitor<T>.VisitCondExpression(const Node: ICondExpressionNode): T;
var
pair: TCondPair;
begin
Result := Default(T);
for pair in Node.Pairs do
begin
Visit(pair.Condition);
Visit(pair.Branch);
end;
Visit(Node.ElseBranch);
end;
function TAstVisitor<T>.VisitLambdaExpression(const Node: ILambdaExpressionNode): T;
begin
Result := Default(T);
Visit(Node.Parameters);
Visit(Node.Body);
end;
function TAstVisitor<T>.VisitFunctionCall(const Node: IFunctionCallNode): T;
begin
Result := Default(T);
Visit(Node.Callee);
Visit(Node.Arguments);
end;
function TAstVisitor<T>.VisitMacroExpansionNode(const Node: IMacroExpansionNode): T;
begin
Result := Default(T);
Visit(Node.CallNode);
Visit(Node.ExpandedBody);
end;
function TAstVisitor<T>.VisitBlockExpression(const Node: IBlockExpressionNode): T;
begin
Result := Default(T);
Visit(Node.Expressions);
end;
function TAstVisitor<T>.VisitVariableDeclaration(const Node: IVariableDeclarationNode): T;
begin
Result := Default(T);
Visit(Node.Target);
Visit(Node.Initializer);
end;
function TAstVisitor<T>.VisitAssignment(const Node: IAssignmentNode): T;
begin
Result := Default(T);
Visit(Node.Target);
Visit(Node.Value);
end;
function TAstVisitor<T>.VisitMacroDefinition(const Node: IMacroDefinitionNode): T;
begin
Result := Default(T);
Visit(Node.Name);
Visit(Node.Parameters);
Visit(Node.Body);
end;
function TAstVisitor<T>.VisitQuasiquote(const Node: IQuasiquoteNode): T;
begin
Result := Default(T);
Visit(Node.Expression);
end;
function TAstVisitor<T>.VisitUnquote(const Node: IUnquoteNode): T;
begin
Result := Default(T);
Visit(Node.Expression);
end;
function TAstVisitor<T>.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T;
begin
Result := Default(T);
Visit(Node.Expression);
end;
function TAstVisitor<T>.VisitIndexer(const Node: IIndexerNode): T;
begin
Result := Default(T);
Visit(Node.Base);
Visit(Node.Index);
end;
function TAstVisitor<T>.VisitMemberAccess(const Node: IMemberAccessNode): T;
begin
Result := Default(T);
Visit(Node.Base);
Visit(Node.Member);
end;
function TAstVisitor<T>.VisitRecordLiteral(const Node: IRecordLiteralNode): T;
begin
Result := Default(T);
Visit(Node.Fields);
end;
function TAstVisitor<T>.VisitCreateSeries(const Node: ICreateSeriesNode): T;
begin
Result := Default(T);
end;
function TAstVisitor<T>.VisitAddSeriesItem(const Node: IAddSeriesItemNode): T;
begin
Result := Default(T);
Visit(Node.Series);
Visit(Node.Value);
Visit(Node.Lookback);
end;
function TAstVisitor<T>.VisitSeriesLength(const Node: ISeriesLengthNode): T;
begin
Result := Default(T);
Visit(Node.Series);
end;
function TAstVisitor<T>.VisitRecurNode(const Node: IRecurNode): T;
begin
Result := Default(T);
Visit(Node.Arguments);
end;
function TAstVisitor<T>.VisitNop(const Node: INopNode): T;
begin
Result := Default(T);
end;
function TAstVisitor<T>.VisitPipeInput(const Node: IPipeInputNode): T;
begin
Result := Default(T);
Visit(Node.StreamSource);
Visit(Node.Selectors);
end;
function TAstVisitor<T>.VisitPipeSelectorList(const Node: IPipeSelectorList): T;
begin
Result := Default(T);
for var Item in Node do
Visit(Item);
end;
function TAstVisitor<T>.VisitPipeInputList(const Node: IPipeInputList): T;
begin
Result := Default(T);
for var Item in Node do
Visit(Item);
end;
function TAstVisitor<T>.VisitPipe(const Node: IPipeNode): T;
begin
Result := Default(T);
Visit(Node.Inputs);
Visit(Node.Transformation);
end;
{ TAstTransformer }
// --- Base Virtual Implementations ---
function TAstTransformer.VisitConstant(const Node: IConstantNode): IAstNode;
begin
Result := Node;
end;
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
begin
Result := Node;
end;
function TAstTransformer.VisitKeyword(const Node: IKeywordNode): IAstNode;
begin
Result := Node;
end;
function TAstTransformer.VisitNop(const Node: INopNode): IAstNode;
begin
Result := Node;
end;
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
begin
Result := Node;
end;
// --- List Transformations ---
function TAstTransformer.VisitParameterList(const Node: IParameterList): IAstNode;
var
hasChanged: Boolean;
newItems: TArray<IIdentifierNode>;
item, newItem: IIdentifierNode;
i: Integer;
begin
hasChanged := False;
SetLength(newItems, Node.Count);
for i := 0 to Node.Count - 1 do
begin
item := Node[i];
newItem := Visit(item).AsIdentifier;
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
// Manually reconstruct using concrete class to verify transformation
Result := TParameterList.Create(newItems, Node.Identity);
end;
function TAstTransformer.VisitArgumentList(const Node: IArgumentList): IAstNode;
var
hasChanged: Boolean;
newItems: TArray<IAstNode>;
item, newItem: IAstNode;
i: Integer;
begin
hasChanged := False;
SetLength(newItems, Node.Count);
for i := 0 to Node.Count - 1 do
begin
item := Node[i];
newItem := Visit(item);
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
Result := TArgumentList.Create(newItems, Node.Identity);
end;
function TAstTransformer.VisitExpressionList(const Node: IExpressionList): IAstNode;
var
hasChanged: Boolean;
newItems: TArray<IAstNode>;
item, newItem: IAstNode;
i: Integer;
begin
hasChanged := False;
SetLength(newItems, Node.Count);
for i := 0 to Node.Count - 1 do
begin
item := Node[i];
newItem := Visit(item);
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
Result := TExpressionList.Create(newItems, Node.Identity);
end;
function TAstTransformer.VisitRecordFieldList(const Node: IRecordFieldList): IAstNode;
var
hasChanged: Boolean;
newItems: TArray<IRecordFieldNode>;
item, newItem: IRecordFieldNode;
i: Integer;
begin
hasChanged := False;
SetLength(newItems, Node.Count);
for i := 0 to Node.Count - 1 do
begin
item := Node[i];
// Ensure we transform the field node itself via Visit to trigger VisitRecordField
newItem := Visit(item).AsRecordField;
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
Result := TRecordFieldList.Create(newItems, Node.Identity);
end;
function TAstTransformer.VisitRecordField(const Node: IRecordFieldNode): IAstNode;
var
newKey: IKeywordNode;
newValue: IAstNode;
begin
newKey := Visit(Node.Key).AsKeyword;
newValue := Visit(Node.Value);
if (newKey = Node.Key) and (newValue = Node.Value) then
Result := Node
else
Result := TAst.RecordField(newKey, newValue, Node.Identity.Location);
end;
// --- Node Transformations ---
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
var
newCond, newThen, newElse: IAstNode;
begin
newCond := Visit(Node.Condition);
newThen := Visit(Node.ThenBranch);
newElse := Visit(Node.ElseBranch);
if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
Result := Node
else
Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
end;
function TAstTransformer.VisitCondExpression(const Node: ICondExpressionNode): IAstNode;
var
newPairs: TArray<TCondPair>;
newElse: IAstNode;
hasChanged: Boolean;
i: Integer;
newCond, newBranch: IAstNode;
begin
hasChanged := False;
SetLength(newPairs, Length(Node.Pairs));
for i := 0 to High(Node.Pairs) do
begin
newCond := Visit(Node.Pairs[i].Condition);
newBranch := Visit(Node.Pairs[i].Branch);
newPairs[i] := TCondPair.Create(newCond, newBranch);
if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then
hasChanged := True;
end;
newElse := Visit(Node.ElseBranch); // Visit handles nil
if newElse <> Node.ElseBranch then
hasChanged := True;
if not hasChanged then
Result := Node
else
Result := TAst.CondExpr(Node.Identity, newPairs, newElse, Node.StaticType);
end;
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
newParams: IParameterList;
newBody: IAstNode;
begin
newParams := Visit(Node.Parameters).AsParameterList;
newBody := Visit(Node.Body);
if (newParams = Node.Parameters) and (newBody = Node.Body) then
Result := Node
else
begin
// Rebuild Lambda via concrete constructor to keep Layout/Descriptor info
// TAst factory would reset layout info.
Result :=
TLambdaExpressionNode.Create(
newParams,
newBody,
Node.StaticType,
Node.Layout,
Node.Descriptor,
Node.Upvalues,
Node.HasNestedLambdas,
Node.IsPure,
Node.Identity
);
end;
end;
function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var
newCallee: IAstNode;
newArgs: IArgumentList;
begin
newCallee := Visit(Node.Callee);
newArgs := Visit(Node.Arguments).AsArgumentList;
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
Result := Node
else
begin
Result :=
TFunctionCallNode
.Create(newCallee, newArgs, Node.StaticType, Node.IsTailCall, Node.StaticTarget, Node.IsTargetPure, Node.Identity);
end;
end;
function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
var
newBody: IAstNode;
begin
newBody := Visit(Node.ExpandedBody);
if newBody = Node.ExpandedBody then
exit(Node);
// Macro Expansion Nodes are structural, reuse identity.
// Note: We don't visit the CallNode as it is the "source" which doesn't change during this transformation typically
Result := TAst.MacroExpansionNode(Node.Identity, Node.CallNode, newBody);
end;
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
var
newExprs: IExpressionList;
begin
newExprs := Visit(Node.Expressions).AsExpressionList;
if newExprs = Node.Expressions then
Result := Node
else
// Rebuild manually to preserve type
Result := TBlockExpressionNode.Create(newExprs, Node.StaticType, Node.Identity);
end;
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
var
newTarget: IAstNode;
newInit: IAstNode;
begin
newTarget := Visit(Node.Target);
newInit := Visit(Node.Initializer);
if (newTarget = Node.Target) and (newInit = Node.Initializer) then
Result := Node
else
begin
Result := TAst.VarDecl(Node.Identity, newTarget, newInit, Node.StaticType, Node.IsBoxed);
end;
end;
function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): IAstNode;
var
newTarget: IAstNode;
newValue: IAstNode;
begin
newTarget := Visit(Node.Target);
newValue := Visit(Node.Value);
if (newTarget = Node.Target) and (newValue = Node.Value) then
Result := Node
else
Result := TAst.Assign(Node.Identity, newTarget, newValue, Node.StaticType);
end;
function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
begin
// Macro definitions are usually stripped/ignored in transformers,
// but we return them as-is to support partial pipelines.
Result := Node;
end;
function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
var
newExpr: IAstNode;
begin
newExpr := Visit(Node.Expression);
if newExpr = Node.Expression then
Result := Node
else
Result := TAst.Quasiquote(Node.Identity, newExpr);
end;
function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode;
var
newExpr: IAstNode;
begin
newExpr := Visit(Node.Expression);
if newExpr = Node.Expression then
Result := Node
else
Result := TAst.Unquote(Node.Identity, newExpr);
end;
function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
var
newExpr: IAstNode;
begin
newExpr := Visit(Node.Expression);
if (newExpr = Node.Expression) then
Result := Node
else
Result := TAst.UnquoteSplicing(Node.Identity, newExpr.AsQuasiquote);
end;
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode;
var
newBase, newIndex: IAstNode;
begin
newBase := Visit(Node.Base);
newIndex := Visit(Node.Index);
if (newBase = Node.Base) and (newIndex = Node.Index) then
Result := Node
else
Result := TAst.Indexer(Node.Identity, newBase, newIndex, Node.StaticType);
end;
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
var
newBase: IAstNode;
newMember: IKeywordNode;
begin
newBase := Visit(Node.Base);
newMember := Visit(Node.Member).AsKeyword;
if (newBase = Node.Base) and (newMember = Node.Member) then
Result := Node
else
Result := TAst.MemberAccess(Node.Identity, newBase, newMember, Node.StaticType);
end;
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
var
newFields: IRecordFieldList;
begin
newFields := Visit(Node.Fields).AsRecordFieldList;
if newFields = Node.Fields then
Result := Node
else
begin
Result := TRecordLiteralNode.Create(newFields, Node.ScalarDefinition, Node.GenericDefinition, Node.StaticType, Node.Identity);
end;
end;
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
var
newSeries: IIdentifierNode;
newValue, newLookback: IAstNode;
begin
newSeries := Visit(Node.Series).AsIdentifier;
newValue := Visit(Node.Value);
newLookback := Visit(Node.Lookback);
if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then
Result := Node
else
Result := TAst.AddSeriesItem(Node.Identity, newSeries, newValue, newLookback, Node.StaticType);
end;
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
var
newSeries: IIdentifierNode;
begin
newSeries := Visit(Node.Series).AsIdentifier;
if newSeries = Node.Series then
Result := Node
else
Result := TAst.SeriesLength(Node.Identity, newSeries, Node.StaticType);
end;
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode;
var
newArgs: IArgumentList;
begin
newArgs := Visit(Node.Arguments).AsArgumentList;
if newArgs = Node.Arguments then
Result := Node
else
Result := TRecurNode.Create(newArgs, Node.StaticType, Node.Identity);
end;
function TAstTransformer.VisitPipeInput(const Node: IPipeInputNode): IAstNode;
var
newSource: IIdentifierNode;
newSels: IPipeSelectorList;
begin
newSource := Visit(Node.StreamSource).AsIdentifier;
newSels := Visit(Node.Selectors).AsPipeSelectorList;
if (newSource = Node.StreamSource) and (newSels = Node.Selectors) then
Result := Node
else
Result := TAst.PipeInput(newSource, newSels, Node.Identity.Location);
end;
function TAstTransformer.VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode;
var
hasChanged: Boolean;
newItems: TArray<IKeywordNode>;
item, newItem: IKeywordNode;
i: Integer;
begin
hasChanged := False;
SetLength(newItems, Node.Count);
for i := 0 to Node.Count - 1 do
begin
item := Node[i];
newItem := Visit(item).AsKeyword;
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
// Manually reconstruct using helper
Result := TAst.PipeSelectorList(newItems, Node.Identity.Location);
end;
function TAstTransformer.VisitPipeInputList(const Node: IPipeInputList): IAstNode;
var
hasChanged: Boolean;
newItems: TArray<IPipeInputNode>;
item, newItem: IPipeInputNode;
i: Integer;
begin
hasChanged := False;
SetLength(newItems, Node.Count);
for i := 0 to Node.Count - 1 do
begin
item := Node[i];
newItem := Visit(item).AsPipeInput; // Explicit cast via new helper
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
// Using TList implementation directly implies we are creating the list structure
// Since IPipeInputList is just INodeList<IPipeInputNode>, we reuse TPipeInputList
Result := TPipeInputList.Create(newItems, Node.Identity);
end;
function TAstTransformer.VisitPipe(const Node: IPipeNode): IAstNode;
var
newInputs: IPipeInputList;
newTrans: ILambdaExpressionNode;
begin
newInputs := Visit(Node.Inputs).AsPipeInputList;
newTrans := Visit(Node.Transformation).AsLambdaExpression;
if (newInputs = Node.Inputs) and (newTrans = Node.Transformation) then
Result := Node
else
Result := TAst.Pipe(Node.Identity, newInputs, newTrans, Node.StaticType);
end;
end.