unit Myc.Ast.Identities;

interface

uses
    System.SysUtils,
    Myc.Data.Value,
    Myc.Data.Keyword;

type
    // Forward declarations
    INamedIdentity = interface;
    IConstantIdentity = interface;
    IKeywordIdentity = interface;
    IDefinitionIdentity = interface;
    IListIdentity = interface;

    // Represents a position in the source code.
    ISourceLocation = interface
        function GetLine: Integer;
        function GetCol: Integer;
        function ToString: string;
        property Line: Integer read GetLine;
        property Col: Integer read GetCol;
    end;

    // Discriminator enum for identity types.
    TAstIdentityKind = (
        ikStructural, // Pure location, no extra data (e.g. If, Block)
        ikNamed, // Identifier, MacroDef (Name + Location)
        ikConstant, // Literals (Value + Location)
        ikKeyword, // Keywords (Keyword + Location)
        ikDefinition, // Complex definitions (DefString + Location)
        ikList // Lists (Open/Close Tokens + Separator + Location)
    );

    // Base interface for the "Green Tree" nodes.
    // Holds the raw syntax data and optional source location.
    IAstIdentity = interface
        {$region 'private'}
        function GetKind: TAstIdentityKind;
        function GetLocation: ISourceLocation;
        {$endregion}

        function ToString: string;

        // Explicit casting methods (Fail-Fast)
        function AsNamed: INamedIdentity;
        function AsConstant: IConstantIdentity;
        function AsKeyword: IKeywordIdentity;
        function AsDefinition: IDefinitionIdentity;
        function AsList: IListIdentity;

        property Kind: TAstIdentityKind read GetKind;

        // Optional: Returns nil if the node has no source mapping (e.g. generated code)
        property Location: ISourceLocation read GetLocation;
    end;

    INamedIdentity = interface(IAstIdentity)
        {$region 'private'}
        function GetName: string;
        {$endregion}
        property Name: string read GetName;
    end;

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

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

    IDefinitionIdentity = interface(IAstIdentity)
        {$region 'private'}
        function GetDefinition: string;
        {$endregion}
        property Definition: string read GetDefinition;
    end;

    // Spezifische Identität für Listen
    IListIdentity = interface(IAstIdentity)
        {$region 'private'}
        function GetOpenToken: string;
        function GetCloseToken: string;
        function GetSeparator: string;
        {$endregion}
        property OpenToken: string read GetOpenToken;
        property CloseToken: string read GetCloseToken;
        property Separator: string read GetSeparator;
    end;

    // Factory and static access.
    TIdentities = record
    public
        class function Location(ALine, ACol: Integer): ISourceLocation; static;

        class function Structural(const ALoc: ISourceLocation = nil): IAstIdentity; static;
        class function Identifier(const AName: string; const ALoc: ISourceLocation = nil): INamedIdentity; static;
        class function Constant(const AValue: TDataValue; const ALoc: ISourceLocation = nil): IConstantIdentity; static;
        class function Keyword(const AValue: IKeyword; const ALoc: ISourceLocation = nil): IKeywordIdentity; static;
        class function Definition(const ADef: string; const ALoc: ISourceLocation = nil): IDefinitionIdentity; static;

        class function List(const AOpen, AClose, ASeparator: string; const ALoc: ISourceLocation = nil): IListIdentity; static;
    end;

implementation

type
    TSourceLocation = class(TInterfacedObject, ISourceLocation)
    private
        FLine, FCol: Integer;
        function GetLine: Integer;
        function GetCol: Integer;
    public
        constructor Create(ALine, ACol: Integer);
        function ToString: string; override;
    end;

    // Abstract base implementation providing the casting logic
    TAbstractIdentity = class(TInterfacedObject, IAstIdentity)
    private
        FLocation: ISourceLocation;
        function GetKind: TAstIdentityKind; virtual; abstract;
        function GetLocation: ISourceLocation;
    public
        constructor Create(const ALoc: ISourceLocation);
        function ToString: string; override;

        function AsNamed: INamedIdentity; virtual;
        function AsConstant: IConstantIdentity; virtual;
        function AsKeyword: IKeywordIdentity; virtual;
        function AsDefinition: IDefinitionIdentity; virtual;
        function AsList: IListIdentity; virtual;
    end;

    TStructuralIdentity = class(TAbstractIdentity)
    protected
        function GetKind: TAstIdentityKind; override;
    end;

    TNamedIdentity = class(TAbstractIdentity, INamedIdentity)
    private
        FName: string;
        function GetName: string;
    protected
        function GetKind: TAstIdentityKind; override;
    public
        constructor Create(const AName: string; const ALoc: ISourceLocation);
        function ToString: string; override;
        function AsNamed: INamedIdentity; override;
    end;

    TConstantIdentity = class(TAbstractIdentity, IConstantIdentity)
    private
        FValue: TDataValue;
        function GetValue: TDataValue;
    protected
        function GetKind: TAstIdentityKind; override;
    public
        constructor Create(const AValue: TDataValue; const ALoc: ISourceLocation);
        function ToString: string; override;
        function AsConstant: IConstantIdentity; override;
    end;

    TKeywordIdentity = class(TAbstractIdentity, IKeywordIdentity)
    private
        FValue: IKeyword;
        function GetValue: IKeyword;
    protected
        function GetKind: TAstIdentityKind; override;
    public
        constructor Create(const AValue: IKeyword; const ALoc: ISourceLocation);
        function ToString: string; override;
        function AsKeyword: IKeywordIdentity; override;
    end;

    TDefinitionIdentity = class(TAbstractIdentity, IDefinitionIdentity)
    private
        FDefinition: string;
        function GetDefinition: string;
    protected
        function GetKind: TAstIdentityKind; override;
    public
        constructor Create(const ADef: string; const ALoc: ISourceLocation);
        function ToString: string; override;
        function AsDefinition: IDefinitionIdentity; override;
    end;

    TListIdentity = class(TAbstractIdentity, IListIdentity)
    private
        FOpen, FClose, FSeparator: string;
        function GetOpenToken: string;
        function GetCloseToken: string;
        function GetSeparator: string;
    protected
        function GetKind: TAstIdentityKind; override;
    public
        constructor Create(const AOpen, AClose, ASeparator: string; const ALoc: ISourceLocation);
        function ToString: string; override;
        function AsList: IListIdentity; override;
    end;

{ TSourceLocation }

constructor TSourceLocation.Create(ALine, ACol: Integer);
begin
    inherited Create;
    FLine := ALine;
    FCol := ACol;
end;

function TSourceLocation.GetCol: Integer;
begin
    Result := FCol;
end;

function TSourceLocation.GetLine: Integer;
begin
    Result := FLine;
end;

function TSourceLocation.ToString: string;
begin
    Result := Format('Line %d, Col %d', [FLine, FCol]);
end;

{ TAbstractIdentity }

constructor TAbstractIdentity.Create(const ALoc: ISourceLocation);
begin
    inherited Create;
    FLocation := ALoc;
end;

function TAbstractIdentity.GetLocation: ISourceLocation;
begin
    Result := FLocation;
end;

function TAbstractIdentity.ToString: string;
begin
    if Assigned(FLocation) then
        Result := FLocation.ToString
    else
        Result := '<unknown location>';
end;

function TAbstractIdentity.AsNamed: INamedIdentity;
begin
    raise EInvalidCast.Create('Identity is not a Named Identity');
end;

function TAbstractIdentity.AsConstant: IConstantIdentity;
begin
    raise EInvalidCast.Create('Identity is not a Constant Identity');
end;

function TAbstractIdentity.AsKeyword: IKeywordIdentity;
begin
    raise EInvalidCast.Create('Identity is not a Keyword Identity');
end;

function TAbstractIdentity.AsDefinition: IDefinitionIdentity;
begin
    raise EInvalidCast.Create('Identity is not a Definition Identity');
end;

function TAbstractIdentity.AsList: IListIdentity;
begin
    raise EInvalidCast.Create('Identity is not a List Identity');
end;

{ TStructuralIdentity }

function TStructuralIdentity.GetKind: TAstIdentityKind;
begin
    Result := ikStructural;
end;

{ TNamedIdentity }

constructor TNamedIdentity.Create(const AName: string; const ALoc: ISourceLocation);
begin
    inherited Create(ALoc);
    FName := AName;
end;

function TNamedIdentity.GetKind: TAstIdentityKind;
begin
    Result := ikNamed;
end;

function TNamedIdentity.GetName: string;
begin
    Result := FName;
end;

function TNamedIdentity.AsNamed: INamedIdentity;
begin
    Result := Self;
end;

function TNamedIdentity.ToString: string;
begin
    Result := Format('Name: "%s" (%s)', [FName, inherited ToString]);
end;

{ TConstantIdentity }

constructor TConstantIdentity.Create(const AValue: TDataValue; const ALoc: ISourceLocation);
begin
    inherited Create(ALoc);
    FValue := AValue;
end;

function TConstantIdentity.GetKind: TAstIdentityKind;
begin
    Result := ikConstant;
end;

function TConstantIdentity.GetValue: TDataValue;
begin
    Result := FValue;
end;

function TConstantIdentity.AsConstant: IConstantIdentity;
begin
    Result := Self;
end;

function TConstantIdentity.ToString: string;
begin
    Result := Format('Const: %s (%s)', [FValue.ToString, inherited ToString]);
end;

{ TKeywordIdentity }

constructor TKeywordIdentity.Create(const AValue: IKeyword; const ALoc: ISourceLocation);
begin
    inherited Create(ALoc);
    FValue := AValue;
end;

function TKeywordIdentity.GetKind: TAstIdentityKind;
begin
    Result := ikKeyword;
end;

function TKeywordIdentity.GetValue: IKeyword;
begin
    Result := FValue;
end;

function TKeywordIdentity.AsKeyword: IKeywordIdentity;
begin
    Result := Self;
end;

function TKeywordIdentity.ToString: string;
begin
    Result := Format('Keyword: :%s (%s)', [FValue.Name, inherited ToString]);
end;

{ TDefinitionIdentity }

constructor TDefinitionIdentity.Create(const ADef: string; const ALoc: ISourceLocation);
begin
    inherited Create(ALoc);
    FDefinition := ADef;
end;

function TDefinitionIdentity.GetKind: TAstIdentityKind;
begin
    Result := ikDefinition;
end;

function TDefinitionIdentity.GetDefinition: string;
begin
    Result := FDefinition;
end;

function TDefinitionIdentity.AsDefinition: IDefinitionIdentity;
begin
    Result := Self;
end;

function TDefinitionIdentity.ToString: string;
begin
    Result := Format('Def: "%s" (%s)', [FDefinition, inherited ToString]);
end;

{ TListIdentity }

constructor TListIdentity.Create(const AOpen, AClose, ASeparator: string; const ALoc: ISourceLocation);
begin
    inherited Create(ALoc);
    FOpen := AOpen;
    FClose := AClose;
    FSeparator := ASeparator;
end;

function TListIdentity.GetKind: TAstIdentityKind;
begin
    Result := ikList;
end;

function TListIdentity.GetOpenToken: string;
begin
    Result := FOpen;
end;

function TListIdentity.GetCloseToken: string;
begin
    Result := FClose;
end;

function TListIdentity.GetSeparator: string;
begin
    Result := FSeparator;
end;

function TListIdentity.AsList: IListIdentity;
begin
    Result := Self;
end;

function TListIdentity.ToString: string;
begin
    Result := Format('List: "%s...%s" (Sep: "%s") (%s)', [FOpen, FClose, FSeparator, inherited ToString]);
end;

{ TIdentities }

class function TIdentities.Location(ALine, ACol: Integer): ISourceLocation;
begin
    Result := TSourceLocation.Create(ALine, ACol);
end;

class function TIdentities.Structural(const ALoc: ISourceLocation): IAstIdentity;
begin
    Result := TStructuralIdentity.Create(ALoc);
end;

class function TIdentities.Identifier(const AName: string; const ALoc: ISourceLocation): INamedIdentity;
begin
    Result := TNamedIdentity.Create(AName, ALoc);
end;

class function TIdentities.Constant(const AValue: TDataValue; const ALoc: ISourceLocation): IConstantIdentity;
begin
    Result := TConstantIdentity.Create(AValue, ALoc);
end;

class function TIdentities.Keyword(const AValue: IKeyword; const ALoc: ISourceLocation): IKeywordIdentity;
begin
    Result := TKeywordIdentity.Create(AValue, ALoc);
end;

class function TIdentities.Definition(const ADef: string; const ALoc: ISourceLocation): IDefinitionIdentity;
begin
    Result := TDefinitionIdentity.Create(ADef, ALoc);
end;

class function TIdentities.List(const AOpen, AClose, ASeparator: string; const ALoc: ISourceLocation): IListIdentity;
begin
    Result := TListIdentity.Create(AOpen, AClose, ASeparator, ALoc);
end;

end.
