From 5376f8924a08a95be9f09ef2f448ef9823d34fd3 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Tue, 9 Dec 2025 13:55:09 +0100 Subject: [PATCH] IScalarRecord --- Src/AST/Myc.Ast.Evaluator.pas | 28 ++------ Src/Data/Myc.Data.Keyword.pas | 7 -- Src/Data/Myc.Data.Scalar.pas | 118 ++++++++++++++++++---------------- Src/Data/Myc.Data.Value.pas | 34 +++++----- 4 files changed, 86 insertions(+), 101 deletions(-) diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index bb4b0e1..4fdd94f 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -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).'); diff --git a/Src/Data/Myc.Data.Keyword.pas b/Src/Data/Myc.Data.Keyword.pas index 67b4136..8a78f2b 100644 --- a/Src/Data/Myc.Data.Keyword.pas +++ b/Src/Data/Myc.Data.Keyword.pas @@ -74,7 +74,6 @@ type FFields: TArray>; FFirst, FLast: Integer; - function GetItem(Idx: Integer): TPair; function GetItems(Idx: Integer): TPair; 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.TKeywordMapping.GetItem(Idx: Integer): TPair; -begin - Result := FFields[Idx]; -end; - // Interface method for Items property function TKeywordMappingRegistry.TKeywordMapping.GetItems(Idx: Integer): TPair; begin diff --git a/Src/Data/Myc.Data.Scalar.pas b/Src/Data/Myc.Data.Scalar.pas index 19c7596..c45d60d 100644 --- a/Src/Data/Myc.Data.Scalar.pas +++ b/Src/Data/Myc.Data.Scalar.pas @@ -146,20 +146,18 @@ type IScalarRecord = IKeywordMapping; TScalarRecordRegistry = TKeywordMappingRegistry; - // A record of scalar values, based on a definition. - TScalarRecord = record - public - constructor Create(const ADef: IScalarRecordDefinition; const AFields: TArray); - function GetDef: IScalarRecordDefinition; inline; - function GetFields: TArray; inline; - function GetKeys(const Key: IKeyword): TScalar; - property Def: IScalarRecordDefinition read GetDef; - property Fields: TArray read GetFields; - property Keys[const Key: IKeyword]: TScalar read GetKeys; default; - strict private + TScalarRecord = class(TInterfacedObject, IScalarRecord) + private FDef: IScalarRecordDefinition; - FFields: TArray; - end deprecated; + FData: TArray; + + function IndexOf(const Key: IKeyword): Integer; + function GetCount: Integer; + function GetItems(Idx: Integer): TPair; + function GetFields(const Key: IKeyword): TScalar; + public + constructor Create(const ADef: IScalarRecordDefinition; const AData: TArray); + 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); -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; -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); +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; +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); diff --git a/Src/Data/Myc.Data.Value.pas b/Src/Data/Myc.Data.Value.pas index d88db99..ef5bc3e 100644 --- a/Src/Data/Myc.Data.Value.pas +++ b/Src/Data/Myc.Data.Value.pas @@ -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; 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; 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).Value; + Result := IScalarRecord(FInterface); end; function TDataValue.AsGenericRecord: IKeywordMapping; @@ -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.Create(AValue); + Result.FKind := vkScalarRecord; + Result.FInterface := AValue; end; class function TDataValue.FromGenericRecord(const AValue: IKeywordMapping): 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('', [sb.ToString]); + Result := Format('', [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';