From 769497887f8c71fdac7ee6da16963c4a9faed636 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Tue, 26 Aug 2025 14:39:05 +0200 Subject: [PATCH] New type system --- Src/Data/Myc.Data.Types.Records.pas | 26 +- Src/Data/Myc.Data.Types.pas | 831 +++++++++++++++++++++++----- Src/Data/TestDataTypes.pas | 216 ++++---- 3 files changed, 813 insertions(+), 260 deletions(-) diff --git a/Src/Data/Myc.Data.Types.Records.pas b/Src/Data/Myc.Data.Types.Records.pas index 2d902ca..0832074 100644 --- a/Src/Data/Myc.Data.Types.Records.pas +++ b/Src/Data/Myc.Data.Types.Records.pas @@ -11,25 +11,25 @@ uses type // Custom comparer for TArray to be used as a dictionary key. - TRecordFieldComparer = class(TInterfacedObject, IEqualityComparer>) + TDataRecordFieldComparer = class(TInterfacedObject, IEqualityComparer>) public - function Equals(const Left, Right: TArray): Boolean; reintroduce; - function GetHashCode(const Value: TArray): Integer; reintroduce; + function Equals(const Left, Right: TArray): Boolean; reintroduce; + function GetHashCode(const Value: TArray): Integer; reintroduce; end; // Implements the record data type. TImplDataRecordType = class(TInterfacedObject, IDataType, IDataRecordType) private - FFields: TArray; + FFields: TArray; FFieldMap: TDictionary; // For fast lookups by name FEmptyValue: IDataRecordValue; // Cached instance for zero-field records function GetName: String; function GetKind: TDataKind; function GetFieldCount: Integer; - function GetField(Idx: Integer): TRecordField; + function GetField(Idx: Integer): TDataRecordField; function IndexOf(const AName: string): Integer; public - constructor Create(const AFields: array of TRecordField); + constructor Create(const AFields: array of TDataRecordField); destructor Destroy; override; function CreateValue(const AItems: array of IDataValue): IDataRecordValue; end; @@ -96,9 +96,9 @@ type constructor Create(const ADataType: IDataRecordType; const AItems: array of IDataValue); end; -{ TRecordFieldComparer } +{ TDataRecordFieldComparer } -function TRecordFieldComparer.Equals(const Left, Right: TArray): Boolean; +function TDataRecordFieldComparer.Equals(const Left, Right: TArray): Boolean; var i: Integer; begin @@ -113,9 +113,9 @@ begin Result := True; end; -function TRecordFieldComparer.GetHashCode(const Value: TArray): Integer; +function TDataRecordFieldComparer.GetHashCode(const Value: TArray): Integer; var - field: TRecordField; + field: TDataRecordField; begin Result := 0; for field in Value do @@ -319,10 +319,10 @@ end; { TImplDataRecordType } -constructor TImplDataRecordType.Create(const AFields: array of TRecordField); +constructor TImplDataRecordType.Create(const AFields: array of TDataRecordField); var i: Integer; - field: TRecordField; + field: TDataRecordField; begin inherited Create; FFieldMap := TDictionary.Create; @@ -372,7 +372,7 @@ begin end; end; -function TImplDataRecordType.GetField(Idx: Integer): TRecordField; +function TImplDataRecordType.GetField(Idx: Integer): TDataRecordField; begin Result := FFields[Idx]; end; diff --git a/Src/Data/Myc.Data.Types.pas b/Src/Data/Myc.Data.Types.pas index 6a207bd..7383d8a 100644 --- a/Src/Data/Myc.Data.Types.pas +++ b/Src/Data/Myc.Data.Types.pas @@ -145,7 +145,7 @@ type property Items[Idx: Integer]: IDataValue read GetItem; default; end; - TRecordField = record + TDataRecordField = record DataType: IDataType; Name: string; constructor Create(const AName: string; const ADataType: IDataType); @@ -154,12 +154,12 @@ type IDataRecordType = interface(IDataType) {$region 'private'} function GetFieldCount: Integer; - function GetField(Idx: Integer): TRecordField; + function GetField(Idx: Integer): TDataRecordField; {$endregion} function IndexOf(const AName: string): Integer; function CreateValue(const AItems: array of IDataValue): IDataRecordValue; property FieldCount: Integer read GetFieldCount; - property Fields[Idx: Integer]: TRecordField read GetField; default; + property Fields[Idx: Integer]: TDataRecordField read GetField; default; end; IDataTupleValue = interface(IDataValue) @@ -234,72 +234,142 @@ type end; TVoid = record + type + TValue = record + private + FVoidValue: IDataVoidValue; + function GetDataType: TDataType.TVoid; + public + constructor Create(const AVoidValue: IDataVoidValue); + class operator Implicit(const A: IDataVoidValue): TDataType.TVoid.TValue; overload; inline; + class operator Implicit(const A: TDataType.TVoid.TValue): IDataVoidValue; overload; inline; + class operator Implicit(const A: TDataType.TVoid.TValue): TDataType.TValue; overload; inline; + property DataType: TDataType.TVoid read GetDataType; + end; private FVoidType: IDataVoidType; - function GetValue: IDataVoidValue; inline; + function GetValue: TDataType.TVoid.TValue; inline; public constructor Create(const AVoidType: IDataVoidType); class operator Implicit(const A: IDataVoidType): TDataType.TVoid; overload; inline; class operator Implicit(const A: TDataType.TVoid): IDataVoidType; overload; inline; - property Value: IDataVoidValue read GetValue; + property Value: TDataType.TVoid.TValue read GetValue; end; TOrdinal = record + type + TValue = record + private + FOrdinalValue: IDataOrdinalValue; + function GetDataType: TDataType.TOrdinal; + function GetValue: Int64; inline; + public + constructor Create(const AOrdinalValue: IDataOrdinalValue); + class operator Implicit(const A: IDataOrdinalValue): TDataType.TOrdinal.TValue; overload; inline; + class operator Implicit(const A: TDataType.TOrdinal.TValue): IDataOrdinalValue; overload; inline; + class operator Implicit(const A: TDataType.TOrdinal.TValue): TDataType.TValue; overload; inline; + property Value: Int64 read GetValue; + property DataType: TDataType.TOrdinal read GetDataType; + end; private FOrdinalType: IDataOrdinalType; public constructor Create(const AOrdinalType: IDataOrdinalType); class operator Implicit(const A: IDataOrdinalType): TDataType.TOrdinal; overload; inline; class operator Implicit(const A: TDataType.TOrdinal): IDataOrdinalType; overload; inline; - function CreateValue(Init: Int64): IDataOrdinalValue; inline; + function CreateValue(Init: Int64): TDataType.TOrdinal.TValue; inline; end; TFloat = record + type + TValue = record + private + FFloatValue: IDataFloatValue; + function GetDataType: TDataType.TFloat; + function GetValue: Double; inline; + public + constructor Create(const AFloatValue: IDataFloatValue); + class operator Implicit(const A: IDataFloatValue): TDataType.TFloat.TValue; overload; inline; + class operator Implicit(const A: TDataType.TFloat.TValue): IDataFloatValue; overload; inline; + class operator Implicit(const A: TDataType.TFloat.TValue): TDataType.TValue; overload; inline; + property Value: Double read GetValue; + property DataType: TDataType.TFloat read GetDataType; + end; private FFloatType: IDataFloatType; public constructor Create(const AFloatType: IDataFloatType); class operator Implicit(const A: IDataFloatType): TDataType.TFloat; overload; inline; class operator Implicit(const A: TDataType.TFloat): IDataFloatType; overload; inline; - function CreateValue(Init: Double): IDataFloatValue; inline; + function CreateValue(Init: Double): TDataType.TFloat.TValue; inline; end; TText = record + type + TValue = record + private + FTextValue: IDataTextValue; + function GetDataType: TDataType.TText; + function GetValue: string; inline; + public + constructor Create(const ATextValue: IDataTextValue); + class operator Implicit(const A: IDataTextValue): TDataType.TText.TValue; overload; inline; + class operator Implicit(const A: TDataType.TText.TValue): IDataTextValue; overload; inline; + class operator Implicit(const A: TDataType.TText.TValue): TDataType.TValue; overload; inline; + property Value: string read GetValue; + property DataType: TDataType.TText read GetDataType; + end; private FTextType: IDataTextType; public constructor Create(const ATextType: IDataTextType); class operator Implicit(const A: IDataTextType): TDataType.TText; overload; inline; class operator Implicit(const A: TDataType.TText): IDataTextType; overload; inline; - function CreateValue(const AValue: string): IDataTextValue; inline; + function CreateValue(const AValue: string): TDataType.TText.TValue; inline; end; TTimestamp = record + type + TValue = record + private + FTimestampValue: IDataTimestampValue; + function GetDataType: TDataType.TTimestamp; + function GetValue: TDateTime; inline; + public + constructor Create(const ATimestampValue: IDataTimestampValue); + class operator Implicit(const A: IDataTimestampValue): TDataType.TTimestamp.TValue; overload; inline; + class operator Implicit(const A: TDataType.TTimestamp.TValue): IDataTimestampValue; overload; inline; + class operator Implicit(const A: TDataType.TTimestamp.TValue): TDataType.TValue; overload; inline; + property Value: TDateTime read GetValue; + property DataType: TDataType.TTimestamp read GetDataType; + end; private FTimestampType: IDataTimestampType; public constructor Create(const ATimestampType: IDataTimestampType); class operator Implicit(const A: IDataTimestampType): TDataType.TTimestamp; overload; inline; class operator Implicit(const A: TDataType.TTimestamp): IDataTimestampType; overload; inline; - function CreateValue(const AValue: TDateTime): IDataTimestampValue; inline; + function CreateValue(const AValue: TDateTime): TDataType.TTimestamp.TValue; inline; end; TDecimal = record type TValue = record private - // The wrapped interface instance. FDecimalValue: IDataDecimalValue; + function GetDataType: TDataType.TDecimal; + function GetScale: Integer; inline; function GetValue: Int64; inline; public constructor Create(const ADecimalValue: IDataDecimalValue); - class operator Implicit(const A: IDataDecimalValue): TDataType.TDecimal.TValue; overload; inline; class operator Implicit(const A: TDataType.TDecimal.TValue): IDataDecimalValue; overload; inline; - + class operator Implicit(const A: TDataType.TDecimal.TValue): TDataType.TValue; overload; inline; + function ToDouble: Double; property Value: Int64 read GetValue; + property Scale: Integer read GetScale; + property DataType: TDataType.TDecimal read GetDataType; end; - private FDecimalType: IDataDecimalType; function GetScale: Integer; inline; @@ -312,6 +382,20 @@ type end; TEnum = record + type + TValue = record + private + FEnumValue: IDataEnumValue; + function GetDataType: TDataType.TEnum; + function GetValue: Integer; inline; + public + constructor Create(const AEnumValue: IDataEnumValue); + class operator Implicit(const A: IDataEnumValue): TDataType.TEnum.TValue; overload; inline; + class operator Implicit(const A: TDataType.TEnum.TValue): IDataEnumValue; overload; inline; + class operator Implicit(const A: TDataType.TEnum.TValue): TDataType.TValue; overload; inline; + property Value: Integer read GetValue; + property DataType: TDataType.TEnum read GetDataType; + end; private FEnumType: IDataEnumType; function GetIdentifier(Idx: Integer): string; inline; @@ -321,38 +405,84 @@ type class operator Implicit(const A: IDataEnumType): TDataType.TEnum; overload; inline; class operator Implicit(const A: TDataType.TEnum): IDataEnumType; overload; inline; function IndexOf(const AIdentifier: string): Integer; inline; - function CreateValue(const AValue: Integer): IDataEnumValue; overload; inline; - function CreateValue(const AIdentifier: string): IDataEnumValue; overload; inline; + function CreateValue(const AValue: Integer): TDataType.TEnum.TValue; overload; inline; + function CreateValue(const AIdentifier: string): TDataType.TEnum.TValue; overload; inline; property IdentifierCount: Integer read GetIdentifierCount; property Identifiers[Idx: Integer]: string read GetIdentifier; default; end; TRecord = record + type + TValue = record + private + FRecordValue: IDataRecordValue; + function GetDataType: TDataType.TRecord; + function GetItems(Idx: Integer): TDataType.TValue; inline; + public + constructor Create(const ARecordValue: IDataRecordValue); + class operator Implicit(const A: IDataRecordValue): TDataType.TRecord.TValue; overload; inline; + class operator Implicit(const A: TDataType.TRecord.TValue): IDataRecordValue; overload; inline; + class operator Implicit(const A: TDataType.TRecord.TValue): TDataType.TValue; overload; inline; + property Items[Idx: Integer]: TDataType.TValue read GetItems; default; + property DataType: TDataType.TRecord read GetDataType; + end; private FRecordType: IDataRecordType; function GetFieldCount: Integer; inline; - function GetField(Idx: Integer): TRecordField; inline; + function GetField(Idx: Integer): TDataRecordField; inline; public constructor Create(const ARecordType: IDataRecordType); class operator Implicit(const A: IDataRecordType): TDataType.TRecord; overload; inline; class operator Implicit(const A: TDataType.TRecord): IDataRecordType; overload; inline; function IndexOf(const AName: string): Integer; inline; - function CreateValue(const AItems: array of IDataValue): IDataRecordValue; + function CreateValue(const AItems: array of IDataValue): TDataType.TRecord.TValue; property FieldCount: Integer read GetFieldCount; - property Fields[Idx: Integer]: TRecordField read GetField; default; + property Fields[Idx: Integer]: TDataRecordField read GetField; default; end; TTuple = record + type + TValue = record + private + FTupleValue: IDataTupleValue; + function GetDataType: TDataType.TTuple; + function GetItems(Idx: Integer): TDataType.TValue; inline; + function GetItemCount: Integer; inline; + public + constructor Create(const ATupleValue: IDataTupleValue); + class operator Implicit(const A: IDataTupleValue): TDataType.TTuple.TValue; overload; inline; + class operator Implicit(const A: TDataType.TTuple.TValue): IDataTupleValue; overload; inline; + class operator Implicit(const A: TDataType.TTuple.TValue): TDataType.TValue; overload; inline; + property ItemCount: Integer read GetItemCount; + property Items[Idx: Integer]: TDataType.TValue read GetItems; default; + property DataType: TDataType.TTuple read GetDataType; + end; private FTupleType: IDataTupleType; public constructor Create(const ATupleType: IDataTupleType); class operator Implicit(const A: IDataTupleType): TDataType.TTuple; overload; inline; class operator Implicit(const A: TDataType.TTuple): IDataTupleType; overload; inline; - function CreateValue(const AItems: array of IDataValue): IDataTupleValue; + function CreateValue(const AItems: array of IDataValue): TDataType.TTuple.TValue; end; TArray = record + type + TValue = record + private + FArrayValue: IDataArrayValue; + function GetDataType: TDataType.TArray; + function GetItems(Idx: Integer): TDataType.TValue; inline; + function GetElementCount: Integer; inline; + public + constructor Create(const AArrayValue: IDataArrayValue); + class operator Implicit(const A: IDataArrayValue): TDataType.TArray.TValue; overload; inline; + class operator Implicit(const A: TDataType.TArray.TValue): IDataArrayValue; overload; inline; + class operator Implicit(const A: TDataType.TArray.TValue): TDataType.TValue; overload; inline; + property ElementCount: Integer read GetElementCount; + property Items[Idx: Integer]: TDataType.TValue read GetItems; default; + property DataType: TDataType.TArray read GetDataType; + end; private FArrayType: IDataArrayType; function GetElementType: IDataType; inline; @@ -360,13 +490,26 @@ type constructor Create(const AArrayType: IDataArrayType); class operator Implicit(const A: IDataArrayType): TDataType.TArray; overload; inline; class operator Implicit(const A: TDataType.TArray): IDataArrayType; overload; inline; - function CreateValue(const AItems: array of IDataValue): IDataArrayValue; + function CreateValue(const AItems: array of IDataValue): TDataType.TArray.TValue; property ElementType: IDataType read GetElementType; end; TMethod = record type TProc = reference to function(const AValue: TDataType.TValue): TDataType.TValue; + TValue = record + private + FMethodValue: IDataMethodValue; + function GetDataType: TDataType.TMethod; + function GetValue: TDataMethodProc; inline; + public + constructor Create(const AMethodValue: IDataMethodValue); + class operator Implicit(const A: IDataMethodValue): TDataType.TMethod.TValue; overload; inline; + class operator Implicit(const A: TDataType.TMethod.TValue): IDataMethodValue; overload; inline; + class operator Implicit(const A: TDataType.TMethod.TValue): TDataType.TValue; overload; inline; + property Value: TDataMethodProc read GetValue; + property DataType: TDataType.TMethod read GetDataType; + end; private FMethodType: IDataMethodType; function GetArgType: IDataType; inline; @@ -377,7 +520,7 @@ type class operator Implicit(const A: TDataType.TMethod): IDataMethodType; overload; inline; property ArgType: IDataType read GetArgType; property ResultType: IDataType read GetResultType; - function CreateValue(const Proc: TProc): IDataMethodValue; inline; + function CreateValue(const Proc: TProc): TDataType.TMethod.TValue; inline; end; strict private @@ -386,7 +529,7 @@ type function GetKind: TDataKind; inline; class var FArrayTypeRegistry: TDictionary; - FRecordTypeRegistry: TDictionary, IDataRecordType>; + FRecordTypeRegistry: TDictionary, IDataRecordType>; FMethodTypeRegistry: TDictionary, IDataMethodType>; FDecimalTypes: TArray; class constructor CreateClass; @@ -418,8 +561,8 @@ type class function Tuple: TDataType.TTuple; static; inline; class function MethodOf(const AArgType, AResultType: IDataType): TDataType.TMethod; static; class function ArrayOf(const AElementType: IDataType): TDataType.TArray; static; - class function RecordOf(const Fields: TArray): TDataType.TRecord; overload; static; - class function RecordOf(const Fields: array of TRecordField): TDataType.TRecord; overload; static; + class function RecordOf(const Fields: TArray): TDataType.TRecord; overload; static; + class function RecordOf(const Fields: array of TDataRecordField): TDataType.TRecord; overload; static; class function EnumOf(const AName: string; const AIdentifiers: array of string): TDataType.TEnum; static; function AsOrdinal: TDataType.TOrdinal; inline; @@ -441,6 +584,7 @@ type implementation uses + System.Math, // Added for Power function System.SyncObjs, System.Generics.Defaults, Myc.Data.Types.Void, @@ -455,9 +599,47 @@ uses Myc.Data.Types.Enum, Myc.Data.Types.Method; -{ TRecordField } +type + TDataRecordFieldComparer = class(TEqualityComparer>) + public + function Equals(const Left, Right: TArray): Boolean; override; + function GetHashCode(const Value: TArray): Integer; override; + end; -constructor TRecordField.Create(const AName: string; const ADataType: IDataType); +{ TDataRecordFieldComparer } + +function TDataRecordFieldComparer.Equals(const Left, Right: TArray): Boolean; +var + i: Integer; +begin + if Length(Left) <> Length(Right) then + Exit(False); + + for i := 0 to High(Left) do + begin + // Compare field names (case-insensitive) and data types (pointer comparison) + if (not SameText(Left[i].Name, Right[i].Name)) or (Left[i].DataType <> Right[i].DataType) then + Exit(False); + end; + + Result := True; +end; + +function TDataRecordFieldComparer.GetHashCode(const Value: TArray): Integer; +var + field: TDataRecordField; +begin + Result := 0; + for field in Value do + begin + // Combine hash codes of field name (case-insensitive) and data type pointer + Result := Result xor TEqualityComparer.Default.GetHashCode(field.Name.ToUpper) xor Integer(NativeInt(field.DataType)); + end; +end; + +{ TDataRecordField } + +constructor TDataRecordField.Create(const AName: string; const ADataType: IDataType); begin Name := AName; DataType := ADataType; @@ -470,11 +652,32 @@ begin FDecimalValue := ADecimalValue; end; +function TDataType.TDecimal.TValue.GetDataType: TDataType.TDecimal; +begin + Result := IDataDecimalType(FDecimalValue.DataType); +end; + +function TDataType.TDecimal.TValue.GetScale: Integer; +begin + Result := GetDataType.Scale; +end; + function TDataType.TDecimal.TValue.GetValue: Int64; begin Result := FDecimalValue.Value; end; +function TDataType.TDecimal.TValue.ToDouble: Double; +var + scaleValue: Integer; +begin + scaleValue := GetDataType.Scale; + if scaleValue > 0 then + Result := FDecimalValue.Value / Power(10, scaleValue) + else + Result := FDecimalValue.Value; +end; + class operator TDataType.TDecimal.TValue.Implicit(const A: IDataDecimalValue): TDataType.TDecimal.TValue; begin Result.FDecimalValue := A; @@ -485,114 +688,9 @@ begin Result := A.FDecimalValue; end; -{ TDataType.TVoid } - -constructor TDataType.TVoid.Create(const AVoidType: IDataVoidType); +class operator TDataType.TDecimal.TValue.Implicit(const A: TDataType.TDecimal.TValue): TDataType.TValue; begin - FVoidType := AVoidType; -end; - -function TDataType.TVoid.GetValue: IDataVoidValue; -begin - Result := FVoidType.Value; -end; - -class operator TDataType.TVoid.Implicit(const A: IDataVoidType): TDataType.TVoid; -begin - Result.FVoidType := A; -end; - -class operator TDataType.TVoid.Implicit(const A: TDataType.TVoid): IDataVoidType; -begin - Result := A.FVoidType; -end; - -{ TDataType.TOrdinal } - -constructor TDataType.TOrdinal.Create(const AOrdinalType: IDataOrdinalType); -begin - FOrdinalType := AOrdinalType; -end; - -function TDataType.TOrdinal.CreateValue(Init: Int64): IDataOrdinalValue; -begin - Result := FOrdinalType.CreateValue(Init); -end; - -class operator TDataType.TOrdinal.Implicit(const A: TDataType.TOrdinal): IDataOrdinalType; -begin - Result := A.FOrdinalType; -end; - -class operator TDataType.TOrdinal.Implicit(const A: IDataOrdinalType): TDataType.TOrdinal; -begin - Result.FOrdinalType := A; -end; - -{ TDataType.TFloat } - -constructor TDataType.TFloat.Create(const AFloatType: IDataFloatType); -begin - FFloatType := AFloatType; -end; - -function TDataType.TFloat.CreateValue(Init: Double): IDataFloatValue; -begin - Result := FFloatType.CreateValue(Init); -end; - -class operator TDataType.TFloat.Implicit(const A: IDataFloatType): TDataType.TFloat; -begin - Result.FFloatType := A; -end; - -class operator TDataType.TFloat.Implicit(const A: TDataType.TFloat): IDataFloatType; -begin - Result := A.FFloatType; -end; - -{ TDataType.TText } - -constructor TDataType.TText.Create(const ATextType: IDataTextType); -begin - FTextType := ATextType; -end; - -function TDataType.TText.CreateValue(const AValue: string): IDataTextValue; -begin - Result := FTextType.CreateValue(AValue); -end; - -class operator TDataType.TText.Implicit(const A: IDataTextType): TDataType.TText; -begin - Result.FTextType := A; -end; - -class operator TDataType.TText.Implicit(const A: TDataType.TText): IDataTextType; -begin - Result := A.FTextType; -end; - -{ TDataType.TTimestamp } - -constructor TDataType.TTimestamp.Create(const ATimestampType: IDataTimestampType); -begin - FTimestampType := ATimestampType; -end; - -function TDataType.TTimestamp.CreateValue(const AValue: TDateTime): IDataTimestampValue; -begin - Result := FTimestampType.CreateValue(AValue); -end; - -class operator TDataType.TTimestamp.Implicit(const A: IDataTimestampType): TDataType.TTimestamp; -begin - Result.FTimestampType := A; -end; - -class operator TDataType.TTimestamp.Implicit(const A: TDataType.TTimestamp): IDataTimestampType; -begin - Result := A.FTimestampType; + Result.Create(A.FDecimalValue); end; { TDataType.TDecimal } @@ -622,6 +720,303 @@ begin Result := A.FDecimalType; end; +{ TDataType.TVoid.TValue } + +constructor TDataType.TVoid.TValue.Create(const AVoidValue: IDataVoidValue); +begin + FVoidValue := AVoidValue; +end; + +function TDataType.TVoid.TValue.GetDataType: TDataType.TVoid; +begin + Result := IDataVoidType(FVoidValue.DataType); +end; + +class operator TDataType.TVoid.TValue.Implicit(const A: IDataVoidValue): TDataType.TVoid.TValue; +begin + Result.FVoidValue := A; +end; + +class operator TDataType.TVoid.TValue.Implicit(const A: TDataType.TVoid.TValue): IDataVoidValue; +begin + Result := A.FVoidValue; +end; + +class operator TDataType.TVoid.TValue.Implicit(const A: TDataType.TVoid.TValue): TDataType.TValue; +begin + Result.Create(A.FVoidValue); +end; + +{ TDataType.TVoid } + +constructor TDataType.TVoid.Create(const AVoidType: IDataVoidType); +begin + FVoidType := AVoidType; +end; + +function TDataType.TVoid.GetValue: TDataType.TVoid.TValue; +begin + Result.Create(FVoidType.Value); +end; + +class operator TDataType.TVoid.Implicit(const A: IDataVoidType): TDataType.TVoid; +begin + Result.FVoidType := A; +end; + +class operator TDataType.TVoid.Implicit(const A: TDataType.TVoid): IDataVoidType; +begin + Result := A.FVoidType; +end; + +{ TDataType.TOrdinal.TValue } + +constructor TDataType.TOrdinal.TValue.Create(const AOrdinalValue: IDataOrdinalValue); +begin + FOrdinalValue := AOrdinalValue; +end; + +function TDataType.TOrdinal.TValue.GetDataType: TDataType.TOrdinal; +begin + Result := IDataOrdinalType(FOrdinalValue.DataType); +end; + +function TDataType.TOrdinal.TValue.GetValue: Int64; +begin + Result := FOrdinalValue.Value; +end; + +class operator TDataType.TOrdinal.TValue.Implicit(const A: IDataOrdinalValue): TDataType.TOrdinal.TValue; +begin + Result.FOrdinalValue := A; +end; + +class operator TDataType.TOrdinal.TValue.Implicit(const A: TDataType.TOrdinal.TValue): IDataOrdinalValue; +begin + Result := A.FOrdinalValue; +end; + +class operator TDataType.TOrdinal.TValue.Implicit(const A: TDataType.TOrdinal.TValue): TDataType.TValue; +begin + Result.Create(A.FOrdinalValue); +end; + +{ TDataType.TOrdinal } + +constructor TDataType.TOrdinal.Create(const AOrdinalType: IDataOrdinalType); +begin + FOrdinalType := AOrdinalType; +end; + +function TDataType.TOrdinal.CreateValue(Init: Int64): TDataType.TOrdinal.TValue; +begin + Result.Create(FOrdinalType.CreateValue(Init)); +end; + +class operator TDataType.TOrdinal.Implicit(const A: TDataType.TOrdinal): IDataOrdinalType; +begin + Result := A.FOrdinalType; +end; + +class operator TDataType.TOrdinal.Implicit(const A: IDataOrdinalType): TDataType.TOrdinal; +begin + Result.FOrdinalType := A; +end; + +{ TDataType.TFloat.TValue } + +constructor TDataType.TFloat.TValue.Create(const AFloatValue: IDataFloatValue); +begin + FFloatValue := AFloatValue; +end; + +function TDataType.TFloat.TValue.GetDataType: TDataType.TFloat; +begin + Result := IDataFloatType(FFloatValue.DataType); +end; + +function TDataType.TFloat.TValue.GetValue: Double; +begin + Result := FFloatValue.Value; +end; + +class operator TDataType.TFloat.TValue.Implicit(const A: IDataFloatValue): TDataType.TFloat.TValue; +begin + Result.FFloatValue := A; +end; + +class operator TDataType.TFloat.TValue.Implicit(const A: TDataType.TFloat.TValue): IDataFloatValue; +begin + Result := A.FFloatValue; +end; + +class operator TDataType.TFloat.TValue.Implicit(const A: TDataType.TFloat.TValue): TDataType.TValue; +begin + Result.Create(A.FFloatValue); +end; + +{ TDataType.TFloat } + +constructor TDataType.TFloat.Create(const AFloatType: IDataFloatType); +begin + FFloatType := AFloatType; +end; + +function TDataType.TFloat.CreateValue(Init: Double): TDataType.TFloat.TValue; +begin + Result.Create(FFloatType.CreateValue(Init)); +end; + +class operator TDataType.TFloat.Implicit(const A: IDataFloatType): TDataType.TFloat; +begin + Result.FFloatType := A; +end; + +class operator TDataType.TFloat.Implicit(const A: TDataType.TFloat): IDataFloatType; +begin + Result := A.FFloatType; +end; + +{ TDataType.TText.TValue } + +constructor TDataType.TText.TValue.Create(const ATextValue: IDataTextValue); +begin + FTextValue := ATextValue; +end; + +function TDataType.TText.TValue.GetDataType: TDataType.TText; +begin + Result := IDataTextType(FTextValue.DataType); +end; + +function TDataType.TText.TValue.GetValue: string; +begin + Result := FTextValue.Value; +end; + +class operator TDataType.TText.TValue.Implicit(const A: IDataTextValue): TDataType.TText.TValue; +begin + Result.FTextValue := A; +end; + +class operator TDataType.TText.TValue.Implicit(const A: TDataType.TText.TValue): IDataTextValue; +begin + Result := A.FTextValue; +end; + +class operator TDataType.TText.TValue.Implicit(const A: TDataType.TText.TValue): TDataType.TValue; +begin + Result.Create(A.FTextValue); +end; + +{ TDataType.TText } + +constructor TDataType.TText.Create(const ATextType: IDataTextType); +begin + FTextType := ATextType; +end; + +function TDataType.TText.CreateValue(const AValue: string): TDataType.TText.TValue; +begin + Result.Create(FTextType.CreateValue(AValue)); +end; + +class operator TDataType.TText.Implicit(const A: IDataTextType): TDataType.TText; +begin + Result.FTextType := A; +end; + +class operator TDataType.TText.Implicit(const A: TDataType.TText): IDataTextType; +begin + Result := A.FTextType; +end; + +{ TDataType.TTimestamp.TValue } + +constructor TDataType.TTimestamp.TValue.Create(const ATimestampValue: IDataTimestampValue); +begin + FTimestampValue := ATimestampValue; +end; + +function TDataType.TTimestamp.TValue.GetDataType: TDataType.TTimestamp; +begin + Result := IDataTimestampType(FTimestampValue.DataType); +end; + +function TDataType.TTimestamp.TValue.GetValue: TDateTime; +begin + Result := FTimestampValue.Value; +end; + +class operator TDataType.TTimestamp.TValue.Implicit(const A: IDataTimestampValue): TDataType.TTimestamp.TValue; +begin + Result.FTimestampValue := A; +end; + +class operator TDataType.TTimestamp.TValue.Implicit(const A: TDataType.TTimestamp.TValue): IDataTimestampValue; +begin + Result := A.FTimestampValue; +end; + +class operator TDataType.TTimestamp.TValue.Implicit(const A: TDataType.TTimestamp.TValue): TDataType.TValue; +begin + Result.Create(A.FTimestampValue); +end; + +{ TDataType.TTimestamp } + +constructor TDataType.TTimestamp.Create(const ATimestampType: IDataTimestampType); +begin + FTimestampType := ATimestampType; +end; + +function TDataType.TTimestamp.CreateValue(const AValue: TDateTime): TDataType.TTimestamp.TValue; +begin + Result.Create(FTimestampType.CreateValue(AValue)); +end; + +class operator TDataType.TTimestamp.Implicit(const A: IDataTimestampType): TDataType.TTimestamp; +begin + Result.FTimestampType := A; +end; + +class operator TDataType.TTimestamp.Implicit(const A: TDataType.TTimestamp): IDataTimestampType; +begin + Result := A.FTimestampType; +end; + +{ TDataType.TEnum.TValue } + +constructor TDataType.TEnum.TValue.Create(const AEnumValue: IDataEnumValue); +begin + FEnumValue := AEnumValue; +end; + +function TDataType.TEnum.TValue.GetDataType: TDataType.TEnum; +begin + Result := IDataEnumType(FEnumValue.DataType); +end; + +function TDataType.TEnum.TValue.GetValue: Integer; +begin + Result := FEnumValue.Value; +end; + +class operator TDataType.TEnum.TValue.Implicit(const A: IDataEnumValue): TDataType.TEnum.TValue; +begin + Result.FEnumValue := A; +end; + +class operator TDataType.TEnum.TValue.Implicit(const A: TDataType.TEnum.TValue): IDataEnumValue; +begin + Result := A.FEnumValue; +end; + +class operator TDataType.TEnum.TValue.Implicit(const A: TDataType.TEnum.TValue): TDataType.TValue; +begin + Result.Create(A.FEnumValue); +end; + { TDataType.TEnum } constructor TDataType.TEnum.Create(const AEnumType: IDataEnumType); @@ -629,12 +1024,12 @@ begin FEnumType := AEnumType; end; -function TDataType.TEnum.CreateValue(const AValue: Integer): IDataEnumValue; +function TDataType.TEnum.CreateValue(const AValue: Integer): TDataType.TEnum.TValue; begin - Result := FEnumType.CreateValue(AValue); + Result.Create(FEnumType.CreateValue(AValue)); end; -function TDataType.TEnum.CreateValue(const AIdentifier: string): IDataEnumValue; +function TDataType.TEnum.CreateValue(const AIdentifier: string): TDataType.TEnum.TValue; var idx: Integer; begin @@ -669,6 +1064,38 @@ begin Result := A.FEnumType; end; +{ TDataType.TMethod.TValue } + +constructor TDataType.TMethod.TValue.Create(const AMethodValue: IDataMethodValue); +begin + FMethodValue := AMethodValue; +end; + +function TDataType.TMethod.TValue.GetDataType: TDataType.TMethod; +begin + Result := IDataMethodType(FMethodValue.DataType); +end; + +function TDataType.TMethod.TValue.GetValue: TDataMethodProc; +begin + Result := FMethodValue.Value; +end; + +class operator TDataType.TMethod.TValue.Implicit(const A: IDataMethodValue): TDataType.TMethod.TValue; +begin + Result.FMethodValue := A; +end; + +class operator TDataType.TMethod.TValue.Implicit(const A: TDataType.TMethod.TValue): IDataMethodValue; +begin + Result := A.FMethodValue; +end; + +class operator TDataType.TMethod.TValue.Implicit(const A: TDataType.TMethod.TValue): TDataType.TValue; +begin + Result.Create(A.FMethodValue); +end; + { TDataType.TMethod } constructor TDataType.TMethod.Create(const AMethodType: IDataMethodType); @@ -676,13 +1103,13 @@ begin FMethodType := AMethodType; end; -function TDataType.TMethod.CreateValue(const Proc: TProc): IDataMethodValue; +function TDataType.TMethod.CreateValue(const Proc: TProc): TDataType.TMethod.TValue; begin if not Assigned(Proc) then raise EArgumentException.Create('Proc'); var cProc: TProc := Proc; - Result := FMethodType.CreateValue(function(const Value: IDataValue): IDataValue begin Result := cProc(Value); end); + Result.Create(FMethodType.CreateValue(function(const Value: IDataValue): IDataValue begin Result := cProc(Value); end)); end; function TDataType.TMethod.GetArgType: IDataType; @@ -705,6 +1132,38 @@ begin Result := A.FMethodType; end; +{ TDataType.TRecord.TValue } + +constructor TDataType.TRecord.TValue.Create(const ARecordValue: IDataRecordValue); +begin + FRecordValue := ARecordValue; +end; + +function TDataType.TRecord.TValue.GetDataType: TDataType.TRecord; +begin + Result := IDataRecordType(FRecordValue.DataType); +end; + +function TDataType.TRecord.TValue.GetItems(Idx: Integer): TDataType.TValue; +begin + Result.Create(FRecordValue.Items[Idx]); +end; + +class operator TDataType.TRecord.TValue.Implicit(const A: IDataRecordValue): TDataType.TRecord.TValue; +begin + Result.FRecordValue := A; +end; + +class operator TDataType.TRecord.TValue.Implicit(const A: TDataType.TRecord.TValue): IDataRecordValue; +begin + Result := A.FRecordValue; +end; + +class operator TDataType.TRecord.TValue.Implicit(const A: TDataType.TRecord.TValue): TDataType.TValue; +begin + Result.Create(A.FRecordValue); +end; + { TDataType.TRecord } constructor TDataType.TRecord.Create(const ARecordType: IDataRecordType); @@ -712,12 +1171,12 @@ begin FRecordType := ARecordType; end; -function TDataType.TRecord.CreateValue(const AItems: array of IDataValue): IDataRecordValue; +function TDataType.TRecord.CreateValue(const AItems: array of IDataValue): TDataType.TRecord.TValue; begin - Result := FRecordType.CreateValue(AItems); + Result.Create(FRecordType.CreateValue(AItems)); end; -function TDataType.TRecord.GetField(Idx: Integer): TRecordField; +function TDataType.TRecord.GetField(Idx: Integer): TDataRecordField; begin Result := FRecordType.GetField(Idx); end; @@ -742,6 +1201,43 @@ begin Result := A.FRecordType; end; +{ TDataType.TTuple.TValue } + +constructor TDataType.TTuple.TValue.Create(const ATupleValue: IDataTupleValue); +begin + FTupleValue := ATupleValue; +end; + +function TDataType.TTuple.TValue.GetDataType: TDataType.TTuple; +begin + Result := IDataTupleType(FTupleValue.DataType); +end; + +function TDataType.TTuple.TValue.GetItems(Idx: Integer): TDataType.TValue; +begin + Result.Create(FTupleValue.Items[Idx]); +end; + +function TDataType.TTuple.TValue.GetItemCount: Integer; +begin + Result := FTupleValue.ItemCount; +end; + +class operator TDataType.TTuple.TValue.Implicit(const A: IDataTupleValue): TDataType.TTuple.TValue; +begin + Result.FTupleValue := A; +end; + +class operator TDataType.TTuple.TValue.Implicit(const A: TDataType.TTuple.TValue): IDataTupleValue; +begin + Result := A.FTupleValue; +end; + +class operator TDataType.TTuple.TValue.Implicit(const A: TDataType.TTuple.TValue): TDataType.TValue; +begin + Result.Create(A.FTupleValue); +end; + { TDataType.TTuple } constructor TDataType.TTuple.Create(const ATupleType: IDataTupleType); @@ -749,9 +1245,9 @@ begin FTupleType := ATupleType; end; -function TDataType.TTuple.CreateValue(const AItems: array of IDataValue): IDataTupleValue; +function TDataType.TTuple.CreateValue(const AItems: array of IDataValue): TDataType.TTuple.TValue; begin - Result := FTupleType.CreateValue(AItems); + Result.Create(FTupleType.CreateValue(AItems)); end; class operator TDataType.TTuple.Implicit(const A: IDataTupleType): TDataType.TTuple; @@ -764,6 +1260,43 @@ begin Result := A.FTupleType; end; +{ TDataType.TArray.TValue } + +constructor TDataType.TArray.TValue.Create(const AArrayValue: IDataArrayValue); +begin + FArrayValue := AArrayValue; +end; + +function TDataType.TArray.TValue.GetDataType: TDataType.TArray; +begin + Result := IDataArrayType(FArrayValue.DataType); +end; + +function TDataType.TArray.TValue.GetElementCount: Integer; +begin + Result := FArrayValue.ElementCount; +end; + +function TDataType.TArray.TValue.GetItems(Idx: Integer): TDataType.TValue; +begin + Result.Create(FArrayValue.Items[Idx]); +end; + +class operator TDataType.TArray.TValue.Implicit(const A: IDataArrayValue): TDataType.TArray.TValue; +begin + Result.FArrayValue := A; +end; + +class operator TDataType.TArray.TValue.Implicit(const A: TDataType.TArray.TValue): IDataArrayValue; +begin + Result := A.FArrayValue; +end; + +class operator TDataType.TArray.TValue.Implicit(const A: TDataType.TArray.TValue): TDataType.TValue; +begin + Result.Create(A.FArrayValue); +end; + { TDataType.TArray } constructor TDataType.TArray.Create(const AArrayType: IDataArrayType); @@ -771,9 +1304,9 @@ begin FArrayType := AArrayType; end; -function TDataType.TArray.CreateValue(const AItems: array of IDataValue): IDataArrayValue; +function TDataType.TArray.CreateValue(const AItems: array of IDataValue): TDataType.TArray.TValue; begin - Result := FArrayType.CreateValue(AItems); + Result.Create(FArrayType.CreateValue(AItems)); end; function TDataType.TArray.GetElementType: IDataType; @@ -804,7 +1337,7 @@ var i: Integer; begin FArrayTypeRegistry := TDictionary.Create; - FRecordTypeRegistry := TDictionary, IDataRecordType>.Create(TRecordFieldComparer.Create); + FRecordTypeRegistry := TDictionary, IDataRecordType>.Create(TDataRecordFieldComparer.Create); SetLength(FDecimalTypes, 19); for i := 0 to 18 do @@ -912,7 +1445,7 @@ begin Result.Create(TImplDataOrdinalType.Singleton); end; -class function TDataType.RecordOf(const Fields: TArray): TDataType.TRecord; +class function TDataType.RecordOf(const Fields: TArray): TDataType.TRecord; var res: IDataRecordType; begin @@ -929,9 +1462,9 @@ begin Result.Create(res); end; -class function TDataType.RecordOf(const Fields: array of TRecordField): TDataType.TRecord; +class function TDataType.RecordOf(const Fields: array of TDataRecordField): TDataType.TRecord; var - tFields: TArray; + tFields: TArray; i: Integer; begin SetLength(tFields, Length(Fields)); diff --git a/Src/Data/TestDataTypes.pas b/Src/Data/TestDataTypes.pas index 79bc7c5..b6135f4 100644 --- a/Src/Data/TestDataTypes.pas +++ b/Src/Data/TestDataTypes.pas @@ -54,9 +54,9 @@ var intType: TDataType.TOrdinal; floatType: TDataType.TFloat; personType1, personType2, otherType: TDataType.TRecord; - personValue: IDataRecordValue; - idValue: IDataOrdinalValue; - floatValue: IDataFloatValue; + personValue: TDataType.TRecord.TValue; + idValue: TDataType.TOrdinal.TValue; + floatValue: TDataType.TFloat.TValue; begin // This test covers the optimized implementation for 2 fields. // --- 1. Setup: Define base types and a record structure --- @@ -64,7 +64,7 @@ begin floatType := TDataType.Float; // --- 2. Test Type Creation and Caching --- - personType1 := TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('Value', floatType)]); + personType1 := TDataType.RecordOf([TDataRecordField.Create('ID', intType), TDataRecordField.Create('Value', floatType)]); // Assertions for the created type Assert.IsNotNull(IDataRecordType(personType1), 'RecordType should be created'); @@ -78,18 +78,18 @@ begin Assert.AreEqual('Record', TDataType(personType1).Name, 'Type name should match expected format'); // Test if the same definition returns the same cached instance - personType2 := TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('Value', floatType)]); + personType2 := TDataType.RecordOf([TDataRecordField.Create('ID', intType), TDataRecordField.Create('Value', floatType)]); Assert.AreSame(IDataRecordType(personType1), IDataRecordType(personType2), 'Types should be cached and return the same instance'); // Test if a different definition returns a new instance - otherType := TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('Data', floatType)]); + otherType := TDataType.RecordOf([TDataRecordField.Create('ID', intType), TDataRecordField.Create('Data', floatType)]); Assert.AreNotSame(IDataRecordType(personType1), IDataRecordType(otherType), 'Different definitions should result in different types'); // --- 3. Test Value Creation and Access --- personValue := personType1.CreateValue([TDataType.Ordinal.CreateValue(123), TDataType.Float.CreateValue(45.67)]); - Assert.IsNotNull(personValue, 'RecordValue should be created'); - Assert.AreSame(IDataRecordType(personType1), personValue.DataType, 'Value should have the correct data type'); + Assert.IsNotNull(IDataRecordValue(personValue), 'RecordValue should be created'); + Assert.AreSame(IDataRecordType(personType1), IDataRecordType(personValue.DataType), 'Value should have the correct data type'); // Access by index idValue := TDataType.TValue(personValue.Items[0]).AsOrdinal; @@ -104,7 +104,7 @@ begin // --- 4. Test Validation and Error Handling --- // Test for duplicate field names during type creation Assert.WillRaise( - procedure begin TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('ID', floatType)]); end, + procedure begin TDataType.RecordOf([TDataRecordField.Create('ID', intType), TDataRecordField.Create('ID', floatType)]); end, EArgumentException, 'Duplicate field names should raise an exception' ); @@ -131,33 +131,38 @@ end; procedure TMyTestObject.TestRecords_Generic; var dataType: TDataType.TRecord; - dataValue: IDataRecordValue; + dataValue: TDataType.TRecord.TValue; begin // Test the generic implementation for records with > 2 fields. dataType := TDataType.RecordOf( [ - TRecordField.Create('A', TDataType.Ordinal), - TRecordField.Create('B', TDataType.Text), - TRecordField.Create('C', TDataType.Float) + TDataRecordField.Create('A', TDataType.Ordinal), + TDataRecordField.Create('B', TDataType.Text), + TDataRecordField.Create('C', TDataType.Float) ] ); dataValue := dataType.CreateValue([TDataType.Ordinal.CreateValue(1), TDataType.Text.CreateValue('two'), TDataType.Float.CreateValue(3.0)]); - Assert.IsNotNull(dataValue, 'Generic record value should be created'); + Assert.IsNotNull(IDataRecordValue(dataValue), 'Generic record value should be created'); Assert.AreEqual(3, dataType.FieldCount, 'Generic record should have 3 fields'); - Assert.AreEqual(Int64(1), TDataType.TValue(dataValue.Items[0]).AsOrdinal.Value, 'Field A is incorrect'); - Assert.AreEqual('two', TDataType.TValue(dataValue.Items[1]).AsText.Value, 'Field B is incorrect'); - Assert.AreEqual(3.0, TDataType.TValue(dataValue.Items[2]).AsFloat.Value, 'Field C is incorrect'); - Assert.AreEqual('', dataValue.AsString, 'Generic record AsString is incorrect'); + + var itemValue0 := dataValue.Items[0].AsOrdinal; + Assert.AreEqual(Int64(1), itemValue0.Value, 'Field A is incorrect'); + var itemValue1 := dataValue.Items[1].AsText; + Assert.AreEqual('two', itemValue1.Value, 'Field B is incorrect'); + var itemValue2 := dataValue.Items[2].AsFloat; + Assert.AreEqual(3.0, itemValue2.Value, 'Field C is incorrect'); + + Assert.AreEqual('', IDataValue(dataValue).AsString, 'Generic record AsString is incorrect'); end; procedure TMyTestObject.TestTuples; var - intValue: IDataOrdinalValue; - floatValue: IDataFloatValue; - tuple1, tuple2, tuple3: IDataTupleValue; + intValue: TDataType.TOrdinal.TValue; + floatValue: TDataType.TFloat.TValue; + tuple1, tuple2, tuple3: TDataType.TTuple.TValue; type1, type2, type3: IDataType; // Keep as interface to test AreSame on the raw interface pointer begin // This test covers optimized implementations for 1 and 2 elements. @@ -168,10 +173,10 @@ begin // --- 2. Test Value Creation and basic properties --- tuple1 := TDataType.Tuple.CreateValue([intValue, floatValue]); - Assert.IsNotNull(tuple1, 'Tuple value should be created'); + Assert.IsNotNull(IDataTupleValue(tuple1), 'Tuple value should be created'); Assert.AreEqual(2, tuple1.ItemCount, 'ItemCount should be on the value'); - Assert.AreSame(intValue, tuple1.Items[0], 'Item at index 0 is incorrect'); - Assert.AreSame(floatValue, tuple1.Items[1], 'Item at index 1 is incorrect'); + Assert.AreSame(IDataValue(intValue), tuple1.Items[0], 'Item at index 0 is incorrect'); + Assert.AreSame(IDataValue(floatValue), tuple1.Items[1], 'Item at index 1 is incorrect'); // --- 3. Test Singleton Type Behavior --- // Create more tuples with different structures @@ -193,7 +198,8 @@ end; procedure TMyTestObject.TestTuples_Generic; var - tupleValue: IDataTupleValue; + tupleValue: TDataType.TTuple.TValue; + item: TDataType.TOrdinal.TValue; begin // Test the generic implementation for tuples with > 5 elements. tupleValue := @@ -208,10 +214,12 @@ begin ] ); - Assert.IsNotNull(tupleValue, 'Generic tuple value should be created'); + Assert.IsNotNull(IDataTupleValue(tupleValue), 'Generic tuple value should be created'); Assert.AreEqual(6, tupleValue.ItemCount, 'Generic tuple should have 6 items'); - Assert.AreEqual(Int64(6), TDataType.TValue(tupleValue.Items[5]).AsOrdinal.Value, 'Last item is incorrect'); - Assert.AreEqual('(1, 2, 3, 4, 5, 6)', tupleValue.AsString, 'Generic tuple AsString is incorrect'); + + item := TDataType.TValue(tupleValue.Items[5]).AsOrdinal; + Assert.AreEqual(Int64(6), item.Value, 'Last item is incorrect'); + Assert.AreEqual('(1, 2, 3, 4, 5, 6)', IDataValue(tupleValue).AsString, 'Generic tuple AsString is incorrect'); end; procedure TMyTestObject.TestArrays; @@ -220,8 +228,9 @@ var floatType: TDataType.TFloat; intArrayType1, intArrayType2: TDataType.TArray; floatArrayType: TDataType.TArray; - arrayValue: IDataArrayValue; - v1, v2: IDataOrdinalValue; + arrayValue: TDataType.TArray.TValue; + v1, v2: TDataType.TOrdinal.TValue; + item: TDataType.TOrdinal.TValue; begin // --- 1. Setup --- intType := TDataType.Ordinal; @@ -252,12 +261,14 @@ begin v2 := TDataType.Ordinal.CreateValue(20); arrayValue := intArrayType1.CreateValue([v1, v2]); - Assert.IsNotNull(arrayValue, 'ArrayValue should be created'); + Assert.IsNotNull(IDataArrayValue(arrayValue), 'ArrayValue should be created'); Assert.AreEqual(2, arrayValue.ElementCount, 'ElementCount should be 2'); // Access items and check values - Assert.AreEqual(Int64(10), TDataType.TValue(arrayValue.Items[0]).AsOrdinal.Value, 'Item at index 0 is incorrect'); - Assert.AreEqual(Int64(20), TDataType.TValue(arrayValue.Items[1]).AsOrdinal.Value, 'Item at index 1 is incorrect'); + item := TDataType.TValue(arrayValue.Items[0]).AsOrdinal; + Assert.AreEqual(Int64(10), item.Value, 'Item at index 0 is incorrect'); + item := TDataType.TValue(arrayValue.Items[1]).AsOrdinal; + Assert.AreEqual(Int64(20), item.Value, 'Item at index 1 is incorrect'); // --- 4. Test Validation and Error Handling --- // Test creating an array with a nil element type @@ -278,37 +289,40 @@ end; procedure TMyTestObject.TestArrays_OptimizedAndGeneric; var arrayType: TDataType.TArray; - empty1, empty2, single, generic: IDataArrayValue; + empty1, empty2, single, generic: TDataType.TArray.TValue; + item: TDataType.TOrdinal.TValue; begin arrayType := TDataType.ArrayOf(TDataType.Ordinal); // Test optimized path for 0 elements (cached singleton per type) empty1 := arrayType.CreateValue([]); empty2 := arrayType.CreateValue([]); - Assert.IsNotNull(empty1, 'Empty array value should be created'); - Assert.AreSame(empty1, empty2, 'Empty array value should be cached and reused'); + Assert.IsNotNull(IDataArrayValue(empty1), 'Empty array value should be created'); + Assert.AreSame(IDataArrayValue(empty1), IDataArrayValue(empty2), 'Empty array value should be cached and reused'); Assert.AreEqual(0, empty1.ElementCount, 'Empty array should have 0 elements'); - Assert.AreEqual('[]', empty1.AsString, 'Empty array AsString is incorrect'); + Assert.AreEqual('[]', IDataValue(empty1).AsString, 'Empty array AsString is incorrect'); // Test optimized path for 1 element single := arrayType.CreateValue([TDataType.Ordinal.CreateValue(42)]); - Assert.IsNotNull(single, 'Single element array should be created'); + Assert.IsNotNull(IDataArrayValue(single), 'Single element array should be created'); Assert.AreEqual(1, single.ElementCount, 'Single element array should have 1 element'); - Assert.AreEqual(Int64(42), TDataType.TValue(single.Items[0]).AsOrdinal.Value, 'Single element value is incorrect'); - Assert.AreEqual('[42]', single.AsString, 'Single element array AsString is incorrect'); + + item := TDataType.TValue(single.Items[0]).AsOrdinal; + Assert.AreEqual(Int64(42), item.Value, 'Single element value is incorrect'); + Assert.AreEqual('[42]', IDataValue(single).AsString, 'Single element array AsString is incorrect'); // Test generic path for > 1 elements generic := arrayType.CreateValue([TDataType.Ordinal.CreateValue(1), TDataType.Ordinal.CreateValue(2), TDataType.Ordinal.CreateValue(3)]); - Assert.IsNotNull(generic, 'Generic array should be created'); + Assert.IsNotNull(IDataArrayValue(generic), 'Generic array should be created'); Assert.AreEqual(3, generic.ElementCount, 'Generic array should have 3 elements'); - Assert.AreEqual('[1, 2, 3]', generic.AsString, 'Generic array AsString is incorrect'); + Assert.AreEqual('[1, 2, 3]', IDataValue(generic).AsString, 'Generic array AsString is incorrect'); end; procedure TMyTestObject.TestTexts; var textType: TDataType.TText; - textValue1, textValue2: IDataTextValue; + textValue1, textValue2, castedValue: TDataType.TText.TValue; dataValue: IDataValue; begin // --- 1. Test Type Creation --- @@ -321,8 +335,8 @@ begin // --- 2. Test Value Creation and Access --- textValue1 := TDataType.Text.CreateValue('Hello World'); - Assert.IsNotNull(textValue1, 'TextValue should be created'); - Assert.AreSame(IDataType(textType), textValue1.DataType, 'Value should have the correct data type'); + Assert.IsNotNull(IDataTextValue(textValue1), 'TextValue should be created'); + Assert.AreSame(IDataType(textType), IDataType(textValue1.DataType), 'Value should have the correct data type'); Assert.AreEqual('Hello World', textValue1.Value, 'Value should be ''Hello World'''); // Test another text value @@ -331,7 +345,8 @@ begin // --- 3. Test AsText casting --- dataValue := TDataType.Text.CreateValue('Test Text'); - Assert.AreEqual('Test Text', TDataType.TValue(dataValue).AsText.Value, 'AsText should return correct value'); + castedValue := TDataType.TValue(dataValue).AsText; + Assert.AreEqual('Test Text', castedValue.Value, 'AsText should return correct value'); // Test invalid cast dataValue := TDataType.Ordinal.CreateValue(123); @@ -344,49 +359,49 @@ end; procedure TMyTestObject.TestTexts_OptimizedEmpty; var - empty1, empty2: IDataTextValue; + empty1, empty2: TDataType.TText.TValue; begin // Test the singleton implementation for the empty string. empty1 := TDataType.Text.CreateValue(''); empty2 := TDataType.Text.CreateValue(''); - Assert.IsNotNull(empty1, 'Empty text value should be created'); - Assert.AreSame(empty1, empty2, 'Empty text value should be a singleton'); + Assert.IsNotNull(IDataTextValue(empty1), 'Empty text value should be created'); + Assert.AreSame(IDataTextValue(empty1), IDataTextValue(empty2), 'Empty text value should be a singleton'); Assert.AreEqual('', empty1.Value, 'Value of empty text should be empty'); - Assert.AreEqual('', empty1.AsString, 'AsString of empty text should be empty'); + Assert.AreEqual('', IDataValue(empty1).AsString, 'AsString of empty text should be empty'); end; procedure TMyTestObject.TestFloats_OptimizedAndGeneric; var - zero1, zero2, nan1, nan2, generic: IDataFloatValue; + zero1, zero2, nan1, nan2, generic: TDataType.TFloat.TValue; begin // Test singleton for 0.0 zero1 := TDataType.Float.CreateValue(0.0); zero2 := TDataType.Float.CreateValue(0.0); - Assert.IsNotNull(zero1, 'Zero float should be created'); - Assert.AreSame(zero1, zero2, 'Zero float should be a singleton'); + Assert.IsNotNull(IDataFloatValue(zero1), 'Zero float should be created'); + Assert.AreSame(IDataFloatValue(zero1), IDataFloatValue(zero2), 'Zero float should be a singleton'); Assert.AreEqual(0.0, zero1.Value, 'Value of zero float is incorrect'); // Test singleton for NaN nan1 := TDataType.Float.CreateValue(NaN); nan2 := TDataType.Float.CreateValue(NaN); - Assert.IsNotNull(nan1, 'NaN float should be created'); - Assert.AreSame(nan1, nan2, 'NaN float should be a singleton'); + Assert.IsNotNull(IDataFloatValue(nan1), 'NaN float should be created'); + Assert.AreSame(IDataFloatValue(nan1), IDataFloatValue(nan2), 'NaN float should be a singleton'); Assert.IsTrue(IsNaN(nan1.Value), 'Value of NaN float should be NaN'); - Assert.AreEqual('NaN', nan1.AsString, 'NaN AsString is incorrect'); + Assert.AreEqual('NaN', IDataValue(nan1).AsString, 'NaN AsString is incorrect'); // Test generic path generic := TDataType.Float.CreateValue(123.45); - Assert.IsNotNull(generic, 'Generic float should be created'); - Assert.AreNotSame(zero1, generic, 'Generic float should not be the zero singleton'); - Assert.AreNotSame(nan1, generic, 'Generic float should not be the NaN singleton'); + Assert.IsNotNull(IDataFloatValue(generic), 'Generic float should be created'); + Assert.AreNotSame(IDataFloatValue(zero1), IDataFloatValue(generic), 'Generic float should not be the zero singleton'); + Assert.AreNotSame(IDataFloatValue(nan1), IDataFloatValue(generic), 'Generic float should not be the NaN singleton'); Assert.AreEqual(123.45, generic.Value, 'Value of generic float is incorrect'); end; procedure TMyTestObject.TestTimestamps; var tsType: TDataType.TTimestamp; - tsValue1, tsValue2: IDataTimestampValue; + tsValue1, tsValue2, castedValue: TDataType.TTimestamp.TValue; dataValue: IDataValue; now: TDateTime; begin @@ -401,8 +416,8 @@ begin now := System.Sysutils.Now; tsValue1 := TDataType.Timestamp.CreateValue(now); - Assert.IsNotNull(tsValue1, 'TimestampValue should be created'); - Assert.AreSame(IDataType(tsType), tsValue1.DataType, 'Value should have the correct data type'); + Assert.IsNotNull(IDataTimestampValue(tsValue1), 'TimestampValue should be created'); + Assert.AreSame(IDataType(tsType), IDataType(tsValue1.DataType), 'Value should have the correct data type'); Assert.AreEqual(now, tsValue1.Value, 'Value should be the same'); // Test another text value @@ -411,7 +426,8 @@ begin // --- 3. Test AsTimestamp casting --- dataValue := TDataType.Timestamp.CreateValue(now); - Assert.AreEqual(now, TDataType.TValue(dataValue).AsTimestamp.Value, 'AsTimestamp should return correct value'); + castedValue := TDataType.TValue(dataValue).AsTimestamp; + Assert.AreEqual(now, castedValue.Value, 'AsTimestamp should return correct value'); // Test invalid cast dataValue := TDataType.Ordinal.CreateValue(123); @@ -425,7 +441,7 @@ end; procedure TMyTestObject.TestEnums; var colorType: TDataType.TEnum; - red, green, blue: IDataEnumValue; + red, green, blue: TDataType.TEnum.TValue; begin // --- 1. Test Type Creation --- colorType := TDataType.EnumOf('Color', ['Red', 'Green', 'Blue']); @@ -445,15 +461,15 @@ begin green := colorType.CreateValue('Green'); blue := colorType.CreateValue(2); - Assert.IsNotNull(red, 'EnumValue from index should be created'); - Assert.AreSame(IDataType(colorType), red.DataType, 'Red should have the correct data type'); + Assert.IsNotNull(IDataEnumValue(red), 'EnumValue from index should be created'); + Assert.AreSame(IDataType(colorType), IDataType(red.DataType), 'Red should have the correct data type'); Assert.AreEqual(0, red.Value, 'Value of Red should be 0'); - Assert.AreEqual('Red', red.AsString, 'AsText of Red should be Red'); + Assert.AreEqual('Red', IDataValue(red).AsString, 'AsText of Red should be Red'); - Assert.IsNotNull(green, 'EnumValue from identifier should be created'); - Assert.AreSame(IDataType(colorType), green.DataType, 'Green should have the correct data type'); + Assert.IsNotNull(IDataEnumValue(green), 'EnumValue from identifier should be created'); + Assert.AreSame(IDataType(colorType), IDataType(green.DataType), 'Green should have the correct data type'); Assert.AreEqual(1, green.Value, 'Value of Green should be 1'); - Assert.AreEqual('Green', green.AsString, 'AsText of Green should be Green'); + Assert.AreEqual('Green', IDataValue(green).AsString, 'AsText of Green should be Green'); Assert.AreEqual(2, blue.Value, 'Value of Blue should be 2'); @@ -479,14 +495,14 @@ end; procedure TMyTestObject.TestAsString; var - ordinalValue: IDataOrdinalValue; - floatValue: IDataFloatValue; - textValue: IDataTextValue; - tsValue: IDataTimestampValue; - enumValue: IDataEnumValue; - arrayValue: IDataArrayValue; - recordValue: IDataRecordValue; - tupleValue: IDataTupleValue; + ordinalValue: TDataType.TOrdinal.TValue; + floatValue: TDataType.TFloat.TValue; + textValue: TDataType.TText.TValue; + tsValue: TDataType.TTimestamp.TValue; + enumValue: TDataType.TEnum.TValue; + arrayValue: TDataType.TArray.TValue; + recordValue: TDataType.TRecord.TValue; + tupleValue: TDataType.TTuple.TValue; colorType: TDataType.TEnum; personType: TDataType.TRecord; intArrayType: TDataType.TArray; @@ -494,39 +510,39 @@ var begin // Ordinal ordinalValue := TDataType.Ordinal.CreateValue(123); - Assert.AreEqual('123', ordinalValue.AsString, 'Ordinal AsString incorrect'); + Assert.AreEqual('123', IDataValue(ordinalValue).AsString, 'Ordinal AsString incorrect'); // Float floatValue := TDataType.Float.CreateValue(45.67); - Assert.AreEqual(FloatToStr(45.67), floatValue.AsString, 'Float AsString incorrect'); + Assert.AreEqual(FloatToStr(45.67), IDataValue(floatValue).AsString, 'Float AsString incorrect'); // Text textValue := TDataType.Text.CreateValue('Hello'); - Assert.AreEqual('Hello', textValue.AsString, 'Text AsString incorrect'); + Assert.AreEqual('Hello', IDataValue(textValue).AsString, 'Text AsString incorrect'); // Timestamp now := System.Sysutils.Now; tsValue := TDataType.Timestamp.CreateValue(now); - Assert.AreEqual(DateTimeToStr(now), tsValue.AsString, 'Timestamp AsString incorrect'); + Assert.AreEqual(DateTimeToStr(now), IDataValue(tsValue).AsString, 'Timestamp AsString incorrect'); // Enum colorType := TDataType.EnumOf('Color', ['Red', 'Green', 'Blue']); enumValue := colorType.CreateValue('Green'); - Assert.AreEqual('Green', enumValue.AsString, 'Enum AsString incorrect'); + Assert.AreEqual('Green', IDataValue(enumValue).AsString, 'Enum AsString incorrect'); // Array intArrayType := TDataType.ArrayOf(TDataType.Ordinal); arrayValue := intArrayType.CreateValue([TDataType.Ordinal.CreateValue(1), TDataType.Ordinal.CreateValue(2)]); - Assert.AreEqual('[1, 2]', arrayValue.AsString, 'Array AsString incorrect'); + Assert.AreEqual('[1, 2]', IDataValue(arrayValue).AsString, 'Array AsString incorrect'); // Record - personType := TDataType.RecordOf([TRecordField.Create('ID', TDataType.Ordinal), TRecordField.Create('Name', TDataType.Text)]); + personType := TDataType.RecordOf([TDataRecordField.Create('ID', TDataType.Ordinal), TDataRecordField.Create('Name', TDataType.Text)]); recordValue := personType.CreateValue([TDataType.Ordinal.CreateValue(1), TDataType.Text.CreateValue('Bob')]); - Assert.AreEqual('', recordValue.AsString, 'Record AsString incorrect'); + Assert.AreEqual('', IDataValue(recordValue).AsString, 'Record AsString incorrect'); // Tuple tupleValue := TDataType.Tuple.CreateValue([TDataType.Ordinal.CreateValue(10), TDataType.Text.CreateValue('Tuple')]); - Assert.AreEqual('(10, Tuple)', tupleValue.AsString, 'Tuple AsString incorrect'); + Assert.AreEqual('(10, Tuple)', IDataValue(tupleValue).AsString, 'Tuple AsString incorrect'); end; procedure TMyTestObject.TestAsTValue; @@ -538,6 +554,7 @@ var intArrayType: TDataType.TArray; personType: TDataType.TRecord; begin + // This test is specifically for the IDataValue.AsTValue method. No changes needed. // --- Ordinal --- dataValue := TDataType.Ordinal.CreateValue(123); tv := dataValue.AsTValue; @@ -599,7 +616,7 @@ begin Assert.AreEqual(NativeInt(0), tv.GetArrayLength, 'Empty Array TValue length is incorrect'); // --- Record --- - personType := TDataType.RecordOf([TRecordField.Create('ID', TDataType.Ordinal), TRecordField.Create('Name', TDataType.Text)]); + personType := TDataType.RecordOf([TDataRecordField.Create('ID', TDataType.Ordinal), TDataRecordField.Create('Name', TDataType.Text)]); dataValue := personType.CreateValue([TDataType.Ordinal.CreateValue(1), TDataType.Text.CreateValue('Bob')]); tv := dataValue.AsTValue; Assert.IsFalse(tv.IsEmpty, 'Record TValue should not be empty'); @@ -626,9 +643,10 @@ var textType: TDataType.TText; methodType1, methodType2, otherMethodType: TDataType.TMethod; myFunc: TDataType.TMethod.TProc; - methodValue: IDataMethodValue; - inputValue: IDataOrdinalValue; - resultValue: IDataValue; + methodValue: TDataType.TMethod.TValue; + inputValue: TDataType.TOrdinal.TValue; + resultValue: TDataType.TValue; + textResult: TDataType.TText.TValue; tv: TValue; begin // --- 1. Setup: Define base types --- @@ -664,7 +682,7 @@ begin myFunc := function(const AValue: TDataType.TValue): TDataType.TValue var - ordinalValue: IDataOrdinalValue; + ordinalValue: TDataType.TOrdinal.TValue; val: Int64; begin ordinalValue := AValue.AsOrdinal; @@ -673,22 +691,24 @@ begin end; methodValue := methodType1.CreateValue(myFunc); - Assert.IsNotNull(methodValue, 'MethodValue should be created'); - Assert.AreSame(IDataType(methodType1), methodValue.DataType, 'Value should have the correct data type'); + Assert.IsNotNull(IDataMethodValue(methodValue), 'MethodValue should be created'); + Assert.AreSame(IDataType(methodType1), IDataType(methodValue.DataType), 'Value should have the correct data type'); // --- 4. Test Execution (Simulated) --- inputValue := TDataType.Ordinal.CreateValue(42); // Execute the function retrieved from the value resultValue := methodValue.Value(inputValue); - Assert.IsNotNull(resultValue, 'Execution result should not be nil'); + Assert.IsNotNull(IDataValue(resultValue), 'Execution result should not be nil'); Assert.AreSame(IDataType(textType), resultValue.DataType, 'Result value should have Text type'); - Assert.AreEqual('42', TDataType.TValue(resultValue).AsText.Value, 'Result value content is incorrect'); + + textResult := resultValue.AsText; + Assert.AreEqual('42', textResult.Value, 'Result value content is incorrect'); // --- 5. Test AsString and AsTValue --- - Assert.AreEqual('', methodValue.AsString, 'Method AsString is incorrect'); + Assert.AreEqual('', IDataValue(methodValue).AsString, 'Method AsString is incorrect'); - tv := methodValue.AsTValue; + tv := IDataValue(methodValue).AsTValue; Assert.IsFalse(tv.IsEmpty, 'Method TValue should not be empty'); Assert.AreEqual(tkInterface, tv.Kind, 'TValue kind for a TMethodProc should be tkInterface, as it is a reference-to-function'); Assert.IsTrue(TypeInfo(TDataMethodProc) = tv.TypeInfo, 'TValue should hold TMethodProc type info');