unit Myc.Ast;

interface

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

type
    TAst = record
    public
        type
            TRegisterLibraryProc = reference to procedure(const Scope: IExecutionScope);
    private
        class var
            FLibraries: TList<TRegisterLibraryProc>;
        class constructor Create;
        class destructor Destroy;
    public
        class procedure RegisterLibrary(const AProc: TRegisterLibraryProc); static;
        class function CreateScope(
            Parent: IExecutionScope;
            const Descriptor: IScopeDescriptor = nil;
            ARegisterLibraries: Boolean = False
        ): IExecutionScope; static;

        // ... (Keep existing methods: Identifier, Constant, Keyword, CreateSeries, Nop, IfExpr, CondExpr, LambdaExpr, MacroDef, Quotes, Call, etc.) ...
        // [Creation] Raw Name -> New INamedIdentity
        class function Identifier(const AName: string; const Loc: ISourceLocation = nil): IIdentifierNode; overload; static;
        class function Identifier(
            const Identity: INamedIdentity;
            const Address: TResolvedAddress;
            const AStaticType: IStaticType = nil
        ): IIdentifierNode; overload; static;

        class function Constant(const AValue: TDataValue; const Loc: ISourceLocation = nil): IConstantNode; overload; static;
        class function Constant(const AValue: String; const Loc: ISourceLocation = nil): IConstantNode; overload; static;
        class function Constant(const Identity: IConstantIdentity; const AStaticType: IStaticType = nil): IConstantNode; overload; static;

        class function Keyword(const AName: string; const Loc: ISourceLocation = nil): IKeywordNode; overload; static;
        class function Keyword(const Identity: IKeywordIdentity): IKeywordNode; overload; static;

        class function CreateSeries(const ADefinition: String; const Loc: ISourceLocation = nil): ICreateSeriesNode; overload; static;
        class function CreateSeries(
            const Identity: IDefinitionIdentity;
            const AStaticType: IStaticType = nil
        ): ICreateSeriesNode; overload; static;

        class function Nop(const Loc: ISourceLocation = nil): IAstNode; overload; static;
        class function Nop(const Identity: IAstIdentity; const AStaticType: IStaticType = nil): IAstNode; overload; static;

        class function IfExpr(
            const ACondition: IAstNode;
            const AThenBranch, AElseBranch: IAstNode;
            const Loc: ISourceLocation = nil
        ): IIfExpressionNode; overload; static;
        class function IfExpr(
            const Identity: IAstIdentity;
            const ACondition: IAstNode;
            const AThenBranch, AElseBranch: IAstNode;
            const AStaticType: IStaticType = nil
        ): IIfExpressionNode; overload; static;

        class function CondExpr(
            const APairs: TArray<TCondPair>;
            const AElseBranch: IAstNode;
            const Loc: ISourceLocation = nil
        ): ICondExpressionNode; overload; static;
        class function CondExpr(
            const Identity: IAstIdentity;
            const APairs: TArray<TCondPair>;
            const AElseBranch: IAstNode;
            const AStaticType: IStaticType = nil
        ): ICondExpressionNode; overload; static;
        class function TernaryExpr(
            const ACondition: IAstNode;
            const AThenBranch, AElseBranch: IAstNode;
            const Loc: ISourceLocation = nil
        ): ICondExpressionNode; overload; static;

        class function LambdaExpr(
            const AParameters: TArray<IIdentifierNode>;
            const ABody: IAstNode;
            const Loc: ISourceLocation = nil
        ): ILambdaExpressionNode; overload; static;
        class function LambdaExpr(
            const Identity: IAstIdentity;
            const AParameters: IParameterList;
            const ABody: IAstNode;
            const ALayout: IScopeLayout = nil;
            const ADescriptor: IScopeDescriptor = nil;
            const AUpvalues: TArray<TResolvedAddress> = nil;
            const AHasNestedLambdas: Boolean = False;
            const AIsPure: Boolean = False;
            const AStaticType: IStaticType = nil
        ): ILambdaExpressionNode; overload; static;

        class function MacroDef(
            const AName: IIdentifierNode;
            const AParameters: TArray<IIdentifierNode>;
            const ABody: IAstNode;
            const Loc: ISourceLocation = nil
        ): IMacroDefinitionNode; overload; static;
        class function MacroDef(
            const Identity: IAstIdentity;
            const AName: IIdentifierNode;
            const AParameters: IParameterList;
            const ABody: IAstNode
        ): IMacroDefinitionNode; overload; static;

        class function Quasiquote(const AExpression: IAstNode; const Loc: ISourceLocation = nil): IQuasiquoteNode; overload; static;
        class function Quasiquote(const Identity: IAstIdentity; const AExpression: IAstNode): IQuasiquoteNode; overload; static;

        class function Unquote(const AExpression: IAstNode; const Loc: ISourceLocation = nil): IUnquoteNode; overload; static;
        class function Unquote(const Identity: IAstIdentity; const AExpression: IAstNode): IUnquoteNode; overload; static;

        class function UnquoteSplicing(
            const AExpression: IQuasiquoteNode;
            const Loc: ISourceLocation = nil
        ): IUnquoteSplicingNode; overload; static;
        class function UnquoteSplicing(
            const Identity: IAstIdentity;
            const AExpression: IQuasiquoteNode
        ): IUnquoteSplicingNode; overload; static;

        class function FunctionCall(
            const ACallee: IAstNode;
            const AArguments: TArray<IAstNode>;
            const Loc: ISourceLocation = nil
        ): IFunctionCallNode; overload; static;
        class function FunctionCall(
            const Identity: IAstIdentity;
            const ACallee: IAstNode;
            const AArguments: IArgumentList;
            const AStaticType: IStaticType = nil;
            const AIsTailCall: Boolean = False;
            const AStaticTarget: TDataValue.TFunc = nil;
            const AIsTargetPure: Boolean = False
        ): IFunctionCallNode; overload; static;

        class function MacroExpansionNode(
            const AOriginalCallNode: IFunctionCallNode;
            const AExpandedBody: IAstNode;
            const Loc: ISourceLocation = nil
        ): IMacroExpansionNode; overload; static;
        class function MacroExpansionNode(
            const Identity: IAstIdentity;
            const AOriginalCallNode: IFunctionCallNode;
            const AExpandedBody: IAstNode
        ): IMacroExpansionNode; overload; static;

        class function Recur(const AArguments: array of IAstNode; const Loc: ISourceLocation = nil): IRecurNode; overload; static;
        class function Recur(
            const Identity: IAstIdentity;
            const AArguments: IArgumentList;
            const AStaticType: IStaticType = nil
        ): IRecurNode; overload; static;

        class function Block(
            const AExpressions: array of IAstNode;
            const Loc: ISourceLocation = nil
        ): IBlockExpressionNode; overload; static;
        class function Block(
            const Identity: IAstIdentity;
            const AExpressions: IExpressionList;
            const AStaticType: IStaticType = nil
        ): IBlockExpressionNode; overload; static;

        class function VarDecl(
            const AIdentifier: IAstNode;
            AInitializer: IAstNode = nil;
            const Loc: ISourceLocation = nil
        ): IVariableDeclarationNode; overload; static;
        class function VarDecl(
            const Identity: IAstIdentity;
            const AIdentifier: IAstNode;
            AInitializer: IAstNode;
            const AStaticType: IStaticType = nil;
            const AIsBoxed: Boolean = False
        ): IVariableDeclarationNode; overload; static;

        class function Assign(const ATarget, AValue: IAstNode; const Loc: ISourceLocation = nil): IAssignmentNode; overload; static;
        class function Assign(
            const Identity: IAstIdentity;
            const ATarget, AValue: IAstNode;
            const AStaticType: IStaticType = nil
        ): IAssignmentNode; overload; static;

        class function Indexer(
            const ABase: IAstNode;
            const AIndex: IAstNode;
            const Loc: ISourceLocation = nil
        ): IIndexerNode; overload; static;
        class function Indexer(
            const Identity: IAstIdentity;
            const ABase, AIndex: IAstNode;
            const AStaticType: IStaticType = nil
        ): IIndexerNode; overload; static;

        class function MemberAccess(
            const ABase: IAstNode;
            const AMember: IKeywordNode;
            const Loc: ISourceLocation = nil
        ): IMemberAccessNode; overload; static;
        class function MemberAccess(
            const Identity: IAstIdentity;
            const ABase: IAstNode;
            const AMember: IKeywordNode;
            const AStaticType: IStaticType = nil
        ): IMemberAccessNode; overload; static;

        class function RecordField(
            const AKey: IKeywordNode;
            const AValue: IAstNode;
            const Loc: ISourceLocation = nil
        ): IRecordFieldNode; overload; static;
        class function RecordField(
            const Identity: IAstIdentity;
            const AKey: IKeywordNode;
            const AValue: IAstNode
        ): IRecordFieldNode; overload; static;

        class function RecordLiteral(
            const AFields: TArray<IRecordFieldNode>;
            const Loc: ISourceLocation = nil
        ): IRecordLiteralNode; overload; static;
        class function RecordLiteral(
            const Identity: IAstIdentity;
            const AFields: IRecordFieldList;
            const AScalarDefinition: IScalarRecordDefinition = nil;
            const AGenericDefinition: IGenericRecordDefinition = nil;
            const AStaticType: IStaticType = nil
        ): IRecordLiteralNode; overload; static;

        class function AddSeriesItem(
            const ASeries: IIdentifierNode;
            const AValue: IAstNode;
            const ALookback: IAstNode = nil;
            const Loc: ISourceLocation = nil
        ): IAddSeriesItemNode; overload; static;
        class function AddSeriesItem(
            const Identity: IAstIdentity;
            const ASeries: IIdentifierNode;
            const AValue: IAstNode;
            const ALookback: IAstNode;
            const AStaticType: IStaticType = nil
        ): IAddSeriesItemNode; overload; static;

        class function SeriesLength(const ASeries: IIdentifierNode; const Loc: ISourceLocation = nil): ISeriesLengthNode; overload; static;
        class function SeriesLength(
            const Identity: IAstIdentity;
            const ASeries: IIdentifierNode;
            const AStaticType: IStaticType = nil
        ): ISeriesLengthNode; overload; static;

        // --- PIPES (Updated) ---

        // Helper: Create a SelectorList from an array of KeywordNodes
        class function PipeSelectorList(const AKeywords: TArray<IKeywordNode>; const Loc: ISourceLocation = nil): IPipeSelectorList; static;

        // Updated PipeInput: Accepts IPipeSelectorList instead of single Keyword
        class function PipeInput(
            const AStream: IIdentifierNode;
            const ASelectors: IPipeSelectorList;
            const Loc: ISourceLocation = nil
        ): IPipeInputNode; static;

        class function Pipe(
            const AInputs: TArray<IPipeInputNode>;
            const ATransformation: ILambdaExpressionNode;
            const Loc: ISourceLocation = nil
        ): IPipeNode; overload; static;

        class function Pipe(
            const Identity: IAstIdentity;
            const AInputs: IPipeInputList;
            const ATransformation: ILambdaExpressionNode;
            const AStaticType: IStaticType = nil
        ): IPipeNode; overload; static;
    end;

implementation

uses
    System.Generics.Defaults;

{ TAst }

class constructor TAst.Create;
begin
    FLibraries := TList<TRegisterLibraryProc>.Create;
end;

class destructor TAst.Destroy;
begin
    FLibraries.Free;
end;

class procedure TAst.RegisterLibrary(const AProc: TRegisterLibraryProc);
begin
    FLibraries.Add(AProc);
end;

class function TAst.CreateScope(
    Parent: IExecutionScope;
    const Descriptor: IScopeDescriptor = nil;
    ARegisterLibraries: Boolean = False
): IExecutionScope;
begin
    if Assigned(Descriptor) then
        Result := Descriptor.CreateScope(Parent)
    else
        Result := TScope.CreateScope(Parent, nil, nil);

    if ARegisterLibraries then
    begin
        for var libProc in FLibraries do
            libProc(Result);
    end;
end;

class function TAst.Identifier(const AName: string; const Loc: ISourceLocation): IIdentifierNode;
begin
    var id := TIdentities.Identifier(AName, Loc);
    Result := TIdentifierNode.Create(id, Default(TResolvedAddress), TTypes.Unknown);
end;

class function TAst.Identifier(
    const Identity: INamedIdentity;
    const Address: TResolvedAddress;
    const AStaticType: IStaticType
): IIdentifierNode;
begin
    Result :=
        TIdentifierNode.Create(
            Identity,
            Address,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown
        );
end;

class function TAst.Constant(const AValue: TDataValue; const Loc: ISourceLocation): IConstantNode;
begin
    var constType: IStaticType;
    case AValue.Kind of
        TDataValueKind.vkScalar: constType := TTypes.FromScalarKind(AValue.AsScalar.Kind);
        TDataValueKind.vkText: constType := TTypes.Text;
        TDataValueKind.vkVoid: constType := TTypes.Void;
    else
        constType := TTypes.Unknown;
    end;
    var id := TIdentities.Constant(AValue, Loc);
    Result := TConstantNode.Create(id, constType);
end;

class function TAst.Constant(const AValue: String; const Loc: ISourceLocation): IConstantNode;
begin
    Result := TAst.Constant(TDataValue(AValue), Loc);
end;

class function TAst.Constant(const Identity: IConstantIdentity; const AStaticType: IStaticType): IConstantNode;
begin
    Result :=
        TConstantNode.Create(
            Identity,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown
        );
end;

class function TAst.Keyword(const AName: string; const Loc: ISourceLocation): IKeywordNode;
begin
    var val := TKeywordRegistry.Intern(AName);
    var id := TIdentities.Keyword(val, Loc);
    Result := TKeywordNode.Create(id);
end;

class function TAst.Keyword(const Identity: IKeywordIdentity): IKeywordNode;
begin
    Result := TKeywordNode.Create(Identity);
end;

class function TAst.CreateSeries(const ADefinition: String; const Loc: ISourceLocation): ICreateSeriesNode;
begin
    var id := TIdentities.Definition(ADefinition, Loc);
    Result := TCreateSeriesNode.Create(id, TTypes.Unknown);
end;

class function TAst.CreateSeries(const Identity: IDefinitionIdentity; const AStaticType: IStaticType): ICreateSeriesNode;
begin
    Result :=
        TCreateSeriesNode.Create(
            Identity,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown
        );
end;

class function TAst.Nop(const Loc: ISourceLocation): IAstNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TNopNode.Create(TTypes.Unknown, id);
end;

class function TAst.Nop(const Identity: IAstIdentity; const AStaticType: IStaticType): IAstNode;
begin
    Result :=
        TNopNode.Create(
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.IfExpr(const ACondition, AThenBranch, AElseBranch: IAstNode; const Loc: ISourceLocation): IIfExpressionNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TIfExpressionNode.Create(ACondition, AThenBranch, AElseBranch, TTypes.Unknown, id);
end;

class function TAst.IfExpr(
    const Identity: IAstIdentity;
    const ACondition, AThenBranch, AElseBranch: IAstNode;
    const AStaticType: IStaticType
): IIfExpressionNode;
begin
    Result :=
        TIfExpressionNode.Create(
            ACondition,
            AThenBranch,
            AElseBranch,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.CondExpr(const APairs: TArray<TCondPair>; const AElseBranch: IAstNode; const Loc: ISourceLocation): ICondExpressionNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TCondExpressionNode.Create(APairs, AElseBranch, TTypes.Unknown, id);
end;

class function TAst.CondExpr(
    const Identity: IAstIdentity;
    const APairs: TArray<TCondPair>;
    const AElseBranch: IAstNode;
    const AStaticType: IStaticType
): ICondExpressionNode;
begin
    Result :=
        TCondExpressionNode.Create(
            APairs,
            AElseBranch,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.TernaryExpr(const ACondition, AThenBranch, AElseBranch: IAstNode; const Loc: ISourceLocation): ICondExpressionNode;
begin
    var pair := TCondPair.Create(ACondition, AThenBranch);
    Result := TAst.CondExpr([pair], AElseBranch, Loc);
end;

class function TAst.LambdaExpr(
    const AParameters: TArray<IIdentifierNode>;
    const ABody: IAstNode;
    const Loc: ISourceLocation
): ILambdaExpressionNode;
begin
    var listId := TIdentities.List('[', ']', ' ', Loc);
    var paramList := TParameterList.Create(AParameters, listId);
    var id := TIdentities.Structural(Loc);
    var body := ABody;
    if body = nil then
        body := Block([]);
    Result := TLambdaExpressionNode.Create(paramList, body, TTypes.Unknown, nil, nil, nil, False, False, id);
end;

class function TAst.LambdaExpr(
    const Identity: IAstIdentity;
    const AParameters: IParameterList;
    const ABody: IAstNode;
    const ALayout: IScopeLayout;
    const ADescriptor: IScopeDescriptor;
    const AUpvalues: TArray<TResolvedAddress>;
    const AHasNestedLambdas, AIsPure: Boolean;
    const AStaticType: IStaticType
): ILambdaExpressionNode;
begin
    Result :=
        TLambdaExpressionNode.Create(
            AParameters,
            ABody,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            ALayout,
            ADescriptor,
            AUpvalues,
            AHasNestedLambdas,
            AIsPure,
            Identity
        );
end;

class function TAst.MacroDef(
    const AName: IIdentifierNode;
    const AParameters: TArray<IIdentifierNode>;
    const ABody: IAstNode;
    const Loc: ISourceLocation
): IMacroDefinitionNode;
begin
    var listId := TIdentities.List('[', ']', ' ', Loc);
    var paramList := TParameterList.Create(AParameters, listId);
    var id := TIdentities.Structural(Loc);
    Result := TMacroDefinitionNode.Create(AName, paramList, ABody, id);
end;

class function TAst.MacroDef(
    const Identity: IAstIdentity;
    const AName: IIdentifierNode;
    const AParameters: IParameterList;
    const ABody: IAstNode
): IMacroDefinitionNode;
begin
    Result := TMacroDefinitionNode.Create(AName, AParameters, ABody, Identity);
end;

class function TAst.Quasiquote(const AExpression: IAstNode; const Loc: ISourceLocation): IQuasiquoteNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TQuasiquoteNode.Create(AExpression, id);
end;

class function TAst.Quasiquote(const Identity: IAstIdentity; const AExpression: IAstNode): IQuasiquoteNode;
begin
    Result := TQuasiquoteNode.Create(AExpression, Identity);
end;

class function TAst.Unquote(const AExpression: IAstNode; const Loc: ISourceLocation): IUnquoteNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TUnquoteNode.Create(AExpression, id);
end;

class function TAst.Unquote(const Identity: IAstIdentity; const AExpression: IAstNode): IUnquoteNode;
begin
    Result := TUnquoteNode.Create(AExpression, Identity);
end;

class function TAst.UnquoteSplicing(const AExpression: IQuasiquoteNode; const Loc: ISourceLocation): IUnquoteSplicingNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TUnquoteSplicingNode.Create(AExpression, id);
end;

class function TAst.UnquoteSplicing(const Identity: IAstIdentity; const AExpression: IQuasiquoteNode): IUnquoteSplicingNode;
begin
    Result := TUnquoteSplicingNode.Create(AExpression, Identity);
end;

class function TAst.FunctionCall(
    const ACallee: IAstNode;
    const AArguments: TArray<IAstNode>;
    const Loc: ISourceLocation
): IFunctionCallNode;
begin
    var listId := TIdentities.List('', '', ' ', Loc);
    var argList := TArgumentList.Create(AArguments, listId);
    var id := TIdentities.Structural(Loc);
    Result := TFunctionCallNode.Create(ACallee, argList, TTypes.Unknown, False, nil, False, id);
end;

class function TAst.FunctionCall(
    const Identity: IAstIdentity;
    const ACallee: IAstNode;
    const AArguments: IArgumentList;
    const AStaticType: IStaticType;
    const AIsTailCall: Boolean;
    const AStaticTarget: TDataValue.TFunc;
    const AIsTargetPure: Boolean
): IFunctionCallNode;
begin
    Result :=
        TFunctionCallNode.Create(
            ACallee,
            AArguments,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            AIsTailCall,
            AStaticTarget,
            AIsTargetPure,
            Identity
        );
end;

class function TAst.MacroExpansionNode(
    const AOriginalCallNode: IFunctionCallNode;
    const AExpandedBody: IAstNode;
    const Loc: ISourceLocation
): IMacroExpansionNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TMacroExpansionNode.Create(AOriginalCallNode, AExpandedBody, id);
end;

class function TAst.MacroExpansionNode(
    const Identity: IAstIdentity;
    const AOriginalCallNode: IFunctionCallNode;
    const AExpandedBody: IAstNode
): IMacroExpansionNode;
begin
    Result := TMacroExpansionNode.Create(AOriginalCallNode, AExpandedBody, Identity);
end;

class function TAst.Recur(const AArguments: array of IAstNode; const Loc: ISourceLocation): IRecurNode;
var
    args: TArray<IAstNode>;
    i: Integer;
begin
    SetLength(args, Length(AArguments));
    for i := 0 to High(AArguments) do
        args[i] := AArguments[i];
    var listId := TIdentities.List('', '', ' ', Loc);
    var argList := TArgumentList.Create(args, listId);
    var id := TIdentities.Structural(Loc);
    Result := TRecurNode.Create(argList, TTypes.Void, id);
end;

class function TAst.Recur(const Identity: IAstIdentity; const AArguments: IArgumentList; const AStaticType: IStaticType): IRecurNode;
begin
    Result :=
        TRecurNode.Create(
            AArguments,
            if AStaticType <> nil then AStaticType
            else TTypes.Void,
            Identity
        );
end;

class function TAst.Block(const AExpressions: array of IAstNode; const Loc: ISourceLocation): IBlockExpressionNode;
var
    exprs: TArray<IAstNode>;
    i: Integer;
begin
    SetLength(exprs, Length(AExpressions));
    for i := 0 to High(AExpressions) do
        exprs[i] := AExpressions[i];
    var listId := TIdentities.List('', '', ' ', Loc);
    var exprList := TExpressionList.Create(exprs, listId);
    var id := TIdentities.Structural(Loc);
    Result := TBlockExpressionNode.Create(exprList, TTypes.Unknown, id);
end;

class function TAst.Block(
    const Identity: IAstIdentity;
    const AExpressions: IExpressionList;
    const AStaticType: IStaticType
): IBlockExpressionNode;
begin
    Result :=
        TBlockExpressionNode.Create(
            AExpressions,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.VarDecl(const AIdentifier: IAstNode; AInitializer: IAstNode; const Loc: ISourceLocation): IVariableDeclarationNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TVariableDeclarationNode.Create(AIdentifier, AInitializer, TTypes.Unknown, False, id);
end;

class function TAst.VarDecl(
    const Identity: IAstIdentity;
    const AIdentifier: IAstNode;
    AInitializer: IAstNode;
    const AStaticType: IStaticType;
    const AIsBoxed: Boolean
): IVariableDeclarationNode;
begin
    Result :=
        TVariableDeclarationNode.Create(
            AIdentifier,
            AInitializer,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            AIsBoxed,
            Identity
        );
end;

class function TAst.Assign(const ATarget, AValue: IAstNode; const Loc: ISourceLocation): IAssignmentNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TAssignmentNode.Create(ATarget, AValue, TTypes.Unknown, id);
end;

class function TAst.Assign(const Identity: IAstIdentity; const ATarget, AValue: IAstNode; const AStaticType: IStaticType): IAssignmentNode;
begin
    Result :=
        TAssignmentNode.Create(
            ATarget,
            AValue,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.Indexer(const ABase, AIndex: IAstNode; const Loc: ISourceLocation): IIndexerNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TIndexerNode.Create(ABase, AIndex, TTypes.Unknown, id);
end;

class function TAst.Indexer(const Identity: IAstIdentity; const ABase, AIndex: IAstNode; const AStaticType: IStaticType): IIndexerNode;
begin
    Result :=
        TIndexerNode.Create(
            ABase,
            AIndex,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.MemberAccess(const ABase: IAstNode; const AMember: IKeywordNode; const Loc: ISourceLocation): IMemberAccessNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TMemberAccessNode.Create(ABase, AMember, TTypes.Unknown, id);
end;

class function TAst.MemberAccess(
    const Identity: IAstIdentity;
    const ABase: IAstNode;
    const AMember: IKeywordNode;
    const AStaticType: IStaticType
): IMemberAccessNode;
begin
    Result :=
        TMemberAccessNode.Create(
            ABase,
            AMember,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.RecordField(const AKey: IKeywordNode; const AValue: IAstNode; const Loc: ISourceLocation): IRecordFieldNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TRecordFieldNode.Create(AKey, AValue, id);
end;

class function TAst.RecordField(const Identity: IAstIdentity; const AKey: IKeywordNode; const AValue: IAstNode): IRecordFieldNode;
begin
    Result := TRecordFieldNode.Create(AKey, AValue, Identity);
end;

class function TAst.RecordLiteral(const AFields: TArray<IRecordFieldNode>; const Loc: ISourceLocation): IRecordLiteralNode;
begin
    var listId := TIdentities.List('{', '}', ' ', Loc);
    var fieldList := TRecordFieldList.Create(AFields, listId);
    var id := TIdentities.Structural(Loc);
    Result := TRecordLiteralNode.Create(fieldList, nil, nil, TTypes.Unknown, id);
end;

class function TAst.RecordLiteral(
    const Identity: IAstIdentity;
    const AFields: IRecordFieldList;
    const AScalarDefinition: IScalarRecordDefinition;
    const AGenericDefinition: IGenericRecordDefinition;
    const AStaticType: IStaticType
): IRecordLiteralNode;
begin
    Result :=
        TRecordLiteralNode.Create(
            AFields,
            AScalarDefinition,
            AGenericDefinition,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown,
            Identity
        );
end;

class function TAst.AddSeriesItem(
    const ASeries: IIdentifierNode;
    const AValue: IAstNode;
    const ALookback: IAstNode;
    const Loc: ISourceLocation
): IAddSeriesItemNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TAddSeriesItemNode.Create(ASeries, AValue, ALookback, TTypes.Void, id);
end;

class function TAst.AddSeriesItem(
    const Identity: IAstIdentity;
    const ASeries: IIdentifierNode;
    const AValue: IAstNode;
    const ALookback: IAstNode;
    const AStaticType: IStaticType
): IAddSeriesItemNode;
begin
    Result :=
        TAddSeriesItemNode.Create(
            ASeries,
            AValue,
            ALookback,
            if AStaticType <> nil then AStaticType
            else TTypes.Void,
            Identity
        );
end;

class function TAst.SeriesLength(const ASeries: IIdentifierNode; const Loc: ISourceLocation): ISeriesLengthNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TSeriesLengthNode.Create(ASeries, TTypes.Ordinal, id);
end;

class function TAst.SeriesLength(
    const Identity: IAstIdentity;
    const ASeries: IIdentifierNode;
    const AStaticType: IStaticType
): ISeriesLengthNode;
begin
    Result :=
        TSeriesLengthNode.Create(
            ASeries,
            if AStaticType <> nil then AStaticType
            else TTypes.Ordinal,
            Identity
        );
end;

// --- PIPES ---

class function TAst.PipeSelectorList(const AKeywords: TArray<IKeywordNode>; const Loc: ISourceLocation): IPipeSelectorList;
begin
    var listId := TIdentities.List('[', ']', ' ', Loc);
    Result := TPipeSelectorList.Create(AKeywords, listId);
end;

class function TAst.PipeInput(
    const AStream: IIdentifierNode;
    const ASelectors: IPipeSelectorList;
    const Loc: ISourceLocation
): IPipeInputNode;
begin
    var id := TIdentities.Structural(Loc);
    Result := TPipeInputNode.Create(AStream, ASelectors, id, TTypes.Unknown);
end;

class function TAst.Pipe(
    const AInputs: TArray<IPipeInputNode>;
    const ATransformation: ILambdaExpressionNode;
    const Loc: ISourceLocation
): IPipeNode;
begin
    var listId := TIdentities.List('[', ']', ' ', Loc);
    var inputList := TPipeInputList.Create(AInputs, listId);

    var id := TIdentities.Structural(Loc);
    Result := TPipeNode.Create(inputList, ATransformation, id, TTypes.Unknown);
end;

class function TAst.Pipe(
    const Identity: IAstIdentity;
    const AInputs: IPipeInputList;
    const ATransformation: ILambdaExpressionNode;
    const AStaticType: IStaticType
): IPipeNode;
begin
    Result :=
        TPipeNode.Create(
            AInputs,
            ATransformation,
            Identity,
            if AStaticType <> nil then AStaticType
            else TTypes.Unknown
        );
end;

end.
