New type system - Decimals

This commit is contained in:
Michael Schimmel
2025-08-26 13:47:38 +02:00
parent c434a151dd
commit 1ff603bd10
7 changed files with 337 additions and 197 deletions
+129 -113
View File
@@ -8,8 +8,7 @@ uses
System.RTTI;
type
// Corrected dfVoid to dkVoid and added void type support
TDataKind = (dkVoid, dkOrdinal, dkFloat, dkText, dkTimestamp, dkRecord, dkTuple, dkArray, dkEnum, dkMethod);
TDataKind = (dkVoid, dkOrdinal, dkFloat, dkText, dkTimestamp, dkDecimal, dkRecord, dkTuple, dkArray, dkEnum, dkMethod);
IDataType = interface
{$region 'private'}
@@ -82,6 +81,23 @@ type
function CreateValue(const AValue: TDateTime): IDataTimestampValue;
end;
// Represents a fixed-point decimal data value.
IDataDecimalValue = interface(IDataValue)
{$region 'private'}
function GetValue: Int64;
{$endregion}
property Value: Int64 read GetValue;
end;
// Represents the type of a fixed-point decimal data value.
IDataDecimalType = interface(IDataType)
{$region 'private'}
function GetScale: Integer;
{$endregion}
function CreateValue(const AValue: Int64): IDataDecimalValue;
property Scale: Integer read GetScale;
end;
IDataEnumValue = interface(IDataValue)
{$region 'private'}
function GetValue: Integer;
@@ -190,6 +206,7 @@ type
function AsFloat: IDataFloatValue; inline;
function AsText: IDataTextValue; inline;
function AsTimestamp: IDataTimestampValue; inline;
function AsDecimal: IDataDecimalValue; inline;
function AsRecord: IDataRecordValue; inline;
function AsTuple: IDataTupleValue; inline;
function AsArray: IDataArrayValue; inline;
@@ -204,6 +221,7 @@ type
class operator Implicit(const A: IDataFloatValue): TDataType.TValue; overload; inline;
class operator Implicit(const A: IDataTextValue): TDataType.TValue; overload; inline;
class operator Implicit(const A: IDataTimestampValue): TDataType.TValue; overload; inline;
class operator Implicit(const A: IDataDecimalValue): TDataType.TValue; overload; inline;
class operator Implicit(const A: IDataEnumValue): TDataType.TValue; overload; inline;
class operator Implicit(const A: IDataMethodValue): TDataType.TValue; overload; inline;
class operator Implicit(const A: IDataRecordValue): TDataType.TValue; overload; inline;
@@ -266,6 +284,33 @@ type
function CreateValue(const AValue: TDateTime): IDataTimestampValue; inline;
end;
TDecimal = record
type
TValue = record
private
// The wrapped interface instance.
FDecimalValue: IDataDecimalValue;
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;
property Value: Int64 read GetValue;
end;
private
FDecimalType: IDataDecimalType;
function GetScale: Integer; inline;
public
constructor Create(const ADecimalType: IDataDecimalType);
class operator Implicit(const A: IDataDecimalType): TDataType.TDecimal; overload; inline;
class operator Implicit(const A: TDataType.TDecimal): IDataDecimalType; overload; inline;
function CreateValue(const AValue: Int64): TDataType.TDecimal.TValue; inline;
property Scale: Integer read GetScale;
end;
TEnum = record
private
FEnumType: IDataEnumType;
@@ -343,6 +388,7 @@ type
FArrayTypeRegistry: TDictionary<IDataType, IDataArrayType>;
FRecordTypeRegistry: TDictionary<TArray<TRecordField>, IDataRecordType>;
FMethodTypeRegistry: TDictionary<TPair<IDataType, IDataType>, IDataMethodType>;
FDecimalTypes: TArray<IDataDecimalType>;
class constructor CreateClass;
class destructor DestroyClass;
public
@@ -355,6 +401,7 @@ type
class operator Implicit(const A: TDataType.TFloat): TDataType; overload; inline;
class operator Implicit(const A: TDataType.TText): TDataType; overload; inline;
class operator Implicit(const A: TDataType.TTimestamp): TDataType; overload; inline;
class operator Implicit(const A: TDataType.TDecimal): TDataType; overload; inline;
class operator Implicit(const A: TDataType.TEnum): TDataType; overload; inline;
class operator Implicit(const A: TDataType.TMethod): TDataType; overload; inline;
class operator Implicit(const A: TDataType.TRecord): TDataType; overload; inline;
@@ -367,6 +414,7 @@ type
class function Float: TDataType.TFloat; static; inline;
class function Text: TDataType.TText; static; inline;
class function Timestamp: TDataType.TTimestamp; static; inline;
class function DecimalOf(const AScale: Integer): TDataType.TDecimal; static;
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;
@@ -374,25 +422,6 @@ type
class function RecordOf(const Fields: array of TRecordField): TDataType.TRecord; overload; static;
class function EnumOf(const AName: string; const AIdentifiers: array of string): TDataType.TEnum; static;
// Value factories
class function FromOrdinal(const AValue: Int64): IDataOrdinalValue; static; inline;
class function FromFloat(const AValue: Double): IDataFloatValue; static; inline;
class function FromText(const AValue: string): IDataTextValue; static; inline;
class function FromTimestamp(const AValue: TDateTime): IDataTimestampValue; static; inline;
class function FromTuple(const AItems: array of IDataValue): IDataTupleValue; static;
class function FromMethod(const AMethodType: TDataType.TMethod; const AValue: TMethod.TProc): IDataMethodValue; static; inline;
// Casting
function AsOrdinal: TDataType.TOrdinal; inline;
function AsFloat: TDataType.TFloat; inline;
function AsText: TDataType.TText; inline;
function AsTimestamp: TDataType.TTimestamp; inline;
function AsRecord: TDataType.TRecord; inline;
function AsTuple: TDataType.TTuple; inline;
function AsArray: TDataType.TArray; inline;
function AsEnum: TDataType.TEnum; inline;
function AsMethod: TDataType.TMethod; inline;
property DataType: IDataType read FDataType;
property Name: String read GetName;
property Kind: TDataKind read GetKind;
@@ -408,6 +437,7 @@ uses
Myc.Data.Types.Float,
Myc.Data.Types.Text,
Myc.Data.Types.Timestamp,
Myc.Data.Types.Decimal,
Myc.Data.Types.Arrays,
Myc.Data.Types.Records,
Myc.Data.Types.Tuple,
@@ -422,6 +452,28 @@ begin
DataType := ADataType;
end;
{ TDataType.TDecimal.TValue }
constructor TDataType.TDecimal.TValue.Create(const ADecimalValue: IDataDecimalValue);
begin
FDecimalValue := ADecimalValue;
end;
function TDataType.TDecimal.TValue.GetValue: Int64;
begin
Result := FDecimalValue.Value;
end;
class operator TDataType.TDecimal.TValue.Implicit(const A: IDataDecimalValue): TDataType.TDecimal.TValue;
begin
Result.FDecimalValue := A;
end;
class operator TDataType.TDecimal.TValue.Implicit(const A: TDataType.TDecimal.TValue): IDataDecimalValue;
begin
Result := A.FDecimalValue;
end;
{ TDataType.TVoid }
constructor TDataType.TVoid.Create(const AVoidType: IDataVoidType);
@@ -532,6 +584,33 @@ begin
Result := A.FTimestampType;
end;
{ TDataType.TDecimal }
constructor TDataType.TDecimal.Create(const ADecimalType: IDataDecimalType);
begin
FDecimalType := ADecimalType;
end;
function TDataType.TDecimal.CreateValue(const AValue: Int64): TDataType.TDecimal.TValue;
begin
Result.Create(FDecimalType.CreateValue(AValue));
end;
function TDataType.TDecimal.GetScale: Integer;
begin
Result := FDecimalType.Scale;
end;
class operator TDataType.TDecimal.Implicit(const A: IDataDecimalType): TDataType.TDecimal;
begin
Result.FDecimalType := A;
end;
class operator TDataType.TDecimal.Implicit(const A: TDataType.TDecimal): IDataDecimalType;
begin
Result := A.FDecimalType;
end;
{ TDataType.TEnum }
constructor TDataType.TEnum.Create(const AEnumType: IDataEnumType);
@@ -710,9 +789,16 @@ begin
end;
class constructor TDataType.CreateClass;
var
i: Integer;
begin
FArrayTypeRegistry := TDictionary<IDataType, IDataArrayType>.Create;
FRecordTypeRegistry := TDictionary<TArray<TRecordField>, IDataRecordType>.Create(TRecordFieldComparer.Create);
SetLength(FDecimalTypes, 19);
for i := 0 to 18 do
FDecimalTypes[i] := TImplDataDecimalType.Create(i);
FMethodTypeRegistry :=
TDictionary<TPair<IDataType, IDataType>, IDataMethodType>.Create(
TEqualityComparer<TPair<IDataType, IDataType>>.Construct(
@@ -737,6 +823,7 @@ class destructor TDataType.DestroyClass;
begin
FArrayTypeRegistry.Free;
FRecordTypeRegistry.Free;
FDecimalTypes := nil;
FMethodTypeRegistry.Free;
end;
@@ -760,67 +847,11 @@ begin
Result.Create(res);
end;
function TDataType.AsArray: TDataType.TArray;
class function TDataType.DecimalOf(const AScale: Integer): TDataType.TDecimal;
begin
if (FDataType.Kind <> dkArray) then
raise EInvalidCast.Create('Array expected');
Result.Create(IDataArrayType(FDataType));
end;
function TDataType.AsEnum: TDataType.TEnum;
begin
if (FDataType.Kind <> dkEnum) then
raise EInvalidCast.Create('Enum expected');
Result.Create(IDataEnumType(FDataType));
end;
function TDataType.AsFloat: TDataType.TFloat;
begin
if (FDataType.Kind <> dkFloat) then
raise EInvalidCast.Create('Float expected');
Result.Create(IDataFloatType(FDataType));
end;
function TDataType.AsMethod: TDataType.TMethod;
begin
if (FDataType.Kind <> dkMethod) then
raise EInvalidCast.Create('Method expected');
Result.Create(IDataMethodType(FDataType));
end;
function TDataType.AsOrdinal: TDataType.TOrdinal;
begin
if (FDataType.Kind <> dkOrdinal) then
raise EInvalidCast.Create('Ordinal expected');
Result.Create(IDataOrdinalType(FDataType));
end;
function TDataType.AsRecord: TDataType.TRecord;
begin
if (FDataType.Kind <> dkRecord) then
raise EInvalidCast.Create('Record expected');
Result.Create(IDataRecordType(FDataType));
end;
function TDataType.AsText: TDataType.TText;
begin
if (FDataType.Kind <> dkText) then
raise EInvalidCast.Create('Text expected');
Result.Create(IDataTextType(FDataType));
end;
function TDataType.AsTimestamp: TDataType.TTimestamp;
begin
if (FDataType.Kind <> dkTimestamp) then
raise EInvalidCast.Create('Timestamp expected');
Result.Create(IDataTimestampType(FDataType));
end;
function TDataType.AsTuple: TDataType.TTuple;
begin
if (FDataType.Kind <> dkTuple) then
raise EInvalidCast.Create('Tuple expected');
Result.Create(IDataTupleType(FDataType));
if (AScale < 0) or (AScale > 18) then
raise EArgumentException.Create('Scale for decimal must be between 0 and 18.');
Result.Create(FDecimalTypes[AScale]);
end;
class function TDataType.EnumOf(const AName: string; const AIdentifiers: array of string): TDataType.TEnum;
@@ -833,38 +864,6 @@ begin
Result.Create(TImplDataFloatType.Singleton);
end;
class function TDataType.FromFloat(const AValue: Double): IDataFloatValue;
begin
Result := TDataType.Float.CreateValue(AValue);
end;
class function TDataType.FromMethod(const AMethodType: TDataType.TMethod; const AValue: TMethod.TProc): IDataMethodValue;
begin
if not Assigned(AMethodType.FMethodType) then // Check underlying interface
raise EArgumentException.Create('AMethodType');
Result := AMethodType.CreateValue(AValue);
end;
class function TDataType.FromOrdinal(const AValue: Int64): IDataOrdinalValue;
begin
Result := TDataType.Ordinal.CreateValue(AValue);
end;
class function TDataType.FromText(const AValue: string): IDataTextValue;
begin
Result := TDataType.Text.CreateValue(AValue);
end;
class function TDataType.FromTimestamp(const AValue: TDateTime): IDataTimestampValue;
begin
Result := TDataType.Timestamp.CreateValue(AValue);
end;
class function TDataType.FromTuple(const AItems: array of IDataValue): IDataTupleValue;
begin
Result := TDataType.Tuple.CreateValue(AItems);
end;
function TDataType.GetKind: TDataKind;
begin
Result := FDataType.Kind;
@@ -965,6 +964,11 @@ begin
Result.FDataType := A.FArrayType;
end;
class operator TDataType.Implicit(const A: TDataType.TDecimal): TDataType;
begin
Result.FDataType := A.FDecimalType;
end;
class operator TDataType.Implicit(const A: TDataType.TEnum): TDataType;
begin
Result.FDataType := A.FEnumType;
@@ -1025,6 +1029,13 @@ begin
Result := IDataArrayValue(FDataValue);
end;
function TDataType.TValue.AsDecimal: IDataDecimalValue;
begin
if (FDataValue.DataType.Kind <> dkDecimal) then
raise EInvalidCast.Create('Decimal expected');
Result := IDataDecimalValue(FDataValue);
end;
function TDataType.TValue.AsEnum: IDataEnumValue;
begin
if (FDataValue.DataType.Kind <> dkEnum) then
@@ -1106,6 +1117,11 @@ begin
Result.FDataValue := A;
end;
class operator TDataType.TValue.Implicit(const A: IDataDecimalValue): TDataType.TValue;
begin
Result.FDataValue := A;
end;
class operator TDataType.TValue.Implicit(const A: IDataEnumValue): TDataType.TValue;
begin
Result.FDataValue := A;