IScalarRecord

This commit is contained in:
Michael Schimmel
2025-12-09 13:55:09 +01:00
parent 76c92fa355
commit 5376f8924a
4 changed files with 86 additions and 101 deletions
+6 -22
View File
@@ -338,13 +338,10 @@ begin
case seriesVar.Kind of case seriesVar.Kind of
vkRecordSeries: vkRecordSeries:
begin begin
if (itemValue.Kind <> vkRecord) then if (itemValue.Kind <> vkScalarRecord) then
raise EEvaluatorException.Create('Can only add record values to a TScalarRecordSeries.'); raise EEvaluatorException.Create('Can only add record values to a TScalarRecordSeries.');
with seriesVar.AsRecordSeries do seriesVar.AsRecordSeries.Add(itemValue.AsScalarRecord, lookback);
begin
Add(itemValue.AsRecord, lookback);
end;
end; end;
else else
raise EEvaluatorException.Create('"add" operation is only supported for series types.'); raise EEvaluatorException.Create('"add" operation is only supported for series types.');
@@ -414,7 +411,6 @@ var
key: IKeyword; key: IKeyword;
memberSeries: ISeries; memberSeries: ISeries;
scalarValue: TScalar; scalarValue: TScalar;
rec: TScalarRecord;
begin begin
baseValue := Node.Base.Accept(Self); baseValue := Node.Base.Accept(Self);
indexValue := Node.Index.Accept(Self); indexValue := Node.Index.Accept(Self);
@@ -450,8 +446,7 @@ begin
values[i] := scalarValue.Value; values[i] := scalarValue.Value;
end; end;
rec := TScalarRecord.Create(recSeries.Def, values); Result := TDataValue.FromScalarRecord(TScalarRecord.Create(recSeries.Def, values));
Result := TDataValue.FromRecord(rec);
end; end;
else else
raise EEvaluatorException.Create('Indexer `[]` is not supported for this value type.'); raise EEvaluatorException.Create('Indexer `[]` is not supported for this value type.');
@@ -466,18 +461,8 @@ begin
case baseValue.Kind of case baseValue.Kind of
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries[Node.Member.Value]); vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries[Node.Member.Value]);
vkScalarRecord: Result := TDataValue(baseValue.AsScalarRecord.Fields[Node.Member.Value]);
vkRecord: Result := TDataValue(baseValue.AsRecord[Node.Member.Value]); vkGenericRecord: Result := TDataValue(baseValue.AsGenericRecord.Fields[Node.Member.Value]);
vkGenericRecord:
begin
var rec := baseValue.AsGenericRecord;
var fieldIndex := rec.IndexOf(Node.Member.Value);
if fieldIndex < 0 then
raise EEvaluatorException.CreateFmt('Member ":%s" not found in record.', [Node.Member.Value.Name]);
Result := rec.Items[fieldIndex].Value;
end;
else else
raise EEvaluatorException.Create('Member access operator `.` is not supported for this value type.'); raise EEvaluatorException.Create('Member access operator `.` is not supported for this value type.');
end; end;
@@ -512,8 +497,7 @@ begin
values[i] := valData.AsScalar.Value; values[i] := valData.AsScalar.Value;
end; end;
var rec := TScalarRecord.Create(Node.ScalarDefinition, values); Result := TDataValue.FromScalarRecord(TScalarRecord.Create(Node.ScalarDefinition, values));
Result := TDataValue.FromRecord(rec);
end end
else else
raise EEvaluatorException.Create('RecordLiteral has no definition (Binder/TypeChecker failure).'); raise EEvaluatorException.Create('RecordLiteral has no definition (Binder/TypeChecker failure).');
-7
View File
@@ -74,7 +74,6 @@ type
FFields: TArray<TPair<IKeyword, T>>; FFields: TArray<TPair<IKeyword, T>>;
FFirst, FLast: Integer; FFirst, FLast: Integer;
function GetItem(Idx: Integer): TPair<IKeyword, T>;
function GetItems(Idx: Integer): TPair<IKeyword, T>; function GetItems(Idx: Integer): TPair<IKeyword, T>;
function GetFields(const Key: IKeyword): T; function GetFields(const Key: IKeyword): T;
function GetCount: Integer; function GetCount: Integer;
@@ -221,12 +220,6 @@ begin
Result := Length(FFields); Result := Length(FFields);
end; end;
// Internal helper required for interface implementation match if defined in stricter mode
function TKeywordMappingRegistry<T>.TKeywordMapping.GetItem(Idx: Integer): TPair<IKeyword, T>;
begin
Result := FFields[Idx];
end;
// Interface method for Items property // Interface method for Items property
function TKeywordMappingRegistry<T>.TKeywordMapping.GetItems(Idx: Integer): TPair<IKeyword, T>; function TKeywordMappingRegistry<T>.TKeywordMapping.GetItems(Idx: Integer): TPair<IKeyword, T>;
begin begin
+63 -55
View File
@@ -146,20 +146,18 @@ type
IScalarRecord = IKeywordMapping<TScalar>; IScalarRecord = IKeywordMapping<TScalar>;
TScalarRecordRegistry = TKeywordMappingRegistry<TScalar.TKind>; TScalarRecordRegistry = TKeywordMappingRegistry<TScalar.TKind>;
// A record of scalar values, based on a definition. TScalarRecord = class(TInterfacedObject, IScalarRecord)
TScalarRecord = record private
public
constructor Create(const ADef: IScalarRecordDefinition; const AFields: TArray<TScalar.TValue>);
function GetDef: IScalarRecordDefinition; inline;
function GetFields: TArray<TScalar.TValue>; inline;
function GetKeys(const Key: IKeyword): TScalar;
property Def: IScalarRecordDefinition read GetDef;
property Fields: TArray<TScalar.TValue> read GetFields;
property Keys[const Key: IKeyword]: TScalar read GetKeys; default;
strict private
FDef: IScalarRecordDefinition; FDef: IScalarRecordDefinition;
FFields: TArray<TScalar.TValue>; FData: TArray<TScalar.TValue>;
end deprecated;
function IndexOf(const Key: IKeyword): Integer;
function GetCount: Integer;
function GetItems(Idx: Integer): TPair<IKeyword, TScalar>;
function GetFields(const Key: IKeyword): TScalar;
public
constructor Create(const ADef: IScalarRecordDefinition; const AData: TArray<TScalar.TValue>);
end;
ISeries = interface ISeries = interface
{$region 'private'} {$region 'private'}
@@ -197,7 +195,7 @@ type
function GetTotalCount: Int64; function GetTotalCount: Int64;
function GetFields(const Key: IKeyword): ISeries; function GetFields(const Key: IKeyword): ISeries;
{$endregion} {$endregion}
procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1); procedure Add(const Item: IScalarRecord; Lookback: Int64 = -1);
procedure AddRaw(const Data; Lookback: Int64 = -1); procedure AddRaw(const Data; Lookback: Int64 = -1);
property Count: Int64 read GetCount; property Count: Int64 read GetCount;
property Def: IScalarRecordDefinition read GetDef; property Def: IScalarRecordDefinition read GetDef;
@@ -233,7 +231,7 @@ type
public public
constructor Create(const ADef: IScalarRecordDefinition); constructor Create(const ADef: IScalarRecordDefinition);
destructor Destroy; override; destructor Destroy; override;
procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1); procedure Add(const Item: IScalarRecord; Lookback: Int64 = -1);
// Copies data directly from the Data reference (zero-copy wrapper) // Copies data directly from the Data reference (zero-copy wrapper)
procedure AddRaw(const Data; Lookback: Int64 = -1); procedure AddRaw(const Data; Lookback: Int64 = -1);
@@ -801,36 +799,6 @@ begin
Items := AItems; Items := AItems;
end; end;
{ TScalarRecord }
constructor TScalarRecord.Create(const ADef: IScalarRecordDefinition; const AFields: TArray<TScalar.TValue>);
begin
Assert(ADef.Count = Length(AFields), 'Field definition and value count must match.');
FDef := ADef;
FFields := AFields;
end;
function TScalarRecord.GetDef: IScalarRecordDefinition;
begin
Result := FDef;
end;
function TScalarRecord.GetFields: TArray<TScalar.TValue>;
begin
Result := FFields;
end;
function TScalarRecord.GetKeys(const Key: IKeyword): TScalar;
var
idx: Integer;
begin
idx := FDef.IndexOf(Key);
if idx < 0 then
raise EArgumentException.CreateFmt('Field ":%s" not found in record definition.', [Key.Name]);
Result.Create(FDef[idx].Value, FFields[idx]);
end;
{ TScalarRecordSeries } { TScalarRecordSeries }
constructor TScalarRecordSeries.Create(const ADef: IScalarRecordDefinition); constructor TScalarRecordSeries.Create(const ADef: IScalarRecordDefinition);
@@ -850,22 +818,23 @@ begin
inherited; inherited;
end; end;
procedure TScalarRecordSeries.Add(const Item: TScalarRecord; Lookback: Int64 = -1); procedure TScalarRecordSeries.Add(const Item: IScalarRecord; Lookback: Int64 = -1);
begin begin
FArray.Add(Item.Fields, FDef.Count * Integer(Lookback)); var lb := FDef.Count * Integer(Lookback);
for var i := 0 to FDef.Count - 1 do
FArray.Add(Item[i].Value.Value, lb);
inc(FTotalCount); inc(FTotalCount);
end; end;
procedure TScalarRecordSeries.AddRaw(const Data; Lookback: Int64 = -1); procedure TScalarRecordSeries.AddRaw(const Data; Lookback: Int64 = -1);
type
TValueArr = array[0..MaxInt div SizeOf(TScalar.TValue) - 1] of TScalar.TValue;
PValueArr = ^TValueArr;
var
len: Integer;
begin begin
len := FDef.Count; var lb := FDef.Count * Integer(Lookback);
// Use Slice to pass the raw data as an open array without copying. var P: TScalar.PValue := @Data;
FArray.Add(Slice(PValueArr(@Data)^, len), len * Integer(Lookback)); for var i := 0 to FDef.Count - 1 do
begin
FArray.Add(P^, lb);
inc(P);
end;
inc(FTotalCount); inc(FTotalCount);
end; end;
@@ -1042,6 +1011,45 @@ begin
Result := TScalar.FromInt64(sourceIndex); Result := TScalar.FromInt64(sourceIndex);
end; end;
{ TScalarRecord }
constructor TScalarRecord.Create(const ADef: IScalarRecordDefinition; const AData: TArray<TScalar.TValue>);
begin
inherited Create;
FDef := ADef;
FData := AData;
end;
function TScalarRecord.GetCount: Integer;
begin
Result := FDef.Count;
end;
function TScalarRecord.GetFields(const Key: IKeyword): TScalar;
var
idx: Integer;
begin
idx := FDef.IndexOf(Key);
if idx < 0 then
exit(Default(TScalar));
// Return the raw TValue at the offset
Result.Kind := FDef[idx].Value;
Result.Value := FData[idx];
end;
function TScalarRecord.GetItems(Idx: Integer): TPair<IKeyword, TScalar>;
begin
Result.Key := FDef[Idx].Key;
Result.Value.Kind := FDef[idx].Value;
Result.Value.Value := FData[idx];
end;
function TScalarRecord.IndexOf(const Key: IKeyword): Integer;
begin
Result := FDef.IndexOf(Key);
end;
initialization initialization
Assert(sizeof(TScalar.TValue) = 8); Assert(sizeof(TScalar.TValue) = 8);
+17 -17
View File
@@ -9,7 +9,8 @@ uses
Myc.Utils, Myc.Utils,
Myc.Data.Scalar, Myc.Data.Scalar,
Myc.Data.Series, Myc.Data.Series,
Myc.Data.Keyword; Myc.Data.Keyword,
Myc.Trade.DataFeed;
type type
TDataValueKind = ( TDataValueKind = (
@@ -18,7 +19,7 @@ type
vkText, vkText,
vkSeries, vkSeries,
vkRecordSeries, vkRecordSeries,
vkRecord, vkScalarRecord,
vkGenericRecord, vkGenericRecord,
vkManagedObject, vkManagedObject,
vkMethod, vkMethod,
@@ -78,7 +79,7 @@ type
class function Map(const SourceSeries: ISeries; const MapperFunc: TFunc): ISeries; static; class function Map(const SourceSeries: ISeries; const MapperFunc: TFunc): ISeries; static;
class function FromRecordSeries(const AValue: IRecordSeries): TDataValue; static; inline; class function FromRecordSeries(const AValue: IRecordSeries): TDataValue; static; inline;
class function FromRecord(const [ref] AValue: TScalarRecord): TDataValue; static; inline; class function FromScalarRecord(const AValue: IScalarRecord): TDataValue; static; inline;
class function FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue; static; inline; class function FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue; static; inline;
class function FromSeries(const AValue: ISeries): TDataValue; static; inline; class function FromSeries(const AValue: ISeries): TDataValue; static; inline;
@@ -86,7 +87,7 @@ type
function AsMethod: TFunc; inline; function AsMethod: TFunc; inline;
function AsText: String; inline; function AsText: String; inline;
function AsRecordSeries: IRecordSeries; inline; function AsRecordSeries: IRecordSeries; inline;
function AsRecord: TScalarRecord; inline; function AsScalarRecord: IScalarRecord; inline;
function AsGenericRecord: IKeywordMapping<TDataValue>; inline; function AsGenericRecord: IKeywordMapping<TDataValue>; inline;
function AsSeries: ISeries; inline; function AsSeries: ISeries; inline;
function AsObject: TObject; overload; inline; function AsObject: TObject; overload; inline;
@@ -190,11 +191,11 @@ begin
Result := Pointer(FScalar.Value.AsInt64); Result := Pointer(FScalar.Value.AsInt64);
end; end;
function TDataValue.AsRecord: TScalarRecord; function TDataValue.AsScalarRecord: IScalarRecord;
begin begin
if (FKind <> vkRecord) then if (FKind <> vkScalarRecord) then
raise EInvalidCast.Create('Cannot read value as Record.'); raise EInvalidCast.Create('Cannot read value as Record.');
Result := (FInterface as TVal<TScalarRecord>).Value; Result := IScalarRecord(FInterface);
end; end;
function TDataValue.AsGenericRecord: IKeywordMapping<TDataValue>; function TDataValue.AsGenericRecord: IKeywordMapping<TDataValue>;
@@ -253,10 +254,10 @@ begin
Result.FInterface := AValue; Result.FInterface := AValue;
end; end;
class function TDataValue.FromRecord(const [ref] AValue: TScalarRecord): TDataValue; class function TDataValue.FromScalarRecord(const AValue: IScalarRecord): TDataValue;
begin begin
Result.FKind := vkRecord; Result.FKind := vkScalarRecord;
Result.FInterface := TVal<TScalarRecord>.Create(AValue); Result.FInterface := AValue;
end; end;
class function TDataValue.FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue; class function TDataValue.FromGenericRecord(const AValue: IKeywordMapping<TDataValue>): TDataValue;
@@ -333,24 +334,23 @@ begin
sb.Free; sb.Free;
end; end;
end; end;
vkRecord: vkScalarRecord:
begin begin
var rec := AsRecord; var rec := AsScalarRecord;
sb := TStringBuilder.Create; sb := TStringBuilder.Create;
try try
sb.Append('{'); sb.Append('{');
first := True; first := True;
for i := 0 to rec.Def.Count - 1 do for i := 0 to rec.Count - 1 do
begin begin
if not first then if not first then
sb.Append(','); sb.Append(',');
field := rec.Def[i]; sb.Append(rec[i].Key.Name);
sb.Append(field.Key.Name);
first := False; first := False;
end; end;
sb.Append('}'); sb.Append('}');
// Add field names for records // Add field names for records
Result := Format('<record%s>', [sb.ToString]); Result := Format('<scalar_record%s>', [sb.ToString]);
finally finally
sb.Free; sb.Free;
end; end;
@@ -434,7 +434,7 @@ begin
vkText: Result := 'Text'; vkText: Result := 'Text';
vkSeries: Result := 'Series'; vkSeries: Result := 'Series';
vkRecordSeries: Result := 'RecordSeries'; vkRecordSeries: Result := 'RecordSeries';
vkRecord: Result := 'Record'; vkScalarRecord: Result := 'ScalarRecord';
vkGenericRecord: Result := 'GenericRecord'; vkGenericRecord: Result := 'GenericRecord';
vkMethod: Result := 'Method'; vkMethod: Result := 'Method';
vkInterface: Result := 'Interface'; vkInterface: Result := 'Interface';