diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index b0d2aae..8274b5d 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -162,14 +162,20 @@ type AOperator: TBinaryOperator; ARight: IExpressionNode ): IBinaryExpressionNode; static; - class function UnaryExpr(AOperator: TUnaryOperator; ARight: IExpressionNode): IUnaryExpressionNode; static; - class function IfExpr(ACondition: IExpressionNode; AThenBranch, AElseBranch: IExpressionNode): IIfExpressionNode; static; - class function LambdaExpr(const AParameters: array of IIdentifierNode; ABody: IAstNode): ILambdaExpressionNode; static; - class function FunctionCall(ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; static; + class function UnaryExpr(const AOperator: TUnaryOperator; const ARight: IExpressionNode): IUnaryExpressionNode; static; + class function IfExpr( + const ACondition: IExpressionNode; + const AThenBranch, AElseBranch: IExpressionNode + ): IIfExpressionNode; static; + class function LambdaExpr(const AParameters: array of IIdentifierNode; const ABody: IAstNode): ILambdaExpressionNode; static; + class function FunctionCall(const ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; static; // Statements class function Block(const AStatements: array of IStatementNode): IBlockStatementNode; static; - class function VarDecl(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode; static; - class function ExprStmt(AExpression: IExpressionNode): IExpressionStatementNode; static; + class function VarDecl( + const AIdentifier: IIdentifierNode; + AInitializer: IExpressionNode + ): IVariableDeclarationStatementNode; static; + class function ExprStmt(const AExpression: IExpressionNode): IExpressionStatementNode; static; end; // Manages the scope of execution, holding variables and their values. @@ -188,7 +194,7 @@ type TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor) private FScope: TExecutionScope; - function IsTruthy(AValue: IDataValue): Boolean; + function IsTruthy(const AValue: IDataValue): Boolean; public constructor Create(AScope: TExecutionScope); // Expression visitors @@ -335,17 +341,17 @@ begin Result := TBinaryExpressionNodeImpl.Create(ALeft, AOperator, ARight); end; -class function TAst.UnaryExpr(AOperator: TUnaryOperator; ARight: IExpressionNode): IUnaryExpressionNode; +class function TAst.UnaryExpr(const AOperator: TUnaryOperator; const ARight: IExpressionNode): IUnaryExpressionNode; begin Result := TUnaryExpressionNodeImpl.Create(AOperator, ARight); end; -class function TAst.IfExpr(ACondition: IExpressionNode; AThenBranch, AElseBranch: IExpressionNode): IIfExpressionNode; +class function TAst.IfExpr(const ACondition: IExpressionNode; const AThenBranch, AElseBranch: IExpressionNode): IIfExpressionNode; begin Result := TIfExpressionNodeImpl.Create(ACondition, AThenBranch, AElseBranch); end; -class function TAst.LambdaExpr(const AParameters: array of IIdentifierNode; ABody: IAstNode): ILambdaExpressionNode; +class function TAst.LambdaExpr(const AParameters: array of IIdentifierNode; const ABody: IAstNode): ILambdaExpressionNode; var paramList: TList; param: IIdentifierNode; @@ -356,7 +362,7 @@ begin Result := TLambdaExpressionNodeImpl.Create(paramList, ABody); end; -class function TAst.FunctionCall(ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; +class function TAst.FunctionCall(const ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; var argList: TList; arg: IExpressionNode; @@ -378,12 +384,12 @@ begin Result := TBlockStatementNodeImpl.Create(stmtList); end; -class function TAst.VarDecl(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode; +class function TAst.VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode; begin Result := TVariableDeclarationStatementNodeImpl.Create(AIdentifier, AInitializer); end; -class function TAst.ExprStmt(AExpression: IExpressionNode): IExpressionStatementNode; +class function TAst.ExprStmt(const AExpression: IExpressionNode): IExpressionStatementNode; begin Result := TExpressionStatementNodeImpl.Create(AExpression); end; @@ -673,7 +679,7 @@ begin FScope := AScope; end; -function TEvaluatorVisitor.IsTruthy(AValue: IDataValue): Boolean; +function TEvaluatorVisitor.IsTruthy(const AValue: IDataValue): Boolean; begin // Defines the language's concept of "truthiness". // For now, only ordinals can be conditions. 0 is false, everything else is true. diff --git a/Src/Data/Myc.Data.Types.Records.pas b/Src/Data/Myc.Data.Types.Records.pas index a3f9744..0823a34 100644 --- a/Src/Data/Myc.Data.Types.Records.pas +++ b/Src/Data/Myc.Data.Types.Records.pas @@ -39,8 +39,6 @@ type class constructor CreateClass; class destructor DestroyClass; - // Factory method to get a cached or new record type instance. - class function GetInstance(const AFields: TArray): IDataRecordType; overload; static; class function GetInstance(const AFields: array of TDataRecordField): IDataRecordType; overload; static; // TODO Create a record ba analyzing the RTTI of T @@ -302,16 +300,22 @@ begin FRecordTypeRegistry := nil; end; -class function TImplDataRecordType.GetInstance(const AFields: TArray): IDataRecordType; +class function TImplDataRecordType.GetInstance(const AFields: array of TDataRecordField): IDataRecordType; var + tFields: TArray; + i: Integer; res: IDataRecordType; begin + SetLength(tFields, Length(AFields)); + for i := 0 to High(AFields) do + tFields[i] := AFields[i]; + TMonitor.Enter(FRecordTypeRegistry); try - if not FRecordTypeRegistry.TryGetValue(AFields, res) then + if not FRecordTypeRegistry.TryGetValue(tFields, res) then begin - res := TImplDataRecordType.Create(AFields); - FRecordTypeRegistry.Add(AFields, res); + res := TImplDataRecordType.Create(tFields); + FRecordTypeRegistry.Add(tFields, res); end; finally TMonitor.Exit(FRecordTypeRegistry); @@ -319,17 +323,6 @@ begin Result := res; end; -class function TImplDataRecordType.GetInstance(const AFields: array of TDataRecordField): IDataRecordType; -var - tFields: TArray; - i: Integer; -begin - SetLength(tFields, Length(AFields)); - for i := 0 to High(AFields) do - tFields[i] := AFields[i]; - Result := GetInstance(tFields); -end; - class function TImplDataRecordType.GetInstance: IDataRecordType; var ctx: TRttiContext; diff --git a/Src/Data/Myc.Data.Types.Tuple.pas b/Src/Data/Myc.Data.Types.Tuple.pas index 0b9cb7f..43b6473 100644 --- a/Src/Data/Myc.Data.Types.Tuple.pas +++ b/Src/Data/Myc.Data.Types.Tuple.pas @@ -3,121 +3,283 @@ unit Myc.Data.Types.Tuple; interface uses + System.Generics.Collections, System.SysUtils, + System.Generics.Defaults, + System.Hash, Myc.Data.Types; type - // Implements the singleton tuple data type. + // Custom comparer for TArray to be used as a dictionary key. + TDataTupleTypeComparer = class(TInterfacedObject, IEqualityComparer>) + public + function Equals(const Left, Right: TArray): Boolean; reintroduce; + function GetHashCode(const Value: TArray): Integer; reintroduce; + end; + + // Implements the tuple data type. TImplDataTupleType = class(TInterfacedObject, IDataType, IDataTupleType) strict private + // Class-level registry to cache and reuse tuple type definitions. class var - FSingleton: IDataTupleType; - class constructor CreateClass; + FTupleTypeRegistry: TDictionary, IDataTupleType>; private + FItemTypes: TArray; + FEmptyValue: IDataTupleValue; // Cached instance for zero-field tuples function GetName: String; function GetKind: TDataKind; - public function CreateValue(const AItems: array of IDataValue): IDataTupleValue; - class property Singleton: IDataTupleType read FSingleton; + function GetItemCount: Integer; + function GetItemType(Idx: Integer): IDataType; + public + constructor Create(const AItemTypes: TArray); + destructor Destroy; override; + + class constructor CreateClass; + class destructor DestroyClass; + + class function GetInstance(const AItemTypes: array of IDataType): IDataTupleType; static; end; implementation +uses + System.SyncObjs, + System.Rtti; + type - // Implements the simple tuple data value. + // Implements the tuple data value. TImplDataTupleValue = class(TInterfacedObject, IDataValue, IDataTupleValue) private + FDataType: IDataTupleType; FItems: TArray; function GetDataType: IDataType; function GetAsString: string; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public - constructor Create(const AItems: array of IDataValue); + constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); end; - // Optimized singleton implementation for a tuple with 0 elements. + // Optimized implementation for a tuple with 0 fields. TImplDataTupleValue0 = class(TInterfacedObject, IDataValue, IDataTupleValue) - strict private - class var - FSingleton: IDataTupleValue; - class constructor CreateClass; private + FDataType: IDataTupleType; function GetDataType: IDataType; function GetAsString: string; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public - class property Singleton: IDataTupleValue read FSingleton; + constructor Create(const ADataType: IDataTupleType); end; - // Optimized implementation for a tuple with 1 element. + // Optimized implementation for a tuple with 1 field. TImplDataTupleValue1 = class(TInterfacedObject, IDataValue, IDataTupleValue) private + FDataType: IDataTupleType; FItem0: IDataValue; function GetDataType: IDataType; function GetAsString: string; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public - constructor Create(const AItems: array of IDataValue); + constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); end; - // Optimized implementation for a tuple with 2 elements. + // Optimized implementation for a tuple with 2 fields. TImplDataTupleValue2 = class(TInterfacedObject, IDataValue, IDataTupleValue) private + FDataType: IDataTupleType; FItem0, FItem1: IDataValue; function GetDataType: IDataType; function GetAsString: string; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public - constructor Create(const AItems: array of IDataValue); + constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); end; - // Optimized implementation for a tuple with 3 elements. + // Optimized implementation for a tuple with 3 fields. TImplDataTupleValue3 = class(TInterfacedObject, IDataValue, IDataTupleValue) private + FDataType: IDataTupleType; FItem0, FItem1, FItem2: IDataValue; function GetDataType: IDataType; function GetAsString: string; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public - constructor Create(const AItems: array of IDataValue); + constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); end; - // Optimized implementation for a tuple with 4 elements. + // Optimized implementation for a tuple with 4 fields. TImplDataTupleValue4 = class(TInterfacedObject, IDataValue, IDataTupleValue) private + FDataType: IDataTupleType; FItem0, FItem1, FItem2, FItem3: IDataValue; function GetDataType: IDataType; function GetAsString: string; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public - constructor Create(const AItems: array of IDataValue); + constructor Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); end; - // Optimized implementation for a tuple with 5 elements. - TImplDataTupleValue5 = class(TInterfacedObject, IDataValue, IDataTupleValue) - private - FItem0, FItem1, FItem2, FItem3, FItem4: IDataValue; - function GetDataType: IDataType; - function GetAsString: string; - function GetItemCount: Integer; - function GetItem(Idx: Integer): IDataValue; - public - constructor Create(const AItems: array of IDataValue); +{ TDataTupleTypeComparer } + +function TDataTupleTypeComparer.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 by interface reference. + // It's safe because all IDataType instances are managed by GetInstance singletons. + if (Left[i] <> Right[i]) then + exit(False); end; + Result := True; +end; -{ TImplDataTupleValue (generic implementation for N > 5 elements) } +function TDataTupleTypeComparer.GetHashCode(const Value: TArray): Integer; +var + dataType: IDataType; +begin + Result := 0; + for dataType in Value do + begin + // Use the hash of the interface's memory address. + // This is safe and fast due to the singleton pattern for types. + Result := Integer(PPointer(@dataType)^) xor Result; + end; +end; -constructor TImplDataTupleValue.Create(const AItems: array of IDataValue); +{ TImplDataTupleType } + +class constructor TImplDataTupleType.CreateClass; +begin + // Initialize the global registry for tuple types. + FTupleTypeRegistry := TDictionary, IDataTupleType>.Create(TDataTupleTypeComparer.Create); +end; + +class destructor TImplDataTupleType.DestroyClass; +begin + FTupleTypeRegistry.Free; + FTupleTypeRegistry := nil; +end; + +constructor TImplDataTupleType.Create(const AItemTypes: TArray); +begin + inherited Create; + FItemTypes := AItemTypes; + + // Pre-create the singleton value instance if this is a zero-field tuple. + if (Length(FItemTypes) = 0) then + FEmptyValue := TImplDataTupleValue0.Create(Self); +end; + +destructor TImplDataTupleType.Destroy; +begin + inherited; +end; + +function TImplDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue; +var + i: Integer; +begin + if (Length(AItems) <> Length(FItemTypes)) then + raise EArgumentException + .CreateFmt('Invalid number of items for this tuple type. Expected %d, but got %d.', [Length(FItemTypes), Length(AItems)]); + + for i := 0 to High(AItems) do + if not Assigned(AItems[i]) or (AItems[i].DataType <> FItemTypes[i]) then + raise EArgumentException.CreateFmt( + 'Invalid data type for item at index %d. Expected %s, but got %s.', + [i, FItemTypes[i].Name, AItems[i].DataType.Name]); + + // Use optimized implementations for tuples with 0, 1, 2, 3, or 4 fields. + case Length(FItemTypes) of + 0: Result := FEmptyValue; // Return the pre-cached instance. + 1: Result := TImplDataTupleValue1.Create(Self, AItems); + 2: Result := TImplDataTupleValue2.Create(Self, AItems); + 3: Result := TImplDataTupleValue3.Create(Self, AItems); + 4: Result := TImplDataTupleValue4.Create(Self, AItems); + else + // Use the generic implementation for tuples with more than 4 fields. + Result := TImplDataTupleValue.Create(Self, AItems); + end; +end; + +class function TImplDataTupleType.GetInstance(const AItemTypes: array of IDataType): IDataTupleType; +var + tItems: TArray; + i: Integer; + res: IDataTupleType; +begin + SetLength(tItems, Length(AItemTypes)); + for i := 0 to High(AItemTypes) do + tItems[i] := AItemTypes[i]; + + TMonitor.Enter(FTupleTypeRegistry); + try + if not FTupleTypeRegistry.TryGetValue(tItems, res) then + begin + res := TImplDataTupleType.Create(tItems); + FTupleTypeRegistry.Add(tItems, res); + end; + finally + TMonitor.Exit(FTupleTypeRegistry); + end; + Result := res; +end; + +function TImplDataTupleType.GetItemCount: Integer; +begin + Result := Length(FItemTypes); +end; + +function TImplDataTupleType.GetItemType(Idx: Integer): IDataType; +begin + Result := FItemTypes[Idx]; +end; + +function TImplDataTupleType.GetName: String; +var + i: Integer; + sb: TStringBuilder; +begin + sb := TStringBuilder.Create; + try + sb.Append('Tuple<'); + for i := 0 to High(FItemTypes) do + begin + sb.Append(FItemTypes[i].Name); + if (i < High(FItemTypes)) then + sb.Append(', '); + end; + sb.Append('>'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + +function TImplDataTupleType.GetKind: TDataKind; +begin + Result := dkTuple; +end; + +{ TImplDataTupleValue (generic implementation for N > 4 fields) } + +constructor TImplDataTupleValue.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); var i: Integer; begin inherited Create; + FDataType := ADataType; SetLength(FItems, Length(AItems)); for i := 0 to High(AItems) do FItems[i] := AItems[i]; @@ -125,7 +287,7 @@ end; function TImplDataTupleValue.GetDataType: IDataType; begin - Result := TImplDataTupleType.Singleton; + Result := FDataType; end; function TImplDataTupleValue.GetAsString: string; @@ -161,9 +323,10 @@ end; { TImplDataTupleValue0 } -class constructor TImplDataTupleValue0.CreateClass; +constructor TImplDataTupleValue0.Create(const ADataType: IDataTupleType); begin - FSingleton := TImplDataTupleValue0.Create; + inherited Create; + FDataType := ADataType; end; function TImplDataTupleValue0.GetAsString: string; @@ -173,7 +336,7 @@ end; function TImplDataTupleValue0.GetDataType: IDataType; begin - Result := TImplDataTupleType.Singleton; + Result := FDataType; end; function TImplDataTupleValue0.GetItem(Idx: Integer): IDataValue; @@ -188,20 +351,21 @@ end; { TImplDataTupleValue1 } -constructor TImplDataTupleValue1.Create(const AItems: array of IDataValue); +constructor TImplDataTupleValue1.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); begin inherited Create; + FDataType := ADataType; FItem0 := AItems[0]; end; function TImplDataTupleValue1.GetAsString: string; begin - Result := '(' + FItem0.AsString + ')'; + Result := Format('(%s)', [FItem0.AsString]); end; function TImplDataTupleValue1.GetDataType: IDataType; begin - Result := TImplDataTupleType.Singleton; + Result := FDataType; end; function TImplDataTupleValue1.GetItem(Idx: Integer): IDataValue; @@ -220,21 +384,34 @@ end; { TImplDataTupleValue2 } -constructor TImplDataTupleValue2.Create(const AItems: array of IDataValue); +constructor TImplDataTupleValue2.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); begin inherited Create; + FDataType := ADataType; FItem0 := AItems[0]; FItem1 := AItems[1]; end; function TImplDataTupleValue2.GetAsString: string; +var + sb: TStringBuilder; begin - Result := '(' + FItem0.AsString + ', ' + FItem1.AsString + ')'; + sb := TStringBuilder.Create; + try + sb.Append('('); + sb.Append(FItem0.AsString); + sb.Append(', '); + sb.Append(FItem1.AsString); + sb.Append(')'); + Result := sb.ToString; + finally + sb.Free; + end; end; function TImplDataTupleValue2.GetDataType: IDataType; begin - Result := TImplDataTupleType.Singleton; + Result := FDataType; end; function TImplDataTupleValue2.GetItem(Idx: Integer): IDataValue; @@ -254,9 +431,10 @@ end; { TImplDataTupleValue3 } -constructor TImplDataTupleValue3.Create(const AItems: array of IDataValue); +constructor TImplDataTupleValue3.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); begin inherited Create; + FDataType := ADataType; FItem0 := AItems[0]; FItem1 := AItems[1]; FItem2 := AItems[2]; @@ -283,7 +461,7 @@ end; function TImplDataTupleValue3.GetDataType: IDataType; begin - Result := TImplDataTupleType.Singleton; + Result := FDataType; end; function TImplDataTupleValue3.GetItem(Idx: Integer): IDataValue; @@ -304,9 +482,10 @@ end; { TImplDataTupleValue4 } -constructor TImplDataTupleValue4.Create(const AItems: array of IDataValue); +constructor TImplDataTupleValue4.Create(const ADataType: IDataTupleType; const AItems: array of IDataValue); begin inherited Create; + FDataType := ADataType; FItem0 := AItems[0]; FItem1 := AItems[1]; FItem2 := AItems[2]; @@ -336,7 +515,7 @@ end; function TImplDataTupleValue4.GetDataType: IDataType; begin - Result := TImplDataTupleType.Singleton; + Result := FDataType; end; function TImplDataTupleValue4.GetItem(Idx: Integer): IDataValue; @@ -356,95 +535,4 @@ begin Result := 4; end; -{ TImplDataTupleValue5 } - -constructor TImplDataTupleValue5.Create(const AItems: array of IDataValue); -begin - inherited Create; - FItem0 := AItems[0]; - FItem1 := AItems[1]; - FItem2 := AItems[2]; - FItem3 := AItems[3]; - FItem4 := AItems[4]; -end; - -function TImplDataTupleValue5.GetAsString: string; -var - sb: TStringBuilder; -begin - sb := TStringBuilder.Create; - try - sb.Append('('); - sb.Append(FItem0.AsString); - sb.Append(', '); - sb.Append(FItem1.AsString); - sb.Append(', '); - sb.Append(FItem2.AsString); - sb.Append(', '); - sb.Append(FItem3.AsString); - sb.Append(', '); - sb.Append(FItem4.AsString); - sb.Append(')'); - Result := sb.ToString; - finally - sb.Free; - end; -end; - -function TImplDataTupleValue5.GetDataType: IDataType; -begin - Result := TImplDataTupleType.Singleton; -end; - -function TImplDataTupleValue5.GetItem(Idx: Integer): IDataValue; -begin - case Idx of - 0: Result := FItem0; - 1: Result := FItem1; - 2: Result := FItem2; - 3: Result := FItem3; - 4: Result := FItem4; - else - raise EArgumentException.Create('Index out of bounds'); - end; -end; - -function TImplDataTupleValue5.GetItemCount: Integer; -begin - Result := 5; -end; - -{ TImplDataTupleType } - -class constructor TImplDataTupleType.CreateClass; -begin - FSingleton := TImplDataTupleType.Create; -end; - -function TImplDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue; -begin - // Use optimized implementations for tuples with 0 to 5 elements. - case Length(AItems) of - 0: Result := TImplDataTupleValue0.Singleton; - 1: Result := TImplDataTupleValue1.Create(AItems); - 2: Result := TImplDataTupleValue2.Create(AItems); - 3: Result := TImplDataTupleValue3.Create(AItems); - 4: Result := TImplDataTupleValue4.Create(AItems); - 5: Result := TImplDataTupleValue5.Create(AItems); - else - // Use the generic implementation for tuples with more than 5 elements. - Result := TImplDataTupleValue.Create(AItems); - end; -end; - -function TImplDataTupleType.GetName: String; -begin - Result := 'Tuple'; -end; - -function TImplDataTupleType.GetKind: TDataKind; -begin - Result := dkTuple; -end; - end. diff --git a/Src/Data/Myc.Data.Types.pas b/Src/Data/Myc.Data.Types.pas index 1ac17bb..8832511 100644 --- a/Src/Data/Myc.Data.Types.pas +++ b/Src/Data/Myc.Data.Types.pas @@ -591,11 +591,11 @@ type 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 TupleOf(const AItemTypes: array of IDataType): TDataType.TTuple; overload; static; + class function TupleOf(const AItemValues: array of IDataValue): TDataType.TTuple.TValue; overload; static; class function MethodOf(const AArgType, AResultType: IDataType): TDataType.TMethod; static; class function ArrayOf(const AElementType: IDataType): TDataType.TArray; static; class function VectorOf(const AElementType: IDataType; const AElementCount: Integer): TDataType.TVector; 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 RecordOf: TDataType.TRecord; overload; static; class function EnumOf(const AName: string; const AIdentifiers: array of string): TDataType.TEnum; static; @@ -669,7 +669,7 @@ var scaleValue: Integer; begin scaleValue := GetDataType.Scale; - if scaleValue > 0 then + if (scaleValue > 0) then Result := FDecimalValue.Value / Power(10, scaleValue) else Result := FDecimalValue.Value; @@ -1031,7 +1031,7 @@ var idx: Integer; begin idx := IndexOf(AIdentifier); - if idx < 0 then + if (idx < 0) then raise EArgumentException.CreateFmt('Unknown identifier: %s', [AIdentifier]); Result := CreateValue(idx); end; @@ -1454,11 +1454,6 @@ begin Result.Create(TImplDataOrdinalType.Singleton); end; -class function TDataType.RecordOf(const Fields: TArray): TDataType.TRecord; -begin - Result.Create(TImplDataRecordType.GetInstance(Fields)); -end; - class function TDataType.RecordOf(const Fields: array of TDataRecordField): TDataType.TRecord; begin Result.Create(TImplDataRecordType.GetInstance(Fields)); @@ -1479,9 +1474,31 @@ begin Result.Create(TImplDataTimestampType.Singleton); end; -class function TDataType.Tuple: TDataType.TTuple; +class function TDataType.TupleOf(const AItemTypes: array of IDataType): TDataType.TTuple; +var + tItems: TArray; + i: Integer; begin - Result.Create(TImplDataTupleType.Singleton); + SetLength(tItems, Length(AItemTypes)); + for i := 0 to High(AItemTypes) do + tItems[i] := AItemTypes[i]; + + // Use the instance manager to get a tuple type for the given element types. + Result.Create(TImplDataTupleType.GetInstance(tItems)); +end; + +class function TDataType.TupleOf(const AItemValues: array of IDataValue): TDataType.TTuple.TValue; +var + tItems: TArray; + i: Integer; +begin + SetLength(tItems, Length(AItemValues)); + for i := 0 to High(AItemValues) do + tItems[i] := AItemValues[i].DataType; + + var tTuple := TImplDataTupleType.GetInstance(tItems); + + Result := tTuple.CreateValue(AItemValues); end; class function TDataType.VectorOf(const AElementType: IDataType; const AElementCount: Integer): TDataType.TVector; diff --git a/Src/Data/TestDataTypes.pas b/Src/Data/TestDataTypes.pas index 7c7de74..31a12a1 100644 --- a/Src/Data/TestDataTypes.pas +++ b/Src/Data/TestDataTypes.pas @@ -172,7 +172,7 @@ begin floatValue := TDataType.Float.CreateValue(45.67); // --- 2. Test Value Creation and basic properties --- - tuple1 := TDataType.Tuple.CreateValue([intValue, floatValue]); + tuple1 := TDataType.TupleOf([intValue, floatValue]); Assert.IsNotNull(IDataTupleValue(tuple1), 'Tuple value should be created'); Assert.AreEqual(2, tuple1.ItemCount, 'ItemCount should be on the value'); @@ -181,8 +181,8 @@ begin // --- 3. Test Singleton Type Behavior --- // Create more tuples with different structures - tuple2 := TDataType.Tuple.CreateValue([TDataType.Ordinal.CreateValue(99), TDataType.Float.CreateValue(1.1)]); - tuple3 := TDataType.Tuple.CreateValue([intValue]); + tuple2 := TDataType.TupleOf([TDataType.Ordinal.CreateValue(99), TDataType.Float.CreateValue(1.1)]); + tuple3 := TDataType.TupleOf([intValue]); // Access the DataType via the underlying interface type1 := tuple1.DataType; @@ -190,11 +190,7 @@ begin type3 := tuple3.DataType; Assert.IsNotNull(type1, 'DataType interface should be accessible'); - Assert.AreEqual('Tuple', type1.Name, 'The type name for all tuples should be Tuple'); - - // The core test: All tuples, regardless of their content, must share the exact same singleton type object. - Assert.AreSame(type1, type2, 'All tuple types should be the same singleton instance'); - Assert.AreSame(type1, type3, 'All tuple types should be the same singleton instance'); + Assert.AreEqual('Tuple', type1.Name, 'The type name for all tuples should be Tuple'); end; procedure TMyTestObject.TestTuples_Generic; @@ -204,7 +200,7 @@ var begin // Test the generic implementation for tuples with > 5 elements. tupleValue := - TDataType.Tuple.CreateValue( + TDataType.TupleOf( [ TDataType.Ordinal.CreateValue(1), TDataType.Ordinal.CreateValue(2), @@ -542,7 +538,7 @@ begin Assert.AreEqual('', IDataValue(recordValue).AsString, 'Record AsString incorrect'); // Tuple - tupleValue := TDataType.Tuple.CreateValue([TDataType.Ordinal.CreateValue(10), TDataType.Text.CreateValue('Tuple')]); + tupleValue := TDataType.TupleOf([TDataType.Ordinal.CreateValue(10), TDataType.Text.CreateValue('Tuple')]); Assert.AreEqual('(10, Tuple)', IDataValue(tupleValue).AsString, 'Tuple AsString incorrect'); end;