Adding Pipes
This commit is contained in:
@@ -431,9 +431,6 @@ begin
|
||||
vkRecordSeries:
|
||||
begin
|
||||
recSeries := baseValue.AsRecordSeries;
|
||||
if (index < 0) or (index >= recSeries.TotalCount) then
|
||||
raise EEvaluatorException
|
||||
.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, recSeries.TotalCount]);
|
||||
|
||||
fieldCount := recSeries.Def.Count;
|
||||
SetLength(values, fieldCount);
|
||||
@@ -441,7 +438,12 @@ begin
|
||||
for i := 0 to fieldCount - 1 do
|
||||
begin
|
||||
key := recSeries.Def.Items[i].Key;
|
||||
memberSeries := recSeries[key];
|
||||
memberSeries := recSeries.Fields[key];
|
||||
|
||||
if (index < 0) or (index >= memberSeries.TotalCount) then
|
||||
raise EEvaluatorException
|
||||
.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, memberSeries.TotalCount]);
|
||||
|
||||
scalarValue := memberSeries[index];
|
||||
values[i] := scalarValue.Value;
|
||||
end;
|
||||
@@ -460,7 +462,7 @@ begin
|
||||
baseValue := Node.Base.Accept(Self);
|
||||
|
||||
case baseValue.Kind of
|
||||
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries[Node.Member.Value]);
|
||||
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries.Fields[Node.Member.Value]);
|
||||
vkScalarRecord: Result := TDataValue(baseValue.AsScalarRecord.Fields[Node.Member.Value]);
|
||||
vkGenericRecord: Result := TDataValue(baseValue.AsGenericRecord.Fields[Node.Member.Value]);
|
||||
else
|
||||
@@ -583,7 +585,7 @@ begin
|
||||
|
||||
case seriesValue.Kind of
|
||||
vkSeries: len := seriesValue.AsSeries.Count;
|
||||
vkRecordSeries: len := seriesValue.AsRecordSeries.Count;
|
||||
vkRecordSeries: len := seriesValue.AsRecordSeries.RecordCount;
|
||||
else
|
||||
raise EEvaluatorException
|
||||
.CreateFmt('Cannot get length of type %s.', [GetEnumName(TypeInfo(TDataValueKind), Ord(seriesValue.Kind))]);
|
||||
|
||||
+189
-3
@@ -141,7 +141,11 @@ type
|
||||
akAddSeriesItem,
|
||||
akSeriesLength,
|
||||
akRecur,
|
||||
akNop
|
||||
akNop,
|
||||
// Pipes
|
||||
akPipe,
|
||||
akPipeInput,
|
||||
akPipeInputList
|
||||
);
|
||||
|
||||
TAstNodeKindHelper = record helper for TAstNodeKind
|
||||
@@ -452,10 +456,38 @@ type
|
||||
property Series: IIdentifierNode read GetSeries;
|
||||
end;
|
||||
|
||||
// Represents a single input definition within a pipe: "btc [:Close]"
|
||||
// - StreamSource: The identifier of the stream (e.g., "btc")
|
||||
// - Selector: Optional keyword path to select a field (e.g., :Close)
|
||||
IPipeInputNode = interface(IAstTypedNode)
|
||||
{$region 'private'}
|
||||
function GetStreamSource: IIdentifierNode;
|
||||
function GetSelector: IKeywordNode; // Can be nil if the whole record is used
|
||||
{$endregion}
|
||||
property StreamSource: IIdentifierNode read GetStreamSource;
|
||||
property Selector: IKeywordNode read GetSelector;
|
||||
end;
|
||||
|
||||
// A list of pipe inputs
|
||||
IPipeInputList = interface(INodeList<IPipeInputNode>)
|
||||
end;
|
||||
|
||||
// The main pipe definition: (pipe [inputs...] (fn ...))
|
||||
IPipeNode = interface(IAstTypedNode)
|
||||
{$region 'private'}
|
||||
function GetInputs: IPipeInputList;
|
||||
function GetTransformation: ILambdaExpressionNode;
|
||||
{$endregion}
|
||||
property Inputs: IPipeInputList read GetInputs;
|
||||
property Transformation: ILambdaExpressionNode read GetTransformation;
|
||||
end;
|
||||
|
||||
IParameterList = interface(INodeList<IIdentifierNode>)
|
||||
end;
|
||||
|
||||
IArgumentList = interface(INodeList<IAstNode>)
|
||||
end;
|
||||
|
||||
IExpressionList = interface(INodeList<IAstNode>)
|
||||
end;
|
||||
|
||||
@@ -491,6 +523,12 @@ type
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
function VisitNop(const Node: INopNode): TDataValue;
|
||||
|
||||
// Pipes
|
||||
// Not implemented yet. that's ok, it just needs to compile
|
||||
// function VisitPipeInput(const Node: IPipeInputNode): TDataValue;
|
||||
// function VisitPipeInputList(const Node: IPipeInputList): TDataValue;
|
||||
// function VisitPipe(const Node: IPipeNode): TDataValue;
|
||||
end;
|
||||
|
||||
IEvaluatorVisitor = interface(IAstVisitor)
|
||||
@@ -502,9 +540,10 @@ type
|
||||
TAstNode = class(TInterfacedObject, IAstNode)
|
||||
private
|
||||
FIdentity: IAstIdentity;
|
||||
function GetIdentity: IAstIdentity;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; virtual; abstract;
|
||||
function GetIsTyped: Boolean; virtual;
|
||||
function GetIdentity: IAstIdentity;
|
||||
public
|
||||
constructor Create(const AIdentity: IAstIdentity);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; virtual; abstract;
|
||||
@@ -551,6 +590,7 @@ type
|
||||
private
|
||||
FStaticType: IStaticType;
|
||||
function GetStaticType: IStaticType;
|
||||
protected
|
||||
function GetIsTyped: Boolean; override;
|
||||
public
|
||||
constructor Create(const AStaticType: IStaticType; const AIdentity: IAstIdentity);
|
||||
@@ -617,6 +657,7 @@ type
|
||||
FValue: IAstNode;
|
||||
function GetKey: IKeywordNode;
|
||||
function GetValue: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AKey: IKeywordNode; const AValue: IAstNode; const AIdentity: IAstIdentity);
|
||||
@@ -638,6 +679,7 @@ type
|
||||
private
|
||||
FConstIdentity: IConstantIdentity; // Typed reference for fast access
|
||||
function GetValue: TDataValue;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AIdentity: IConstantIdentity; const AStaticType: IStaticType);
|
||||
@@ -651,6 +693,7 @@ type
|
||||
FAddress: TResolvedAddress;
|
||||
function GetAddress: TResolvedAddress;
|
||||
function GetName: string;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AIdentity: INamedIdentity; const AAddress: TResolvedAddress; const AStaticType: IStaticType);
|
||||
@@ -662,6 +705,7 @@ type
|
||||
private
|
||||
FKeywordIdentity: IKeywordIdentity;
|
||||
function GetValue: IKeyword;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AIdentity: IKeywordIdentity);
|
||||
@@ -673,6 +717,7 @@ type
|
||||
private
|
||||
FDefIdentity: IDefinitionIdentity;
|
||||
function GetDefinition: String;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
|
||||
@@ -688,6 +733,7 @@ type
|
||||
function GetCondition: IAstNode;
|
||||
function GetThenBranch: IAstNode;
|
||||
function GetElseBranch: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -705,6 +751,7 @@ type
|
||||
FElseBranch: IAstNode;
|
||||
function GetPairs: TArray<TCondPair>;
|
||||
function GetElseBranch: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -732,6 +779,7 @@ type
|
||||
function GetUpvalues: TArray<TResolvedAddress>;
|
||||
function GetHasNestedLambdas: Boolean;
|
||||
function GetIsPure: Boolean;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -760,6 +808,7 @@ type
|
||||
function GetIsTailCall: Boolean;
|
||||
function GetStaticTarget: TDataValue.TFunc;
|
||||
function GetIsTargetPure: Boolean;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -783,6 +832,7 @@ type
|
||||
function GetName: IIdentifierNode;
|
||||
function GetParameters: IParameterList;
|
||||
function GetBody: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -801,6 +851,7 @@ type
|
||||
FExpandedBody: IAstNode;
|
||||
function GetCallNode: IFunctionCallNode;
|
||||
function GetExpandedBody: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AIdentity: IAstIdentity);
|
||||
@@ -812,6 +863,7 @@ type
|
||||
private
|
||||
FExpression: IAstNode;
|
||||
function GetExpression: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
|
||||
@@ -823,6 +875,7 @@ type
|
||||
private
|
||||
FExpression: IAstNode;
|
||||
function GetExpression: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
|
||||
@@ -834,6 +887,7 @@ type
|
||||
private
|
||||
FExpression: IQuasiquoteNode;
|
||||
function GetExpression: IQuasiquoteNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AExpression: IQuasiquoteNode; const AIdentity: IAstIdentity);
|
||||
@@ -845,6 +899,7 @@ type
|
||||
private
|
||||
FArguments: IArgumentList;
|
||||
function GetArguments: IArgumentList;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AArguments: IArgumentList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
|
||||
@@ -856,6 +911,7 @@ type
|
||||
private
|
||||
FExpressions: IExpressionList;
|
||||
function GetExpressions: IExpressionList;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AExpressions: IExpressionList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
|
||||
@@ -870,6 +926,7 @@ type
|
||||
function GetTarget: IAstNode;
|
||||
function GetInitializer: IAstNode;
|
||||
function GetIsBoxed: Boolean;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -888,6 +945,7 @@ type
|
||||
FTarget, FValue: IAstNode;
|
||||
function GetTarget: IAstNode;
|
||||
function GetValue: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const ATarget, AValue: IAstNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
|
||||
@@ -900,6 +958,7 @@ type
|
||||
FBase, FIndex: IAstNode;
|
||||
function GetBase: IAstNode;
|
||||
function GetIndex: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const ABase, AIndex: IAstNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
|
||||
@@ -913,6 +972,7 @@ type
|
||||
FMember: IKeywordNode;
|
||||
function GetBase: IAstNode;
|
||||
function GetMember: IKeywordNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -933,6 +993,7 @@ type
|
||||
function GetFields: IRecordFieldList;
|
||||
function GetGenericDefinition: IGenericRecordDefinition;
|
||||
function GetScalarDefinition: IScalarRecordDefinition;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -953,6 +1014,7 @@ type
|
||||
function GetSeries: IIdentifierNode;
|
||||
function GetValue: IAstNode;
|
||||
function GetLookback: IAstNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
@@ -969,6 +1031,7 @@ type
|
||||
private
|
||||
FSeries: IIdentifierNode;
|
||||
function GetSeries: IIdentifierNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const ASeries: IIdentifierNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
|
||||
@@ -977,7 +1040,7 @@ type
|
||||
end;
|
||||
|
||||
TNopNode = class(TAstTypedNode, INopNode)
|
||||
private
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AStaticType: IStaticType; const AIdentity: IAstIdentity);
|
||||
@@ -985,6 +1048,49 @@ type
|
||||
function AsNop: INopNode; override;
|
||||
end;
|
||||
|
||||
TPipeInputNode = class(TAstTypedNode, IPipeInputNode)
|
||||
private
|
||||
FStreamSource: IIdentifierNode;
|
||||
FSelector: IKeywordNode;
|
||||
function GetStreamSource: IIdentifierNode;
|
||||
function GetSelector: IKeywordNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
const AStreamSource: IIdentifierNode;
|
||||
const ASelector: IKeywordNode;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
end;
|
||||
|
||||
TPipeInputList = class(TAstNodeList<IPipeInputNode>, IPipeInputList)
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
end;
|
||||
|
||||
TPipeNode = class(TAstTypedNode, IPipeNode)
|
||||
private
|
||||
FInputs: IPipeInputList;
|
||||
FTransformation: ILambdaExpressionNode;
|
||||
function GetInputs: IPipeInputList;
|
||||
function GetTransformation: ILambdaExpressionNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
const AInputs: IPipeInputList;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@@ -2260,4 +2366,84 @@ begin
|
||||
Inc(FIndex);
|
||||
end;
|
||||
|
||||
{ TPipeInputNode }
|
||||
|
||||
constructor TPipeInputNode.Create(
|
||||
const AStreamSource: IIdentifierNode;
|
||||
const ASelector: IKeywordNode;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
);
|
||||
begin
|
||||
inherited Create(AStaticType, AIdentity);
|
||||
FStreamSource := AStreamSource;
|
||||
FSelector := ASelector;
|
||||
end;
|
||||
|
||||
function TPipeInputNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
begin
|
||||
// Result := Visitor.VisitPipeInput(Self);
|
||||
end;
|
||||
|
||||
function TPipeInputNode.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akPipeInput;
|
||||
end;
|
||||
|
||||
function TPipeInputNode.GetSelector: IKeywordNode;
|
||||
begin
|
||||
Result := FSelector;
|
||||
end;
|
||||
|
||||
function TPipeInputNode.GetStreamSource: IIdentifierNode;
|
||||
begin
|
||||
Result := FStreamSource;
|
||||
end;
|
||||
|
||||
{ TPipeInputList }
|
||||
|
||||
function TPipeInputList.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
begin
|
||||
// Result := Visitor.VisitPipeInputList(Self);
|
||||
end;
|
||||
|
||||
function TPipeInputList.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akPipeInputList;
|
||||
end;
|
||||
|
||||
{ TPipeNode }
|
||||
|
||||
constructor TPipeNode.Create(
|
||||
const AInputs: IPipeInputList;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
);
|
||||
begin
|
||||
inherited Create(AStaticType, AIdentity);
|
||||
FInputs := AInputs;
|
||||
FTransformation := ATransformation;
|
||||
end;
|
||||
|
||||
function TPipeNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
begin
|
||||
// Result := Visitor.VisitPipe(Self);
|
||||
end;
|
||||
|
||||
function TPipeNode.GetInputs: IPipeInputList;
|
||||
begin
|
||||
Result := FInputs;
|
||||
end;
|
||||
|
||||
function TPipeNode.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akPipe;
|
||||
end;
|
||||
|
||||
function TPipeNode.GetTransformation: ILambdaExpressionNode;
|
||||
begin
|
||||
Result := FTransformation;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
(*TODO
|
||||
Ich möchte ein neues Sprachkonzept einbauen. Folgendes Skript skizziert, was möglich sein soll:
|
||||
|
||||
; Record-Streams sind Record-Series, an die "Pipes" andocken können (Observer) um neue Daten zu bekommen
|
||||
; Es handelt sich um reaktives Producer-Consumer-Pattern
|
||||
|
||||
;; Beispiel: SMA auf Close
|
||||
(def btc (create-ticker "BTCUSD")) ; ein Open-High-Low-Close-Record-Stream
|
||||
(def sma (create-sma 20)) ; erzeugt eine Lambda (sma(price)), die den sma20 berechnet
|
||||
|
||||
|
||||
; Das Ergebnis eines pipe-Befehls ist wieder ein Record-Stream.
|
||||
; btc-sma ist also ein Record-Stream, der {:ma ..} records enthält:
|
||||
(def btc-sma
|
||||
(pipe [btc [:Close]]
|
||||
(fn [price]
|
||||
; pipe mappt die Elemente des Record-Streams (hier :Close) auf die Parameter der fn. Price ist als eine ScalarSeries.
|
||||
; (Index=0 ist das neueste Element)
|
||||
; emit ist eine von pipe in den lambda-scope injizierte Funktion, die das neue Element in den Ergebnis-Stream
|
||||
; pusht (und damit die Observer des Ergebnis-Streams benachrichtigt).
|
||||
(emit {:ma (sma (get price 0))})
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
; Diese pipe hat zwei Streams als Input: den Closing-Price und den SMA. Jede pipe kann also N Input Stream haben
|
||||
(def btc-signal
|
||||
(pipe [btc [:Close] btc-sma [:ma]]
|
||||
(fn [price ma]
|
||||
(emit {:signal (? (> (get price 0) (get ma 0)) :buy : sell)})
|
||||
)
|
||||
)
|
||||
)
|
||||
*)
|
||||
|
||||
unit Myc.Ast.Pipes;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Classes,
|
||||
System.Generics.Collections,
|
||||
System.SyncObjs,
|
||||
Myc.Data.Value,
|
||||
Myc.Data.Scalar,
|
||||
Myc.Data.Keyword,
|
||||
Myc.Data.Stream,
|
||||
Myc.Core.Notifier;
|
||||
|
||||
type
|
||||
// Forward declaration
|
||||
TPipeStream = class;
|
||||
|
||||
// mapped fields per input stream
|
||||
TPipeConfig = TArray<TArray<TScalarRecordField>>;
|
||||
|
||||
TPipeSource = class(TInterfacedObject, IStreamObserver)
|
||||
private
|
||||
[Weak]
|
||||
FOwner: TPipeStream;
|
||||
FSource: IStream;
|
||||
FTag: TSubscriptionTag;
|
||||
|
||||
// State for barrier synchronization
|
||||
FLastSeenCycle: Int64;
|
||||
|
||||
procedure OnSignal(const Signal: TStreamSignal);
|
||||
procedure Destroying;
|
||||
|
||||
public
|
||||
constructor Create(AOwner: TPipeStream; ASource: IStream; const AInputs: TArray<TScalarRecordField>);
|
||||
property LastSeenCycle: Int64 read FLastSeenCycle;
|
||||
end;
|
||||
|
||||
// --- Runtime: The Pipe ---
|
||||
// The executable instance of a pipe.
|
||||
// Acts as IStream (Producer) for downstream consumers and manages internal inputs.
|
||||
TPipeStream = class(TInterfacedObject, IStream)
|
||||
private
|
||||
FConfig: TPipeConfig;
|
||||
FSources: TArray<TPipeSource>;
|
||||
FObservers: TMycNotifyList<IStreamObserver>;
|
||||
FSeries: IWriteableScalarRecordSeries;
|
||||
|
||||
// Barrier State
|
||||
FLastFiredCycleID: Int64;
|
||||
|
||||
// Execution Logic
|
||||
// The compiled lambda function representing (fn [inputs...] ...)
|
||||
FLambda: TDataValue.TFunc;
|
||||
FLambdaArgs: TArray<TDataValue>;
|
||||
|
||||
procedure CheckBarrierAndFire(CurrentCycle: Int64);
|
||||
function GetSeries: IScalarRecordSeries;
|
||||
|
||||
public
|
||||
constructor Create(const AConfig: TPipeConfig; const ASources: TArray<IStream>);
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure SetTransformation(const Lambda: TDataValue.TFunc);
|
||||
|
||||
// Injected into lambda scope as "emit"
|
||||
procedure Emit(const Value: IScalarRecord);
|
||||
|
||||
// IStream Implementation
|
||||
function Subscribe(const Observer: IStreamObserver): TSubscriptionTag;
|
||||
procedure Unsubscribe(Tag: TSubscriptionTag);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Myc.Ast.Visitor;
|
||||
|
||||
{ TPipeSource }
|
||||
|
||||
constructor TPipeSource.Create(AOwner: TPipeStream; ASource: IStream; const AInputs: TArray<TScalarRecordField>);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FOwner := AOwner;
|
||||
FSource := ASource;
|
||||
FLastSeenCycle := -1;
|
||||
|
||||
FTag := FSource.Subscribe(Self);
|
||||
end;
|
||||
|
||||
procedure TPipeSource.Destroying;
|
||||
begin
|
||||
FSource.Unsubscribe(FTag);
|
||||
FOwner := nil;
|
||||
end;
|
||||
|
||||
procedure TPipeSource.OnSignal(const Signal: TStreamSignal);
|
||||
begin
|
||||
FLastSeenCycle := Signal.CycleID;
|
||||
|
||||
if Assigned(FOwner) then
|
||||
FOwner.CheckBarrierAndFire(FLastSeenCycle);
|
||||
end;
|
||||
|
||||
{ TPipeStream }
|
||||
|
||||
constructor TPipeStream.Create(const AConfig: TPipeConfig; const ASources: TArray<IStream>);
|
||||
var
|
||||
fields: TArray<TScalarRecordField>;
|
||||
begin
|
||||
inherited Create;
|
||||
FConfig := AConfig;
|
||||
FLastFiredCycleID := -1;
|
||||
|
||||
Assert(Length(AConfig) = Length(ASources));
|
||||
|
||||
SetLength(FSources, Length(ASources));
|
||||
|
||||
// Construct record series definition from source config
|
||||
var n := 0;
|
||||
for var i := 0 to High(AConfig) do
|
||||
begin
|
||||
FSources[i] := TPipeSource.Create(Self, ASources[i], AConfig[i]);
|
||||
FSources[i]._AddRef;
|
||||
inc(n, Length(AConfig[i]));
|
||||
end;
|
||||
|
||||
SetLength(FLambdaArgs, n);
|
||||
|
||||
n := 0;
|
||||
for var i := 0 to High(AConfig) do
|
||||
begin
|
||||
for var j := 0 to High(AConfig[i]) do
|
||||
begin
|
||||
fields[n] := AConfig[i][j];
|
||||
|
||||
FLambdaArgs[n] := TDataValue.FromSeries(ASources[i].Series[j].Value);
|
||||
|
||||
inc(n);
|
||||
end;
|
||||
end;
|
||||
|
||||
FSeries := TScalarRecordSeries.Create(TScalarRecord.TRegistry.Intern(fields));
|
||||
end;
|
||||
|
||||
destructor TPipeStream.Destroy;
|
||||
begin
|
||||
for var src in FSources do
|
||||
begin
|
||||
src.Destroying;
|
||||
src._Release;
|
||||
end;
|
||||
FObservers.Finalize;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TPipeStream.SetTransformation(const Lambda: TDataValue.TFunc);
|
||||
begin
|
||||
FLambda := Lambda;
|
||||
end;
|
||||
|
||||
function TPipeStream.Subscribe(const Observer: IStreamObserver): TSubscriptionTag;
|
||||
begin
|
||||
FObservers.Lock;
|
||||
try
|
||||
Result := FObservers.Advise(Observer);
|
||||
finally
|
||||
FObservers.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPipeStream.Unsubscribe(Tag: TSubscriptionTag);
|
||||
begin
|
||||
FObservers.Lock;
|
||||
try
|
||||
FObservers.Unadvise(Tag);
|
||||
finally
|
||||
FObservers.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPipeStream.CheckBarrierAndFire(CurrentCycle: Int64);
|
||||
begin
|
||||
// Barrier Check
|
||||
for var src in FSources do
|
||||
begin
|
||||
if src.LastSeenCycle < CurrentCycle then
|
||||
exit;
|
||||
end;
|
||||
|
||||
// --- Barrier Open ---
|
||||
|
||||
if FLastFiredCycleID >= CurrentCycle then
|
||||
exit;
|
||||
|
||||
FLastFiredCycleID := CurrentCycle;
|
||||
|
||||
// Execute Lambda
|
||||
if Assigned(FLambda) then
|
||||
begin
|
||||
try
|
||||
FLambda(FLambdaArgs);
|
||||
except
|
||||
// Error handling strategy: Propagate/Crash for now.
|
||||
raise;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPipeStream.Emit(const Value: IScalarRecord);
|
||||
begin
|
||||
FObservers.Lock;
|
||||
try
|
||||
FSeries.Add(Value);
|
||||
var signal := TStreamSignal.Create(skData, FLastFiredCycleID);
|
||||
|
||||
FObservers.Notify(
|
||||
function(const Obs: IStreamObserver): Boolean
|
||||
begin
|
||||
Obs.OnSignal(signal);
|
||||
Result := True;
|
||||
end
|
||||
);
|
||||
finally
|
||||
FObservers.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPipeStream.GetSeries: IScalarRecordSeries;
|
||||
begin
|
||||
Result := FSeries;
|
||||
end;
|
||||
|
||||
end.
|
||||
@@ -1,3 +1,6 @@
|
||||
//==================================================================================================
|
||||
//== FULL UNIT START: Myc.Ast (from Myc.Ast.pas)
|
||||
//==================================================================================================
|
||||
unit Myc.Ast;
|
||||
|
||||
interface
|
||||
@@ -327,6 +330,27 @@ type
|
||||
const AStaticType: IStaticType = nil
|
||||
): ISeriesLengthNode; overload; static;
|
||||
|
||||
// --- PIPES ---
|
||||
|
||||
class function PipeInput(
|
||||
const AStream: IIdentifierNode;
|
||||
const ASelector: IKeywordNode = nil;
|
||||
const Loc: ISourceLocation = nil
|
||||
): IPipeInputNode; static;
|
||||
|
||||
class function Pipe(
|
||||
const AInputs: TArray<IPipeInputNode>;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const Loc: ISourceLocation = nil
|
||||
): IPipeNode; overload; static;
|
||||
|
||||
class function Pipe(
|
||||
const Identity: IAstIdentity;
|
||||
const AInputs: IPipeInputList;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AStaticType: IStaticType = nil
|
||||
): IPipeNode; overload; static;
|
||||
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -909,4 +933,47 @@ begin
|
||||
);
|
||||
end;
|
||||
|
||||
// --- PIPES ---
|
||||
|
||||
class function TAst.PipeInput(const AStream: IIdentifierNode; const ASelector: IKeywordNode; const Loc: ISourceLocation): IPipeInputNode;
|
||||
begin
|
||||
var id := TIdentities.Structural(Loc);
|
||||
// Erzeugt Node aus Myc.Ast.Nodes
|
||||
Result := TPipeInputNode.Create(AStream, ASelector, id, TTypes.Unknown);
|
||||
end;
|
||||
|
||||
class function TAst.Pipe(
|
||||
const AInputs: TArray<IPipeInputNode>;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const Loc: ISourceLocation
|
||||
): IPipeNode;
|
||||
begin
|
||||
var listId := TIdentities.List('[', ']', ' ', Loc);
|
||||
var inputList := TPipeInputList.Create(AInputs, listId);
|
||||
|
||||
var id := TIdentities.Structural(Loc);
|
||||
// Erzeugt Node aus Myc.Ast.Nodes
|
||||
Result := TPipeNode.Create(inputList, ATransformation, id, TTypes.Unknown);
|
||||
end;
|
||||
|
||||
class function TAst.Pipe(
|
||||
const Identity: IAstIdentity;
|
||||
const AInputs: IPipeInputList;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AStaticType: IStaticType
|
||||
): IPipeNode;
|
||||
begin
|
||||
Result :=
|
||||
TPipeNode.Create(
|
||||
AInputs,
|
||||
ATransformation,
|
||||
Identity,
|
||||
if AStaticType <> nil then AStaticType
|
||||
else TTypes.Unknown
|
||||
);
|
||||
end;
|
||||
|
||||
end.
|
||||
//==================================================================================================
|
||||
//== FULL UNIT END: Myc.Ast
|
||||
//==================================================================================================
|
||||
|
||||
Reference in New Issue
Block a user