Refactoring Keyword mapping, introducing tuples

This commit is contained in:
Michael Schimmel
2026-01-04 15:07:02 +01:00
parent f1733c41a0
commit 0a7a6ea2d0
13 changed files with 245 additions and 93 deletions
+2 -2
View File
@@ -629,7 +629,7 @@ begin
idx := baseType.Definition.IndexOf(M.Member.Value);
if idx >= 0 then
begin
var fieldType := TTypes.FromScalarKind(baseType.Definition.Items[idx].Value);
var fieldType := TTypes.FromScalarKind(baseType.Definition[idx]);
if baseType.Kind = stRecordSeries then
resType := TTypes.CreateSeries(fieldType)
else
@@ -819,7 +819,7 @@ begin
var def := streamType.Definition;
var idx := def.IndexOf(sel.Value);
if idx >= 0 then
inferredType := TTypes.FromScalarKind(def.Items[idx].Value);
inferredType := TTypes.FromScalarKind(def[idx]);
end
else if streamType.Kind = stSeries then
begin
+5 -5
View File
@@ -343,7 +343,7 @@ begin
SetLength(vals, rs.Def.Count);
var i64 := idx.AsScalar.Value.AsInt64;
for var k := 0 to rs.Def.Count - 1 do
vals[k] := rs.Fields[rs.Def.Items[k].Key].Items[Integer(i64)].Value;
vals[k] := rs.Fields[rs.Def.Keywords[k]].Items[Integer(i64)].Value;
Result := TDataValue.FromScalarRecord(TScalarRecord.Create(rs.Def, vals));
end
else
@@ -360,7 +360,7 @@ begin
case base.Kind of
vkRecordSeries: Result := TDataValue.FromSeries(base.AsRecordSeries.Fields[N.Member.Value]);
vkScalarRecord: Result := TDataValue(base.AsScalarRecord.Fields[N.Member.Value]);
vkGenericRecord: Result := TDataValue(base.AsGenericRecord.Fields[N.Member.Value]);
vkRecord: Result := TDataValue(base.AsRecord.Fields[N.Member.Value]);
vkStream: Result := TDataValue.FromSeries(base.AsStream.Series.Fields[N.Member.Value]);
else
raise EEvaluatorException.Create('Member error');
@@ -442,7 +442,7 @@ begin
begin
var key := selectors[k].Value;
var idx := srcDef.IndexOf(key);
config[i][k] := TScalarRecordField.Create(key, srcDef.Items[idx].Value);
config[i][k] := TScalarRecordField.Create(key, srcDef[idx]);
end;
end;
lambdaFunc := Visit(N.Transformation).AsMethod();
@@ -461,8 +461,8 @@ begin
if res.IsVoid then
exit(False);
var rec := res.AsScalarRecord;
for k := 0 to rec.Def.Count - 1 do
R[k] := rec.Items[k].Value.Value;
for k := 0 to rec.Count - 1 do
R[k] := rec.Items[k].Value;
Result := True;
end;
Result := TDataValue.FromStream(TPipeStream.Create(config, outputDef, sources, pipeAdapter));
+10 -19
View File
@@ -646,7 +646,7 @@ begin
for i := 0 to Self.FDefinition.Count - 1 do
begin
if (Self.FDefinition[i].Key <> otherDef[i].Key) or (Self.FDefinition[i].Value <> otherDef[i].Value) then
if (Self.FDefinition.Keywords[i] <> otherDef.Keywords[i]) or (Self.FDefinition[i] <> otherDef[i]) then
exit(False);
end;
@@ -656,7 +656,6 @@ end;
function TRecordType.GetHashCode: Integer;
var
i, h: Integer;
field: TPair<IKeyword, TScalar.TKind>;
begin
// Seed with Kind
Result := THashBobJenkins.GetHashValue(FKind, SizeOf(TStaticTypeKind), 0);
@@ -665,15 +664,13 @@ begin
begin
for i := 0 to FDefinition.Count - 1 do
begin
field := FDefinition[i];
// Hash Key
// Keyword identities are unique by pointer, but let's hash their internal ID/Index for safety
h := field.Key.Idx;
h := FDefinition.Keywords[i].Idx;
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
// Hash Field Type Kind
h := Ord(field.Value);
h := Ord(FDefinition[i]);
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
end;
end;
@@ -682,15 +679,13 @@ end;
function TRecordType.ToString: string;
var
i: Integer;
field: TPair<IKeyword, TScalar.TKind>;
begin
Result := GetKind.ToString + '{';
if Assigned(FDefinition) then
begin
for i := 0 to FDefinition.Count - 1 do
begin
field := FDefinition[i];
Result := Result + field.Key.Name + ': ' + field.Value.ToString;
Result := Result + FDefinition.Keywords[i].Name + ': ' + FDefinition[i].ToString;
if i < FDefinition.Count - 1 then
Result := Result + ', ';
end;
@@ -746,7 +741,7 @@ begin
for i := 0 to Self.FDefinition.Count - 1 do
begin
// Deep Check of Field Types
if (Self.FDefinition[i].Key <> otherDef[i].Key) or (not Self.FDefinition[i].Value.IsEqual(otherDef[i].Value)) then
if (Self.FDefinition.Keywords[i] <> otherDef.Keywords[i]) or (not Self.FDefinition[i].IsEqual(otherDef[i])) then
exit(False);
end;
@@ -756,7 +751,6 @@ end;
function TGenericRecordType.GetHashCode: Integer;
var
i, h: Integer;
field: TPair<IKeyword, IStaticType>;
begin
h := Ord(stGenericRecord);
Result := THashBobJenkins.GetHashValue(h, SizeOf(TStaticTypeKind), 0);
@@ -765,16 +759,15 @@ begin
begin
for i := 0 to FDefinition.Count - 1 do
begin
field := FDefinition[i];
// Hash Key (using Idx)
h := field.Key.Idx;
h := FDefinition.Keywords[i].Idx;
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
// Hash Value Type
if Assigned(field.Value) then
var val := FDefinition[i];
if Assigned(val) then
begin
h := field.Value.GetHashCode;
h := val.GetHashCode;
Result := THashBobJenkins.GetHashValue(h, SizeOf(Integer), Result);
end;
end;
@@ -784,15 +777,13 @@ end;
function TGenericRecordType.ToString: string;
var
i: Integer;
field: TPair<IKeyword, IStaticType>;
begin
Result := GetKind.ToString + '{';
if Assigned(FDefinition) then
begin
for i := 0 to FDefinition.Count - 1 do
begin
field := FDefinition[i];
Result := Result + field.Key.Name + ': ' + field.Value.ToString;
Result := Result + FDefinition.Keywords[i].Name + ': ' + FDefinition[i].ToString;
if i < FDefinition.Count - 1 then
Result := Result + ', ';
end;
+25 -11
View File
@@ -6,7 +6,8 @@ uses
System.SysUtils,
System.Generics.Collections,
System.Generics.Defaults,
System.SyncObjs;
System.SyncObjs,
Myc.Data.Tuple;
type
// The runtime representation of an interned keyword
@@ -49,17 +50,18 @@ type
// Defines a mapping from Keywords to a generic value T
IKeywordMapping<T> = interface
{$region 'private'}
function GetItems(Idx: Integer): TPair<IKeyword, T>;
function GetItems(Idx: Integer): T;
function GetFields(const Key: IKeyword): T;
function GetCount: Integer;
function GetKeywords(Idx: Integer): IKeyword;
{$endregion}
// Finds the 0-based index for a given keyword. Returns -1 if not found.
function IndexOf(const Key: IKeyword): Integer;
// Access value by Keyword (raises exception if not found)
property Keywords[Idx: Integer]: IKeyword read GetKeywords;
property Fields[const Key: IKeyword]: T read GetFields;
// Access Key-Value pair by Index (0..Count-1)
property Items[Idx: Integer]: TPair<IKeyword, T> read GetItems; default;
property Items[Idx: Integer]: T read GetItems; default;
property Count: Integer read GetCount;
end;
@@ -74,8 +76,9 @@ type
FFields: TArray<TPair<IKeyword, T>>;
FFirst, FLast: Integer;
function GetItems(Idx: Integer): TPair<IKeyword, T>;
function GetItems(Idx: Integer): T;
function GetFields(const Key: IKeyword): T;
function GetKeywords(Idx: Integer): IKeyword;
function GetCount: Integer;
public
// Expects AFields to be in the desired (canonical) order
@@ -100,7 +103,8 @@ type
FFields: TArray<TPair<IKeyword, T>>;
// IKeywordMapping implementation
function GetItems(Idx: Integer): TPair<IKeyword, T>;
function GetItems(Idx: Integer): T;
function GetKeywords(Idx: Integer): IKeyword;
function GetFields(const Key: IKeyword): T;
function GetCount: Integer;
public
@@ -224,9 +228,9 @@ begin
end;
// Interface method for Items property
function TKeywordMappingRegistry<T>.TKeywordMapping.GetItems(Idx: Integer): TPair<IKeyword, T>;
function TKeywordMappingRegistry<T>.TKeywordMapping.GetItems(Idx: Integer): T;
begin
Result := FFields[Idx];
Result := FFields[Idx].Value;
end;
// Interface method for Fields property
@@ -240,6 +244,11 @@ begin
Result := FFields[idx].Value;
end;
function TKeywordMappingRegistry<T>.TKeywordMapping.GetKeywords(Idx: Integer): IKeyword;
begin
Result := FFields[Idx].Key;
end;
function TKeywordMappingRegistry<T>.TKeywordMapping.IndexOf(const Key: IKeyword): Integer;
var
keyIdx, mapIdx: Integer;
@@ -312,9 +321,14 @@ begin
Result := FFields[idx].Value;
end;
function TKeywordMapping<T>.GetItems(Idx: Integer): TPair<IKeyword, T>;
function TKeywordMapping<T>.GetItems(Idx: Integer): T;
begin
Result := FFields[Idx];
Result := FFields[Idx].Value;
end;
function TKeywordMapping<T>.GetKeywords(Idx: Integer): IKeyword;
begin
Result := FFields[Idx].Key;
end;
function TKeywordMapping<T>.IndexOf(const Key: IKeyword): Integer;
+24 -28
View File
@@ -136,14 +136,7 @@ type
TScalarRecordField = TPair<IKeyword, TScalar.TKind>;
IScalarRecordDefinition = IKeywordMapping<TScalar.TKind>;
IScalarRecord = interface(IKeywordMapping<TScalar>)
{$region 'private'}
function GetDef: IScalarRecordDefinition;
{$endregion}
property Def: IScalarRecordDefinition read GetDef;
end;
TScalarRecord = class(TInterfacedObject, IKeywordMapping<TScalar>, IScalarRecord)
TScalarRecord = class(TInterfacedObject, IKeywordMapping<TScalar>)
type
TRegistry = TKeywordMappingRegistry<TScalar.TKind>;
private
@@ -152,12 +145,11 @@ type
function IndexOf(const Key: IKeyword): Integer;
function GetCount: Integer;
function GetDef: IScalarRecordDefinition;
function GetItems(Idx: Integer): TPair<IKeyword, TScalar>;
function GetItems(Idx: Integer): TScalar;
function GetFields(const Key: IKeyword): TScalar;
function GetKeywords(Idx: Integer): IKeyword;
public
constructor Create(const ADef: IScalarRecordDefinition; const AData: TArray<TScalar.TValue>);
property Def: IScalarRecordDefinition read GetDef;
end;
ISeries = interface
@@ -228,7 +220,8 @@ type
FFields: TArray<TMemberSeries>;
FTotalCount: Int64;
function GetItems(Idx: Integer): TPair<IKeyword, ISeries>;
function GetItems(Idx: Integer): ISeries;
function GetKeywords(Idx: Integer): IKeyword;
function GetCount: Integer;
function IndexOf(const Key: IKeyword): Integer;
@@ -824,8 +817,8 @@ begin
var lb := FDef.Count * Integer(Lookback);
for var i := 0 to FDef.Count - 1 do
begin
Assert(Item[i].Key = FDef[i].Key, 'Mapping does not fit series definition');
FArray.Add(Item[i].Value.Value, lb);
Assert(Item.Keywords[i] = FDef.Keywords[i], 'Mapping does not fit series definition');
FArray.Add(Item[i].Value, lb);
end;
inc(FTotalCount);
end;
@@ -875,10 +868,14 @@ begin
Result := Length(FFields);
end;
function TScalarRecordSeries.GetItems(Idx: Integer): TPair<IKeyword, ISeries>;
function TScalarRecordSeries.GetItems(Idx: Integer): ISeries;
begin
Result.Key := FDef.Items[Idx].Key;
Result.Value := FFields[Idx];
Result := FFields[Idx];
end;
function TScalarRecordSeries.GetKeywords(Idx: Integer): IKeyword;
begin
Result := FDef.Keywords[Idx];
end;
function TScalarRecordSeries.IndexOf(const Key: IKeyword): Integer;
@@ -946,7 +943,7 @@ end;
constructor TScalarRecordSeries.TMemberSeries.Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
begin
inherited Create(ARecordSeries);
FKind := ARecordSeries.FDef[AElementIdx].Value;
FKind := ARecordSeries.FDef[AElementIdx];
FOffset := sizeof(TScalar.TValue) * AElementIdx;
end;
@@ -1043,11 +1040,6 @@ begin
Result := FDef.Count;
end;
function TScalarRecord.GetDef: IScalarRecordDefinition;
begin
Result := FDef;
end;
function TScalarRecord.GetFields(const Key: IKeyword): TScalar;
var
idx: Integer;
@@ -1057,15 +1049,19 @@ begin
exit(Default(TScalar));
// Return the raw TValue at the offset
Result.Kind := FDef[idx].Value;
Result.Kind := FDef[idx];
Result.Value := FData[idx];
end;
function TScalarRecord.GetItems(Idx: Integer): TPair<IKeyword, TScalar>;
function TScalarRecord.GetItems(Idx: Integer): TScalar;
begin
Result.Key := FDef[Idx].Key;
Result.Value.Kind := FDef[idx].Value;
Result.Value.Value := FData[idx];
Result.Kind := FDef[idx];
Result.Value := FData[idx];
end;
function TScalarRecord.GetKeywords(Idx: Integer): IKeyword;
begin
Result := FDef.Keywords[Idx];
end;
function TScalarRecord.IndexOf(const Key: IKeyword): Integer;
+17
View File
@@ -0,0 +1,17 @@
unit Myc.Data.Tuple;
interface
type
ITuple<T> = interface
{$region 'private'}
function GetItems(Idx: Integer): T;
function GetCount: Integer;
{$endregion}
property Items[Idx: Integer]: T read GetItems; default;
property Count: Integer read GetCount;
end;
implementation
end.
+29 -24
View File
@@ -14,23 +14,31 @@ uses
type
TDataValueKind = (
// base types
vkVoid,
vkScalar,
vkText,
vkMethod,
// generic structured types
vkTuple,
vkRecord,
// scalar structured types
// vkScalarTuple, (later)
vkScalarRecord,
// Series and streams
vkSeries,
vkRecordSeries,
vkScalarRecord,
vkGenericRecord,
vkStream,
// internal types
vkManagedObject,
vkMethod,
vkInterface,
vkPointer,
vkStream,
vkGeneric
);
TDataValue = record
type
TTuple = TArray<TDataValue>;
TFunc = reference to function(const ArgNodes: TArray<TDataValue>): TDataValue;
private
@@ -84,7 +92,7 @@ type
class function Map(const SourceSeries: ISeries; const MapperFunc: TFunc): ISeries; static;
class function FromRecordSeries(const AValue: IWriteableScalarRecordSeries): TDataValue; static; inline;
class function FromScalarRecord(const AValue: IScalarRecord): TDataValue; static; inline;
class function FromScalarRecord(const AValue: IKeywordMapping<TScalar>): TDataValue; static; inline;
class function FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue; static; inline;
class function FromSeries(const AValue: ISeries): TDataValue; static; inline;
class function FromStream(const AStream: IStream): TDataValue; static; inline;
@@ -94,8 +102,8 @@ type
function AsMethod: TFunc; inline;
function AsText: String; inline;
function AsRecordSeries: IWriteableScalarRecordSeries; inline;
function AsScalarRecord: IScalarRecord; inline;
function AsGenericRecord: IKeywordMapping<TDataValue>; inline;
function AsScalarRecord: IKeywordMapping<TScalar>; inline;
function AsRecord: IKeywordMapping<TDataValue>; inline;
function AsSeries: ISeries; inline;
function AsObject: TObject; overload; inline;
@@ -141,7 +149,7 @@ begin
vkSeries: Result := 'Series';
vkRecordSeries: Result := 'RecordSeries';
vkScalarRecord: Result := 'ScalarRecord';
vkGenericRecord: Result := 'GenericRecord';
vkRecord: Result := 'Record';
vkManagedObject: Result := 'Object';
vkMethod: Result := 'Method';
vkInterface: Result := 'Interface';
@@ -268,7 +276,7 @@ begin
Result.FInterface := AValue;
end;
class function TDataValue.FromScalarRecord(const AValue: IScalarRecord): TDataValue;
class function TDataValue.FromScalarRecord(const AValue: IKeywordMapping<TScalar>): TDataValue;
begin
Result.FKind := vkScalarRecord;
Result.FInterface := AValue;
@@ -276,7 +284,7 @@ end;
class function TDataValue.FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue;
begin
Result.FKind := vkGenericRecord;
Result.FKind := vkRecord;
Result.FInterface := AValue;
end;
@@ -350,17 +358,17 @@ begin
Result := Pointer(FScalar.Value.AsInt64);
end;
function TDataValue.AsScalarRecord: IScalarRecord;
function TDataValue.AsScalarRecord: IKeywordMapping<TScalar>;
begin
if (FKind <> vkScalarRecord) then
raise EInvalidCast.Create('Cannot read value as Record.');
Result := IScalarRecord(FInterface);
Result := IKeywordMapping<TScalar>(FInterface);
end;
function TDataValue.AsGenericRecord: IKeywordMapping<TDataValue>;
function TDataValue.AsRecord: IKeywordMapping<TDataValue>;
begin
if (FKind <> vkGenericRecord) then
raise EInvalidCast.Create('Cannot read value as GenericRecord.');
if (FKind <> vkRecord) then
raise EInvalidCast.Create('Cannot read value as Record.');
Result := IKeywordMapping<TDataValue>(FInterface);
end;
@@ -388,7 +396,6 @@ end;
function TDataValue.ToString: String;
var
sb: TStringBuilder;
field: TScalarRecordField;
genField: TPair<IKeyword, TDataValue>;
first: Boolean;
i: Integer;
@@ -412,8 +419,7 @@ begin
begin
if not first then
sb.Append(',');
field := series.Def[i];
sb.Append(field.Key.Name);
sb.Append(series.Def.Keywords[i].Name);
first := False;
end;
sb.Append('}');
@@ -433,7 +439,7 @@ begin
begin
if not first then
sb.Append(',');
sb.Append(rec[i].Key.Name);
sb.Append(rec.Keywords[i].Name);
first := False;
end;
sb.Append('}');
@@ -442,9 +448,9 @@ begin
sb.Free;
end;
end;
vkGenericRecord:
vkRecord:
begin
var rec := AsGenericRecord;
var rec := AsRecord;
sb := TStringBuilder.Create;
try
sb.Append('{');
@@ -453,12 +459,11 @@ begin
begin
if not first then
sb.Append(',');
genField := rec[i];
sb.Append(genField.Key.Name);
sb.Append(rec.Keywords[i].Name);
first := False;
end;
sb.Append('}');
Result := Format('<generic_record%s>', [sb.ToString]);
Result := Format('<record%s>', [sb.ToString]);
finally
sb.Free;
end;