unit Myc.Ast.Nodes;

interface

uses
    System.SysUtils,
    System.Generics.Collections,
    Myc.Data.Scalar,
    Myc.Data.Value,
    Myc.Data.Keyword,
    Myc.Ast.Scope,
    Myc.Ast.Types,
    Myc.Ast.Identities;

type
    // --- Forward Declarations ---
    IAstNode = interface;

    // --- Error Handling ---
    TCompilerErrorLevel = (elHint, elWarning, elError);

    TCompilerError = record
        Level: TCompilerErrorLevel;
        Message: string;
        Node: IAstNode;
        constructor Create(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode = nil);
        function ToString: string;
    end;

    ICompilerLog = interface
        procedure Add(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode = nil);
        procedure AddError(const AMessage: string; const ANode: IAstNode = nil);
        procedure AddWarning(const AMessage: string; const ANode: IAstNode = nil);

        function HasErrors: Boolean;
        function GetEntryCount: Integer;
        function GetEntries: TArray<TCompilerError>;

        property Entries: TArray<TCompilerError> read GetEntries;
    end;

    TCompilerLog = class(TInterfacedObject, ICompilerLog)
    private
        FEntries: TList<TCompilerError>;
    public
        constructor Create;
        destructor Destroy; override;
        procedure Add(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode = nil);
        procedure AddError(const AMessage: string; const ANode: IAstNode = nil);
        procedure AddWarning(const AMessage: string; const ANode: IAstNode = nil);
        function HasErrors: Boolean;
        function GetEntryCount: Integer;
        function GetEntries: TArray<TCompilerError>;
    end;

    EAstException = class(Exception);

    ECompilationFailed = class(EAstException)
    private
        FErrors: TArray<TCompilerError>;
    public
        constructor Create(const AErrors: TArray<TCompilerError>);
        function ToString: string; override;
        property Errors: TArray<TCompilerError> read FErrors;
    end;

    // Exception for the new visitor registry dispatch
    ENoHandlerException = class(EAstException)
    public
        constructor Create(const KindName: string);
    end;

    TCompiledFunction = record
    public
        Func: TDataValue.TFunc;
        StaticType: IStaticType;
        IsPure: Boolean;
        constructor Create(const AFunc: TDataValue.TFunc; const AStaticType: IStaticType; AIsPure: Boolean);
    end;

    // --- AST Interfaces ---

    IAstVisitor = interface;
    IAstTypedNode = interface;

    // Core Nodes
    IConstantNode = interface;
    IIdentifierNode = interface;
    IKeywordNode = interface;

    // Lists & Containers
    IParameterList = interface;
    IArgumentList = interface;
    IExpressionList = interface;
    IRecordFieldList = interface;
    IRecordFieldNode = interface;

    // Control Flow & Structure
    IIfExpressionNode = interface;
    ICondExpressionNode = interface;
    ILambdaExpressionNode = interface;
    IFunctionCallNode = interface;
    IMacroExpansionNode = interface;
    IBlockExpressionNode = interface;
    IVariableDeclarationNode = interface;
    IAssignmentNode = interface;
    IMacroDefinitionNode = interface;
    IQuasiquoteNode = interface;
    IUnquoteNode = interface;
    IUnquoteSplicingNode = interface;
    IIndexerNode = interface;
    IMemberAccessNode = interface;
    IRecordLiteralNode = interface;
    ICreateSeriesNode = interface;
    IAddSeriesItemNode = interface;
    ISeriesLengthNode = interface;
    IRecurNode = interface;
    INopNode = interface;

    // Pipes
    IPipeSelectorList = interface;
    IPipeInputNode = interface;
    IPipeInputList = interface;
    IPipeNode = interface;

    // Node Kinds & Helpers
    TAstNodeKind = (
        akConstant,
        akIdentifier,
        akKeyword,
        // Lists
        akParameterList,
        akArgumentList,
        akExpressionList,
        akRecordFieldList,
        akRecordField,
        akIfExpression,
        akCondExpression,
        akLambdaExpression,
        akFunctionCall,
        akMacroExpansion,
        akBlockExpression,
        akVariableDeclaration,
        akAssignment,
        akMacroDefinition,
        akQuasiquote,
        akUnquote,
        akUnquoteSplicing,
        akIndexer,
        akMemberAccess,
        akRecordLiteral,
        akCreateSeries,
        akAddSeriesItem,
        akSeriesLength,
        akRecur,
        akNop,
        // Pipes
        akPipe,
        akPipeInput,
        akPipeSelectorList,
        akPipeInputList
    );

    TAstNodeKindHelper = record helper for TAstNodeKind
    public
        function ToString: string;
    end;

    // Represents a single branch in a Cond expression (Condition -> Branch)
    TCondPair = record
        Condition: IAstNode;
        Branch: IAstNode;
        constructor Create(const ACondition, ABranch: IAstNode);
    end;

    // --- Base Interfaces ---

    IAstNode = interface
        {$region 'private'}
        function GetKind: TAstNodeKind;
        function GetIsTyped: Boolean;
        function GetIdentity: IAstIdentity;
        {$endregion}
        function Accept(const Visitor: IAstVisitor): TDataValue;

        // Fluent Casts / Accessors (Interface based)
        function AsTypedNode: IAstTypedNode;

        function AsConstant: IConstantNode;
        function AsIdentifier: IIdentifierNode;
        function AsKeyword: IKeywordNode;

        function AsParameterList: IParameterList;
        function AsArgumentList: IArgumentList;
        function AsExpressionList: IExpressionList;
        function AsRecordFieldList: IRecordFieldList;
        function AsRecordField: IRecordFieldNode;

        function AsIfExpression: IIfExpressionNode;
        function AsCondExpression: ICondExpressionNode;
        function AsLambdaExpression: ILambdaExpressionNode;
        function AsFunctionCall: IFunctionCallNode;
        function AsMacroExpansion: IMacroExpansionNode;
        function AsBlockExpression: IBlockExpressionNode;
        function AsVariableDeclaration: IVariableDeclarationNode;
        function AsAssignment: IAssignmentNode;
        function AsMacroDefinition: IMacroDefinitionNode;
        function AsQuasiquote: IQuasiquoteNode;
        function AsUnquote: IUnquoteNode;
        function AsUnquoteSplicing: IUnquoteSplicingNode;
        function AsIndexer: IIndexerNode;
        function AsMemberAccess: IMemberAccessNode;
        function AsRecordLiteral: IRecordLiteralNode;
        function AsCreateSeries: ICreateSeriesNode;
        function AsAddSeriesItem: IAddSeriesItemNode;
        function AsSeriesLength: ISeriesLengthNode;
        function AsRecur: IRecurNode;
        function AsNop: INopNode;

        // Pipes
        function AsPipeInput: IPipeInputNode;
        function AsPipeSelectorList: IPipeSelectorList;
        function AsPipeInputList: IPipeInputList;
        function AsPipe: IPipeNode;

        property IsTyped: Boolean read GetIsTyped;
        property Kind: TAstNodeKind read GetKind;
        property Identity: IAstIdentity read GetIdentity;
    end;

    INodeList<T: IAstNode> = interface(IAstNode)
        {$region 'private'}
        function GetCount: Integer;
        function GetItem(Index: Integer): T;
        {$endregion}
        function GetEnumerator: TEnumerator<T>;
        function ToArray: TArray<T>;
        property Count: Integer read GetCount;
        property Items[Index: Integer]: T read GetItem; default;
    end;

    IAstTypedNode = interface(IAstNode)
        {$region 'private'}
        function GetStaticType: IStaticType;
        {$endregion}
        property StaticType: IStaticType read GetStaticType;
    end;

    IRecordFieldNode = interface(IAstNode)
        {$region 'private'}
        function GetKey: IKeywordNode;
        function GetValue: IAstNode;
        {$endregion}
        property Key: IKeywordNode read GetKey;
        property Value: IAstNode read GetValue;
    end;

    IRecordFieldList = interface(INodeList<IRecordFieldNode>)
    end;

    // --- Node Interfaces (Definitions) ---

    // Function Definition (Lambda)
    IFunctionDefinition = interface(IAstTypedNode)
        {$region 'private'}
        function GetBody: IAstNode;
        function GetParameters: IParameterList;
        function GetIsPure: Boolean;
        {$endregion}
        property Body: IAstNode read GetBody;
        property Parameters: IParameterList read GetParameters;
        property IsPure: Boolean read GetIsPure;
    end;

    INopNode = interface(IAstTypedNode)
    end;

    IConstantNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetValue: TDataValue;
        {$endregion}
        property Value: TDataValue read GetValue;
    end;

    IIdentifierNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetAddress: TResolvedAddress;
        function GetName: string;
        {$endregion}
        property Address: TResolvedAddress read GetAddress;
        property Name: string read GetName;
    end;

    IKeywordNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetValue: IKeyword;
        {$endregion}
        property Value: IKeyword read GetValue;
    end;

    IIfExpressionNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetCondition: IAstNode;
        function GetThenBranch: IAstNode;
        function GetElseBranch: IAstNode;
        {$endregion}
        property Condition: IAstNode read GetCondition;
        property ThenBranch: IAstNode read GetThenBranch;
        property ElseBranch: IAstNode read GetElseBranch;
    end;

    ICondExpressionNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetPairs: TArray<TCondPair>;
        function GetElseBranch: IAstNode;
        {$endregion}
        property Pairs: TArray<TCondPair> read GetPairs;
        property ElseBranch: IAstNode read GetElseBranch;
    end;

    ILambdaExpressionNode = interface(IFunctionDefinition)
        {$region 'private'}
        function GetLayout: IScopeLayout;
        function GetDescriptor: IScopeDescriptor;
        function GetUpvalues: TArray<TResolvedAddress>;
        function GetHasNestedLambdas: Boolean;
        {$endregion}
        property Layout: IScopeLayout read GetLayout;
        property Descriptor: IScopeDescriptor read GetDescriptor;
        property Upvalues: TArray<TResolvedAddress> read GetUpvalues;
        property HasNestedLambdas: Boolean read GetHasNestedLambdas;
    end;

    IFunctionCallNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetCallee: IAstNode;
        function GetArguments: IArgumentList;
        function GetIsTailCall: Boolean;
        function GetStaticTarget: TDataValue.TFunc;
        function GetIsTargetPure: Boolean;
        {$endregion}
        property Callee: IAstNode read GetCallee;
        property Arguments: IArgumentList read GetArguments;
        property IsTailCall: Boolean read GetIsTailCall;
        property StaticTarget: TDataValue.TFunc read GetStaticTarget;
        property IsTargetPure: Boolean read GetIsTargetPure;
    end;

    IMacroExpansionNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetCallNode: IFunctionCallNode;
        function GetExpandedBody: IAstNode;
        {$endregion}
        property CallNode: IFunctionCallNode read GetCallNode;
        property ExpandedBody: IAstNode read GetExpandedBody;
    end;

    IRecurNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetArguments: IArgumentList;
        {$endregion}
        property Arguments: IArgumentList read GetArguments;
    end;

    IBlockExpressionNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetExpressions: IExpressionList;
        {$endregion}
        property Expressions: IExpressionList read GetExpressions;
    end;

    IVariableDeclarationNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetTarget: IAstNode;
        function GetInitializer: IAstNode;
        function GetIsBoxed: Boolean;
        {$endregion}
        property Target: IAstNode read GetTarget;
        property Initializer: IAstNode read GetInitializer;
        property IsBoxed: Boolean read GetIsBoxed;
    end;

    IAssignmentNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetTarget: IAstNode;
        function GetValue: IAstNode;
        {$endregion}
        property Target: IAstNode read GetTarget;
        property Value: IAstNode read GetValue;
    end;

    IMacroDefinitionNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetName: IIdentifierNode;
        function GetParameters: IParameterList;
        function GetBody: IAstNode;
        {$endregion}
        property Name: IIdentifierNode read GetName;
        property Parameters: IParameterList read GetParameters;
        property Body: IAstNode read GetBody;
    end;

    IQuasiquoteNode = interface(IAstNode)
        {$region 'private'}
        function GetExpression: IAstNode;
        {$endregion}
        property Expression: IAstNode read GetExpression;
    end;

    IUnquoteNode = interface(IAstNode)
        {$region 'private'}
        function GetExpression: IAstNode;
        {$endregion}
        property Expression: IAstNode read GetExpression;
    end;

    IUnquoteSplicingNode = interface(IAstNode)
        {$region 'private'}
        function GetExpression: IQuasiquoteNode;
        {$endregion}
        property Expression: IQuasiquoteNode read GetExpression;
    end;

    IIndexerNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetBase: IAstNode;
        function GetIndex: IAstNode;
        {$endregion}
        property Base: IAstNode read GetBase;
        property Index: IAstNode read GetIndex;
    end;

    IMemberAccessNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetBase: IAstNode;
        function GetMember: IKeywordNode;
        {$endregion}
        property Base: IAstNode read GetBase;
        property Member: IKeywordNode read GetMember;
    end;

    IRecordLiteralNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetFields: IRecordFieldList;
        function GetGenericDefinition: IGenericRecordDefinition;
        function GetScalarDefinition: IScalarRecordDefinition;
        {$endregion}
        property Fields: IRecordFieldList read GetFields;
        property GenericDefinition: IGenericRecordDefinition read GetGenericDefinition;
        property ScalarDefinition: IScalarRecordDefinition read GetScalarDefinition;
    end;

    ICreateSeriesNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetDefinition: String;
        {$endregion}
        property Definition: String read GetDefinition;
    end;

    IAddSeriesItemNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetSeries: IIdentifierNode;
        function GetValue: IAstNode;
        function GetLookback: IAstNode;
        {$endregion}
        property Series: IIdentifierNode read GetSeries;
        property Value: IAstNode read GetValue;
        property Lookback: IAstNode read GetLookback;
    end;

    ISeriesLengthNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetSeries: IIdentifierNode;
        {$endregion}
        property Series: IIdentifierNode read GetSeries;
    end;

    // A list of selector keywords [ :A :B ]
    IPipeSelectorList = interface(INodeList<IKeywordNode>)
    end;

    // Represents a single input definition within a pipe: "btc [:Close :Open]"
    IPipeInputNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetStreamSource: IIdentifierNode;
        function GetSelectors: IPipeSelectorList;
        {$endregion}
        property StreamSource: IIdentifierNode read GetStreamSource;
        property Selectors: IPipeSelectorList read GetSelectors;
    end;

    // A list of pipe inputs
    IPipeInputList = interface(INodeList<IPipeInputNode>)
    end;

    // The main pipe definition: (pipe [inputs...] (fn ...))
    IPipeNode = interface(IAstTypedNode)
        {$region 'private'}
        function GetInputs: IPipeInputList;
        function GetTransformation: ILambdaExpressionNode;
        {$endregion}
        property Inputs: IPipeInputList read GetInputs;
        property Transformation: ILambdaExpressionNode read GetTransformation;
    end;

    IParameterList = interface(INodeList<IIdentifierNode>)
    end;

    IArgumentList = interface(INodeList<IAstNode>)
    end;

    IExpressionList = interface(INodeList<IAstNode>)
    end;

    IAstVisitor = interface
        function Visit(const Node: IAstNode): TDataValue;
    end;

    IEvaluatorVisitor = interface(IAstVisitor)
        function Execute(const RootNode: IAstNode): TDataValue;
    end;

    TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;

    TAstNode = class(TInterfacedObject, IAstNode)
    private
        FIdentity: IAstIdentity;
        function GetIdentity: IAstIdentity;
    protected
        function GetKind: TAstNodeKind; virtual; abstract;
        function GetIsTyped: Boolean; virtual;
    public
        constructor Create(const AIdentity: IAstIdentity);

        function Accept(const Visitor: IAstVisitor): TDataValue; virtual;

        function AsConstant: IConstantNode; virtual;
        function AsIdentifier: IIdentifierNode; virtual;
        function AsKeyword: IKeywordNode; virtual;

        function AsParameterList: IParameterList; virtual;
        function AsArgumentList: IArgumentList; virtual;
        function AsExpressionList: IExpressionList; virtual;
        function AsRecordFieldList: IRecordFieldList; virtual;
        function AsRecordField: IRecordFieldNode; virtual;

        function AsIfExpression: IIfExpressionNode; virtual;
        function AsCondExpression: ICondExpressionNode; virtual;
        function AsLambdaExpression: ILambdaExpressionNode; virtual;
        function AsFunctionCall: IFunctionCallNode; virtual;
        function AsMacroExpansion: IMacroExpansionNode; virtual;
        function AsBlockExpression: IBlockExpressionNode; virtual;
        function AsVariableDeclaration: IVariableDeclarationNode; virtual;
        function AsAssignment: IAssignmentNode; virtual;
        function AsMacroDefinition: IMacroDefinitionNode; virtual;
        function AsQuasiquote: IQuasiquoteNode; virtual;
        function AsUnquote: IUnquoteNode; virtual;
        function AsUnquoteSplicing: IUnquoteSplicingNode; virtual;
        function AsIndexer: IIndexerNode; virtual;
        function AsMemberAccess: IMemberAccessNode; virtual;
        function AsRecordLiteral: IRecordLiteralNode; virtual;
        function AsCreateSeries: ICreateSeriesNode; virtual;
        function AsAddSeriesItem: IAddSeriesItemNode; virtual;
        function AsSeriesLength: ISeriesLengthNode; virtual;
        function AsRecur: IRecurNode; virtual;
        function AsNop: INopNode; virtual;

        function AsPipeInput: IPipeInputNode; virtual;
        function AsPipeSelectorList: IPipeSelectorList; virtual;
        function AsPipeInputList: IPipeInputList; virtual;
        function AsPipe: IPipeNode; virtual;

        function AsTypedNode: IAstTypedNode; virtual;

        property IsTyped: Boolean read GetIsTyped;
        property Kind: TAstNodeKind read GetKind;
        property Identity: IAstIdentity read GetIdentity;
    end;

    TAstTypedNode = class(TAstNode, IAstTypedNode)
    private
        FStaticType: IStaticType;
        function GetStaticType: IStaticType;
    protected
        function GetIsTyped: Boolean; override;
    public
        constructor Create(const AStaticType: IStaticType; const AIdentity: IAstIdentity);
        function AsTypedNode: IAstTypedNode; override;
        property StaticType: IStaticType read GetStaticType;
    end;

    // --- List Implementation Bases ---

    TAstNodeList<T: IAstNode> = class(TAstNode, INodeList<T>)
        type
            TEnumerator = class(TEnumerator<T>)
            private
                FArray: TArray<T>;
                FIndex: NativeInt;
                function GetCurrent: T; inline;
            protected
                function DoGetCurrent: T; override;
                function DoMoveNext: Boolean; override;
            public
                constructor Create(const AArray: TArray<T>);
                function MoveNext: Boolean; inline;
                property Current: T read GetCurrent;
            end;
    private
        FItems: TArray<T>;
        function GetCount: Integer;
        function GetItem(Index: Integer): T;
    public
        constructor Create(const AItems: TArray<T>; const AIdentity: IAstIdentity);
        function GetEnumerator: TEnumerator<T>;
        function ToArray: TArray<T>;
        property Count: Integer read GetCount;
        property Items[Index: Integer]: T read GetItem; default;
    end;

    TParameterList = class(TAstNodeList<IIdentifierNode>, IParameterList)
    protected
        function GetKind: TAstNodeKind; override;
    public
        function AsParameterList: IParameterList; override;
    end;

    TArgumentList = class(TAstNodeList<IAstNode>, IArgumentList)
    protected
        function GetKind: TAstNodeKind; override;
    public
        function AsArgumentList: IArgumentList; override;
    end;

    TExpressionList = class(TAstNodeList<IAstNode>, IExpressionList)
    protected
        function GetKind: TAstNodeKind; override;
    public
        function AsExpressionList: IExpressionList; override;
    end;

    TRecordFieldNode = class(TAstNode, IRecordFieldNode)
    private
        FKey: IKeywordNode;
        FValue: IAstNode;
        function GetKey: IKeywordNode;
        function GetValue: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AKey: IKeywordNode; const AValue: IAstNode; const AIdentity: IAstIdentity);
        function AsRecordField: IRecordFieldNode; override;
    end;

    TRecordFieldList = class(TAstNodeList<IRecordFieldNode>, IRecordFieldList)
    protected
        function GetKind: TAstNodeKind; override;
    public
        function AsRecordFieldList: IRecordFieldList; override;
    end;

    // --- Core Nodes Implementations ---

    TConstantNode = class(TAstTypedNode, IConstantNode)
    private
        FConstIdentity: IConstantIdentity; // Typed reference for fast access
        function GetValue: TDataValue;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AIdentity: IConstantIdentity; const AStaticType: IStaticType);
        function AsConstant: IConstantNode; override;
    end;

    TIdentifierNode = class(TAstTypedNode, IIdentifierNode)
    private
        FNamedIdentity: INamedIdentity; // Typed reference for fast access
        FAddress: TResolvedAddress;
        function GetAddress: TResolvedAddress;
        function GetName: string;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AIdentity: INamedIdentity; const AAddress: TResolvedAddress; const AStaticType: IStaticType);
        function AsIdentifier: IIdentifierNode; override;
    end;

    TKeywordNode = class(TAstTypedNode, IKeywordNode)
    private
        FKeywordIdentity: IKeywordIdentity;
        function GetValue: IKeyword;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AIdentity: IKeywordIdentity);
        function AsKeyword: IKeywordNode; override;
    end;

    TCreateSeriesNode = class(TAstTypedNode, ICreateSeriesNode)
    private
        FDefIdentity: IDefinitionIdentity;
        function GetDefinition: String;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
        function AsCreateSeries: ICreateSeriesNode; override;
    end;

    // --- Structural Nodes (Identity only for location) ---

    TIfExpressionNode = class(TAstTypedNode, IIfExpressionNode)
    private
        FCondition, FThenBranch, FElseBranch: IAstNode;
        function GetCondition: IAstNode;
        function GetThenBranch: IAstNode;
        function GetElseBranch: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const ACondition, AThenBranch, AElseBranch: IAstNode;
            const AStaticType: IStaticType;
            const AIdentity: IAstIdentity
        );
        function AsIfExpression: IIfExpressionNode; override;
    end;

    TCondExpressionNode = class(TAstTypedNode, ICondExpressionNode)
    private
        FPairs: TArray<TCondPair>;
        FElseBranch: IAstNode;
        function GetPairs: TArray<TCondPair>;
        function GetElseBranch: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const APairs: TArray<TCondPair>;
            const AElseBranch: IAstNode;
            const AStaticType: IStaticType;
            const AIdentity: IAstIdentity
        );
        function AsCondExpression: ICondExpressionNode; override;
    end;

    TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode, IFunctionDefinition)
    private
        FParameters: IParameterList;
        FBody: IAstNode;
        FLayout: IScopeLayout;
        FDescriptor: IScopeDescriptor;
        FUpvalues: TArray<TResolvedAddress>;
        FHasNestedLambdas, FIsPure: Boolean;
        function GetParameters: IParameterList;
        function GetBody: IAstNode;
        function GetLayout: IScopeLayout;
        function GetDescriptor: IScopeDescriptor;
        function GetUpvalues: TArray<TResolvedAddress>;
        function GetHasNestedLambdas: Boolean;
        function GetIsPure: Boolean;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const AParameters: IParameterList;
            const ABody: IAstNode;
            const AStaticType: IStaticType;
            const ALayout: IScopeLayout;
            const ADescriptor: IScopeDescriptor;
            const AUpvalues: TArray<TResolvedAddress>;
            const AHasNestedLambdas, AIsPure: Boolean;
            const AIdentity: IAstIdentity
        );
        function AsLambdaExpression: ILambdaExpressionNode; override;
    end;

    TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode)
    private
        FCallee: IAstNode;
        FArguments: IArgumentList;
        FIsTailCall: Boolean;
        FStaticTarget: TDataValue.TFunc;
        FIsTargetPure: Boolean;
        function GetCallee: IAstNode;
        function GetArguments: IArgumentList;
        function GetIsTailCall: Boolean;
        function GetStaticTarget: TDataValue.TFunc;
        function GetIsTargetPure: Boolean;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const ACallee: IAstNode;
            const AArguments: IArgumentList;
            const AStaticType: IStaticType;
            const AIsTailCall: Boolean;
            const AStaticTarget: TDataValue.TFunc;
            const AIsTargetPure: Boolean;
            const AIdentity: IAstIdentity
        );
        function AsFunctionCall: IFunctionCallNode; override;
    end;

    TMacroDefinitionNode = class(TAstTypedNode, IMacroDefinitionNode)
    private
        FName: IIdentifierNode;
        FParameters: IParameterList;
        FBody: IAstNode;
        function GetName: IIdentifierNode;
        function GetParameters: IParameterList;
        function GetBody: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const AName: IIdentifierNode;
            const AParameters: IParameterList;
            const ABody: IAstNode;
            const AIdentity: IAstIdentity
        );
        function AsMacroDefinition: IMacroDefinitionNode; override;
    end;

    TMacroExpansionNode = class(TAstTypedNode, IMacroExpansionNode)
    private
        FCallNode: IFunctionCallNode;
        FExpandedBody: IAstNode;
        function GetCallNode: IFunctionCallNode;
        function GetExpandedBody: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AIdentity: IAstIdentity);
        function AsMacroExpansion: IMacroExpansionNode; override;
    end;

    TQuasiquoteNode = class(TAstNode, IQuasiquoteNode)
    private
        FExpression: IAstNode;
        function GetExpression: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
        function AsQuasiquote: IQuasiquoteNode; override;
    end;

    TUnquoteNode = class(TAstNode, IUnquoteNode)
    private
        FExpression: IAstNode;
        function GetExpression: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
        function AsUnquote: IUnquoteNode; override;
    end;

    TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode)
    private
        FExpression: IQuasiquoteNode;
        function GetExpression: IQuasiquoteNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AExpression: IQuasiquoteNode; const AIdentity: IAstIdentity);
        function AsUnquoteSplicing: IUnquoteSplicingNode; override;
    end;

    TRecurNode = class(TAstTypedNode, IRecurNode)
    private
        FArguments: IArgumentList;
        function GetArguments: IArgumentList;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AArguments: IArgumentList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
        function AsRecur: IRecurNode; override;
    end;

    TBlockExpressionNode = class(TAstTypedNode, IBlockExpressionNode)
    private
        FExpressions: IExpressionList;
        function GetExpressions: IExpressionList;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AExpressions: IExpressionList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
        function AsBlockExpression: IBlockExpressionNode; override;
    end;

    TVariableDeclarationNode = class(TAstTypedNode, IVariableDeclarationNode)
    private
        FTarget, FInitializer: IAstNode;
        FIsBoxed: Boolean;
        function GetTarget: IAstNode;
        function GetInitializer: IAstNode;
        function GetIsBoxed: Boolean;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const ATarget: IAstNode;
            AInitializer: IAstNode;
            const AStaticType: IStaticType;
            const AIsBoxed: Boolean;
            const AIdentity: IAstIdentity
        );
        function AsVariableDeclaration: IVariableDeclarationNode; override;
    end;

    TAssignmentNode = class(TAstTypedNode, IAssignmentNode)
    private
        FTarget, FValue: IAstNode;
        function GetTarget: IAstNode;
        function GetValue: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const ATarget, AValue: IAstNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
        function AsAssignment: IAssignmentNode; override;
    end;

    TIndexerNode = class(TAstTypedNode, IIndexerNode)
    private
        FBase, FIndex: IAstNode;
        function GetBase: IAstNode;
        function GetIndex: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const ABase, AIndex: IAstNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
        function AsIndexer: IIndexerNode; override;
    end;

    TMemberAccessNode = class(TAstTypedNode, IMemberAccessNode)
    private
        FBase: IAstNode;
        FMember: IKeywordNode;
        function GetBase: IAstNode;
        function GetMember: IKeywordNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const ABase: IAstNode;
            const AMember: IKeywordNode;
            const AStaticType: IStaticType;
            const AIdentity: IAstIdentity
        );
        function AsMemberAccess: IMemberAccessNode; override;
    end;

    TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode)
    private
        FFields: IRecordFieldList;
        FScalarDef: IScalarRecordDefinition;
        FGenericDef: IGenericRecordDefinition;
        function GetFields: IRecordFieldList;
        function GetGenericDefinition: IGenericRecordDefinition;
        function GetScalarDefinition: IScalarRecordDefinition;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const AFields: IRecordFieldList;
            const AScalarDef: IScalarRecordDefinition;
            const AGenericDef: IGenericRecordDefinition;
            const AStaticType: IStaticType;
            const AIdentity: IAstIdentity
        );
        function AsRecordLiteral: IRecordLiteralNode; override;
    end;

    TAddSeriesItemNode = class(TAstTypedNode, IAddSeriesItemNode)
    private
        FSeries: IIdentifierNode;
        FValue, FLookback: IAstNode;
        function GetSeries: IIdentifierNode;
        function GetValue: IAstNode;
        function GetLookback: IAstNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const ASeries: IIdentifierNode;
            const AValue, ALookback: IAstNode;
            const AStaticType: IStaticType;
            const AIdentity: IAstIdentity
        );
        function AsAddSeriesItem: IAddSeriesItemNode; override;
    end;

    TSeriesLengthNode = class(TAstTypedNode, ISeriesLengthNode)
    private
        FSeries: IIdentifierNode;
        function GetSeries: IIdentifierNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const ASeries: IIdentifierNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
        function AsSeriesLength: ISeriesLengthNode; override;
    end;

    TNopNode = class(TAstTypedNode, INopNode)
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(const AStaticType: IStaticType; const AIdentity: IAstIdentity);
        function AsNop: INopNode; override;
    end;

    TPipeSelectorList = class(TAstNodeList<IKeywordNode>, IPipeSelectorList)
    protected
        function GetKind: TAstNodeKind; override;
    public
        function AsPipeSelectorList: IPipeSelectorList; override;
    end;

    TPipeInputNode = class(TAstTypedNode, IPipeInputNode)
    private
        FStreamSource: IIdentifierNode;
        FSelectors: IPipeSelectorList;
        function GetStreamSource: IIdentifierNode;
        function GetSelectors: IPipeSelectorList;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const AStreamSource: IIdentifierNode;
            const ASelectors: IPipeSelectorList;
            const AIdentity: IAstIdentity;
            const AStaticType: IStaticType
        );
        function AsPipeInput: IPipeInputNode; override;
    end;

    TPipeInputList = class(TAstNodeList<IPipeInputNode>, IPipeInputList)
    protected
        function GetKind: TAstNodeKind; override;
    public
        function AsPipeInputList: IPipeInputList; override;
    end;

    TPipeNode = class(TAstTypedNode, IPipeNode)
    private
        FInputs: IPipeInputList;
        FTransformation: ILambdaExpressionNode;
        function GetInputs: IPipeInputList;
        function GetTransformation: ILambdaExpressionNode;
    protected
        function GetKind: TAstNodeKind; override;
    public
        constructor Create(
            const AInputs: IPipeInputList;
            const ATransformation: ILambdaExpressionNode;
            const AIdentity: IAstIdentity;
            const AStaticType: IStaticType
        );
        function AsPipe: IPipeNode; override;
    end;

implementation

uses
    System.Classes,
    System.Rtti,
    System.StrUtils;

{ TCompiledFunction }

constructor TCompiledFunction.Create(const AFunc: TDataValue.TFunc; const AStaticType: IStaticType; AIsPure: Boolean);
begin
    Func := AFunc;
    StaticType := AStaticType;
    IsPure := AIsPure;
end;

{ TAstNodeKindHelper }

function TAstNodeKindHelper.ToString: string;
begin
    Result := TRttiEnumerationType.GetName<TAstNodeKind>(Self);
    Assert(StartsText('ak', Result));
    Result := Result.Substring(2);
end;

{ TCondPair }

constructor TCondPair.Create(const ACondition, ABranch: IAstNode);
begin
    Condition := ACondition;
    Branch := ABranch;
end;

{ TCompilerError }

constructor TCompilerError.Create(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode);
begin
    Level := ALevel;
    Message := AMessage;
    Node := ANode;
end;

function TCompilerError.ToString: string;
var
    location: string;
begin
    location := '';
    if Assigned(Node) and Assigned(Node.Identity) and Assigned(Node.Identity.Location) then
    begin
        location := Node.Identity.Location.ToString + ' ';
    end
    else if Assigned(Node) and Assigned(Node.Identity) then
    begin
        location := '[' + Node.Identity.ToString + '] ';
    end;

    case Level of
        elHint: Result := '[Hint] ';
        elWarning: Result := '[Warning] ';
        elError: Result := '[Error] ';
    end;
    Result := Result + location + Message;
end;

{ TCompilerLog }

constructor TCompilerLog.Create;
begin
    inherited Create;
    FEntries := TList<TCompilerError>.Create;
end;

destructor TCompilerLog.Destroy;
begin
    FEntries.Free;
    inherited;
end;

procedure TCompilerLog.Add(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode);
begin
    FEntries.Add(TCompilerError.Create(ALevel, AMessage, ANode));
end;

procedure TCompilerLog.AddError(const AMessage: string; const ANode: IAstNode);
begin
    Add(elError, AMessage, ANode);
end;

procedure TCompilerLog.AddWarning(const AMessage: string; const ANode: IAstNode);
begin
    Add(elWarning, AMessage, ANode);
end;

function TCompilerLog.HasErrors: Boolean;
var
    entry: TCompilerError;
begin
    Result := False;
    for entry in FEntries do
        if entry.Level = elError then
            Exit(True);
end;

function TCompilerLog.GetEntryCount: Integer;
begin
    Result := FEntries.Count;
end;

function TCompilerLog.GetEntries: TArray<TCompilerError>;
begin
    Result := FEntries.ToArray;
end;

{ ECompilationFailed }

constructor ECompilationFailed.Create(const AErrors: TArray<TCompilerError>);
var
    msg: string;
begin
    if Length(AErrors) > 0 then
        msg := Format('Compilation failed with %d error(s). First: %s', [Length(AErrors), AErrors[0].Message])
    else
        msg := 'Compilation failed with unspecified errors.';
    inherited Create(msg);
    FErrors := AErrors;
end;

function ECompilationFailed.ToString: string;
var
    sb: TStringBuilder;
    err: TCompilerError;
begin
    sb := TStringBuilder.Create;
    try
        sb.AppendLine(Message);
        for err in FErrors do
        begin
            sb.Append(' - ');
            sb.AppendLine(err.ToString);
        end;
        Result := sb.ToString;
    finally
        sb.Free;
    end;
end;

{ ENoHandlerException }

constructor ENoHandlerException.Create(const KindName: string);
begin
    inherited CreateFmt('No visitor handler registered for AST node kind: %s', [KindName]);
end;

{ TAstNode }

constructor TAstNode.Create(const AIdentity: IAstIdentity);
begin
    inherited Create;
    FIdentity := AIdentity;
end;

function TAstNode.GetIdentity: IAstIdentity;
begin
    Result := FIdentity;
end;

function TAstNode.GetIsTyped: Boolean;
begin
    Result := False;
end;

function TAstNode.Accept(const Visitor: IAstVisitor): TDataValue;
begin
    // Central Dispatch: Delegate to the generic Visit method of the visitor.
    // The visitor uses Node.Kind to look up the correct handler.
    Result := Visitor.Visit(Self);
end;

// Default implementations for As... casting methods (Return nil)
function TAstNode.AsConstant: IConstantNode;
begin
    Result := nil;
end;

function TAstNode.AsIdentifier: IIdentifierNode;
begin
    Result := nil;
end;

function TAstNode.AsKeyword: IKeywordNode;
begin
    Result := nil;
end;

function TAstNode.AsParameterList: IParameterList;
begin
    Result := nil;
end;

function TAstNode.AsArgumentList: IArgumentList;
begin
    Result := nil;
end;

function TAstNode.AsExpressionList: IExpressionList;
begin
    Result := nil;
end;

function TAstNode.AsRecordFieldList: IRecordFieldList;
begin
    Result := nil;
end;

function TAstNode.AsRecordField: IRecordFieldNode;
begin
    Result := nil;
end;

function TAstNode.AsIfExpression: IIfExpressionNode;
begin
    Result := nil;
end;

function TAstNode.AsCondExpression: ICondExpressionNode;
begin
    Result := nil;
end;

function TAstNode.AsLambdaExpression: ILambdaExpressionNode;
begin
    Result := nil;
end;

function TAstNode.AsFunctionCall: IFunctionCallNode;
begin
    Result := nil;
end;

function TAstNode.AsMacroExpansion: IMacroExpansionNode;
begin
    Result := nil;
end;

function TAstNode.AsBlockExpression: IBlockExpressionNode;
begin
    Result := nil;
end;

function TAstNode.AsVariableDeclaration: IVariableDeclarationNode;
begin
    Result := nil;
end;

function TAstNode.AsAssignment: IAssignmentNode;
begin
    Result := nil;
end;

function TAstNode.AsMacroDefinition: IMacroDefinitionNode;
begin
    Result := nil;
end;

function TAstNode.AsQuasiquote: IQuasiquoteNode;
begin
    Result := nil;
end;

function TAstNode.AsUnquote: IUnquoteNode;
begin
    Result := nil;
end;

function TAstNode.AsUnquoteSplicing: IUnquoteSplicingNode;
begin
    Result := nil;
end;

function TAstNode.AsIndexer: IIndexerNode;
begin
    Result := nil;
end;

function TAstNode.AsMemberAccess: IMemberAccessNode;
begin
    Result := nil;
end;

function TAstNode.AsRecordLiteral: IRecordLiteralNode;
begin
    Result := nil;
end;

function TAstNode.AsCreateSeries: ICreateSeriesNode;
begin
    Result := nil;
end;

function TAstNode.AsAddSeriesItem: IAddSeriesItemNode;
begin
    Result := nil;
end;

function TAstNode.AsSeriesLength: ISeriesLengthNode;
begin
    Result := nil;
end;

function TAstNode.AsRecur: IRecurNode;
begin
    Result := nil;
end;

function TAstNode.AsNop: INopNode;
begin
    Result := nil;
end;

function TAstNode.AsTypedNode: IAstTypedNode;
begin
    Result := nil;
end;

function TAstNode.AsPipeInput: IPipeInputNode;
begin
    Result := nil;
end;

function TAstNode.AsPipeSelectorList: IPipeSelectorList;
begin
    Result := nil;
end;

function TAstNode.AsPipeInputList: IPipeInputList;
begin
    Result := nil;
end;

function TAstNode.AsPipe: IPipeNode;
begin
    Result := nil;
end;

{ TAstTypedNode }

constructor TAstTypedNode.Create(const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
    inherited Create(AIdentity);
    FStaticType := AStaticType;
end;

function TAstTypedNode.GetStaticType: IStaticType;
begin
    Result := FStaticType;
end;

function TAstTypedNode.GetIsTyped: Boolean;
begin
    Result := True;
end;

function TAstTypedNode.AsTypedNode: IAstTypedNode;
begin
    Result := Self;
end;

{ TAstNodeList<T> }

constructor TAstNodeList<T>.Create(const AItems: TArray<T>; const AIdentity: IAstIdentity);
begin
    inherited Create(AIdentity);
    FItems := AItems;
end;

function TAstNodeList<T>.GetCount: Integer;
begin
    Result := Length(FItems);
end;

function TAstNodeList<T>.GetEnumerator: TEnumerator<T>;
begin
    Result := TEnumerator.Create(FItems);
end;

function TAstNodeList<T>.GetItem(Index: Integer): T;
begin
    Result := FItems[Index];
end;

function TAstNodeList<T>.ToArray: TArray<T>;
begin
    Result := FItems;
end;

{ TParameterList }

function TParameterList.AsParameterList: IParameterList;
begin
    Result := Self;
end;

function TParameterList.GetKind: TAstNodeKind;
begin
    Result := akParameterList;
end;

{ TArgumentList }

function TArgumentList.AsArgumentList: IArgumentList;
begin
    Result := Self;
end;

function TArgumentList.GetKind: TAstNodeKind;
begin
    Result := akArgumentList;
end;

{ TExpressionList }

function TExpressionList.AsExpressionList: IExpressionList;
begin
    Result := Self;
end;

function TExpressionList.GetKind: TAstNodeKind;
begin
    Result := akExpressionList;
end;

{ TRecordFieldNode }

constructor TRecordFieldNode.Create(const AKey: IKeywordNode; const AValue: IAstNode; const AIdentity: IAstIdentity);
begin
    inherited Create(AIdentity);
    FKey := AKey;
    FValue := AValue;
end;

function TRecordFieldNode.AsRecordField: IRecordFieldNode;
begin
    Result := Self;
end;

function TRecordFieldNode.GetKey: IKeywordNode;
begin
    Result := FKey;
end;

function TRecordFieldNode.GetKind: TAstNodeKind;
begin
    Result := akRecordField;
end;

function TRecordFieldNode.GetValue: IAstNode;
begin
    Result := FValue;
end;

{ TRecordFieldList }

function TRecordFieldList.AsRecordFieldList: IRecordFieldList;
begin
    Result := Self;
end;

function TRecordFieldList.GetKind: TAstNodeKind;
begin
    Result := akRecordFieldList;
end;

{ TConstantNode }

constructor TConstantNode.Create(const AIdentity: IConstantIdentity; const AStaticType: IStaticType);
begin
    inherited Create(AStaticType, AIdentity);
    FConstIdentity := AIdentity;
end;

function TConstantNode.AsConstant: IConstantNode;
begin
    Result := Self;
end;

function TConstantNode.GetKind: TAstNodeKind;
begin
    Result := akConstant;
end;

function TConstantNode.GetValue: TDataValue;
begin
    Result := FConstIdentity.Value;
end;

{ TIdentifierNode }

constructor TIdentifierNode.Create(const AIdentity: INamedIdentity; const AAddress: TResolvedAddress; const AStaticType: IStaticType);
begin
    inherited Create(AStaticType, AIdentity);
    FNamedIdentity := AIdentity;
    FAddress := AAddress;
end;

function TIdentifierNode.AsIdentifier: IIdentifierNode;
begin
    Result := Self;
end;

function TIdentifierNode.GetAddress: TResolvedAddress;
begin
    Result := FAddress;
end;

function TIdentifierNode.GetKind: TAstNodeKind;
begin
    Result := akIdentifier;
end;

function TIdentifierNode.GetName: string;
begin
    Result := FNamedIdentity.Name;
end;

{ TKeywordNode }

constructor TKeywordNode.Create(const AIdentity: IKeywordIdentity);
begin
    inherited Create(TTypes.Keyword, AIdentity);
    FKeywordIdentity := AIdentity;
end;

function TKeywordNode.AsKeyword: IKeywordNode;
begin
    Result := Self;
end;

function TKeywordNode.GetKind: TAstNodeKind;
begin
    Result := akKeyword;
end;

function TKeywordNode.GetValue: IKeyword;
begin
    Result := FKeywordIdentity.Value;
end;

{ TCreateSeriesNode }

constructor TCreateSeriesNode.Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
begin
    inherited Create(AStaticType, AIdentity);
    FDefIdentity := AIdentity;
end;

function TCreateSeriesNode.AsCreateSeries: ICreateSeriesNode;
begin
    Result := Self;
end;

function TCreateSeriesNode.GetDefinition: String;
begin
    Result := FDefIdentity.Definition;
end;

function TCreateSeriesNode.GetKind: TAstNodeKind;
begin
    Result := akCreateSeries;
end;

{ TIfExpressionNode }

constructor TIfExpressionNode.Create(
    const ACondition, AThenBranch, AElseBranch: IAstNode;
    const AStaticType: IStaticType;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);
    FCondition := ACondition;
    FThenBranch := AThenBranch;
    FElseBranch := AElseBranch;
end;

function TIfExpressionNode.AsIfExpression: IIfExpressionNode;
begin
    Result := Self;
end;

function TIfExpressionNode.GetCondition: IAstNode;
begin
    Result := FCondition;
end;
function TIfExpressionNode.GetThenBranch: IAstNode;
begin
    Result := FThenBranch;
end;
function TIfExpressionNode.GetElseBranch: IAstNode;
begin
    Result := FElseBranch;
end;
function TIfExpressionNode.GetKind: TAstNodeKind;
begin
    Result := akIfExpression;
end;

{ TCondExpressionNode }

constructor TCondExpressionNode.Create(
    const APairs: TArray<TCondPair>;
    const AElseBranch: IAstNode;
    const AStaticType: IStaticType;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);
    FPairs := APairs;
    FElseBranch := AElseBranch;
end;

function TCondExpressionNode.AsCondExpression: ICondExpressionNode;
begin
    Result := Self;
end;

function TCondExpressionNode.GetPairs: TArray<TCondPair>;
begin
    Result := FPairs;
end;

function TCondExpressionNode.GetElseBranch: IAstNode;
begin
    Result := FElseBranch;
end;

function TCondExpressionNode.GetKind: TAstNodeKind;
begin
    Result := akCondExpression;
end;

{ TLambdaExpressionNode }

constructor TLambdaExpressionNode.Create(
    const AParameters: IParameterList;
    const ABody: IAstNode;
    const AStaticType: IStaticType;
    const ALayout: IScopeLayout;
    const ADescriptor: IScopeDescriptor;
    const AUpvalues: TArray<TResolvedAddress>;
    const AHasNestedLambdas, AIsPure: Boolean;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);

    Assert(Assigned(AParameters));
    Assert(Assigned(ABody));

    FParameters := AParameters;
    FBody := ABody;
    FLayout := ALayout;
    FDescriptor := ADescriptor;
    FUpvalues := AUpvalues;
    FHasNestedLambdas := AHasNestedLambdas;
    FIsPure := AIsPure;
end;

function TLambdaExpressionNode.AsLambdaExpression: ILambdaExpressionNode;
begin
    Result := Self;
end;

function TLambdaExpressionNode.GetBody: IAstNode;
begin
    Result := FBody;
end;
function TLambdaExpressionNode.GetParameters: IParameterList;
begin
    Result := FParameters;
end;
function TLambdaExpressionNode.GetLayout: IScopeLayout;
begin
    Result := FLayout;
end;
function TLambdaExpressionNode.GetDescriptor: IScopeDescriptor;
begin
    Result := FDescriptor;
end;
function TLambdaExpressionNode.GetUpvalues: TArray<TResolvedAddress>;
begin
    Result := FUpvalues;
end;
function TLambdaExpressionNode.GetHasNestedLambdas: Boolean;
begin
    Result := FHasNestedLambdas;
end;
function TLambdaExpressionNode.GetIsPure: Boolean;
begin
    Result := FIsPure;
end;
function TLambdaExpressionNode.GetKind: TAstNodeKind;
begin
    Result := akLambdaExpression;
end;

{ TFunctionCallNode }

constructor TFunctionCallNode.Create(
    const ACallee: IAstNode;
    const AArguments: IArgumentList;
    const AStaticType: IStaticType;
    const AIsTailCall: Boolean;
    const AStaticTarget: TDataValue.TFunc;
    const AIsTargetPure: Boolean;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);
    FCallee := ACallee;
    FArguments := AArguments;
    FIsTailCall := AIsTailCall;
    FStaticTarget := AStaticTarget;
    FIsTargetPure := AIsTargetPure;
end;

function TFunctionCallNode.AsFunctionCall: IFunctionCallNode;
begin
    Result := Self;
end;

function TFunctionCallNode.GetCallee: IAstNode;
begin
    Result := FCallee;
end;
function TFunctionCallNode.GetArguments: IArgumentList;
begin
    Result := FArguments;
end;
function TFunctionCallNode.GetIsTailCall: Boolean;
begin
    Result := FIsTailCall;
end;
function TFunctionCallNode.GetStaticTarget: TDataValue.TFunc;
begin
    Result := FStaticTarget;
end;
function TFunctionCallNode.GetIsTargetPure: Boolean;
begin
    Result := FIsTargetPure;
end;
function TFunctionCallNode.GetKind: TAstNodeKind;
begin
    Result := akFunctionCall;
end;

{ TMacroDefinitionNode }

constructor TMacroDefinitionNode.Create(
    const AName: IIdentifierNode;
    const AParameters: IParameterList;
    const ABody: IAstNode;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(TTypes.Void, AIdentity);
    FName := AName;
    FParameters := AParameters;
    FBody := ABody;
end;

function TMacroDefinitionNode.AsMacroDefinition: IMacroDefinitionNode;
begin
    Result := Self;
end;

function TMacroDefinitionNode.GetName: IIdentifierNode;
begin
    Result := FName;
end;
function TMacroDefinitionNode.GetParameters: IParameterList;
begin
    Result := FParameters;
end;
function TMacroDefinitionNode.GetBody: IAstNode;
begin
    Result := FBody;
end;
function TMacroDefinitionNode.GetKind: TAstNodeKind;
begin
    Result := akMacroDefinition;
end;

{ TMacroExpansionNode }

constructor TMacroExpansionNode.Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AIdentity: IAstIdentity);
begin
    if AExpandedBody.IsTyped then
        inherited Create(AExpandedBody.AsTypedNode.StaticType, AIdentity)
    else
        inherited Create(TTypes.Unknown, AIdentity);
    FCallNode := ACallNode;
    FExpandedBody := AExpandedBody;
end;

function TMacroExpansionNode.AsMacroExpansion: IMacroExpansionNode;
begin
    Result := Self;
end;

function TMacroExpansionNode.GetCallNode: IFunctionCallNode;
begin
    Result := FCallNode;
end;
function TMacroExpansionNode.GetExpandedBody: IAstNode;
begin
    Result := FExpandedBody;
end;
function TMacroExpansionNode.GetKind: TAstNodeKind;
begin
    Result := akMacroExpansion;
end;

{ TQuasiquoteNode }

constructor TQuasiquoteNode.Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
begin
    inherited Create(AIdentity);
    FExpression := AExpression;
end;

function TQuasiquoteNode.AsQuasiquote: IQuasiquoteNode;
begin
    Result := Self;
end;

function TQuasiquoteNode.GetExpression: IAstNode;
begin
    Result := FExpression;
end;
function TQuasiquoteNode.GetKind: TAstNodeKind;
begin
    Result := akQuasiquote;
end;

{ TUnquoteNode }

constructor TUnquoteNode.Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
begin
    inherited Create(AIdentity);
    FExpression := AExpression;
end;

function TUnquoteNode.AsUnquote: IUnquoteNode;
begin
    Result := Self;
end;

function TUnquoteNode.GetExpression: IAstNode;
begin
    Result := FExpression;
end;
function TUnquoteNode.GetKind: TAstNodeKind;
begin
    Result := akUnquote;
end;

{ TUnquoteSplicingNode }

constructor TUnquoteSplicingNode.Create(const AExpression: IQuasiquoteNode; const AIdentity: IAstIdentity);
begin
    inherited Create(AIdentity);
    FExpression := AExpression;
end;

function TUnquoteSplicingNode.AsUnquoteSplicing: IUnquoteSplicingNode;
begin
    Result := Self;
end;

function TUnquoteSplicingNode.GetExpression: IQuasiquoteNode;
begin
    Result := FExpression;
end;
function TUnquoteSplicingNode.GetKind: TAstNodeKind;
begin
    Result := akUnquoteSplicing;
end;

{ TRecurNode }

constructor TRecurNode.Create(const AArguments: IArgumentList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
    inherited Create(AStaticType, AIdentity);
    FArguments := AArguments;
end;

function TRecurNode.AsRecur: IRecurNode;
begin
    Result := Self;
end;

function TRecurNode.GetArguments: IArgumentList;
begin
    Result := FArguments;
end;
function TRecurNode.GetKind: TAstNodeKind;
begin
    Result := akRecur;
end;

{ TBlockExpressionNode }

constructor TBlockExpressionNode.Create(const AExpressions: IExpressionList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
    inherited Create(AStaticType, AIdentity);
    FExpressions := AExpressions;
end;

function TBlockExpressionNode.AsBlockExpression: IBlockExpressionNode;
begin
    Result := Self;
end;

function TBlockExpressionNode.GetExpressions: IExpressionList;
begin
    Result := FExpressions;
end;
function TBlockExpressionNode.GetKind: TAstNodeKind;
begin
    Result := akBlockExpression;
end;

{ TVariableDeclarationNode }

constructor TVariableDeclarationNode.Create(
    const ATarget: IAstNode;
    AInitializer: IAstNode;
    const AStaticType: IStaticType;
    const AIsBoxed: Boolean;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);
    FTarget := ATarget;
    FInitializer := AInitializer;
    FIsBoxed := AIsBoxed;
end;

function TVariableDeclarationNode.AsVariableDeclaration: IVariableDeclarationNode;
begin
    Result := Self;
end;

function TVariableDeclarationNode.GetTarget: IAstNode;
begin
    Result := FTarget;
end;
function TVariableDeclarationNode.GetInitializer: IAstNode;
begin
    Result := FInitializer;
end;
function TVariableDeclarationNode.GetIsBoxed: Boolean;
begin
    Result := FIsBoxed;
end;
function TVariableDeclarationNode.GetKind: TAstNodeKind;
begin
    Result := akVariableDeclaration;
end;

{ TAssignmentNode }

constructor TAssignmentNode.Create(const ATarget, AValue: IAstNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
    inherited Create(AStaticType, AIdentity);
    FTarget := ATarget;
    FValue := AValue;
end;

function TAssignmentNode.AsAssignment: IAssignmentNode;
begin
    Result := Self;
end;

function TAssignmentNode.GetTarget: IAstNode;
begin
    Result := FTarget;
end;
function TAssignmentNode.GetValue: IAstNode;
begin
    Result := FValue;
end;
function TAssignmentNode.GetKind: TAstNodeKind;
begin
    Result := akAssignment;
end;

{ TIndexerNode }

constructor TIndexerNode.Create(const ABase, AIndex: IAstNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
    inherited Create(AStaticType, AIdentity);
    FBase := ABase;
    FIndex := AIndex;
end;

function TIndexerNode.AsIndexer: IIndexerNode;
begin
    Result := Self;
end;

function TIndexerNode.GetBase: IAstNode;
begin
    Result := FBase;
end;
function TIndexerNode.GetIndex: IAstNode;
begin
    Result := FIndex;
end;
function TIndexerNode.GetKind: TAstNodeKind;
begin
    Result := akIndexer;
end;

{ TMemberAccessNode }

constructor TMemberAccessNode.Create(
    const ABase: IAstNode;
    const AMember: IKeywordNode;
    const AStaticType: IStaticType;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);
    FBase := ABase;
    FMember := AMember;
end;

function TMemberAccessNode.AsMemberAccess: IMemberAccessNode;
begin
    Result := Self;
end;

function TMemberAccessNode.GetBase: IAstNode;
begin
    Result := FBase;
end;
function TMemberAccessNode.GetMember: IKeywordNode;
begin
    Result := FMember;
end;
function TMemberAccessNode.GetKind: TAstNodeKind;
begin
    Result := akMemberAccess;
end;

{ TRecordLiteralNode }

constructor TRecordLiteralNode.Create(
    const AFields: IRecordFieldList;
    const AScalarDef: IScalarRecordDefinition;
    const AGenericDef: IGenericRecordDefinition;
    const AStaticType: IStaticType;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);
    FFields := AFields;
    FScalarDef := AScalarDef;
    FGenericDef := AGenericDef;
end;

function TRecordLiteralNode.AsRecordLiteral: IRecordLiteralNode;
begin
    Result := Self;
end;

function TRecordLiteralNode.GetFields: IRecordFieldList;
begin
    Result := FFields;
end;
function TRecordLiteralNode.GetGenericDefinition: IGenericRecordDefinition;
begin
    Result := FGenericDef;
end;
function TRecordLiteralNode.GetScalarDefinition: IScalarRecordDefinition;
begin
    Result := FScalarDef;
end;
function TRecordLiteralNode.GetKind: TAstNodeKind;
begin
    Result := akRecordLiteral;
end;

{ TAddSeriesItemNode }

constructor TAddSeriesItemNode.Create(
    const ASeries: IIdentifierNode;
    const AValue, ALookback: IAstNode;
    const AStaticType: IStaticType;
    const AIdentity: IAstIdentity
);
begin
    inherited Create(AStaticType, AIdentity);
    FSeries := ASeries;
    FValue := AValue;
    FLookback := ALookback;
end;

function TAddSeriesItemNode.AsAddSeriesItem: IAddSeriesItemNode;
begin
    Result := Self;
end;

function TAddSeriesItemNode.GetSeries: IIdentifierNode;
begin
    Result := FSeries;
end;
function TAddSeriesItemNode.GetValue: IAstNode;
begin
    Result := FValue;
end;
function TAddSeriesItemNode.GetLookback: IAstNode;
begin
    Result := FLookback;
end;
function TAddSeriesItemNode.GetKind: TAstNodeKind;
begin
    Result := akAddSeriesItem;
end;

{ TSeriesLengthNode }

constructor TSeriesLengthNode.Create(const ASeries: IIdentifierNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
    inherited Create(AStaticType, AIdentity);
    FSeries := ASeries;
end;

function TSeriesLengthNode.AsSeriesLength: ISeriesLengthNode;
begin
    Result := Self;
end;

function TSeriesLengthNode.GetSeries: IIdentifierNode;
begin
    Result := FSeries;
end;
function TSeriesLengthNode.GetKind: TAstNodeKind;
begin
    Result := akSeriesLength;
end;

{ TNopNode }

constructor TNopNode.Create(const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
    inherited Create(AStaticType, AIdentity);
end;

function TNopNode.AsNop: INopNode;
begin
    Result := Self;
end;

function TNopNode.GetKind: TAstNodeKind;
begin
    Result := akNop;
end;

constructor TAstNodeList<T>.TEnumerator.Create(const AArray: TArray<T>);
begin
    inherited Create;
    FArray := AArray;
    FIndex := -1;
end;

function TAstNodeList<T>.TEnumerator.DoGetCurrent: T;
begin
    Result := Current;
end;

function TAstNodeList<T>.TEnumerator.DoMoveNext: Boolean;
begin
    Result := MoveNext;
end;

function TAstNodeList<T>.TEnumerator.GetCurrent: T;
begin
    Result := FArray[FIndex];
end;

function TAstNodeList<T>.TEnumerator.MoveNext: Boolean;
begin
    Result := FIndex < High(FArray);
    if Result then
        Inc(FIndex);
end;

{ TPipeSelectorList }

function TPipeSelectorList.AsPipeSelectorList: IPipeSelectorList;
begin
    Result := Self;
end;

function TPipeSelectorList.GetKind: TAstNodeKind;
begin
    Result := akPipeSelectorList;
end;

{ TPipeInputNode }

constructor TPipeInputNode.Create(
    const AStreamSource: IIdentifierNode;
    const ASelectors: IPipeSelectorList;
    const AIdentity: IAstIdentity;
    const AStaticType: IStaticType
);
begin
    inherited Create(AStaticType, AIdentity);
    FStreamSource := AStreamSource;
    FSelectors := ASelectors;
end;

function TPipeInputNode.AsPipeInput: IPipeInputNode;
begin
    Result := Self;
end;

function TPipeInputNode.GetKind: TAstNodeKind;
begin
    Result := akPipeInput;
end;

function TPipeInputNode.GetSelectors: IPipeSelectorList;
begin
    Result := FSelectors;
end;

function TPipeInputNode.GetStreamSource: IIdentifierNode;
begin
    Result := FStreamSource;
end;

{ TPipeInputList }

function TPipeInputList.AsPipeInputList: IPipeInputList;
begin
    Result := Self;
end;

function TPipeInputList.GetKind: TAstNodeKind;
begin
    Result := akPipeInputList;
end;

{ TPipeNode }

constructor TPipeNode.Create(
    const AInputs: IPipeInputList;
    const ATransformation: ILambdaExpressionNode;
    const AIdentity: IAstIdentity;
    const AStaticType: IStaticType
);
begin
    inherited Create(AStaticType, AIdentity);
    FInputs := AInputs;
    FTransformation := ATransformation;
end;

function TPipeNode.AsPipe: IPipeNode;
begin
    Result := Self;
end;

function TPipeNode.GetInputs: IPipeInputList;
begin
    Result := FInputs;
end;

function TPipeNode.GetKind: TAstNodeKind;
begin
    Result := akPipe;
end;

function TPipeNode.GetTransformation: ILambdaExpressionNode;
begin
    Result := FTransformation;
end;

end.
