Keywords as basic scalar type

Script enhancements
This commit is contained in:
Michael Schimmel
2025-11-01 00:56:59 +01:00
parent 689dede600
commit 957171f089
7 changed files with 498 additions and 78 deletions
+105 -1
View File
@@ -22,7 +22,8 @@ type
stMethod,
stSeries,
stRecord,
stRecordSeries
stRecordSeries,
stGenericRecord
);
TStaticTypeKindHelper = record helper for TStaticTypeKind
@@ -40,6 +41,10 @@ type
property ReturnType: IStaticType read GetReturnType;
end;
// Defines a mapping for generic record fields (Key -> StaticType)
IGenericRecordDefinition = IKeywordMapping<IStaticType>;
TGenericRecordRegistry = TKeywordMappingRegistry<IStaticType>;
// Represents a complete static type definition.
IStaticType = interface
{$region 'private'}
@@ -47,6 +52,7 @@ type
function GetElementType: IStaticType;
function GetSignature: IMethodSignature;
function GetDefinition: IScalarRecordDefinition;
function GetGenericDefinition: IGenericRecordDefinition;
{$endregion}
// The kind of type (e.g., Ordinal, Series, etc.)
@@ -57,6 +63,8 @@ type
property Signature: IMethodSignature read GetSignature;
// The definition (if Kind = stRecord or stRecordSeries)
property Definition: IScalarRecordDefinition read GetDefinition;
// The definition (if Kind = stGenericRecord)
property GenericDefinition: IGenericRecordDefinition read GetGenericDefinition;
// Checks for type equality
function IsEqual(const Other: IStaticType): Boolean;
@@ -96,6 +104,7 @@ type
class function CreateMethod(const AParamTypes: TArray<IStaticType>; const AReturnType: IStaticType): IStaticType; static;
class function CreateRecord(const ADef: IScalarRecordDefinition): IStaticType; static;
class function CreateRecordSeries(const ADef: IScalarRecordDefinition): IStaticType; static;
class function CreateGenericRecord(const ADef: IGenericRecordDefinition): IStaticType; static;
class function FromScalarKind(AKind: TScalar.TKind): IStaticType; static;
end;
@@ -134,6 +143,7 @@ begin
stSeries: Result := 'Series';
stRecord: Result := 'Record';
stRecordSeries: Result := 'RecordSeries';
stGenericRecord: Result := 'GenericRecord'; // Added
else
Result := 'ErrorType';
end;
@@ -149,6 +159,7 @@ type
function GetElementType: IStaticType; virtual;
function GetSignature: IMethodSignature; virtual;
function GetDefinition: IScalarRecordDefinition; virtual;
function GetGenericDefinition: IGenericRecordDefinition; virtual; // Added
function IsEqual(const Other: IStaticType): Boolean; virtual;
function ToString: string; override;
end;
@@ -169,6 +180,12 @@ begin
Result := Default(IScalarRecordDefinition);
end;
// Added
function TAbstractStaticType.GetGenericDefinition: IGenericRecordDefinition;
begin
Result := nil;
end;
function TAbstractStaticType.IsEqual(const Other: IStaticType): Boolean;
begin
// Default implementation: types are equal if their kinds are equal.
@@ -343,6 +360,7 @@ end;
// ---
type
// Represents stRecord and stRecordSeries (Scalar Records)
TRecordType = class(TAbstractStaticType)
private
FKind: TStaticTypeKind;
@@ -414,6 +432,78 @@ begin
Result := Result + '}';
end;
// ---
// Added: Represents stGenericRecord
type
TGenericRecordType = class(TAbstractStaticType)
private
FDefinition: IGenericRecordDefinition;
public
constructor Create(const ADef: IGenericRecordDefinition);
function GetKind: TStaticTypeKind; override;
function GetGenericDefinition: IGenericRecordDefinition; override;
function IsEqual(const Other: IStaticType): Boolean; override;
function ToString: string; override;
end;
constructor TGenericRecordType.Create(const ADef: IGenericRecordDefinition);
begin
inherited Create;
FDefinition := ADef;
end;
function TGenericRecordType.GetKind: TStaticTypeKind;
begin
Result := stGenericRecord;
end;
function TGenericRecordType.GetGenericDefinition: IGenericRecordDefinition;
begin
Result := FDefinition;
end;
function TGenericRecordType.IsEqual(const Other: IStaticType): Boolean;
var
i: Integer;
begin
if (not Assigned(Other)) or (Other.Kind <> stGenericRecord) then
exit(False);
var otherDef := Other.GenericDefinition;
if (not Assigned(Self.FDefinition)) or (not Assigned(otherDef)) then
exit(False);
if Length(Self.FDefinition.Fields) <> Length(otherDef.Fields) then
exit(False);
for i := 0 to High(Self.FDefinition.Fields) do
begin
// Compare keys (fast) and static types (must use IsEqual)
if (Self.FDefinition.Fields[i].Key <> otherDef.Fields[i].Key)
or (not Self.FDefinition.Fields[i].Value.IsEqual(otherDef.Fields[i].Value)) then
exit(False);
end;
Result := True;
end;
function TGenericRecordType.ToString: string;
var
i: Integer;
begin
Result := GetKind.ToString + '{';
if Assigned(FDefinition) then
begin
for i := 0 to High(FDefinition.Fields) do
begin
Result := Result + FDefinition.Fields[i].Key.Name + ': ' + FDefinition.Fields[i].Value.ToString;
if i < High(FDefinition.Fields) then
Result := Result + ', ';
end;
end;
Result := Result + '}';
end;
{ TTypes (Factory) }
class constructor TTypes.Create;
@@ -442,6 +532,12 @@ begin
Result := TRecordType.Create(stRecordSeries, ADef);
end;
// Added
class function TTypes.CreateGenericRecord(const ADef: IGenericRecordDefinition): IStaticType;
begin
Result := TGenericRecordType.Create(ADef);
end;
class function TTypes.CreateSeries(const AElementType: IStaticType): IStaticType;
begin
Result := TSeriesType.Create(AElementType);
@@ -484,6 +580,9 @@ begin
// TODO: Implement full assignment compatibility rules (e.g., for records/series)
// Allow assigning a scalar record to a generic record? (Maybe later)
// Allow assigning a generic record to a scalar record? (If fields match)
Result := False;
end;
@@ -513,6 +612,10 @@ begin
if A.IsEqual(B) then
exit(A);
// Cannot promote stGenericRecord or stRecord with anything other than itself/unknown
if (A.Kind in [stRecord, stGenericRecord]) or (B.Kind in [stRecord, stGenericRecord]) then
raise ETypeException.CreateFmt('Cannot promote types %s and %s', [A.ToString, B.ToString]);
// Cannot promote other combinations (e.g., Ordinal and Keyword)
raise ETypeException.CreateFmt('Cannot promote types %s and %s', [A.ToString, B.ToString]);
end;
@@ -562,6 +665,7 @@ begin
TScalar.TBinaryOp.Equal, TScalar.TBinaryOp.NotEqual:
begin
// Allow equality checks for Ordinal, Float, or Keyword
// (Records are not yet supported for equality)
if not (promotedKind in [stOrdinal, stFloat, stKeyword]) then
raise ETypeException.CreateFmt(
'Comparison operator %s requires Ordinal, Float, or Keyword, but got %s after promotion',