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 abstract(TInterfacedObject, IAstVisitor)
    strict private
        // IAstVisitor explicit implementation (bridge methods)
        function IAstVisitor.VisitConstant = DoVisitConstant;
        function IAstVisitor.VisitIdentifier = DoVisitIdentifier;
        function IAstVisitor.VisitKeyword = DoVisitKeyword;

        function IAstVisitor.VisitParameterList = DoVisitParameterList;
        function IAstVisitor.VisitArgumentList = DoVisitArgumentList;
        function IAstVisitor.VisitExpressionList = DoVisitExpressionList;
        function IAstVisitor.VisitRecordFieldList = DoVisitRecordFieldList;
        function IAstVisitor.VisitRecordField = DoVisitRecordField;

        function IAstVisitor.VisitIfExpression = DoVisitIfExpression;
        function IAstVisitor.VisitCondExpression = DoVisitCondExpression; // Updated
        function IAstVisitor.VisitLambdaExpression = DoVisitLambdaExpression;
        function IAstVisitor.VisitFunctionCall = DoVisitFunctionCall;
        function IAstVisitor.VisitMacroExpansionNode = DoVisitMacroExpansionNode;
        function IAstVisitor.VisitBlockExpression = DoVisitBlockExpression;
        function IAstVisitor.VisitVariableDeclaration = DoVisitVariableDeclaration;
        function IAstVisitor.VisitAssignment = DoVisitAssignment;
        function IAstVisitor.VisitMacroDefinition = DoVisitMacroDefinition;
        function IAstVisitor.VisitQuasiquote = DoVisitQuasiquote;
        function IAstVisitor.VisitUnquote = DoVisitUnquote;
        function IAstVisitor.VisitUnquoteSplicing = DoVisitUnquoteSplicing;
        function IAstVisitor.VisitIndexer = DoVisitIndexer;
        function IAstVisitor.VisitMemberAccess = DoVisitMemberAccess;
        function IAstVisitor.VisitRecordLiteral = DoVisitRecordLiteral;
        function IAstVisitor.VisitCreateSeries = DoVisitCreateSeries;
        function IAstVisitor.VisitAddSeriesItem = DoVisitAddSeriesItem;
        function IAstVisitor.VisitSeriesLength = DoVisitSeriesLength;
        function IAstVisitor.VisitRecurNode = DoVisitRecurNode;
        function IAstVisitor.VisitNop = DoVisitNop;

        // Private bridge method implementations
        function DoVisitConstant(const Node: IConstantNode): TDataValue;
        function DoVisitIdentifier(const Node: IIdentifierNode): TDataValue;
        function DoVisitKeyword(const Node: IKeywordNode): TDataValue;

        function DoVisitParameterList(const Node: IParameterList): TDataValue;
        function DoVisitArgumentList(const Node: IArgumentList): TDataValue;
        function DoVisitExpressionList(const Node: IExpressionList): TDataValue;
        function DoVisitRecordFieldList(const Node: IRecordFieldList): TDataValue;
        function DoVisitRecordField(const Node: IRecordFieldNode): TDataValue;

        function DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue;
        function DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue; // Updated
        function DoVisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
        function DoVisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
        function DoVisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
        function DoVisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
        function DoVisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
        function DoVisitAssignment(const Node: IAssignmentNode): TDataValue;
        function DoVisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
        function DoVisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
        function DoVisitUnquote(const Node: IUnquoteNode): TDataValue;
        function DoVisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
        function DoVisitIndexer(const Node: IIndexerNode): TDataValue;
        function DoVisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
        function DoVisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
        function DoVisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
        function DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
        function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
        function DoVisitRecurNode(const Node: IRecurNode): TDataValue;
        function DoVisitNop(const Node: INopNode): TDataValue;

    protected
        // Visit a node.
        function Accept(const Node: IAstNode): T; virtual;

        // Default visitor functions implemented analogous to TAstVisitor
        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; // Updated
        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;
    end;

    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; // Updated
        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;
    end;

    TVoid = record
    end;

    TAstVisitor = class(TAstVisitor<TVoid>);

implementation

{ TAstVisitor<T> }

function TAstVisitor<T>.Accept(const Node: IAstNode): T;
begin
    if Assigned(Node) then
        Result := Node.Accept(Self).AsGeneric<T>
    else
        Result := Default(T);
end;

function TAstVisitor<T>.DoVisitConstant(const Node: IConstantNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitConstant(Node));
end;
function TAstVisitor<T>.DoVisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitIdentifier(Node));
end;
function TAstVisitor<T>.DoVisitKeyword(const Node: IKeywordNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitKeyword(Node));
end;

function TAstVisitor<T>.DoVisitParameterList(const Node: IParameterList): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitParameterList(Node));
end;
function TAstVisitor<T>.DoVisitArgumentList(const Node: IArgumentList): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitArgumentList(Node));
end;
function TAstVisitor<T>.DoVisitExpressionList(const Node: IExpressionList): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitExpressionList(Node));
end;
function TAstVisitor<T>.DoVisitRecordFieldList(const Node: IRecordFieldList): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitRecordFieldList(Node));
end;
function TAstVisitor<T>.DoVisitRecordField(const Node: IRecordFieldNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitRecordField(Node));
end;

function TAstVisitor<T>.DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitIfExpression(Node));
end;
function TAstVisitor<T>.DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitCondExpression(Node));
end;
function TAstVisitor<T>.DoVisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitLambdaExpression(Node));
end;
function TAstVisitor<T>.DoVisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitFunctionCall(Node));
end;
function TAstVisitor<T>.DoVisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitMacroExpansionNode(Node));
end;
function TAstVisitor<T>.DoVisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitBlockExpression(Node));
end;
function TAstVisitor<T>.DoVisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitVariableDeclaration(Node));
end;
function TAstVisitor<T>.DoVisitAssignment(const Node: IAssignmentNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitAssignment(Node));
end;
function TAstVisitor<T>.DoVisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitMacroDefinition(Node));
end;
function TAstVisitor<T>.DoVisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitQuasiquote(Node));
end;
function TAstVisitor<T>.DoVisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitUnquote(Node));
end;
function TAstVisitor<T>.DoVisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitUnquoteSplicing(Node));
end;
function TAstVisitor<T>.DoVisitIndexer(const Node: IIndexerNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitIndexer(Node));
end;
function TAstVisitor<T>.DoVisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitMemberAccess(Node));
end;
function TAstVisitor<T>.DoVisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitRecordLiteral(Node));
end;
function TAstVisitor<T>.DoVisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitCreateSeries(Node));
end;
function TAstVisitor<T>.DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitAddSeriesItem(Node));
end;
function TAstVisitor<T>.DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitSeriesLength(Node));
end;
function TAstVisitor<T>.DoVisitRecurNode(const Node: IRecurNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitRecurNode(Node));
end;
function TAstVisitor<T>.DoVisitNop(const Node: INopNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(VisitNop(Node));
end;

// --- Default implementations (analogous to TAstVisitor) ---

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
        Accept(Item);
end;

function TAstVisitor<T>.VisitArgumentList(const Node: IArgumentList): T;
begin
    Result := Default(T);
    for var Item in Node do
        Accept(Item);
end;

function TAstVisitor<T>.VisitExpressionList(const Node: IExpressionList): T;
begin
    Result := Default(T);
    for var Item in Node do
        Accept(Item);
end;

function TAstVisitor<T>.VisitRecordFieldList(const Node: IRecordFieldList): T;
begin
    Result := Default(T);
    for var Item in Node do
        Accept(Item);
end;

function TAstVisitor<T>.VisitRecordField(const Node: IRecordFieldNode): T;
begin
    Result := Default(T);
    Accept(Node.Key);
    Accept(Node.Value);
end;

function TAstVisitor<T>.VisitIfExpression(const Node: IIfExpressionNode): T;
begin
    Result := Default(T);
    Accept(Node.Condition);
    Accept(Node.ThenBranch);
    Accept(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
        Accept(pair.Condition);
        Accept(pair.Branch);
    end;
    Accept(Node.ElseBranch);
end;

function TAstVisitor<T>.VisitLambdaExpression(const Node: ILambdaExpressionNode): T;
begin
    Result := Default(T);
    Accept(Node.Parameters);
    Accept(Node.Body);
end;

function TAstVisitor<T>.VisitFunctionCall(const Node: IFunctionCallNode): T;
begin
    Result := Default(T);
    Accept(Node.Callee);
    Accept(Node.Arguments);
end;

function TAstVisitor<T>.VisitMacroExpansionNode(const Node: IMacroExpansionNode): T;
begin
    Result := Default(T);
    Accept(Node.CallNode);
    Accept(Node.ExpandedBody);
end;

function TAstVisitor<T>.VisitBlockExpression(const Node: IBlockExpressionNode): T;
begin
    Result := Default(T);
    Accept(Node.Expressions);
end;

function TAstVisitor<T>.VisitVariableDeclaration(const Node: IVariableDeclarationNode): T;
begin
    Result := Default(T);
    Accept(Node.Target);
    Accept(Node.Initializer);
end;

function TAstVisitor<T>.VisitAssignment(const Node: IAssignmentNode): T;
begin
    Result := Default(T);
    Accept(Node.Target);
    Accept(Node.Value);
end;

function TAstVisitor<T>.VisitMacroDefinition(const Node: IMacroDefinitionNode): T;
begin
    Result := Default(T);
    Accept(Node.Name);
    Accept(Node.Parameters);
    Accept(Node.Body);
end;

function TAstVisitor<T>.VisitQuasiquote(const Node: IQuasiquoteNode): T;
begin
    Result := Default(T);
    Accept(Node.Expression);
end;

function TAstVisitor<T>.VisitUnquote(const Node: IUnquoteNode): T;
begin
    Result := Default(T);
    Accept(Node.Expression);
end;

function TAstVisitor<T>.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T;
begin
    Result := Default(T);
    Accept(Node.Expression);
end;

function TAstVisitor<T>.VisitIndexer(const Node: IIndexerNode): T;
begin
    Result := Default(T);
    Accept(Node.Base);
    Accept(Node.Index);
end;

function TAstVisitor<T>.VisitMemberAccess(const Node: IMemberAccessNode): T;
begin
    Result := Default(T);
    Accept(Node.Base);
    Accept(Node.Member);
end;

function TAstVisitor<T>.VisitRecordLiteral(const Node: IRecordLiteralNode): T;
begin
    Result := Default(T);
    Accept(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);
    Accept(Node.Series);
    Accept(Node.Value);
    Accept(Node.Lookback);
end;

function TAstVisitor<T>.VisitSeriesLength(const Node: ISeriesLengthNode): T;
begin
    Result := Default(T);
    Accept(Node.Series);
end;

function TAstVisitor<T>.VisitRecurNode(const Node: IRecurNode): T;
begin
    Result := Default(T);
    Accept(Node.Arguments);
end;

function TAstVisitor<T>.VisitNop(const Node: INopNode): T;
begin
    Result := Default(T);
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 := Accept(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 := Accept(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 := Accept(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 Accept to trigger VisitRecordField
        newItem := Accept(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 := Accept(Node.Key).AsKeyword;
    newValue := Accept(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 := Accept(Node.Condition);
    newThen := Accept(Node.ThenBranch);
    newElse := Accept(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 := Accept(Node.Pairs[i].Condition);
        newBranch := Accept(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 := Accept(Node.ElseBranch); // Accept 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 := Accept(Node.Parameters).AsParameterList;
    newBody := Accept(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 := Accept(Node.Callee);
    newArgs := Accept(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 := Accept(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 := Accept(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 := Accept(Node.Target);
    newInit := Accept(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 := Accept(Node.Target);
    newValue := Accept(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 := Accept(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 := Accept(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 := Accept(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 := Accept(Node.Base);
    newIndex := Accept(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 := Accept(Node.Base);
    newMember := Accept(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 := Accept(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 := Accept(Node.Series).AsIdentifier;
    newValue := Accept(Node.Value);
    newLookback := Accept(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 := Accept(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 := Accept(Node.Arguments).AsArgumentList;

    if newArgs = Node.Arguments then
        Result := Node
    else
        Result := TRecurNode.Create(newArgs, Node.StaticType, Node.Identity);
end;

end.
