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;

    // Handler signature is now strictly based on IAstNode to ensure binary compatibility
    // with the dispatch array.
    TNodeHandler<T> = reference to function(const Node: IAstNode): T;

    TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor)
    strict private
        FHandlers: array[TAstNodeKind] of TNodeHandler<T>;

        // IAstVisitor bridge
        function IAstVisitor.Visit = VisitBridge;
        function VisitBridge(const Node: IAstNode): TDataValue;

        // Sentinel to fill gaps (Fail Fast protection)
        function DefaultSentinel(const Node: IAstNode): T;

    protected
        // --- Registration ---

        // Register a handler. No generic overload anymore.
        procedure Register(Kind: TAstNodeKind; const Handler: TNodeHandler<T>); virtual;

        // Subclasses implement this to call Register()
        procedure SetupHandlers; virtual;

        // --- Dispatch ---

        function Visit(const Node: IAstNode): T; virtual;
        function Accept(const Node: IAstNode): T; virtual;

    public
        procedure AfterConstruction; override;
        procedure BeforeDestruction; override;
    end;

    // Helper for transformation visitors (returns IAstNode)
    // Implements recursive reconstruction: If a child changes, a new node is returned.
    TAstTransformer = class abstract(TAstVisitor<IAstNode>)
    protected
        // Leaves
        function VisitLeaf(const N: IAstNode): IAstNode;

        // Lists
        function VisitParameterList(const N: IAstNode): IAstNode;
        function VisitArgumentList(const N: IAstNode): IAstNode;
        function VisitExpressionList(const N: IAstNode): IAstNode;
        function VisitRecordFieldList(const N: IAstNode): IAstNode;
        function VisitRecordField(const N: IAstNode): IAstNode;

        // Structures
        function VisitIfExpression(const N: IAstNode): IAstNode;
        function VisitCondExpression(const N: IAstNode): IAstNode;
        function VisitLambdaExpression(const N: IAstNode): IAstNode;
        function VisitFunctionCall(const N: IAstNode): IAstNode;
        function VisitMacroExpansionNode(const N: IAstNode): IAstNode;
        function VisitBlockExpression(const N: IAstNode): IAstNode;
        function VisitVariableDeclaration(const N: IAstNode): IAstNode;
        function VisitAssignment(const N: IAstNode): IAstNode;
        function VisitMacroDefinition(const N: IAstNode): IAstNode;

        // Wrappers
        function VisitQuasiquote(const N: IAstNode): IAstNode;
        function VisitUnquote(const N: IAstNode): IAstNode;
        function VisitUnquoteSplicing(const N: IAstNode): IAstNode;

        function VisitIndexer(const N: IAstNode): IAstNode;
        function VisitMemberAccess(const N: IAstNode): IAstNode;
        function VisitRecordLiteral(const N: IAstNode): IAstNode;
        function VisitAddSeriesItem(const N: IAstNode): IAstNode;
        function VisitSeriesLength(const N: IAstNode): IAstNode;
        function VisitRecurNode(const N: IAstNode): IAstNode;

        // Pipes
        function VisitPipeInput(const N: IAstNode): IAstNode;
        function VisitPipeSelectorList(const N: IAstNode): IAstNode;
        function VisitPipeInputList(const N: IAstNode): IAstNode;
        function VisitPipe(const N: IAstNode): IAstNode;

        procedure SetupHandlers; override;
    end;

    TVoid = record
    end;

    // A concrete "Walker" that traverses all children by default.
    // Useful for Analyzers, Linters, or Searchers.
    TAstVisitor = class(TAstVisitor<TVoid>)
    protected
        // Walker implementations
        function WalkLeaf(const N: IAstNode): TVoid;

        function WalkParameterList(const N: IAstNode): TVoid;
        function WalkArgumentList(const N: IAstNode): TVoid;
        function WalkExpressionList(const N: IAstNode): TVoid;
        function WalkRecordFieldList(const N: IAstNode): TVoid;
        function WalkRecordField(const N: IAstNode): TVoid;

        function WalkIfExpression(const N: IAstNode): TVoid;
        function WalkCondExpression(const N: IAstNode): TVoid;
        function WalkLambdaExpression(const N: IAstNode): TVoid;
        function WalkFunctionCall(const N: IAstNode): TVoid;
        function WalkMacroExpansionNode(const N: IAstNode): TVoid;
        function WalkBlockExpression(const N: IAstNode): TVoid;
        function WalkVariableDeclaration(const N: IAstNode): TVoid;
        function WalkAssignment(const N: IAstNode): TVoid;
        function WalkMacroDefinition(const N: IAstNode): TVoid;
        function WalkWrapper(const N: IAstNode): TVoid; // Quasiquote, Unquote, etc.
        function WalkIndexer(const N: IAstNode): TVoid;
        function WalkMemberAccess(const N: IAstNode): TVoid;
        function WalkRecordLiteral(const N: IAstNode): TVoid;
        function WalkAddSeriesItem(const N: IAstNode): TVoid;
        function WalkSeriesLength(const N: IAstNode): TVoid;
        function WalkRecurNode(const N: IAstNode): TVoid;

        function WalkPipeInput(const N: IAstNode): TVoid;
        function WalkPipeSelectorList(const N: IAstNode): TVoid;
        function WalkPipeInputList(const N: IAstNode): TVoid;
        function WalkPipe(const N: IAstNode): TVoid;

        procedure SetupHandlers; override;
    end;

implementation

{ TAstVisitor<T> }

procedure TAstVisitor<T>.AfterConstruction;
var
    k: TAstNodeKind;
begin
    inherited;

    // 1. Initialize with Sentinel (Fail Fast / Safety)
    for k := Low(TAstNodeKind) to High(TAstNodeKind) do
        FHandlers[k] := DefaultSentinel;

    // 2. Allow subclass to overwrite with real handlers
    SetupHandlers;
end;

procedure TAstVisitor<T>.BeforeDestruction;
begin
    for var i := Low(TAstNodeKind) to High(TAstNodeKind) do
        FHandlers[i] := nil;
    inherited;
end;

function TAstVisitor<T>.DefaultSentinel(const Node: IAstNode): T;
begin
    // Controlled crash for missing handlers.
    raise ENoHandlerException.Create(Node.Kind.ToString);
end;

procedure TAstVisitor<T>.SetupHandlers;
begin
    // Base implementation does nothing.
    // Subclasses must call Register() here.
end;

procedure TAstVisitor<T>.Register(Kind: TAstNodeKind; const Handler: TNodeHandler<T>);
begin
    if Assigned(Handler) then
        FHandlers[Kind] := Handler
    else
        FHandlers[Kind] := DefaultSentinel;
end;

function TAstVisitor<T>.Visit(const Node: IAstNode): T;
begin
    if Assigned(Node) then
        Result := FHandlers[Node.Kind](Node)
    else
        Result := Default(T);
end;

function TAstVisitor<T>.Accept(const Node: IAstNode): T;
begin
    Result := Visit(Node);
end;

function TAstVisitor<T>.VisitBridge(const Node: IAstNode): TDataValue;
begin
    Result := TDataValue.FromGeneric<T>(Visit(Node));
end;

{ TAstTransformer }

procedure TAstTransformer.SetupHandlers;
begin
    // Leaves (Identity)
    Register(akConstant, VisitLeaf);
    Register(akIdentifier, VisitLeaf);
    Register(akKeyword, VisitLeaf);
    Register(akNop, VisitLeaf);
    Register(akCreateSeries, VisitLeaf);

    // Recursive Structures
    Register(akParameterList, VisitParameterList);
    Register(akArgumentList, VisitArgumentList);
    Register(akExpressionList, VisitExpressionList);
    Register(akRecordFieldList, VisitRecordFieldList);
    Register(akRecordField, VisitRecordField);

    Register(akIfExpression, VisitIfExpression);
    Register(akCondExpression, VisitCondExpression);
    Register(akLambdaExpression, VisitLambdaExpression);
    Register(akFunctionCall, VisitFunctionCall);
    Register(akMacroExpansion, VisitMacroExpansionNode);
    Register(akBlockExpression, VisitBlockExpression);
    Register(akVariableDeclaration, VisitVariableDeclaration);
    Register(akAssignment, VisitAssignment);
    Register(akMacroDefinition, VisitMacroDefinition);

    Register(akQuasiquote, VisitQuasiquote);
    Register(akUnquote, VisitUnquote);
    Register(akUnquoteSplicing, VisitUnquoteSplicing);

    Register(akIndexer, VisitIndexer);
    Register(akMemberAccess, VisitMemberAccess);
    Register(akRecordLiteral, VisitRecordLiteral);
    Register(akAddSeriesItem, VisitAddSeriesItem);
    Register(akSeriesLength, VisitSeriesLength);
    Register(akRecur, VisitRecurNode);

    Register(akPipeInput, VisitPipeInput);
    Register(akPipeSelectorList, VisitPipeSelectorList);
    Register(akPipeInputList, VisitPipeInputList);
    Register(akPipe, VisitPipe);
end;

function TAstTransformer.VisitLeaf(const N: IAstNode): IAstNode;
begin
    Result := N;
end;

// --- Lists ---

function TAstTransformer.VisitParameterList(const N: IAstNode): IAstNode;
var
    L: IParameterList;
    hasChanged: Boolean;
    newItems: TArray<IIdentifierNode>;
    item, newItem: IIdentifierNode;
    i: Integer;
begin
    L := N.AsParameterList;
    hasChanged := False;
    SetLength(newItems, L.Count);
    for i := 0 to L.Count - 1 do
    begin
        item := L[i];
        // We must cast the result back to IIdentifierNode.
        newItem := Visit(item).AsIdentifier;
        newItems[i] := newItem;
        if item <> newItem then
            hasChanged := True;
    end;

    if not hasChanged then
        Result := N
    else
        Result := TParameterList.Create(newItems, N.Identity);
end;

function TAstTransformer.VisitArgumentList(const N: IAstNode): IAstNode;
var
    L: IArgumentList;
    hasChanged: Boolean;
    newItems: TArray<IAstNode>;
    item, newItem: IAstNode;
    i: Integer;
begin
    L := N.AsArgumentList;
    hasChanged := False;
    SetLength(newItems, L.Count);
    for i := 0 to L.Count - 1 do
    begin
        item := L[i];
        newItem := Visit(item);
        newItems[i] := newItem;
        if item <> newItem then
            hasChanged := True;
    end;

    if not hasChanged then
        Result := N
    else
        Result := TArgumentList.Create(newItems, N.Identity);
end;

function TAstTransformer.VisitExpressionList(const N: IAstNode): IAstNode;
var
    L: IExpressionList;
    hasChanged: Boolean;
    newItems: TArray<IAstNode>;
    item, newItem: IAstNode;
    i: Integer;
begin
    L := N.AsExpressionList;
    hasChanged := False;
    SetLength(newItems, L.Count);
    for i := 0 to L.Count - 1 do
    begin
        item := L[i];
        newItem := Visit(item);
        newItems[i] := newItem;
        if item <> newItem then
            hasChanged := True;
    end;

    if not hasChanged then
        Result := N
    else
        Result := TExpressionList.Create(newItems, N.Identity);
end;

function TAstTransformer.VisitRecordFieldList(const N: IAstNode): IAstNode;
var
    L: IRecordFieldList;
    hasChanged: Boolean;
    newItems: TArray<IRecordFieldNode>;
    item, newItem: IRecordFieldNode;
    i: Integer;
begin
    L := N.AsRecordFieldList;
    hasChanged := False;
    SetLength(newItems, L.Count);
    for i := 0 to L.Count - 1 do
    begin
        item := L[i];
        newItem := Visit(item).AsRecordField;
        newItems[i] := newItem;
        if item <> newItem then
            hasChanged := True;
    end;

    if not hasChanged then
        Result := N
    else
        Result := TRecordFieldList.Create(newItems, N.Identity);
end;

function TAstTransformer.VisitRecordField(const N: IAstNode): IAstNode;
var
    F: IRecordFieldNode;
    newKey: IKeywordNode;
    newValue: IAstNode;
begin
    F := N.AsRecordField;
    newKey := Visit(F.Key).AsKeyword;
    newValue := Visit(F.Value);

    if (newKey = F.Key) and (newValue = F.Value) then
        Result := N
    else
        Result := TAst.RecordField(N.Identity, newKey, newValue);
end;

// --- Structures ---

function TAstTransformer.VisitIfExpression(const N: IAstNode): IAstNode;
var
    E: IIfExpressionNode;
    c, t, eBr: IAstNode;
begin
    E := N.AsIfExpression;
    c := Visit(E.Condition);
    t := Visit(E.ThenBranch);
    eBr := Visit(E.ElseBranch);

    if (c = E.Condition) and (t = E.ThenBranch) and (eBr = E.ElseBranch) then
        Result := N
    else
        Result := TAst.IfExpr(N.Identity, c, t, eBr, E.StaticType);
end;

function TAstTransformer.VisitCondExpression(const N: IAstNode): IAstNode;
var
    E: ICondExpressionNode;
    hasChanged: Boolean;
    newPairs: TArray<TCondPair>;
    newElse, c, b: IAstNode;
    i: Integer;
begin
    E := N.AsCondExpression;
    hasChanged := False;
    SetLength(newPairs, Length(E.Pairs));
    for i := 0 to High(E.Pairs) do
    begin
        c := Visit(E.Pairs[i].Condition);
        b := Visit(E.Pairs[i].Branch);
        newPairs[i] := TCondPair.Create(c, b);
        if (c <> E.Pairs[i].Condition) or (b <> E.Pairs[i].Branch) then
            hasChanged := True;
    end;

    newElse := Visit(E.ElseBranch);
    if newElse <> E.ElseBranch then
        hasChanged := True;

    if not hasChanged then
        Result := N
    else
        Result := TAst.CondExpr(N.Identity, newPairs, newElse, E.StaticType);
end;

function TAstTransformer.VisitLambdaExpression(const N: IAstNode): IAstNode;
var
    E: ILambdaExpressionNode;
    p: IParameterList;
    b: IAstNode;
begin
    E := N.AsLambdaExpression;
    p := Visit(E.Parameters).AsParameterList;
    b := Visit(E.Body);

    if (p = E.Parameters) and (b = E.Body) then
        Result := N
    else
        // Reconstruct with full metadata
        Result :=
            TLambdaExpressionNode.Create(p, b, E.StaticType, E.Layout, E.Descriptor, E.Upvalues, E.HasNestedLambdas, E.IsPure, N.Identity);
end;

function TAstTransformer.VisitFunctionCall(const N: IAstNode): IAstNode;
var
    C: IFunctionCallNode;
    callee: IAstNode;
    args: IArgumentList;
begin
    C := N.AsFunctionCall;
    callee := Visit(C.Callee);
    args := Visit(C.Arguments).AsArgumentList;

    if (callee = C.Callee) and (args = C.Arguments) then
        Result := N
    else
        Result := TAst.FunctionCall(N.Identity, callee, args, C.StaticType, C.IsTailCall, C.StaticTarget, C.IsTargetPure);
end;

function TAstTransformer.VisitMacroExpansionNode(const N: IAstNode): IAstNode;
var
    M: IMacroExpansionNode;
    exp: IAstNode;
begin
    M := N.AsMacroExpansion;
    exp := Visit(M.ExpandedBody);
    if exp = M.ExpandedBody then
        Result := N
    else
        Result := TAst.MacroExpansionNode(N.Identity, M.CallNode, exp);
end;

function TAstTransformer.VisitBlockExpression(const N: IAstNode): IAstNode;
var
    B: IBlockExpressionNode;
    e: IExpressionList;
begin
    B := N.AsBlockExpression;
    e := Visit(B.Expressions).AsExpressionList;
    if e = B.Expressions then
        Result := N
    else
        Result := TAst.Block(N.Identity, e, B.StaticType);
end;

function TAstTransformer.VisitVariableDeclaration(const N: IAstNode): IAstNode;
var
    V: IVariableDeclarationNode;
    t, i: IAstNode;
begin
    V := N.AsVariableDeclaration;
    t := Visit(V.Target);
    i := Visit(V.Initializer);
    if (t = V.Target) and (i = V.Initializer) then
        Result := N
    else
        Result := TAst.VarDecl(N.Identity, t, i, V.StaticType, V.IsBoxed);
end;

function TAstTransformer.VisitAssignment(const N: IAstNode): IAstNode;
var
    A: IAssignmentNode;
    t, v: IAstNode;
begin
    A := N.AsAssignment;
    t := Visit(A.Target);
    v := Visit(A.Value);
    if (t = A.Target) and (v = A.Value) then
        Result := N
    else
        Result := TAst.Assign(N.Identity, t, v, A.StaticType);
end;

function TAstTransformer.VisitMacroDefinition(const N: IAstNode): IAstNode;
begin
    // Macro definitions are typically constant during runtime transformation, return as is.
    Result := N;
end;

function TAstTransformer.VisitQuasiquote(const N: IAstNode): IAstNode;
var
    Q: IQuasiquoteNode;
    e: IAstNode;
begin
    Q := N.AsQuasiquote;
    e := Visit(Q.Expression);
    if e = Q.Expression then
        Result := N
    else
        Result := TAst.Quasiquote(N.Identity, e);
end;

function TAstTransformer.VisitUnquote(const N: IAstNode): IAstNode;
var
    U: IUnquoteNode;
    e: IAstNode;
begin
    U := N.AsUnquote;
    e := Visit(U.Expression);
    if e = U.Expression then
        Result := N
    else
        Result := TAst.Unquote(N.Identity, e);
end;

function TAstTransformer.VisitUnquoteSplicing(const N: IAstNode): IAstNode;
var
    U: IUnquoteSplicingNode;
    e: IAstNode;
begin
    U := N.AsUnquoteSplicing;
    e := Visit(U.Expression);
    if e = U.Expression then
        Result := N
    else
        Result := TAst.UnquoteSplicing(N.Identity, e.AsQuasiquote);
end;

function TAstTransformer.VisitIndexer(const N: IAstNode): IAstNode;
var
    I: IIndexerNode;
    b, idx: IAstNode;
begin
    I := N.AsIndexer;
    b := Visit(I.Base);
    idx := Visit(I.Index);
    if (b = I.Base) and (idx = I.Index) then
        Result := N
    else
        Result := TAst.Indexer(N.Identity, b, idx, I.StaticType);
end;

function TAstTransformer.VisitMemberAccess(const N: IAstNode): IAstNode;
var
    M: IMemberAccessNode;
    b: IAstNode;
begin
    M := N.AsMemberAccess;
    b := Visit(M.Base);
    if b = M.Base then
        Result := N
    else
        Result := TAst.MemberAccess(N.Identity, b, M.Member, M.StaticType);
end;

function TAstTransformer.VisitRecordLiteral(const N: IAstNode): IAstNode;
var
    R: IRecordLiteralNode;
    f: IRecordFieldList;
begin
    R := N.AsRecordLiteral;
    f := Visit(R.Fields).AsRecordFieldList;
    if f = R.Fields then
        Result := N
    else
        Result := TAst.RecordLiteral(N.Identity, f, R.ScalarDefinition, R.GenericDefinition, R.StaticType);
end;

function TAstTransformer.VisitAddSeriesItem(const N: IAstNode): IAstNode;
var
    A: IAddSeriesItemNode;
    s, v, l: IAstNode;
begin
    A := N.AsAddSeriesItem;
    s := Visit(A.Series);
    v := Visit(A.Value);
    l := Visit(A.Lookback);

    if (s = A.Series) and (v = A.Value) and (l = A.Lookback) then
        Result := N
    else
        Result := TAst.AddSeriesItem(N.Identity, s.AsIdentifier, v, l, A.StaticType);
end;

function TAstTransformer.VisitSeriesLength(const N: IAstNode): IAstNode;
var
    S: ISeriesLengthNode;
    series: IAstNode;
begin
    S := N.AsSeriesLength;
    series := Visit(S.Series);
    if series = S.Series then
        Result := N
    else
        Result := TAst.SeriesLength(N.Identity, series.AsIdentifier, S.StaticType);
end;

function TAstTransformer.VisitRecurNode(const N: IAstNode): IAstNode;
var
    R: IRecurNode;
    a: IArgumentList;
begin
    R := N.AsRecur;
    a := Visit(R.Arguments).AsArgumentList;
    if a = R.Arguments then
        Result := N
    else
        Result := TAst.Recur(N.Identity, a, R.StaticType);
end;

function TAstTransformer.VisitPipeInput(const N: IAstNode): IAstNode;
var
    P: IPipeInputNode;
    s: IAstNode;
    sel: IPipeSelectorList;
begin
    P := N.AsPipeInput;
    s := Visit(P.StreamSource);
    sel := Visit(P.Selectors).AsPipeSelectorList;

    if (s = P.StreamSource) and (sel = P.Selectors) then
        Result := N
    else
        Result := TAst.PipeInput(s.AsIdentifier, sel, N.Identity.Location);
end;

function TAstTransformer.VisitPipeSelectorList(const N: IAstNode): IAstNode;
var
    L: IPipeSelectorList;
    hasChanged: Boolean;
    newItems: TArray<IKeywordNode>;
    item, newItem: IKeywordNode;
    i: Integer;
begin
    L := N.AsPipeSelectorList;
    hasChanged := False;
    SetLength(newItems, L.Count);
    for i := 0 to L.Count - 1 do
    begin
        item := L[i];
        newItem := Visit(item).AsKeyword;
        newItems[i] := newItem;
        if item <> newItem then
            hasChanged := True;
    end;

    if not hasChanged then
        Result := N
    else
        Result := TAst.PipeSelectorList(newItems, N.Identity.Location);
end;

function TAstTransformer.VisitPipeInputList(const N: IAstNode): IAstNode;
var
    L: IPipeInputList;
    hasChanged: Boolean;
    newItems: TArray<IPipeInputNode>;
    item, newItem: IPipeInputNode;
    i: Integer;
begin
    L := N.AsPipeInputList;
    hasChanged := False;
    SetLength(newItems, L.Count);
    for i := 0 to L.Count - 1 do
    begin
        item := L[i];
        newItem := Visit(item).AsPipeInput;
        newItems[i] := newItem;
        if item <> newItem then
            hasChanged := True;
    end;

    if not hasChanged then
        Result := N
    else
        Result := TPipeInputList.Create(newItems, N.Identity);
end;

function TAstTransformer.VisitPipe(const N: IAstNode): IAstNode;
var
    P: IPipeNode;
    inp: IPipeInputList;
    trans: ILambdaExpressionNode;
begin
    P := N.AsPipe;
    inp := Visit(P.Inputs).AsPipeInputList;
    trans := Visit(P.Transformation).AsLambdaExpression;

    if (inp = P.Inputs) and (trans = P.Transformation) then
        Result := N
    else
        Result := TAst.Pipe(N.Identity, inp, trans, P.StaticType);
end;

{ TAstVisitor (Walker) }

procedure TAstVisitor.SetupHandlers;
begin
    // Leaves (Do nothing)
    Register(akConstant, WalkLeaf);
    Register(akIdentifier, WalkLeaf);
    Register(akKeyword, WalkLeaf);
    Register(akNop, WalkLeaf);
    Register(akCreateSeries, WalkLeaf);

    // Recursive Structures
    Register(akParameterList, WalkParameterList);
    Register(akArgumentList, WalkArgumentList);
    Register(akExpressionList, WalkExpressionList);
    Register(akRecordFieldList, WalkRecordFieldList);
    Register(akRecordField, WalkRecordField);

    Register(akIfExpression, WalkIfExpression);
    Register(akCondExpression, WalkCondExpression);
    Register(akLambdaExpression, WalkLambdaExpression);
    Register(akFunctionCall, WalkFunctionCall);
    Register(akMacroExpansion, WalkMacroExpansionNode);
    Register(akBlockExpression, WalkBlockExpression);
    Register(akVariableDeclaration, WalkVariableDeclaration);
    Register(akAssignment, WalkAssignment);
    Register(akMacroDefinition, WalkMacroDefinition);

    // Wrappers (Generic Node Handler)
    Register(akQuasiquote, WalkWrapper);
    Register(akUnquote, WalkWrapper);
    Register(akUnquoteSplicing, WalkWrapper);

    Register(akIndexer, WalkIndexer);
    Register(akMemberAccess, WalkMemberAccess);
    Register(akRecordLiteral, WalkRecordLiteral);
    Register(akAddSeriesItem, WalkAddSeriesItem);
    Register(akSeriesLength, WalkSeriesLength);
    Register(akRecur, WalkRecurNode);

    // Pipes
    Register(akPipeInput, WalkPipeInput);
    Register(akPipeSelectorList, WalkPipeSelectorList);
    Register(akPipeInputList, WalkPipeInputList);
    Register(akPipe, WalkPipe);
end;

function TAstVisitor.WalkLeaf(const N: IAstNode): TVoid;
begin
    // Do nothing
end;

function TAstVisitor.WalkWrapper(const N: IAstNode): TVoid;
begin
    if N.Kind = akQuasiquote then
        Visit(N.AsQuasiquote.Expression)
    else if N.Kind = akUnquote then
        Visit(N.AsUnquote.Expression)
    else if N.Kind = akUnquoteSplicing then
        Visit(N.AsUnquoteSplicing.Expression);
end;

function TAstVisitor.WalkParameterList(const N: IAstNode): TVoid;
begin
    for var item in N.AsParameterList do
        Visit(item);
end;

function TAstVisitor.WalkArgumentList(const N: IAstNode): TVoid;
begin
    for var item in N.AsArgumentList do
        Visit(item);
end;

function TAstVisitor.WalkExpressionList(const N: IAstNode): TVoid;
begin
    for var item in N.AsExpressionList do
        Visit(item);
end;

function TAstVisitor.WalkRecordFieldList(const N: IAstNode): TVoid;
begin
    for var item in N.AsRecordFieldList do
        Visit(item);
end;

function TAstVisitor.WalkRecordField(const N: IAstNode): TVoid;
begin
    var F := N.AsRecordField;
    Visit(F.Key);
    Visit(F.Value);
end;

function TAstVisitor.WalkIfExpression(const N: IAstNode): TVoid;
begin
    var E := N.AsIfExpression;
    Visit(E.Condition);
    Visit(E.ThenBranch);
    Visit(E.ElseBranch);
end;

function TAstVisitor.WalkCondExpression(const N: IAstNode): TVoid;
begin
    var E := N.AsCondExpression;
    for var pair in E.Pairs do
    begin
        Visit(pair.Condition);
        Visit(pair.Branch);
    end;
    Visit(E.ElseBranch);
end;

function TAstVisitor.WalkLambdaExpression(const N: IAstNode): TVoid;
begin
    var E := N.AsLambdaExpression;
    Visit(E.Parameters);
    Visit(E.Body);
end;

function TAstVisitor.WalkFunctionCall(const N: IAstNode): TVoid;
begin
    var C := N.AsFunctionCall;
    Visit(C.Callee);
    Visit(C.Arguments);
end;

function TAstVisitor.WalkMacroExpansionNode(const N: IAstNode): TVoid;
begin
    Visit(N.AsMacroExpansion.ExpandedBody);
end;

function TAstVisitor.WalkBlockExpression(const N: IAstNode): TVoid;
begin
    Visit(N.AsBlockExpression.Expressions);
end;

function TAstVisitor.WalkVariableDeclaration(const N: IAstNode): TVoid;
begin
    var V := N.AsVariableDeclaration;
    Visit(V.Target);
    Visit(V.Initializer);
end;

function TAstVisitor.WalkAssignment(const N: IAstNode): TVoid;
begin
    var A := N.AsAssignment;
    Visit(A.Target);
    Visit(A.Value);
end;

function TAstVisitor.WalkMacroDefinition(const N: IAstNode): TVoid;
begin
    var M := N.AsMacroDefinition;
    Visit(M.Name);
    Visit(M.Parameters);
    Visit(M.Body);
end;

function TAstVisitor.WalkIndexer(const N: IAstNode): TVoid;
begin
    var I := N.AsIndexer;
    Visit(I.Base);
    Visit(I.Index);
end;

function TAstVisitor.WalkMemberAccess(const N: IAstNode): TVoid;
begin
    var M := N.AsMemberAccess;
    Visit(M.Base);
    Visit(M.Member);
end;

function TAstVisitor.WalkRecordLiteral(const N: IAstNode): TVoid;
begin
    Visit(N.AsRecordLiteral.Fields);
end;

function TAstVisitor.WalkAddSeriesItem(const N: IAstNode): TVoid;
begin
    var A := N.AsAddSeriesItem;
    Visit(A.Series);
    Visit(A.Value);
    Visit(A.Lookback);
end;

function TAstVisitor.WalkSeriesLength(const N: IAstNode): TVoid;
begin
    Visit(N.AsSeriesLength.Series);
end;

function TAstVisitor.WalkRecurNode(const N: IAstNode): TVoid;
begin
    Visit(N.AsRecur.Arguments);
end;

function TAstVisitor.WalkPipeInput(const N: IAstNode): TVoid;
begin
    var P := N.AsPipeInput;
    Visit(P.StreamSource);
    Visit(P.Selectors);
end;

function TAstVisitor.WalkPipeSelectorList(const N: IAstNode): TVoid;
begin
    for var item in N.AsPipeSelectorList do
        Visit(item);
end;

function TAstVisitor.WalkPipeInputList(const N: IAstNode): TVoid;
begin
    for var item in N.AsPipeInputList do
        Visit(item);
end;

function TAstVisitor.WalkPipe(const N: IAstNode): TVoid;
begin
    var P := N.AsPipe;
    Visit(P.Inputs);
    Visit(P.Transformation);
end;

end.
