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