RTL custom interface types as records
This commit is contained in:
@@ -8,9 +8,9 @@ uses
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Scalar,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes, // Provides EAstException
|
||||
Myc.Ast.Scope,
|
||||
Myc.Data.Keyword;
|
||||
Myc.Data.Keyword,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Scope;
|
||||
|
||||
type
|
||||
// Exception for runtime errors during script evaluation
|
||||
|
||||
@@ -101,6 +101,8 @@ type
|
||||
class function Add_Ordinal_Float_Float(A: Int64; B: Double): Double; static;
|
||||
[TRtlExport('+', True)]
|
||||
class function Add_Float_Ordinal_Float(A: Double; B: Int64): Double; static;
|
||||
[TRtlExport('+', True)]
|
||||
class function Add_Text_Text_Text(const A: String; const B: String): String; static;
|
||||
|
||||
// Subtract
|
||||
[TRtlExport('-', True)]
|
||||
@@ -249,7 +251,10 @@ class function TRtlFunctions.Add(const Args: TArray<TDataValue>): TDataValue;
|
||||
begin
|
||||
if Length(Args) <> 2 then
|
||||
raise EArgumentException.Create('Operator + requires 2 arguments.');
|
||||
Result := Args[0].AsScalar + Args[1].AsScalar;
|
||||
if (Args[0].Kind = vkText) and (Args[1].Kind = vkText) then
|
||||
Result := Args[0].AsText + Args[1].AsText
|
||||
else
|
||||
Result := Args[0].AsScalar + Args[1].AsScalar;
|
||||
end;
|
||||
|
||||
class function TRtlFunctions.Subtract(const Args: TArray<TDataValue>): TDataValue;
|
||||
@@ -915,6 +920,11 @@ begin
|
||||
Result := System.Abs(A);
|
||||
end;
|
||||
|
||||
class function TRtlFunctions.Add_Text_Text_Text(const A: String; const B: String): String;
|
||||
begin
|
||||
Result := A + B;
|
||||
end;
|
||||
|
||||
class function TRtlFunctions.Round_Ordinal_Ordinal(A: Int64): Int64;
|
||||
begin
|
||||
Result := A;
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
unit Myc.Ast.RTL.TypeRegistry;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Classes,
|
||||
System.Rtti,
|
||||
System.TypInfo,
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Value,
|
||||
Myc.Data.Scalar,
|
||||
Myc.Data.Keyword,
|
||||
Myc.Ast.Types,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.RTL;
|
||||
|
||||
type
|
||||
EInteropException = class(Exception);
|
||||
ERecursionException = class(EInteropException);
|
||||
|
||||
// Factory delegate for Interfaces
|
||||
TInterfaceFactoryDelegate<T: IInterface> = reference to function(const Args: TArray<TDataValue>): T;
|
||||
|
||||
// Central registry mapping Delphi Interfaces to AST static types and handling
|
||||
// runtime wrapping (Shadow Records).
|
||||
TRtlTypeRegistry = class
|
||||
private
|
||||
class var
|
||||
FCtx: TRttiContext;
|
||||
FKnownTypes: TDictionary<PTypeInfo, IStaticType>;
|
||||
FAnalysisStack: TList<PTypeInfo>;
|
||||
|
||||
// --- Analysis ---
|
||||
class function AnalyzeType(ATypeInfo: PTypeInfo): IStaticType; static;
|
||||
class function GenerateInterfaceDefinition(RType: TRttiInterfaceType): IStaticType; static;
|
||||
class procedure EnterAnalysis(Info: PTypeInfo); static;
|
||||
class procedure ExitAnalysis(Info: PTypeInfo); static;
|
||||
|
||||
// --- Marshalling ---
|
||||
class function ToTValue(const AData: TDataValue; ATargetType: TRttiType): TValue; static;
|
||||
class function FromTValue(const AValue: TValue): TDataValue; static;
|
||||
|
||||
// --- Wrapping ---
|
||||
class function WrapInstance(const Instance: IInterface; TypeInfo: PTypeInfo): TDataValue; static;
|
||||
|
||||
public
|
||||
class constructor Create;
|
||||
class destructor Destroy;
|
||||
|
||||
// --- Registration ---
|
||||
// Registers a Delphi Interface type. Raises ERecursionException on cycles.
|
||||
class procedure RegisterType<T: IInterface>; overload;
|
||||
class procedure RegisterType(Info: PTypeInfo); overload;
|
||||
|
||||
// --- Factories ---
|
||||
// Registers a factory function. T must be registered via RegisterType<T> first.
|
||||
class procedure RegisterFactory<T: IInterface>(
|
||||
const Scope: IExecutionScope;
|
||||
const FactoryName: string;
|
||||
const FactoryArgs: TArray<IStaticType>;
|
||||
const FactoryDelegate: TInterfaceFactoryDelegate<T>
|
||||
);
|
||||
|
||||
// --- Resolution ---
|
||||
class function GetStaticType(Info: PTypeInfo): IStaticType;
|
||||
class function ResolveType(RType: TRttiType): IStaticType;
|
||||
|
||||
class property Context: TRttiContext read FCtx;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Myc.Ast.RTL.Core;
|
||||
|
||||
{ TRtlTypeRegistry }
|
||||
|
||||
class constructor TRtlTypeRegistry.Create;
|
||||
begin
|
||||
FCtx := TRttiContext.Create;
|
||||
FKnownTypes := TDictionary<PTypeInfo, IStaticType>.Create;
|
||||
FAnalysisStack := TList<PTypeInfo>.Create;
|
||||
end;
|
||||
|
||||
class destructor TRtlTypeRegistry.Destroy;
|
||||
begin
|
||||
FAnalysisStack.Free;
|
||||
FKnownTypes.Free;
|
||||
FCtx.Free;
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Type Analysis
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class procedure TRtlTypeRegistry.EnterAnalysis(Info: PTypeInfo);
|
||||
begin
|
||||
if FAnalysisStack.Contains(Info) then
|
||||
raise ERecursionException.CreateFmt('Recursive type definition detected for "%s".', [string(Info.Name)]);
|
||||
FAnalysisStack.Add(Info);
|
||||
end;
|
||||
|
||||
class procedure TRtlTypeRegistry.ExitAnalysis(Info: PTypeInfo);
|
||||
begin
|
||||
FAnalysisStack.Remove(Info);
|
||||
end;
|
||||
|
||||
class procedure TRtlTypeRegistry.RegisterType<T>;
|
||||
begin
|
||||
RegisterType(TypeInfo(T));
|
||||
end;
|
||||
|
||||
class procedure TRtlTypeRegistry.RegisterType(Info: PTypeInfo);
|
||||
begin
|
||||
if FKnownTypes.ContainsKey(Info) then
|
||||
exit;
|
||||
|
||||
EnterAnalysis(Info);
|
||||
try
|
||||
var staticType := AnalyzeType(Info);
|
||||
FKnownTypes.Add(Info, staticType);
|
||||
finally
|
||||
ExitAnalysis(Info);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TRtlTypeRegistry.GetStaticType(Info: PTypeInfo): IStaticType;
|
||||
begin
|
||||
if not FKnownTypes.TryGetValue(Info, Result) then
|
||||
Result := TTypes.Unknown;
|
||||
end;
|
||||
|
||||
class function TRtlTypeRegistry.ResolveType(RType: TRttiType): IStaticType;
|
||||
begin
|
||||
if not Assigned(RType) then
|
||||
exit(TTypes.Void);
|
||||
|
||||
if FKnownTypes.TryGetValue(RType.Handle, Result) then
|
||||
exit;
|
||||
|
||||
if FAnalysisStack.Contains(RType.Handle) then
|
||||
raise ERecursionException.CreateFmt('Recursive type reference detected during resolution of "%s".', [RType.Name]);
|
||||
|
||||
case RType.TypeKind of
|
||||
tkInteger, tkInt64: Result := TTypes.Ordinal;
|
||||
tkFloat: Result := TTypes.Float;
|
||||
tkString, tkUString, tkWString, tkLString, tkChar, tkWChar: Result := TTypes.Text;
|
||||
tkEnumeration:
|
||||
if RType.Handle = TypeInfo(Boolean) then
|
||||
Result := TTypes.Boolean
|
||||
else
|
||||
Result := TTypes.Ordinal;
|
||||
tkInterface: raise EInteropException.CreateFmt('Interface "%s" is not registered. Call RegisterType first.', [RType.Name]);
|
||||
else
|
||||
Result := TTypes.Unknown;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TRtlTypeRegistry.AnalyzeType(ATypeInfo: PTypeInfo): IStaticType;
|
||||
var
|
||||
rType: TRttiType;
|
||||
begin
|
||||
rType := FCtx.GetType(ATypeInfo);
|
||||
case rType.TypeKind of
|
||||
tkInterface: Result := GenerateInterfaceDefinition(rType as TRttiInterfaceType);
|
||||
else
|
||||
Result := ResolveType(rType);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TRtlTypeRegistry.GenerateInterfaceDefinition(RType: TRttiInterfaceType): IStaticType;
|
||||
var
|
||||
methods: TArray<TRttiMethod>;
|
||||
props: TArray<TRttiProperty>;
|
||||
fields: TList<TPair<IKeyword, IStaticType>>;
|
||||
|
||||
function MakeSig(Method: TRttiMethod): IStaticType;
|
||||
var
|
||||
ps: TArray<TRttiParameter>;
|
||||
pTypes: TArray<IStaticType>;
|
||||
returnType: IStaticType;
|
||||
k: Integer;
|
||||
begin
|
||||
ps := Method.GetParameters;
|
||||
SetLength(pTypes, Length(ps));
|
||||
for k := 0 to High(ps) do
|
||||
pTypes[k] := ResolveType(ps[k].ParamType);
|
||||
|
||||
if Assigned(Method.ReturnType) then
|
||||
returnType := ResolveType(Method.ReturnType)
|
||||
else
|
||||
returnType := TTypes.Void;
|
||||
|
||||
Result := TTypes.CreateMethod(pTypes, returnType);
|
||||
end;
|
||||
|
||||
begin
|
||||
fields := TList<TPair<IKeyword, IStaticType>>.Create;
|
||||
try
|
||||
methods := RType.GetMethods;
|
||||
for var m in methods do
|
||||
begin
|
||||
// Only map standard procedures/functions
|
||||
if m.MethodKind in [mkProcedure, mkFunction] then
|
||||
fields.Add(TPair<IKeyword, IStaticType>.Create(TKeywordRegistry.Intern(m.Name), MakeSig(m)));
|
||||
end;
|
||||
|
||||
props := RType.GetProperties;
|
||||
for var p in props do
|
||||
begin
|
||||
if p.IsReadable then
|
||||
begin
|
||||
var propType := ResolveType(p.PropertyType);
|
||||
var getterSig := TTypes.CreateMethod([], propType);
|
||||
fields.Add(TPair<IKeyword, IStaticType>.Create(TKeywordRegistry.Intern(p.Name), getterSig));
|
||||
end;
|
||||
end;
|
||||
|
||||
var def := TGenericRecordRegistry.Intern(fields.ToArray);
|
||||
Result := TTypes.CreateGenericRecord(def);
|
||||
finally
|
||||
fields.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Marshalling
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class function TRtlTypeRegistry.ToTValue(const AData: TDataValue; ATargetType: TRttiType): TValue;
|
||||
begin
|
||||
case AData.Kind of
|
||||
vkScalar:
|
||||
begin
|
||||
var sc := AData.AsScalar;
|
||||
case sc.Kind of
|
||||
TScalar.TKind.Ordinal:
|
||||
begin
|
||||
if ATargetType.Handle = TypeInfo(Integer) then
|
||||
Result := TValue.From<Integer>(Integer(sc.Value.AsInt64))
|
||||
else if ATargetType.Handle = TypeInfo(Byte) then
|
||||
Result := TValue.From<Byte>(Byte(sc.Value.AsInt64))
|
||||
else if ATargetType.Handle = TypeInfo(Word) then
|
||||
Result := TValue.From<Word>(Word(sc.Value.AsInt64))
|
||||
else
|
||||
Result := TValue.From<Int64>(sc.Value.AsInt64);
|
||||
end;
|
||||
TScalar.TKind.Float, TScalar.TKind.DateTime: Result := TValue.From<Double>(sc.Value.AsDouble);
|
||||
TScalar.TKind.Boolean: Result := TValue.From<Boolean>(sc.Value.AsInt64 <> 0);
|
||||
TScalar.TKind.Keyword:
|
||||
if ATargetType.TypeKind in [tkString, tkUString, tkWString, tkLString] then
|
||||
Result := TValue.From<String>(TKeywordRegistry.GetName(sc.Value.AsInt64))
|
||||
else
|
||||
Result := TValue.From<Int64>(sc.Value.AsInt64);
|
||||
end;
|
||||
end;
|
||||
vkText: Result := TValue.From<string>(AData.AsText);
|
||||
vkVoid: Result := TValue.Empty;
|
||||
else
|
||||
raise EInteropException.CreateFmt('Marshalling for DataKind %s not implemented.', [TRttiEnumerationType.GetName(AData.Kind)]);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TRtlTypeRegistry.FromTValue(const AValue: TValue): TDataValue;
|
||||
begin
|
||||
if AValue.IsEmpty then
|
||||
exit(TDataValue.Void);
|
||||
|
||||
case AValue.Kind of
|
||||
tkInteger, tkInt64: Result := TScalar.FromInt64(AValue.AsInt64);
|
||||
tkFloat: Result := TScalar.FromDouble(AValue.AsExtended);
|
||||
tkString, tkUString, tkWString, tkLString: Result := AValue.AsString;
|
||||
tkEnumeration:
|
||||
if AValue.TypeInfo = TypeInfo(Boolean) then
|
||||
Result := TScalar.FromBoolean(AValue.AsBoolean)
|
||||
else
|
||||
Result := TScalar.FromInt64(AValue.AsOrdinal);
|
||||
|
||||
// Recursively wrap returned interfaces
|
||||
tkInterface: Result := WrapInstance(AValue.AsInterface, AValue.TypeInfo);
|
||||
else
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Runtime Wrapping (Shadow Record Creation)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class function TRtlTypeRegistry.WrapInstance(const Instance: IInterface; TypeInfo: PTypeInfo): TDataValue;
|
||||
var
|
||||
rType: TRttiType;
|
||||
rIntf: TRttiInterfaceType;
|
||||
recFields: TDictionary<IKeyword, TDataValue>;
|
||||
begin
|
||||
if Instance = nil then
|
||||
exit(TDataValue.Void);
|
||||
|
||||
rType := FCtx.GetType(TypeInfo);
|
||||
if not (rType is TRttiInterfaceType) then
|
||||
exit(TDataValue.Void);
|
||||
|
||||
rIntf := rType as TRttiInterfaceType;
|
||||
recFields := TDictionary<IKeyword, TDataValue>.Create;
|
||||
try
|
||||
// Wrap Methods
|
||||
for var m in rIntf.GetMethods do
|
||||
begin
|
||||
if (m.MethodKind in [mkProcedure, mkFunction]) then
|
||||
begin
|
||||
var key := TKeywordRegistry.Intern(m.Name);
|
||||
|
||||
if recFields.ContainsKey(key) then
|
||||
raise EInteropException.Create('Overloading not supported');
|
||||
|
||||
var methodFactory :=
|
||||
function(const Instance: IInterface; Meth: TRttiMethod): TDataValue.TFunc
|
||||
begin
|
||||
var inst := TValue.From(Instance);
|
||||
var params := Meth.GetParameters;
|
||||
Result :=
|
||||
function(const Args: TArray<TDataValue>): TDataValue
|
||||
var
|
||||
rttiArgs: TArray<TValue>;
|
||||
res: TValue;
|
||||
i: Integer;
|
||||
begin
|
||||
if Length(Args) <> Length(params) then
|
||||
raise Exception.CreateFmt('Method %s expects %d args', [m.Name, Length(params)]);
|
||||
|
||||
SetLength(rttiArgs, Length(params));
|
||||
for i := 0 to High(params) do
|
||||
rttiArgs[i] := ToTValue(Args[i], params[i].ParamType);
|
||||
|
||||
res := Meth.Invoke(inst, rttiArgs);
|
||||
|
||||
Result := FromTValue(res);
|
||||
end;
|
||||
end;
|
||||
|
||||
recFields.Add(key, methodFactory(Instance, m));
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := TDataValue.FromGenericRecord(TKeywordMapping<TDataValue>.Create(recFields.ToArray));
|
||||
finally
|
||||
recFields.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Factories
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class procedure TRtlTypeRegistry.RegisterFactory<T>(
|
||||
const Scope: IExecutionScope;
|
||||
const FactoryName: string;
|
||||
const FactoryArgs: TArray<IStaticType>;
|
||||
const FactoryDelegate: TInterfaceFactoryDelegate<T>
|
||||
);
|
||||
var
|
||||
staticType: IStaticType;
|
||||
ti: PTypeInfo;
|
||||
begin
|
||||
ti := TypeInfo(T);
|
||||
staticType := GetStaticType(ti);
|
||||
if staticType.Kind = stUnknown then
|
||||
raise EInteropException.CreateFmt('Interface "%s" must be registered via RegisterType<T> first.', [string(ti.Name)]);
|
||||
|
||||
var factoryWrapper: TDataValue.TFunc :=
|
||||
function(const Args: TArray<TDataValue>): TDataValue
|
||||
var
|
||||
instance: T;
|
||||
begin
|
||||
instance := FactoryDelegate(Args);
|
||||
// Result wrapping will handle reference counting via closures
|
||||
Result := WrapInstance(instance, ti);
|
||||
end;
|
||||
|
||||
var factorySig := TTypes.CreateMethod(FactoryArgs, staticType);
|
||||
Scope.Define(FactoryName, TDataValue(factoryWrapper), factorySig);
|
||||
end;
|
||||
|
||||
end.
|
||||
+21
-1
@@ -84,6 +84,7 @@ type
|
||||
TNativeFunc_OF_O = function(A: Int64; B: Double): Int64;
|
||||
TNativeFunc_FO_F = function(A: Double; B: Int64): Double;
|
||||
TNativeFunc_FO_O = function(A: Double; B: Int64): Int64;
|
||||
TNativeFunc_TT_T = function(const A: String; const B: String): String;
|
||||
|
||||
private
|
||||
class var
|
||||
@@ -105,6 +106,7 @@ type
|
||||
class function CreateWrapper_OF_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||
class function CreateWrapper_FO_F(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||
class function CreateWrapper_FO_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||
class function CreateWrapper_TT_T(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||
class function CreateStaticWrapper(
|
||||
method: TRttiMethod;
|
||||
retType: IStaticType;
|
||||
@@ -656,6 +658,20 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TRtlRegistry.CreateWrapper_TT_T(CodeAddress: Pointer): TDataValue.TFunc;
|
||||
begin
|
||||
Result :=
|
||||
function(const Args: TArray<TDataValue>): TDataValue
|
||||
var
|
||||
A: String;
|
||||
B: String;
|
||||
begin
|
||||
A := Args[0].AsText;
|
||||
B := Args[1].AsText;
|
||||
Result := TNativeFunc_TT_T(CodeAddress)(A, B);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TRtlRegistry.CreateStaticWrapper(
|
||||
method: TRttiMethod;
|
||||
retType: IStaticType;
|
||||
@@ -736,7 +752,11 @@ begin
|
||||
begin
|
||||
// Keywords are passed as Int64 (index)
|
||||
Result := CreateWrapper_OO_O(ptr); // e.g. Equal_Keyword_Keyword
|
||||
end;
|
||||
end
|
||||
else if (k1 = stText) and (k2 = stText) then
|
||||
begin
|
||||
Result := CreateWrapper_TT_T(ptr); // e.g. String concat
|
||||
end
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
@@ -227,10 +227,6 @@ type
|
||||
function VisitNop(const Node: INopNode): TVoid; override;
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// IMPLEMENTATION: Helpers & Basic Types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function TTokenKindHelper.ToString: String;
|
||||
begin
|
||||
case Self of
|
||||
@@ -259,9 +255,7 @@ begin
|
||||
Result := TIdentities.Location(Line, Col);
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// IMPLEMENTATION: Lexer
|
||||
// -----------------------------------------------------------------------------
|
||||
{ EParserException }
|
||||
|
||||
constructor EParserException.Create(const AMessage: string; ALine, ACol: Integer);
|
||||
begin
|
||||
@@ -270,6 +264,8 @@ begin
|
||||
FCol := ACol;
|
||||
end;
|
||||
|
||||
{ TLexer }
|
||||
|
||||
constructor TLexer.Create(const ASource: string);
|
||||
begin
|
||||
inherited Create;
|
||||
@@ -811,9 +807,7 @@ begin
|
||||
Result := expr.Node;
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// IMPLEMENTATION: PrettyPrintVisitor
|
||||
// -----------------------------------------------------------------------------
|
||||
{ TPrettyPrintVisitor }
|
||||
|
||||
constructor TPrettyPrintVisitor.Create;
|
||||
begin
|
||||
@@ -1136,9 +1130,7 @@ begin
|
||||
Append(')');
|
||||
end;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// IMPLEMENTATION: TAstScript Facade
|
||||
// -----------------------------------------------------------------------------
|
||||
{ TAstScript }
|
||||
|
||||
class function TAstScript.Parse(const ASource: string): IAstNode;
|
||||
var
|
||||
|
||||
@@ -28,15 +28,11 @@ type
|
||||
RecCount: Integer;
|
||||
end;
|
||||
|
||||
// Converts a TScalarBatch into a stream of IScalarRecord events using the cursor pattern.
|
||||
// NOTE: This uses a zero-copy approach. Consumers must process data synchronously or copy it.
|
||||
TScalarBatchUnpacker = class(TMycConverter<TScalarBatch, IScalarRecord>)
|
||||
private
|
||||
FDef: IScalarRecordDefinition;
|
||||
protected
|
||||
function Consume(const Value: TScalarBatch): TState; override;
|
||||
public
|
||||
constructor Create(const ADef: IScalarRecordDefinition);
|
||||
IBroker = interface
|
||||
function GetEquity: Double;
|
||||
procedure Buy(Count: Integer);
|
||||
procedure Sell(Count: Integer);
|
||||
property Equity: Double read GetEquity;
|
||||
end;
|
||||
|
||||
IScalarBatchLoader = interface
|
||||
@@ -220,39 +216,6 @@ begin
|
||||
Result := FDef.IndexOf(Key);
|
||||
end;
|
||||
|
||||
{ TScalarBatchUnpacker }
|
||||
|
||||
constructor TScalarBatchUnpacker.Create(const ADef: IScalarRecordDefinition);
|
||||
begin
|
||||
inherited Create;
|
||||
FDef := ADef;
|
||||
end;
|
||||
|
||||
function TScalarBatchUnpacker.Consume(const Value: TScalarBatch): TState;
|
||||
begin
|
||||
if (Length(Value.Data) > 0) and (Value.RecCount > 0) then
|
||||
begin
|
||||
var Stride := Length(Value.Data) div Value.RecCount;
|
||||
var pCursor: TScalar.PValue := @Value.Data[0];
|
||||
|
||||
// Initialize reused View object (Cursor Pattern)
|
||||
// This object implements IScalarRecord and is updated for each tick.
|
||||
var ViewObj := TScalarRecordView.Create(FDef);
|
||||
var View := ViewObj as IScalarRecord;
|
||||
|
||||
for var i := 0 to Value.RecCount - 1 do
|
||||
begin
|
||||
ViewObj.SetData(pCursor);
|
||||
|
||||
// Broadcast to all listeners
|
||||
Broadcast(View);
|
||||
|
||||
Inc(pCursor, Stride);
|
||||
end;
|
||||
end;
|
||||
Result := TState.Null;
|
||||
end;
|
||||
|
||||
{ TScalarBatchLoader.TDataFile }
|
||||
|
||||
constructor TScalarBatchLoader.TDataFile.Create(const APath, ASymbol, AExtension: String; AYear, AMonth: Integer; const ABasename: String);
|
||||
|
||||
Reference in New Issue
Block a user