Pipe Parameter is now a Tuple
This commit is contained in:
@@ -55,9 +55,6 @@ type
|
||||
function VisitTuple(const Node: IAstNode): Boolean;
|
||||
|
||||
// Pipe Support
|
||||
function VisitPipeInput(const Node: IAstNode): Boolean;
|
||||
function VisitPipeSelectorList(const Node: IAstNode): Boolean;
|
||||
function VisitPipeInputList(const Node: IAstNode): Boolean;
|
||||
function VisitPipe(const Node: IAstNode): Boolean;
|
||||
|
||||
// Helper to check if a node is pure (handling nil gracefully)
|
||||
@@ -131,9 +128,6 @@ begin
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
// Pipes
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
@@ -328,34 +322,11 @@ end;
|
||||
|
||||
// --- Pipe Support ---
|
||||
|
||||
function TPurityAnalyzer.VisitPipeInput(const Node: IAstNode): Boolean;
|
||||
begin
|
||||
var P := Node.AsPipeInput;
|
||||
// Input node itself is declarative.
|
||||
// But we check its parts just in case.
|
||||
Result := IsNodePure(P.StreamSource) and IsNodePure(P.Selectors);
|
||||
end;
|
||||
|
||||
function TPurityAnalyzer.VisitPipeSelectorList(const Node: IAstNode): Boolean;
|
||||
begin
|
||||
for var item in Node.AsPipeSelectorList do
|
||||
if not IsNodePure(item) then
|
||||
exit(False);
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function TPurityAnalyzer.VisitPipeInputList(const Node: IAstNode): Boolean;
|
||||
begin
|
||||
for var item in Node.AsPipeInputList do
|
||||
if not IsNodePure(item) then
|
||||
exit(False);
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function TPurityAnalyzer.VisitPipe(const Node: IAstNode): Boolean;
|
||||
begin
|
||||
var P := Node.AsPipe;
|
||||
// A pipe is pure if its input definitions are pure AND its transformation lambda is pure.
|
||||
// A pipe is pure if its inputs are pure AND its transformation lambda is pure.
|
||||
// P.Inputs is an ITupleNode (recursive Tuple of Tuples), so VisitTuple handles it.
|
||||
Result := IsNodePure(P.Inputs) and IsNodePure(P.Transformation);
|
||||
end;
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ type
|
||||
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||
|
||||
// Pipe Support
|
||||
function VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||
function VisitPipe(const Node: IAstNode): IAstNode;
|
||||
|
||||
protected
|
||||
@@ -242,7 +241,6 @@ begin
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
// Pipe Support
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
@@ -328,8 +326,6 @@ begin
|
||||
for i := 0 to count - 1 do
|
||||
begin
|
||||
newElements[i] := Accept(T.Items[i]);
|
||||
// FIX: Ensure the node is typed before accessing StaticType.
|
||||
// Structural nodes (like RecordField) are NOT typed and might crash or return garbage.
|
||||
if newElements[i].IsTyped then
|
||||
elementTypes[i] := newElements[i].AsTypedNode.StaticType
|
||||
else
|
||||
@@ -347,9 +343,6 @@ begin
|
||||
firstType := elementTypes[0];
|
||||
isHomogeneous := True;
|
||||
|
||||
// Check for Homogeneity (Exact type equality of all elements)
|
||||
// Note: If any element is Unknown, we assume Heterogeneous (Tuple) unless all are unknown?
|
||||
// For now, Unknown breaks homogeneity.
|
||||
for i := 1 to count - 1 do
|
||||
begin
|
||||
if not firstType.IsEqual(elementTypes[i]) then
|
||||
@@ -361,20 +354,15 @@ begin
|
||||
|
||||
if isHomogeneous and (firstType.Kind <> stUnknown) then
|
||||
begin
|
||||
// It is at least a Vector.
|
||||
// Check if it should be promoted to a Matrix (i.e., elements are Vectors or Matrices).
|
||||
|
||||
if firstType.Kind = stVector then
|
||||
begin
|
||||
// Vector of Vectors -> Matrix (2D)
|
||||
// New Dimensions = [OuterCount, InnerCount]
|
||||
newDim := [count, firstType.AsVector.Count];
|
||||
finalType := TTypes.CreateMatrix(firstType.AsVector.ElementType, newDim);
|
||||
end
|
||||
else if firstType.Kind = stMatrix then
|
||||
begin
|
||||
// Vector of Matrices -> Higher dimensional Matrix (N+1)
|
||||
// New Dimensions = [OuterCount, Dim0, Dim1...]
|
||||
// Vector of Matrices -> Higher dimensional Matrix
|
||||
commonDim := firstType.AsMatrix.Dimensions;
|
||||
SetLength(newDim, Length(commonDim) + 1);
|
||||
|
||||
@@ -518,30 +506,22 @@ var
|
||||
exprs: ITupleNode;
|
||||
i: Integer;
|
||||
begin
|
||||
// 1. Transform children via inherited logic
|
||||
// This delegates to TAstTransformer.VisitBlockExpression, which calls Visit(B.Expressions).
|
||||
// B.Expressions is a Tuple, so it routes to TTypeChecker.VisitTuple.
|
||||
var transformedNode := inherited VisitBlockExpression(Node);
|
||||
|
||||
// Safety check: Did the transformer return a node?
|
||||
if not Assigned(transformedNode) then
|
||||
raise ECompilationFailed.Create([TCompilerError.Create(elError, 'Internal Error: Block transformation returned nil.', Node)]);
|
||||
|
||||
newBlock := transformedNode.AsBlockExpression;
|
||||
exprs := newBlock.Expressions;
|
||||
|
||||
// 2. Validate expressions
|
||||
if exprs.Count > 0 then
|
||||
begin
|
||||
// Check for NIL entries which indicate failed transformation of children
|
||||
for i := 0 to exprs.Count - 1 do
|
||||
begin
|
||||
if exprs.Items[i] = nil then
|
||||
raise ECompilationFailed.Create(
|
||||
[TCompilerError.Create(elError, Format('Internal Error: Block expression #%d transformed to nil.', [i]), Node)]);
|
||||
end;
|
||||
|
||||
// The type of the block is the type of the last expression
|
||||
blockType := exprs.Items[exprs.Count - 1].AsTypedNode.StaticType;
|
||||
end
|
||||
else
|
||||
@@ -783,12 +763,6 @@ var
|
||||
newFields: ITupleNode;
|
||||
begin
|
||||
R := Node.AsRecordLiteral;
|
||||
|
||||
// FIX: Do NOT use inherited VisitRecordLiteral() here, because it expects an IRecordLiteralNode
|
||||
// but R.Fields is an ITupleNode. Using inherited would dispatch to VisitRecordLiteral with wrong type
|
||||
// or trigger recursive confusion.
|
||||
// Instead, directly visit the fields Tuple. This routes to TTypeChecker.VisitTuple.
|
||||
|
||||
newFields := Visit(R.Fields).AsTuple;
|
||||
|
||||
var count := newFields.Count;
|
||||
@@ -798,10 +772,6 @@ begin
|
||||
|
||||
for i := 0 to count - 1 do
|
||||
begin
|
||||
// Note: Elements of the fields tuple are RecordFieldNodes.
|
||||
// RecordFieldNodes are NOT Typed (IAstTypedNode), they are structural.
|
||||
// So we cannot check .StaticType on the field itself.
|
||||
// We must check the .Value of the field.
|
||||
var field := newFields.Items[i].AsRecordField;
|
||||
key := field.Key.Value;
|
||||
valType := field.Value.AsTypedNode.StaticType;
|
||||
@@ -876,89 +846,133 @@ end;
|
||||
// PIPE IMPLEMENTATION (Type Checking)
|
||||
// =============================================================================
|
||||
|
||||
function TTypeChecker.VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeInputNode;
|
||||
newSource: IIdentifierNode;
|
||||
sourceType: IStaticType;
|
||||
begin
|
||||
P := Node.AsPipeInput;
|
||||
newSource := Accept(P.StreamSource).AsIdentifier;
|
||||
sourceType := newSource.AsTypedNode.StaticType;
|
||||
|
||||
if (sourceType.Kind <> stUnknown) then
|
||||
begin
|
||||
if not ((sourceType.Kind = stSeries) or (sourceType.Kind = stRecordSeries)) then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(
|
||||
Format('Pipe input "%s" must be a Series or RecordSeries, but got %s.', [newSource.Name, sourceType.ToString]),
|
||||
Node
|
||||
);
|
||||
end
|
||||
else if (sourceType.Kind = stRecordSeries) then
|
||||
begin
|
||||
var def := sourceType.AsRecord.Definition;
|
||||
for var sel in P.Selectors do
|
||||
begin
|
||||
if def.IndexOf(sel.Value) < 0 then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(Format('Field ":%s" not found in stream "%s".', [sel.Value.Name, newSource.Name]), sel);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := TAst.PipeInput(newSource, P.Selectors, Node.Identity.Location);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitPipe(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeNode;
|
||||
rawInputs: ITupleNode;
|
||||
i, k: Integer;
|
||||
inputNode: IPipeInputNode;
|
||||
newInputs: TArray<IPipeInputNode>;
|
||||
streamType: IStaticType;
|
||||
newInputs: TList<IAstNode>;
|
||||
paramTypes: TList<IStaticType>;
|
||||
lambda: ILambdaExpressionNode;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newParamsAsNodes: TArray<IAstNode>;
|
||||
inferredType: IStaticType;
|
||||
|
||||
// Iteration vars
|
||||
inputPair: IAstNode;
|
||||
rawTuple: ITupleNode;
|
||||
streamNode: IIdentifierNode;
|
||||
selectorsNode: ITupleNode;
|
||||
newSelectors: TList<IAstNode>;
|
||||
streamType: IStaticType;
|
||||
begin
|
||||
P := Node.AsPipe;
|
||||
SetLength(newInputs, P.Inputs.Count);
|
||||
rawInputs := P.Inputs; // This is the Tuple of Tuples [[id [sel]] ...]
|
||||
|
||||
newInputs := TList<IAstNode>.Create;
|
||||
paramTypes := TList<IStaticType>.Create;
|
||||
try
|
||||
for i := 0 to P.Inputs.Count - 1 do
|
||||
// 1. Iterate over input definitions
|
||||
for i := 0 to rawInputs.Count - 1 do
|
||||
begin
|
||||
inputNode := Accept(P.Inputs[i]).AsPipeInput;
|
||||
newInputs[i] := inputNode;
|
||||
inputPair := rawInputs.Items[i];
|
||||
|
||||
streamType := inputNode.StreamSource.AsTypedNode.StaticType;
|
||||
|
||||
for var sel in inputNode.Selectors do
|
||||
// Validate Structure: [StreamSource, [Selectors]]
|
||||
if inputPair.Kind <> akTuple then
|
||||
begin
|
||||
inferredType := TTypes.Unknown;
|
||||
if streamType.Kind = stRecordSeries then
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe input must be a vector: [Source [Selectors]].', inputPair);
|
||||
continue;
|
||||
end;
|
||||
|
||||
rawTuple := inputPair.AsTuple;
|
||||
if rawTuple.Count <> 2 then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe input vector must have exactly 2 elements.', inputPair);
|
||||
continue;
|
||||
end;
|
||||
|
||||
// Element 0: Stream Identifier
|
||||
if rawTuple.Items[0].Kind <> akIdentifier then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe source must be an identifier.', rawTuple.Items[0]);
|
||||
continue;
|
||||
end;
|
||||
|
||||
// Resolve Stream Identifier (Type Binding)
|
||||
streamNode := Accept(rawTuple.Items[0]).AsIdentifier;
|
||||
streamType := streamNode.StaticType;
|
||||
|
||||
// Validate Stream Type
|
||||
if (streamType.Kind <> stUnknown) then
|
||||
begin
|
||||
if not ((streamType.Kind = stSeries) or (streamType.Kind = stRecordSeries)) then
|
||||
begin
|
||||
var def := streamType.AsRecord.Definition;
|
||||
var idx := def.IndexOf(sel.Value);
|
||||
if idx >= 0 then
|
||||
inferredType := TTypes.FromScalarKind(def[idx]);
|
||||
end
|
||||
else if streamType.Kind = stSeries then
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(
|
||||
Format('Pipe input "%s" must be a Series or RecordSeries, but got %s.', [streamNode.Name, streamType.ToString]),
|
||||
streamNode
|
||||
);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Element 1: Selector Vector
|
||||
if rawTuple.Items[1].Kind <> akTuple then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe selectors must be a vector of keywords.', rawTuple.Items[1]);
|
||||
continue;
|
||||
end;
|
||||
selectorsNode := rawTuple.Items[1].AsTuple;
|
||||
newSelectors := TList<IAstNode>.Create;
|
||||
try
|
||||
// Iterate Selectors
|
||||
for k := 0 to selectorsNode.Count - 1 do
|
||||
begin
|
||||
if Assigned(streamType.AsSeries.ElementType) then
|
||||
inferredType := streamType.AsSeries.ElementType
|
||||
else
|
||||
inferredType := TTypes.Ordinal;
|
||||
var selItem := selectorsNode.Items[k];
|
||||
if selItem.Kind <> akKeyword then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Selector must be a keyword.', selItem);
|
||||
continue;
|
||||
end;
|
||||
|
||||
var kw := selItem.AsKeyword;
|
||||
newSelectors.Add(kw); // Keywords don't need 'Accept' as they are constant, but we keep them structurally
|
||||
|
||||
// Infer Type for Lambda Parameter
|
||||
inferredType := TTypes.Unknown;
|
||||
if streamType.Kind = stRecordSeries then
|
||||
begin
|
||||
var def := streamType.AsRecord.Definition;
|
||||
var idx := def.IndexOf(kw.Value);
|
||||
if idx >= 0 then
|
||||
inferredType := TTypes.FromScalarKind(def[idx])
|
||||
else if Assigned(FLog) then
|
||||
FLog.AddError(Format('Field ":%s" not found in stream "%s".', [kw.Value.Name, streamNode.Name]), selItem);
|
||||
end
|
||||
else if streamType.Kind = stSeries then
|
||||
begin
|
||||
if Assigned(streamType.AsSeries.ElementType) then
|
||||
inferredType := streamType.AsSeries.ElementType
|
||||
else
|
||||
inferredType := TTypes.Ordinal;
|
||||
end;
|
||||
paramTypes.Add(inferredType);
|
||||
end;
|
||||
|
||||
paramTypes.Add(inferredType);
|
||||
// Reconstruct the typed Input Tuple [StreamIdent, [Selectors]]
|
||||
var typedSelectorTuple := TAst.Tuple(selectorsNode.Identity, newSelectors.ToArray, TTypes.Unknown);
|
||||
var typedInputPair := TAst.Tuple(rawTuple.Identity, [streamNode, typedSelectorTuple], TTypes.Unknown);
|
||||
newInputs.Add(typedInputPair);
|
||||
finally
|
||||
newSelectors.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
// 2. Process Transformation Lambda
|
||||
lambda := P.Transformation;
|
||||
|
||||
if lambda.Parameters.Count <> paramTypes.Count then
|
||||
@@ -1004,6 +1018,7 @@ begin
|
||||
|
||||
var typedLambda := Accept(preTypedLambda).AsLambdaExpression;
|
||||
|
||||
// 3. Determine Result Type
|
||||
var lambdaRetType := typedLambda.AsTypedNode.StaticType.AsMethod.Signatures[0].ReturnType;
|
||||
var pipeType: IStaticType;
|
||||
|
||||
@@ -1024,8 +1039,12 @@ begin
|
||||
pipeType := TTypes.Unknown;
|
||||
end;
|
||||
|
||||
Result := TAst.Pipe(Node.Identity, TPipeInputList.Create(newInputs, P.Inputs.Identity), typedLambda, pipeType);
|
||||
// Reconstruct the Pipe Node with typed Inputs tuple and typed Lambda
|
||||
var typedInputsTuple := TAst.Tuple(rawInputs.Identity, newInputs.ToArray, TTypes.Unknown);
|
||||
Result := TAst.Pipe(Node.Identity, typedInputsTuple, typedLambda, pipeType);
|
||||
|
||||
finally
|
||||
newInputs.Free;
|
||||
paramTypes.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
+18
-79
@@ -55,14 +55,7 @@ type
|
||||
function VisitAddSeriesItem(const Node: IAstNode): TVoid;
|
||||
function VisitSeriesLength(const Node: IAstNode): TVoid;
|
||||
function VisitNop(const Node: IAstNode): TVoid;
|
||||
|
||||
// Unified List Visitor
|
||||
function VisitTuple(const Node: IAstNode): TVoid;
|
||||
|
||||
// Pipe Visitors (Specific lists kept for now)
|
||||
function VisitPipeInput(const Node: IAstNode): TVoid;
|
||||
function VisitPipeSelectorList(const Node: IAstNode): TVoid;
|
||||
function VisitPipeInputList(const Node: IAstNode): TVoid;
|
||||
function VisitPipe(const Node: IAstNode): TVoid;
|
||||
|
||||
protected
|
||||
@@ -87,9 +80,7 @@ var
|
||||
dumper: TAstDumper;
|
||||
begin
|
||||
if (not Assigned(Output)) or (not Assigned(RootNode)) then
|
||||
begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
Output.Clear;
|
||||
dumper := TAstDumper.Create(Output);
|
||||
@@ -112,12 +103,8 @@ begin
|
||||
Register(akConstant, VisitConstant);
|
||||
Register(akIdentifier, VisitIdentifier);
|
||||
Register(akKeyword, VisitKeyword);
|
||||
|
||||
// Unified List
|
||||
Register(akTuple, VisitTuple);
|
||||
// Element
|
||||
Register(akRecordField, VisitRecordField);
|
||||
|
||||
Register(akIfExpression, VisitIfExpression);
|
||||
Register(akCondExpression, VisitCondExpression);
|
||||
Register(akLambdaExpression, VisitLambdaExpression);
|
||||
@@ -138,19 +125,13 @@ begin
|
||||
Register(akSeriesLength, VisitSeriesLength);
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akNop, VisitNop);
|
||||
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
procedure TAstDumper.Execute(const RootNode: IAstNode);
|
||||
begin
|
||||
if Assigned(RootNode) then
|
||||
begin
|
||||
Visit(RootNode);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstDumper.Indent;
|
||||
@@ -165,21 +146,16 @@ end;
|
||||
|
||||
procedure TAstDumper.Log(const Text: string; const Node: IAstNode);
|
||||
var
|
||||
typeStr: string;
|
||||
staticType: IStaticType;
|
||||
begin
|
||||
typeStr := '';
|
||||
var typeStr := '';
|
||||
if Assigned(Node) and Node.IsTyped then
|
||||
begin
|
||||
staticType := Node.AsTypedNode.StaticType;
|
||||
if Assigned(staticType) then
|
||||
begin
|
||||
typeStr := Format(' <Type: %s>', [staticType.ToString]);
|
||||
end
|
||||
typeStr := Format(' <Type: %s>', [staticType.ToString])
|
||||
else
|
||||
begin
|
||||
typeStr := ' <Type: nil>';
|
||||
end;
|
||||
end;
|
||||
FOutput.Add(StringOfChar(' ', FIndent) + Text + typeStr);
|
||||
end;
|
||||
@@ -211,13 +187,9 @@ var
|
||||
begin
|
||||
I := Node.AsIdentifier;
|
||||
if I.Address.Kind <> akUnresolved then
|
||||
begin
|
||||
LogFmt('Identifier: %s -> %s', [I.Name, FormatAddress(I.Address)], Node);
|
||||
end
|
||||
LogFmt('Identifier: %s -> %s', [I.Name, FormatAddress(I.Address)], Node)
|
||||
else
|
||||
begin
|
||||
LogFmt('Identifier: %s (unbound)', [I.Name], Node);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitKeyword(const Node: IAstNode): TVoid;
|
||||
@@ -270,7 +242,6 @@ end;
|
||||
function TAstDumper.VisitLambdaExpression(const Node: IAstNode): TVoid;
|
||||
var
|
||||
E: ILambdaExpressionNode;
|
||||
addr: TResolvedAddress;
|
||||
symbols: TArray<string>;
|
||||
layout: IScopeLayout;
|
||||
slot: Integer;
|
||||
@@ -306,17 +277,15 @@ begin
|
||||
|
||||
Log('Parameters:');
|
||||
Indent;
|
||||
Visit(E.Parameters); // Visits Tuple
|
||||
Visit(E.Parameters);
|
||||
Unindent;
|
||||
|
||||
if Length(E.Upvalues) > 0 then
|
||||
begin
|
||||
LogFmt('Captured Upvalues (%d):', [Length(E.Upvalues)]);
|
||||
Indent;
|
||||
for addr in E.Upvalues do
|
||||
begin
|
||||
for var addr in E.Upvalues do
|
||||
Log(FormatAddress(addr));
|
||||
end;
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
@@ -364,7 +333,7 @@ begin
|
||||
Log('Callee:');
|
||||
Visit(C.Callee);
|
||||
LogFmt('Arguments (%d):', [args.Count]);
|
||||
Visit(args); // Visits Tuple
|
||||
Visit(args);
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
@@ -506,6 +475,17 @@ begin
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitRecordField(const Node: IAstNode): TVoid;
|
||||
var
|
||||
F: IRecordFieldNode;
|
||||
begin
|
||||
F := Node.AsRecordField;
|
||||
LogFmt('Field :%s', [F.Key.Value.Name]);
|
||||
Indent;
|
||||
Visit(F.Value);
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitCreateSeries(const Node: IAstNode): TVoid;
|
||||
begin
|
||||
LogFmt('CreateSeries: %s', [Node.AsCreateSeries.Definition], Node);
|
||||
@@ -543,8 +523,6 @@ begin
|
||||
Log('Nop', Node);
|
||||
end;
|
||||
|
||||
// --- Unified List Visitor ---
|
||||
|
||||
function TAstDumper.VisitTuple(const Node: IAstNode): TVoid;
|
||||
var
|
||||
T: ITupleNode;
|
||||
@@ -563,45 +541,6 @@ begin
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitRecordField(const Node: IAstNode): TVoid;
|
||||
var
|
||||
F: IRecordFieldNode;
|
||||
begin
|
||||
F := Node.AsRecordField;
|
||||
LogFmt('Field :%s', [F.Key.Value.Name]);
|
||||
Indent;
|
||||
Visit(F.Value);
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
// --- Pipe Visitors ---
|
||||
|
||||
function TAstDumper.VisitPipeInput(const Node: IAstNode): TVoid;
|
||||
var
|
||||
P: IPipeInputNode;
|
||||
begin
|
||||
P := Node.AsPipeInput;
|
||||
Log('PipeInput', Node);
|
||||
Indent;
|
||||
Log('Source:');
|
||||
Visit(P.StreamSource);
|
||||
Log('Selectors:');
|
||||
Visit(P.Selectors);
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitPipeSelectorList(const Node: IAstNode): TVoid;
|
||||
begin
|
||||
for var item in Node.AsPipeSelectorList do
|
||||
Visit(item);
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitPipeInputList(const Node: IAstNode): TVoid;
|
||||
begin
|
||||
for var item in Node.AsPipeInputList do
|
||||
Visit(item);
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitPipe(const Node: IAstNode): TVoid;
|
||||
var
|
||||
P: IPipeNode;
|
||||
@@ -609,7 +548,7 @@ begin
|
||||
P := Node.AsPipe;
|
||||
Log('Pipe', Node);
|
||||
Indent;
|
||||
Log('Inputs:');
|
||||
Log('Inputs (Tuple of Tuples):');
|
||||
Visit(P.Inputs);
|
||||
Log('Transformation:');
|
||||
Visit(P.Transformation);
|
||||
|
||||
@@ -454,35 +454,61 @@ end;
|
||||
function TEvaluatorVisitor.VisitPipe(const N: IPipeNode): TDataValue;
|
||||
var
|
||||
i: Integer;
|
||||
inputNode: IPipeInputNode;
|
||||
sources: TArray<IStream>;
|
||||
config: TPipeConfig;
|
||||
inputVal: TDataValue;
|
||||
sourceSeries: IScalarRecordSeries;
|
||||
lambdaFunc: TDataValue.TFunc;
|
||||
|
||||
inputsTuple: ITupleNode;
|
||||
entryTuple: ITupleNode;
|
||||
sourceId: IIdentifierNode;
|
||||
selectorsTuple: ITupleNode;
|
||||
begin
|
||||
SetLength(sources, N.Inputs.Count);
|
||||
SetLength(config, N.Inputs.Count);
|
||||
for i := 0 to N.Inputs.Count - 1 do
|
||||
inputsTuple := N.Inputs;
|
||||
|
||||
SetLength(sources, inputsTuple.Count);
|
||||
SetLength(config, inputsTuple.Count);
|
||||
|
||||
for i := 0 to inputsTuple.Count - 1 do
|
||||
begin
|
||||
inputNode := N.Inputs.Items[i].AsPipeInput;
|
||||
inputVal := FScope[inputNode.StreamSource.Address];
|
||||
// Unpack: [Source, [Selectors]]
|
||||
// Note: Structure is guaranteed by TypeChecker
|
||||
entryTuple := inputsTuple.Items[i].AsTuple;
|
||||
|
||||
sourceId := entryTuple.Items[0].AsIdentifier;
|
||||
selectorsTuple := entryTuple.Items[1].AsTuple;
|
||||
|
||||
// Resolve Source Stream from Scope
|
||||
// The Binder has already linked sourceId to its variable address
|
||||
inputVal := FScope[sourceId.Address];
|
||||
|
||||
if (inputVal.Kind <> vkStream) then
|
||||
raise EEvaluatorException.Create('Stream expected');
|
||||
raise EEvaluatorException.Create(Format('Variable "%s" is not a Stream.', [sourceId.Name]));
|
||||
|
||||
sources[i] := inputVal.AsStream;
|
||||
sourceSeries := sources[i].Series;
|
||||
var srcDef := sourceSeries.Def;
|
||||
var selectors := inputNode.Selectors;
|
||||
SetLength(config[i], selectors.Count);
|
||||
for var k := 0 to selectors.Count - 1 do
|
||||
|
||||
// Build Selectors Config
|
||||
SetLength(config[i], selectorsTuple.Count);
|
||||
for var k := 0 to selectorsTuple.Count - 1 do
|
||||
begin
|
||||
var key := selectors.Items[k].AsKeyword.Value;
|
||||
var key := selectorsTuple.Items[k].AsKeyword.Value;
|
||||
var idx := srcDef.IndexOf(key);
|
||||
// Index check already done in TypeChecker, but good for safety
|
||||
if idx < 0 then
|
||||
raise EEvaluatorException.Create(Format('Field :%s not found in stream.', [key.Name]));
|
||||
|
||||
config[i][k] := TScalarRecordField.Create(key, srcDef[idx]);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Compile the transformation function
|
||||
lambdaFunc := Visit(N.Transformation).AsMethod();
|
||||
|
||||
var outputDef := N.StaticType.AsRecord.Definition;
|
||||
|
||||
var pipeAdapter: TPipeStream.TPipeLambda :=
|
||||
function(const S: array of ISeries; out R: array of TScalar.TValue): Boolean
|
||||
var
|
||||
@@ -493,14 +519,19 @@ begin
|
||||
SetLength(args, Length(S));
|
||||
for k := 0 to High(S) do
|
||||
args[k] := S[k].Items[0];
|
||||
|
||||
res := lambdaFunc(args);
|
||||
|
||||
if res.IsVoid then
|
||||
exit(False);
|
||||
|
||||
var rec := res.AsScalarRecord;
|
||||
for k := 0 to rec.Count - 1 do
|
||||
R[k] := rec.Items[k].Value;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
Result := TPipeStream.Create(config, outputDef, sources, pipeAdapter);
|
||||
end;
|
||||
|
||||
|
||||
+84
-257
@@ -18,8 +18,6 @@ type
|
||||
function Deserialize(const AJson: TJSONObject): IAstNode;
|
||||
end;
|
||||
|
||||
// TJsonAstConverter implements the visitor pattern for serialization
|
||||
// and uses factory functions for deserialization.
|
||||
TJsonAstConverter = class(TAstVisitor<TJSONObject>, IJsonAstConverter)
|
||||
private
|
||||
procedure DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string);
|
||||
@@ -52,16 +50,15 @@ type
|
||||
function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
|
||||
function JsonToNopNode(const AObj: TJSONObject): INopNode;
|
||||
function JsonToTupleNode(const AObj: TJSONObject): ITupleNode;
|
||||
function JsonToPipeNode(const AObj: TJSONObject): IPipeNode;
|
||||
|
||||
strict private
|
||||
// Serialization Visitors (IAstNode signature)
|
||||
// Serialization Visitors
|
||||
function VisitConstant(const Node: IAstNode): TJSONObject;
|
||||
function VisitIdentifier(const Node: IAstNode): TJSONObject;
|
||||
function VisitKeyword(const Node: IAstNode): TJSONObject;
|
||||
|
||||
function VisitTuple(const Node: IAstNode): TJSONObject;
|
||||
function VisitRecordField(const Node: IAstNode): TJSONObject;
|
||||
|
||||
function VisitIfExpression(const Node: IAstNode): TJSONObject;
|
||||
function VisitCondExpression(const Node: IAstNode): TJSONObject;
|
||||
function VisitLambdaExpression(const Node: IAstNode): TJSONObject;
|
||||
@@ -82,10 +79,6 @@ type
|
||||
function VisitAddSeriesItem(const Node: IAstNode): TJSONObject;
|
||||
function VisitSeriesLength(const Node: IAstNode): TJSONObject;
|
||||
function VisitNop(const Node: IAstNode): TJSONObject;
|
||||
|
||||
function VisitPipeInput(const Node: IAstNode): TJSONObject;
|
||||
function VisitPipeSelectorList(const Node: IAstNode): TJSONObject;
|
||||
function VisitPipeInputList(const Node: IAstNode): TJSONObject;
|
||||
function VisitPipe(const Node: IAstNode): TJSONObject;
|
||||
|
||||
protected
|
||||
@@ -100,7 +93,8 @@ type
|
||||
implementation
|
||||
|
||||
uses
|
||||
Myc.Data.Keyword;
|
||||
Myc.Data.Keyword,
|
||||
Myc.Ast.Identities;
|
||||
|
||||
{ TJsonAstConverter }
|
||||
|
||||
@@ -114,10 +108,8 @@ begin
|
||||
Register(akConstant, VisitConstant);
|
||||
Register(akIdentifier, VisitIdentifier);
|
||||
Register(akKeyword, VisitKeyword);
|
||||
|
||||
Register(akTuple, VisitTuple);
|
||||
Register(akRecordField, VisitRecordField);
|
||||
|
||||
Register(akIfExpression, VisitIfExpression);
|
||||
Register(akCondExpression, VisitCondExpression);
|
||||
Register(akLambdaExpression, VisitLambdaExpression);
|
||||
@@ -138,19 +130,13 @@ begin
|
||||
Register(akSeriesLength, VisitSeriesLength);
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akNop, VisitNop);
|
||||
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONObject;
|
||||
begin
|
||||
if not Assigned(RootNode) then
|
||||
begin
|
||||
exit(nil);
|
||||
end;
|
||||
Result := Visit(RootNode);
|
||||
end;
|
||||
|
||||
@@ -159,8 +145,6 @@ begin
|
||||
Result := JsonToNode(AJson);
|
||||
end;
|
||||
|
||||
{ Serialization Visitors }
|
||||
|
||||
procedure TJsonAstConverter.DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string);
|
||||
var
|
||||
valObj, scalarObj: TJSONObject;
|
||||
@@ -222,12 +206,9 @@ begin
|
||||
T := Node.AsTuple;
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('Tuple'));
|
||||
|
||||
elemsArray := TJSONArray.Create;
|
||||
for i := 0 to T.Count - 1 do
|
||||
begin
|
||||
elemsArray.Add(Visit(T.Items[i]));
|
||||
end;
|
||||
Result.AddPair('Elements', elemsArray);
|
||||
end;
|
||||
|
||||
@@ -241,15 +222,10 @@ begin
|
||||
Result.AddPair('NodeType', TJSONString.Create('IfExpr'));
|
||||
Result.AddPair('Condition', Visit(E.Condition));
|
||||
Result.AddPair('ThenBranch', Visit(E.ThenBranch));
|
||||
|
||||
if Assigned(E.ElseBranch) then
|
||||
begin
|
||||
elseObj := Visit(E.ElseBranch);
|
||||
end
|
||||
elseObj := Visit(E.ElseBranch)
|
||||
else
|
||||
begin
|
||||
elseObj := TJSONNull.Create;
|
||||
end;
|
||||
Result.AddPair('ElseBranch', elseObj);
|
||||
end;
|
||||
|
||||
@@ -270,16 +246,10 @@ begin
|
||||
pairObj.AddPair('Branch', Visit(E.Pairs[i].Branch));
|
||||
pairsArray.Add(pairObj);
|
||||
end;
|
||||
|
||||
if Assigned(E.ElseBranch) then
|
||||
begin
|
||||
elseObj := Visit(E.ElseBranch);
|
||||
end
|
||||
elseObj := Visit(E.ElseBranch)
|
||||
else
|
||||
begin
|
||||
elseObj := TJSONNull.Create;
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('CondExpr'));
|
||||
Result.AddPair('Pairs', pairsArray);
|
||||
@@ -289,41 +259,23 @@ end;
|
||||
function TJsonAstConverter.VisitLambdaExpression(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
L: ILambdaExpressionNode;
|
||||
paramsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
L := Node.AsLambdaExpression;
|
||||
paramsArray := TJSONArray.Create;
|
||||
// Iterate Tuple
|
||||
for i := 0 to L.Parameters.Count - 1 do
|
||||
begin
|
||||
paramsArray.Add(Visit(L.Parameters.Items[i]));
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('LambdaExpr'));
|
||||
Result.AddPair('Parameters', paramsArray);
|
||||
Result.AddPair('Parameters', Visit(L.Parameters));
|
||||
Result.AddPair('Body', Visit(L.Body));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitMacroDefinition(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
M: IMacroDefinitionNode;
|
||||
paramsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
M := Node.AsMacroDefinition;
|
||||
paramsArray := TJSONArray.Create;
|
||||
// Iterate Tuple
|
||||
for i := 0 to M.Parameters.Count - 1 do
|
||||
begin
|
||||
paramsArray.Add(Visit(M.Parameters.Items[i]));
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('MacroDef'));
|
||||
Result.AddPair('Name', Visit(M.Name));
|
||||
Result.AddPair('Parameters', paramsArray);
|
||||
Result.AddPair('Parameters', Visit(M.Parameters));
|
||||
Result.AddPair('Body', Visit(M.Body));
|
||||
end;
|
||||
|
||||
@@ -351,80 +303,38 @@ end;
|
||||
function TJsonAstConverter.VisitFunctionCall(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
C: IFunctionCallNode;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
C := Node.AsFunctionCall;
|
||||
argsArray := TJSONArray.Create;
|
||||
// Iterate Tuple
|
||||
for i := 0 to C.Arguments.Count - 1 do
|
||||
begin
|
||||
argsArray.Add(Visit(C.Arguments.Items[i]));
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('FunctionCall'));
|
||||
Result.AddPair('Callee', Visit(C.Callee));
|
||||
Result.AddPair('Arguments', argsArray);
|
||||
Result.AddPair('Arguments', Visit(C.Arguments));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitMacroExpansionNode(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
M: IMacroExpansionNode;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
M := Node.AsMacroExpansion;
|
||||
argsArray := TJSONArray.Create;
|
||||
// Iterate Tuple
|
||||
for i := 0 to M.CallNode.Arguments.Count - 1 do
|
||||
begin
|
||||
argsArray.Add(Visit(M.CallNode.Arguments.Items[i]));
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('MacroExpansion'));
|
||||
Result.AddPair('Callee', Visit(M.CallNode.Callee));
|
||||
Result.AddPair('Arguments', argsArray);
|
||||
Result.AddPair('Arguments', Visit(M.CallNode.Arguments));
|
||||
Result.AddPair('ExpandedBody', Visit(M.ExpandedBody));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitRecurNode(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
R: IRecurNode;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
R := Node.AsRecur;
|
||||
argsArray := TJSONArray.Create;
|
||||
// Iterate Tuple
|
||||
for i := 0 to R.Arguments.Count - 1 do
|
||||
begin
|
||||
argsArray.Add(Visit(R.Arguments.Items[i]));
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('Recur'));
|
||||
Result.AddPair('Arguments', argsArray);
|
||||
Result.AddPair('Arguments', Visit(Node.AsRecur.Arguments));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitBlockExpression(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
B: IBlockExpressionNode;
|
||||
exprsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
B := Node.AsBlockExpression;
|
||||
exprsArray := TJSONArray.Create;
|
||||
// Iterate Tuple
|
||||
for i := 0 to B.Expressions.Count - 1 do
|
||||
begin
|
||||
exprsArray.Add(Visit(B.Expressions.Items[i]));
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('Block'));
|
||||
Result.AddPair('Expressions', exprsArray);
|
||||
Result.AddPair('Expressions', Visit(Node.AsBlockExpression.Expressions));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitVariableDeclaration(const Node: IAstNode): TJSONObject;
|
||||
@@ -434,14 +344,9 @@ var
|
||||
begin
|
||||
V := Node.AsVariableDeclaration;
|
||||
if Assigned(V.Initializer) then
|
||||
begin
|
||||
initObj := Visit(V.Initializer);
|
||||
end
|
||||
initObj := Visit(V.Initializer)
|
||||
else
|
||||
begin
|
||||
initObj := TJSONNull.Create;
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('VarDecl'));
|
||||
Result.AddPair('Identifier', Visit(V.Target));
|
||||
@@ -482,21 +387,10 @@ begin
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitRecordLiteral(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
R: IRecordLiteralNode;
|
||||
fieldsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
R := Node.AsRecordLiteral;
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('RecordLiteral'));
|
||||
fieldsArray := TJSONArray.Create;
|
||||
// Iterate Tuple
|
||||
for i := 0 to R.Fields.Count - 1 do
|
||||
begin
|
||||
fieldsArray.Add(Visit(R.Fields.Items[i]));
|
||||
end;
|
||||
Result.AddPair('Fields', fieldsArray);
|
||||
Result.AddPair('Fields', Visit(Node.AsRecordLiteral.Fields));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitRecordField(const Node: IAstNode): TJSONObject;
|
||||
@@ -523,14 +417,9 @@ var
|
||||
begin
|
||||
A := Node.AsAddSeriesItem;
|
||||
if Assigned(A.Lookback) then
|
||||
begin
|
||||
lookbackObj := Visit(A.Lookback);
|
||||
end
|
||||
lookbackObj := Visit(A.Lookback)
|
||||
else
|
||||
begin
|
||||
lookbackObj := TJSONNull.Create;
|
||||
end;
|
||||
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('AddSeriesItem'));
|
||||
Result.AddPair('Series', Visit(A.Series));
|
||||
@@ -551,21 +440,15 @@ begin
|
||||
Result.AddPair('NodeType', TJSONString.Create('Nop'));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitPipeInput(const Node: IAstNode): TJSONObject;
|
||||
begin
|
||||
raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
|
||||
end;
|
||||
function TJsonAstConverter.VisitPipeSelectorList(const Node: IAstNode): TJSONObject;
|
||||
begin
|
||||
raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
|
||||
end;
|
||||
function TJsonAstConverter.VisitPipeInputList(const Node: IAstNode): TJSONObject;
|
||||
begin
|
||||
raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
|
||||
end;
|
||||
function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONObject;
|
||||
var
|
||||
P: IPipeNode;
|
||||
begin
|
||||
raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
|
||||
P := Node.AsPipe;
|
||||
Result := TJSONObject.Create;
|
||||
Result.AddPair('NodeType', TJSONString.Create('Pipe'));
|
||||
Result.AddPair('Inputs', Visit(P.Inputs));
|
||||
Result.AddPair('Transformation', Visit(P.Transformation));
|
||||
end;
|
||||
|
||||
{ Deserialization Handlers }
|
||||
@@ -590,17 +473,11 @@ begin
|
||||
end;
|
||||
end
|
||||
else if SameText(kindStr, 'Text') then
|
||||
begin
|
||||
Result := valObj.GetValue<string>('Value');
|
||||
end
|
||||
Result := valObj.GetValue<string>('Value')
|
||||
else if SameText(kindStr, 'Void') then
|
||||
begin
|
||||
Result := TDataValue.Void;
|
||||
end
|
||||
Result := TDataValue.Void
|
||||
else
|
||||
begin
|
||||
raise ENotSupportedException.Create('Unsupported TDataValue kind.');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToNode(const AJson: TJSONValue; const ExpectedType: string): IAstNode;
|
||||
@@ -609,9 +486,7 @@ var
|
||||
nodeType: string;
|
||||
begin
|
||||
if not (AJson is TJSONObject) then
|
||||
begin
|
||||
raise EInvalidCast.Create('Expected JSON object.');
|
||||
end;
|
||||
obj := AJson as TJSONObject;
|
||||
nodeType := obj.GetValue<string>('NodeType');
|
||||
|
||||
@@ -665,6 +540,8 @@ begin
|
||||
Result := JsonToNopNode(obj)
|
||||
else if nodeType = 'Tuple' then
|
||||
Result := JsonToTupleNode(obj)
|
||||
else if nodeType = 'Pipe' then
|
||||
Result := JsonToPipeNode(obj)
|
||||
else
|
||||
raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s".', [nodeType]);
|
||||
end;
|
||||
@@ -689,14 +566,9 @@ var
|
||||
elseNode: IAstNode;
|
||||
begin
|
||||
if AObj.GetValue('ElseBranch') is TJSONNull then
|
||||
begin
|
||||
elseNode := nil;
|
||||
end
|
||||
elseNode := nil
|
||||
else
|
||||
begin
|
||||
elseNode := JsonToNode(AObj.GetValue('ElseBranch'));
|
||||
end;
|
||||
|
||||
Result := TAst.IfExpr(JsonToNode(AObj.GetValue('Condition')), JsonToNode(AObj.GetValue('ThenBranch')), elseNode);
|
||||
end;
|
||||
|
||||
@@ -710,23 +582,15 @@ begin
|
||||
pairsArray := AObj.GetValue<TJSONArray>('Pairs');
|
||||
SetLength(pairs, pairsArray.Count);
|
||||
for i := 0 to pairsArray.Count - 1 do
|
||||
begin
|
||||
pairs[i] :=
|
||||
TCondPair.Create(
|
||||
JsonToNode((pairsArray.Items[i] as TJSONObject).GetValue('Condition')),
|
||||
JsonToNode((pairsArray.Items[i] as TJSONObject).GetValue('Branch'))
|
||||
);
|
||||
end;
|
||||
|
||||
if AObj.GetValue('ElseBranch') is TJSONNull then
|
||||
begin
|
||||
elseNode := nil;
|
||||
end
|
||||
elseNode := nil
|
||||
else
|
||||
begin
|
||||
elseNode := JsonToNode(AObj.GetValue('ElseBranch'));
|
||||
end;
|
||||
|
||||
Result := TAst.CondExpr(pairs, elseNode);
|
||||
end;
|
||||
|
||||
@@ -742,32 +606,37 @@ end;
|
||||
|
||||
function TJsonAstConverter.JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode;
|
||||
var
|
||||
params: TArray<IIdentifierNode>;
|
||||
paramArray: TJSONArray;
|
||||
i: Integer;
|
||||
identNodes: TList<IIdentifierNode>;
|
||||
node: IAstNode;
|
||||
begin
|
||||
paramArray := AObj.GetValue<TJSONArray>('Parameters');
|
||||
SetLength(params, paramArray.Count);
|
||||
for i := 0 to paramArray.Count - 1 do
|
||||
begin
|
||||
params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject);
|
||||
identNodes := TList<IIdentifierNode>.Create;
|
||||
try
|
||||
for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.ToArray do
|
||||
identNodes.Add(node.AsIdentifier);
|
||||
Result := TAst.LambdaExpr(identNodes.ToArray, JsonToNode(AObj.GetValue('Body')));
|
||||
finally
|
||||
identNodes.Free;
|
||||
end;
|
||||
Result := TAst.LambdaExpr(params, JsonToNode(AObj.GetValue('Body')));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode;
|
||||
var
|
||||
params: TArray<IIdentifierNode>;
|
||||
paramArray: TJSONArray;
|
||||
i: Integer;
|
||||
identNodes: TList<IIdentifierNode>;
|
||||
node: IAstNode;
|
||||
begin
|
||||
paramArray := AObj.GetValue<TJSONArray>('Parameters');
|
||||
SetLength(params, paramArray.Count);
|
||||
for i := 0 to paramArray.Count - 1 do
|
||||
begin
|
||||
params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject);
|
||||
identNodes := TList<IIdentifierNode>.Create;
|
||||
try
|
||||
for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.ToArray do
|
||||
identNodes.Add(node.AsIdentifier);
|
||||
Result :=
|
||||
TAst.MacroDef(
|
||||
JsonToIdentifierNode(AObj.GetValue('Name') as TJSONObject),
|
||||
identNodes.ToArray,
|
||||
JsonToNode(AObj.GetValue('Body'))
|
||||
);
|
||||
finally
|
||||
identNodes.Free;
|
||||
end;
|
||||
Result := TAst.MacroDef(JsonToIdentifierNode(AObj.GetValue('Name') as TJSONObject), params, JsonToNode(AObj.GetValue('Body')));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
|
||||
@@ -786,69 +655,29 @@ begin
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
|
||||
var
|
||||
args: TArray<IAstNode>;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
argsArray := AObj.GetValue<TJSONArray>('Arguments');
|
||||
SetLength(args, argsArray.Count);
|
||||
for i := 0 to argsArray.Count - 1 do
|
||||
begin
|
||||
args[i] := JsonToNode(argsArray.Items[i]);
|
||||
end;
|
||||
Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), args);
|
||||
Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), JsonToNode(AObj.GetValue('Arguments')).AsTuple.ToArray);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode;
|
||||
var
|
||||
callee, expandedBody: IAstNode;
|
||||
args: TArray<IAstNode>;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
tempCallNode: IFunctionCallNode;
|
||||
argsTuple: ITupleNode;
|
||||
begin
|
||||
callee := JsonToNode(AObj.GetValue('Callee'));
|
||||
expandedBody := JsonToNode(AObj.GetValue('ExpandedBody'));
|
||||
argsArray := AObj.GetValue<TJSONArray>('Arguments');
|
||||
SetLength(args, argsArray.Count);
|
||||
for i := 0 to argsArray.Count - 1 do
|
||||
begin
|
||||
args[i] := JsonToNode(argsArray.Items[i]);
|
||||
end;
|
||||
|
||||
tempCallNode := TAst.FunctionCall(callee, args);
|
||||
Result := TAst.MacroExpansionNode(tempCallNode, expandedBody);
|
||||
argsTuple := JsonToNode(AObj.GetValue('Arguments')).AsTuple;
|
||||
Result := TAst.MacroExpansionNode(TAst.FunctionCall(callee, argsTuple.ToArray), expandedBody);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
|
||||
var
|
||||
args: TArray<IAstNode>;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
argsArray := AObj.GetValue<TJSONArray>('Arguments');
|
||||
SetLength(args, argsArray.Count);
|
||||
for i := 0 to argsArray.Count - 1 do
|
||||
begin
|
||||
args[i] := JsonToNode(argsArray.Items[i]);
|
||||
end;
|
||||
Result := TAst.Recur(args);
|
||||
Result := TAst.Recur(JsonToNode(AObj.GetValue('Arguments')).AsTuple.ToArray);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
|
||||
var
|
||||
exprs: TArray<IAstNode>;
|
||||
exprsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
exprsArray := AObj.GetValue<TJSONArray>('Expressions');
|
||||
SetLength(exprs, exprsArray.Count);
|
||||
for i := 0 to exprsArray.Count - 1 do
|
||||
begin
|
||||
exprs[i] := JsonToNode(exprsArray.Items[i]);
|
||||
end;
|
||||
Result := TAst.Block(exprs);
|
||||
Result := TAst.Block(JsonToNode(AObj.GetValue('Expressions')).AsTuple.ToArray);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode;
|
||||
@@ -856,14 +685,9 @@ var
|
||||
initNode: IAstNode;
|
||||
begin
|
||||
if AObj.GetValue('Initializer') is TJSONNull then
|
||||
begin
|
||||
initNode := nil;
|
||||
end
|
||||
initNode := nil
|
||||
else
|
||||
begin
|
||||
initNode := JsonToNode(AObj.GetValue('Initializer'));
|
||||
end;
|
||||
|
||||
Result := TAst.VarDecl(JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject), initNode);
|
||||
end;
|
||||
|
||||
@@ -879,26 +703,27 @@ end;
|
||||
|
||||
function TJsonAstConverter.JsonToMemberAccessNode(const AObj: TJSONObject): IMemberAccessNode;
|
||||
begin
|
||||
Result := TAst.MemberAccess(JsonToNode(AObj.GetValue('Base')), JsonToKeywordNode(AObj.GetValue('Member') as TJSONObject));
|
||||
Result := TAst.MemberAccess(JsonToNode(AObj.GetValue('Base')), JsonToKeywordNode(AObj.GetValue('Member') as TJSONObject).AsKeyword);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToRecordLiteralNode(const AObj: TJSONObject): IRecordLiteralNode;
|
||||
var
|
||||
fields: TArray<IRecordFieldNode>;
|
||||
fieldsArray: TJSONArray;
|
||||
fields: TList<IRecordFieldNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
fieldsArray := AObj.GetValue<TJSONArray>('Fields');
|
||||
SetLength(fields, fieldsArray.Count);
|
||||
for i := 0 to fieldsArray.Count - 1 do
|
||||
begin
|
||||
fields[i] :=
|
||||
TAst.RecordField(
|
||||
TAst.Keyword((fieldsArray.Items[i] as TJSONObject).GetValue<string>('Name')),
|
||||
JsonToNode((fieldsArray.Items[i] as TJSONObject).GetValue('Value'))
|
||||
);
|
||||
fields := TList<IRecordFieldNode>.Create;
|
||||
try
|
||||
for i := 0 to fieldsArray.Count - 1 do
|
||||
begin
|
||||
var fieldObj := fieldsArray.Items[i] as TJSONObject;
|
||||
fields.Add(TAst.RecordField(TAst.Keyword(fieldObj.GetValue<string>('Name')), JsonToNode(fieldObj.GetValue('Value'))));
|
||||
end;
|
||||
Result := TAst.RecordLiteral(fields.ToArray);
|
||||
finally
|
||||
fields.Free;
|
||||
end;
|
||||
Result := TAst.RecordLiteral(fields);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode;
|
||||
@@ -911,14 +736,9 @@ var
|
||||
lookNode: IAstNode;
|
||||
begin
|
||||
if AObj.GetValue('Lookback') is TJSONNull then
|
||||
begin
|
||||
lookNode := nil;
|
||||
end
|
||||
lookNode := nil
|
||||
else
|
||||
begin
|
||||
lookNode := JsonToNode(AObj.GetValue('Lookback'));
|
||||
end;
|
||||
|
||||
Result :=
|
||||
TAst.AddSeriesItem(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject), JsonToNode(AObj.GetValue('Value')), lookNode);
|
||||
end;
|
||||
@@ -935,17 +755,24 @@ end;
|
||||
|
||||
function TJsonAstConverter.JsonToTupleNode(const AObj: TJSONObject): ITupleNode;
|
||||
var
|
||||
elems: TArray<IAstNode>;
|
||||
elemsArray: TJSONArray;
|
||||
elems: TList<IAstNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
elemsArray := AObj.GetValue<TJSONArray>('Elements');
|
||||
SetLength(elems, elemsArray.Count);
|
||||
for i := 0 to elemsArray.Count - 1 do
|
||||
begin
|
||||
elems[i] := JsonToNode(elemsArray.Items[i]);
|
||||
elems := TList<IAstNode>.Create;
|
||||
try
|
||||
for i := 0 to elemsArray.Count - 1 do
|
||||
elems.Add(JsonToNode(elemsArray.Items[i]));
|
||||
Result := TAst.Tuple(elems.ToArray);
|
||||
finally
|
||||
elems.Free;
|
||||
end;
|
||||
Result := TAst.Tuple(elems); // Factory handles identity/type defaults
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToPipeNode(const AObj: TJSONObject): IPipeNode;
|
||||
begin
|
||||
Result := TAst.Pipe(JsonToNode(AObj.GetValue('Inputs')).AsTuple, JsonToNode(AObj.GetValue('Transformation')).AsLambdaExpression);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+9
-241
@@ -109,9 +109,6 @@ type
|
||||
INopNode = interface;
|
||||
|
||||
// Pipes
|
||||
IPipeSelectorList = interface;
|
||||
IPipeInputNode = interface;
|
||||
IPipeInputList = interface;
|
||||
IPipeNode = interface;
|
||||
|
||||
// Node Kinds & Helpers
|
||||
@@ -144,10 +141,7 @@ type
|
||||
akRecur,
|
||||
akNop,
|
||||
// Pipes
|
||||
akPipe,
|
||||
akPipeInput,
|
||||
akPipeSelectorList,
|
||||
akPipeInputList
|
||||
akPipe
|
||||
);
|
||||
|
||||
TAstNodeKindHelper = record helper for TAstNodeKind
|
||||
@@ -204,9 +198,6 @@ type
|
||||
function AsNop: INopNode;
|
||||
|
||||
// Pipes
|
||||
function AsPipeInput: IPipeInputNode;
|
||||
function AsPipeSelectorList: IPipeSelectorList;
|
||||
function AsPipeInputList: IPipeInputList;
|
||||
function AsPipe: IPipeNode;
|
||||
|
||||
property IsTyped: Boolean read GetIsTyped;
|
||||
@@ -214,17 +205,6 @@ type
|
||||
property Identity: IAstIdentity read GetIdentity;
|
||||
end;
|
||||
|
||||
INodeList<T: IAstNode> = interface(IAstNode)
|
||||
{$region 'private'}
|
||||
function GetCount: Integer;
|
||||
function GetItem(Index: Integer): T;
|
||||
{$endregion}
|
||||
function GetEnumerator: TEnumerator<T>;
|
||||
function ToArray: TArray<T>;
|
||||
property Count: Integer read GetCount;
|
||||
property Items[Index: Integer]: T read GetItem; default;
|
||||
end;
|
||||
|
||||
IAstTypedNode = interface(IAstNode)
|
||||
{$region 'private'}
|
||||
function GetStaticType: IStaticType;
|
||||
@@ -468,31 +448,13 @@ type
|
||||
property Series: IIdentifierNode read GetSeries;
|
||||
end;
|
||||
|
||||
// A list of selector keywords [ :A :B ]
|
||||
IPipeSelectorList = interface(INodeList<IKeywordNode>)
|
||||
end;
|
||||
|
||||
// Represents a single input definition within a pipe: "btc [:Close :Open]"
|
||||
IPipeInputNode = interface(IAstTypedNode)
|
||||
{$region 'private'}
|
||||
function GetStreamSource: IIdentifierNode;
|
||||
function GetSelectors: IPipeSelectorList;
|
||||
{$endregion}
|
||||
property StreamSource: IIdentifierNode read GetStreamSource;
|
||||
property Selectors: IPipeSelectorList read GetSelectors;
|
||||
end;
|
||||
|
||||
// A list of pipe inputs
|
||||
IPipeInputList = interface(INodeList<IPipeInputNode>)
|
||||
end;
|
||||
|
||||
// The main pipe definition: (pipe [inputs...] (fn ...))
|
||||
// The main pipe definition: (pipe [[source [:sel]]...] (fn ...))
|
||||
IPipeNode = interface(IAstTypedNode)
|
||||
{$region 'private'}
|
||||
function GetInputs: IPipeInputList;
|
||||
function GetInputs: ITupleNode;
|
||||
function GetTransformation: ILambdaExpressionNode;
|
||||
{$endregion}
|
||||
property Inputs: IPipeInputList read GetInputs;
|
||||
property Inputs: ITupleNode read GetInputs;
|
||||
property Transformation: ILambdaExpressionNode read GetTransformation;
|
||||
end;
|
||||
|
||||
@@ -546,9 +508,6 @@ type
|
||||
function AsRecur: IRecurNode; virtual;
|
||||
function AsNop: INopNode; virtual;
|
||||
|
||||
function AsPipeInput: IPipeInputNode; virtual;
|
||||
function AsPipeSelectorList: IPipeSelectorList; virtual;
|
||||
function AsPipeInputList: IPipeInputList; virtual;
|
||||
function AsPipe: IPipeNode; virtual;
|
||||
|
||||
function AsTypedNode: IAstTypedNode; virtual;
|
||||
@@ -570,35 +529,6 @@ type
|
||||
property StaticType: IStaticType read GetStaticType;
|
||||
end;
|
||||
|
||||
// --- List Implementation Bases ---
|
||||
|
||||
TAstNodeList<T: IAstNode> = class(TAstNode, INodeList<T>)
|
||||
type
|
||||
TEnumerator = class(TEnumerator<T>)
|
||||
private
|
||||
FArray: TArray<T>;
|
||||
FIndex: NativeInt;
|
||||
function GetCurrent: T; inline;
|
||||
protected
|
||||
function DoGetCurrent: T; override;
|
||||
function DoMoveNext: Boolean; override;
|
||||
public
|
||||
constructor Create(const AArray: TArray<T>);
|
||||
function MoveNext: Boolean; inline;
|
||||
property Current: T read GetCurrent;
|
||||
end;
|
||||
private
|
||||
FItems: TArray<T>;
|
||||
function GetCount: Integer;
|
||||
function GetItem(Index: Integer): T;
|
||||
public
|
||||
constructor Create(const AItems: TArray<T>; const AIdentity: IAstIdentity);
|
||||
function GetEnumerator: TEnumerator<T>;
|
||||
function ToArray: TArray<T>;
|
||||
property Count: Integer read GetCount;
|
||||
property Items[Index: Integer]: T read GetItem; default;
|
||||
end;
|
||||
|
||||
TRecordFieldNode = class(TAstNode, IRecordFieldNode)
|
||||
private
|
||||
FKey: IKeywordNode;
|
||||
@@ -977,49 +907,17 @@ type
|
||||
function AsNop: INopNode; override;
|
||||
end;
|
||||
|
||||
TPipeSelectorList = class(TAstNodeList<IKeywordNode>, IPipeSelectorList)
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
function AsPipeSelectorList: IPipeSelectorList; override;
|
||||
end;
|
||||
|
||||
TPipeInputNode = class(TAstTypedNode, IPipeInputNode)
|
||||
private
|
||||
FStreamSource: IIdentifierNode;
|
||||
FSelectors: IPipeSelectorList;
|
||||
function GetStreamSource: IIdentifierNode;
|
||||
function GetSelectors: IPipeSelectorList;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
const AStreamSource: IIdentifierNode;
|
||||
const ASelectors: IPipeSelectorList;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
);
|
||||
function AsPipeInput: IPipeInputNode; override;
|
||||
end;
|
||||
|
||||
TPipeInputList = class(TAstNodeList<IPipeInputNode>, IPipeInputList)
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
function AsPipeInputList: IPipeInputList; override;
|
||||
end;
|
||||
|
||||
TPipeNode = class(TAstTypedNode, IPipeNode)
|
||||
private
|
||||
FInputs: IPipeInputList;
|
||||
FInputs: ITupleNode;
|
||||
FTransformation: ILambdaExpressionNode;
|
||||
function GetInputs: IPipeInputList;
|
||||
function GetInputs: ITupleNode;
|
||||
function GetTransformation: ILambdaExpressionNode;
|
||||
protected
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
const AInputs: IPipeInputList;
|
||||
const AInputs: ITupleNode;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
@@ -1329,21 +1227,6 @@ begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TAstNode.AsPipeInput: IPipeInputNode;
|
||||
begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TAstNode.AsPipeSelectorList: IPipeSelectorList;
|
||||
begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TAstNode.AsPipeInputList: IPipeInputList;
|
||||
begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TAstNode.AsPipe: IPipeNode;
|
||||
begin
|
||||
Result := nil;
|
||||
@@ -1372,34 +1255,6 @@ begin
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
{ TAstNodeList<T> }
|
||||
|
||||
constructor TAstNodeList<T>.Create(const AItems: TArray<T>; const AIdentity: IAstIdentity);
|
||||
begin
|
||||
inherited Create(AIdentity);
|
||||
FItems := AItems;
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.GetCount: Integer;
|
||||
begin
|
||||
Result := Length(FItems);
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.GetEnumerator: TEnumerator<T>;
|
||||
begin
|
||||
Result := TEnumerator.Create(FItems);
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.GetItem(Index: Integer): T;
|
||||
begin
|
||||
Result := FItems[Index];
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.ToArray: TArray<T>;
|
||||
begin
|
||||
Result := FItems;
|
||||
end;
|
||||
|
||||
{ TRecordFieldNode }
|
||||
|
||||
constructor TRecordFieldNode.Create(const AKey: IKeywordNode; const AValue: IAstNode; const AIdentity: IAstIdentity);
|
||||
@@ -2160,97 +2015,10 @@ begin
|
||||
Result := akNop;
|
||||
end;
|
||||
|
||||
constructor TAstNodeList<T>.TEnumerator.Create(const AArray: TArray<T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FArray := AArray;
|
||||
FIndex := -1;
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.TEnumerator.DoGetCurrent: T;
|
||||
begin
|
||||
Result := Current;
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.TEnumerator.DoMoveNext: Boolean;
|
||||
begin
|
||||
Result := MoveNext;
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.TEnumerator.GetCurrent: T;
|
||||
begin
|
||||
Result := FArray[FIndex];
|
||||
end;
|
||||
|
||||
function TAstNodeList<T>.TEnumerator.MoveNext: Boolean;
|
||||
begin
|
||||
Result := FIndex < High(FArray);
|
||||
if Result then
|
||||
Inc(FIndex);
|
||||
end;
|
||||
|
||||
{ TPipeSelectorList }
|
||||
|
||||
function TPipeSelectorList.AsPipeSelectorList: IPipeSelectorList;
|
||||
begin
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function TPipeSelectorList.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akPipeSelectorList;
|
||||
end;
|
||||
|
||||
{ TPipeInputNode }
|
||||
|
||||
constructor TPipeInputNode.Create(
|
||||
const AStreamSource: IIdentifierNode;
|
||||
const ASelectors: IPipeSelectorList;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
);
|
||||
begin
|
||||
inherited Create(AStaticType, AIdentity);
|
||||
FStreamSource := AStreamSource;
|
||||
FSelectors := ASelectors;
|
||||
end;
|
||||
|
||||
function TPipeInputNode.AsPipeInput: IPipeInputNode;
|
||||
begin
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function TPipeInputNode.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akPipeInput;
|
||||
end;
|
||||
|
||||
function TPipeInputNode.GetSelectors: IPipeSelectorList;
|
||||
begin
|
||||
Result := FSelectors;
|
||||
end;
|
||||
|
||||
function TPipeInputNode.GetStreamSource: IIdentifierNode;
|
||||
begin
|
||||
Result := FStreamSource;
|
||||
end;
|
||||
|
||||
{ TPipeInputList }
|
||||
|
||||
function TPipeInputList.AsPipeInputList: IPipeInputList;
|
||||
begin
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function TPipeInputList.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akPipeInputList;
|
||||
end;
|
||||
|
||||
{ TPipeNode }
|
||||
|
||||
constructor TPipeNode.Create(
|
||||
const AInputs: IPipeInputList;
|
||||
const AInputs: ITupleNode;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AIdentity: IAstIdentity;
|
||||
const AStaticType: IStaticType
|
||||
@@ -2266,7 +2034,7 @@ begin
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function TPipeNode.GetInputs: IPipeInputList;
|
||||
function TPipeNode.GetInputs: ITupleNode;
|
||||
begin
|
||||
Result := FInputs;
|
||||
end;
|
||||
|
||||
@@ -53,9 +53,6 @@ type
|
||||
function VisitRecurNode(const N: IAstNode): TVoid;
|
||||
function VisitNop(const N: IAstNode): TVoid;
|
||||
|
||||
function VisitPipeInput(const N: IAstNode): TVoid;
|
||||
function VisitPipeSelectorList(const N: IAstNode): TVoid;
|
||||
function VisitPipeInputList(const N: IAstNode): TVoid;
|
||||
function VisitPipe(const N: IAstNode): TVoid;
|
||||
|
||||
protected
|
||||
@@ -115,9 +112,6 @@ begin
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akNop, VisitNop);
|
||||
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
@@ -155,54 +149,14 @@ end;
|
||||
|
||||
// --- Implementations ---
|
||||
|
||||
function TPrettyPrintVisitor.VisitPipeInput(const N: IAstNode): TVoid;
|
||||
var
|
||||
P: IPipeInputNode;
|
||||
begin
|
||||
P := N.AsPipeInput;
|
||||
Visit(P.StreamSource);
|
||||
Append(' ');
|
||||
Visit(P.Selectors);
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitPipeSelectorList(const N: IAstNode): TVoid;
|
||||
var
|
||||
L: IPipeSelectorList;
|
||||
i: Integer;
|
||||
begin
|
||||
L := N.AsPipeSelectorList;
|
||||
Append('[');
|
||||
for i := 0 to L.Count - 1 do
|
||||
begin
|
||||
if i > 0 then
|
||||
Append(' ');
|
||||
Visit(L[i]);
|
||||
end;
|
||||
Append(']');
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitPipeInputList(const N: IAstNode): TVoid;
|
||||
var
|
||||
L: IPipeInputList;
|
||||
i: Integer;
|
||||
begin
|
||||
L := N.AsPipeInputList;
|
||||
Append('[');
|
||||
for i := 0 to L.Count - 1 do
|
||||
begin
|
||||
if i > 0 then
|
||||
Append(' ');
|
||||
Visit(L[i]);
|
||||
end;
|
||||
Append(']');
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitPipe(const N: IAstNode): TVoid;
|
||||
var
|
||||
P: IPipeNode;
|
||||
begin
|
||||
P := N.AsPipe;
|
||||
Append('(pipe ');
|
||||
// Inputs is a Tuple containing Tuples.
|
||||
// VisitTuple will automatically print [ ... ] for the outer and inner lists.
|
||||
Visit(P.Inputs);
|
||||
Indent;
|
||||
NewLine;
|
||||
|
||||
@@ -532,59 +532,21 @@ begin
|
||||
begin
|
||||
if SameText(head.Token.Text, 'pipe') then
|
||||
begin
|
||||
// (pipe [stream1 [:Key1 :Key2] stream2 [:Key3]] (fn ...))
|
||||
// (pipe [[stream1 [:Key1 :Key2]] [stream2 [:Key3]]] (fn ...))
|
||||
if Length(tailNodes) <> 2 then
|
||||
Error('Syntax Error: ''pipe'' requires [inputs] vector and (fn) transformation.');
|
||||
|
||||
if tailNodes[0].Kind <> akTuple then
|
||||
Error('Syntax Error: ''pipe'' first argument must be a vector [...].');
|
||||
|
||||
// Access tuple elements safely
|
||||
var inputsTuple := tailNodes[0].AsTuple;
|
||||
if (inputsTuple.Count mod 2) <> 0 then
|
||||
Error('Syntax Error: Pipe inputs must be pairs of Identifier and Selector Vector (e.g. [btc [:Close]]).');
|
||||
// NOTE: We do NOT validate inner tuples or keywords here anymore.
|
||||
// It is just a Tuple of Tuples structurally.
|
||||
// The TypeChecker will ensure valid structure (Series + Keywords).
|
||||
|
||||
var pipeInputs := TList<IPipeInputNode>.Create;
|
||||
try
|
||||
var k := 0;
|
||||
while k < inputsTuple.Count do
|
||||
begin
|
||||
var streamNode := inputsTuple.Items[k];
|
||||
if streamNode.Kind <> akIdentifier then
|
||||
Error('Syntax Error: Pipe input stream must be an identifier.');
|
||||
if tailNodes[1].Kind <> akLambdaExpression then
|
||||
Error('Syntax Error: Pipe transformation must be a lambda (fn).');
|
||||
|
||||
var selNode := inputsTuple.Items[k + 1];
|
||||
var streamId := streamNode.AsIdentifier;
|
||||
var selKeywords: TArray<IKeywordNode>;
|
||||
|
||||
if selNode.Kind <> akTuple then
|
||||
Error('Syntax Error: Pipe selector must be a vector of keywords (e.g. [:Close :High]).');
|
||||
|
||||
var selTuple := selNode.AsTuple;
|
||||
if selTuple.Count = 0 then
|
||||
Error('Syntax Error: Selector list cannot be empty.');
|
||||
|
||||
SetLength(selKeywords, selTuple.Count);
|
||||
for var m := 0 to selTuple.Count - 1 do
|
||||
begin
|
||||
if selTuple.Items[m].Kind <> akKeyword then
|
||||
Error('Syntax Error: Selector vector must contain only keywords.');
|
||||
selKeywords[m] := selTuple.Items[m].AsKeyword;
|
||||
end;
|
||||
|
||||
var selectorList := TAst.PipeSelectorList(selKeywords, selNode.Identity.Location);
|
||||
pipeInputs.Add(TAst.PipeInput(streamId, selectorList, streamId.Identity.Location));
|
||||
|
||||
Inc(k, 2);
|
||||
end;
|
||||
|
||||
if tailNodes[1].Kind <> akLambdaExpression then
|
||||
Error('Syntax Error: Pipe transformation must be a lambda (fn).');
|
||||
|
||||
Result := TAst.Pipe(pipeInputs.ToArray, tailNodes[1].AsLambdaExpression, startLoc);
|
||||
finally
|
||||
pipeInputs.Free;
|
||||
end;
|
||||
Result := TAst.Pipe(tailNodes[0].AsTuple, tailNodes[1].AsLambdaExpression, startLoc);
|
||||
end
|
||||
else if SameText(head.Token.Text, 'fn') then
|
||||
begin
|
||||
|
||||
+2
-101
@@ -83,9 +83,6 @@ type
|
||||
function VisitTuple(const N: IAstNode): IAstNode;
|
||||
|
||||
// Pipes
|
||||
function VisitPipeInput(const N: IAstNode): IAstNode;
|
||||
function VisitPipeSelectorList(const N: IAstNode): IAstNode;
|
||||
function VisitPipeInputList(const N: IAstNode): IAstNode;
|
||||
function VisitPipe(const N: IAstNode): IAstNode;
|
||||
|
||||
procedure SetupHandlers; override;
|
||||
@@ -122,9 +119,6 @@ type
|
||||
|
||||
function WalkTuple(const N: IAstNode): TVoid;
|
||||
|
||||
function WalkPipeInput(const N: IAstNode): TVoid;
|
||||
function WalkPipeSelectorList(const N: IAstNode): TVoid;
|
||||
function WalkPipeInputList(const N: IAstNode): TVoid;
|
||||
function WalkPipe(const N: IAstNode): TVoid;
|
||||
|
||||
procedure SetupHandlers; override;
|
||||
@@ -233,9 +227,6 @@ begin
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
// Pipes
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
@@ -522,82 +513,14 @@ begin
|
||||
Result := TAst.Recur(N.Identity, a, R.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitPipeInput(const N: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeInputNode;
|
||||
s: IAstNode;
|
||||
sel: IPipeSelectorList;
|
||||
begin
|
||||
P := N.AsPipeInput;
|
||||
s := Visit(P.StreamSource);
|
||||
sel := Visit(P.Selectors).AsPipeSelectorList;
|
||||
|
||||
if (s = P.StreamSource) and (sel = P.Selectors) then
|
||||
Result := N
|
||||
else
|
||||
Result := TAst.PipeInput(s.AsIdentifier, sel, N.Identity.Location);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitPipeSelectorList(const N: IAstNode): IAstNode;
|
||||
var
|
||||
L: IPipeSelectorList;
|
||||
hasChanged: Boolean;
|
||||
newItems: TArray<IKeywordNode>;
|
||||
item, newItem: IKeywordNode;
|
||||
i: Integer;
|
||||
begin
|
||||
L := N.AsPipeSelectorList;
|
||||
hasChanged := False;
|
||||
SetLength(newItems, L.Count);
|
||||
for i := 0 to L.Count - 1 do
|
||||
begin
|
||||
item := L[i];
|
||||
newItem := Visit(item).AsKeyword;
|
||||
newItems[i] := newItem;
|
||||
if item <> newItem then
|
||||
hasChanged := True;
|
||||
end;
|
||||
|
||||
if not hasChanged then
|
||||
Result := N
|
||||
else
|
||||
Result := TPipeSelectorList.Create(newItems, N.Identity);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitPipeInputList(const N: IAstNode): IAstNode;
|
||||
var
|
||||
L: IPipeInputList;
|
||||
hasChanged: Boolean;
|
||||
newItems: TArray<IPipeInputNode>;
|
||||
item, newItem: IPipeInputNode;
|
||||
i: Integer;
|
||||
begin
|
||||
L := N.AsPipeInputList;
|
||||
hasChanged := False;
|
||||
SetLength(newItems, L.Count);
|
||||
for i := 0 to L.Count - 1 do
|
||||
begin
|
||||
item := L[i];
|
||||
newItem := Visit(item).AsPipeInput;
|
||||
newItems[i] := newItem;
|
||||
if item <> newItem then
|
||||
hasChanged := True;
|
||||
end;
|
||||
|
||||
if not hasChanged then
|
||||
Result := N
|
||||
else
|
||||
Result := TPipeInputList.Create(newItems, N.Identity);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitPipe(const N: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeNode;
|
||||
inp: IPipeInputList;
|
||||
inp: ITupleNode;
|
||||
trans: ILambdaExpressionNode;
|
||||
begin
|
||||
P := N.AsPipe;
|
||||
inp := Visit(P.Inputs).AsPipeInputList;
|
||||
inp := Visit(P.Inputs).AsTuple;
|
||||
trans := Visit(P.Transformation).AsLambdaExpression;
|
||||
|
||||
if (inp = P.Inputs) and (trans = P.Transformation) then
|
||||
@@ -673,9 +596,6 @@ begin
|
||||
Register(akTuple, WalkTuple);
|
||||
|
||||
// Pipes
|
||||
Register(akPipeInput, WalkPipeInput);
|
||||
Register(akPipeSelectorList, WalkPipeSelectorList);
|
||||
Register(akPipeInputList, WalkPipeInputList);
|
||||
Register(akPipe, WalkPipe);
|
||||
end;
|
||||
|
||||
@@ -803,25 +723,6 @@ begin
|
||||
Visit(N.AsRecur.Arguments);
|
||||
end;
|
||||
|
||||
function TAstVisitor.WalkPipeInput(const N: IAstNode): TVoid;
|
||||
begin
|
||||
var P := N.AsPipeInput;
|
||||
Visit(P.StreamSource);
|
||||
Visit(P.Selectors);
|
||||
end;
|
||||
|
||||
function TAstVisitor.WalkPipeSelectorList(const N: IAstNode): TVoid;
|
||||
begin
|
||||
for var item in N.AsPipeSelectorList do
|
||||
Visit(item);
|
||||
end;
|
||||
|
||||
function TAstVisitor.WalkPipeInputList(const N: IAstNode): TVoid;
|
||||
begin
|
||||
for var item in N.AsPipeInputList do
|
||||
Visit(item);
|
||||
end;
|
||||
|
||||
function TAstVisitor.WalkPipe(const N: IAstNode): TVoid;
|
||||
begin
|
||||
var P := N.AsPipe;
|
||||
|
||||
+5
-36
@@ -269,23 +269,15 @@ type
|
||||
|
||||
// --- PIPES ---
|
||||
|
||||
class function PipeSelectorList(const AKeywords: TArray<IKeywordNode>; const Loc: ISourceLocation = nil): IPipeSelectorList; static;
|
||||
|
||||
class function PipeInput(
|
||||
const AStream: IIdentifierNode;
|
||||
const ASelectors: IPipeSelectorList;
|
||||
const Loc: ISourceLocation = nil
|
||||
): IPipeInputNode; static;
|
||||
|
||||
class function Pipe(
|
||||
const AInputs: TArray<IPipeInputNode>;
|
||||
const AInputs: ITupleNode;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const Loc: ISourceLocation = nil
|
||||
): IPipeNode; overload; static;
|
||||
|
||||
class function Pipe(
|
||||
const Identity: IAstIdentity;
|
||||
const AInputs: IPipeInputList;
|
||||
const AInputs: ITupleNode;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AStaticType: IStaticType = nil
|
||||
): IPipeNode; overload; static;
|
||||
@@ -892,38 +884,15 @@ end;
|
||||
|
||||
// --- PIPES ---
|
||||
|
||||
class function TAst.PipeSelectorList(const AKeywords: TArray<IKeywordNode>; const Loc: ISourceLocation): IPipeSelectorList;
|
||||
begin
|
||||
var listId := TIdentities.List('[', ']', ' ', Loc);
|
||||
Result := TPipeSelectorList.Create(AKeywords, listId);
|
||||
end;
|
||||
|
||||
class function TAst.PipeInput(
|
||||
const AStream: IIdentifierNode;
|
||||
const ASelectors: IPipeSelectorList;
|
||||
const Loc: ISourceLocation
|
||||
): IPipeInputNode;
|
||||
class function TAst.Pipe(const AInputs: ITupleNode; const ATransformation: ILambdaExpressionNode; const Loc: ISourceLocation): IPipeNode;
|
||||
begin
|
||||
var id := TIdentities.Structural(Loc);
|
||||
Result := TPipeInputNode.Create(AStream, ASelectors, 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);
|
||||
Result := TPipeNode.Create(inputList, ATransformation, id, TTypes.Unknown);
|
||||
Result := TPipeNode.Create(AInputs, ATransformation, id, TTypes.Unknown);
|
||||
end;
|
||||
|
||||
class function TAst.Pipe(
|
||||
const Identity: IAstIdentity;
|
||||
const AInputs: IPipeInputList;
|
||||
const AInputs: ITupleNode;
|
||||
const ATransformation: ILambdaExpressionNode;
|
||||
const AStaticType: IStaticType
|
||||
): IPipeNode;
|
||||
|
||||
@@ -11,7 +11,7 @@ uses
|
||||
Myc.Fmx.AstEditor.Node;
|
||||
|
||||
type
|
||||
// Unified handler for all list-like structures (Parameters, Arguments, Vectors, etc.)
|
||||
// Unified handler for all list-like structures (Parameters, Arguments, Vectors, and Pipe-Inputs)
|
||||
TTupleHandler = class(TBaseNodeHandler<ITupleNode>)
|
||||
private
|
||||
FChildNodes: TList<TAstViewNode>;
|
||||
@@ -22,30 +22,11 @@ type
|
||||
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
// --- Pipe Specific Lists (Not yet unified) ---
|
||||
|
||||
TPipeSelectorListHandler = class(TBaseNodeHandler<IPipeSelectorList>)
|
||||
private
|
||||
FChildNodes: TList<TAstViewNode>;
|
||||
public
|
||||
constructor Create(const ANode: IPipeSelectorList);
|
||||
destructor Destroy; override;
|
||||
procedure BuildUI(OwnerNode: TAstViewNode); override;
|
||||
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
TPipeInputListHandler = class(TBaseNodeHandler<IPipeInputList>)
|
||||
private
|
||||
FChildNodes: TList<TAstViewNode>;
|
||||
public
|
||||
constructor Create(const ANode: IPipeInputList);
|
||||
destructor Destroy; override;
|
||||
procedure BuildUI(OwnerNode: TAstViewNode); override;
|
||||
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.UITypes;
|
||||
|
||||
{ TTupleHandler }
|
||||
|
||||
constructor TTupleHandler.Create(const ANode: ITupleNode);
|
||||
@@ -65,20 +46,26 @@ var
|
||||
visu: IAstVisualizer;
|
||||
i: Integer;
|
||||
childView: TAstViewNode;
|
||||
lbl: TTextNode;
|
||||
begin
|
||||
// Tuples act as lists, generally horizontal flow (like [1 2 3]).
|
||||
// Note: If Vertical flow is needed (e.g. for Blocks), the Parent View should probably
|
||||
// override this or we need a context flag. Defaulting to Horizontal.
|
||||
|
||||
// Tuples act as lists, using horizontal flow (e.g. [1 2 3] or [[src [:sel]]])
|
||||
OwnerNode.Frameless := True;
|
||||
OwnerNode.Orientation := loHorizontal;
|
||||
OwnerNode.Alignment := laCenter;
|
||||
|
||||
// Pass same depth, or increment? Usually lists don't indent depth unless they are blocks.
|
||||
// Opening Bracket
|
||||
lbl := OwnerNode.AddLabel(OwnerNode, '[');
|
||||
lbl.FontSettings.Font.Style := [TFontStyle.fsBold];
|
||||
lbl.Color := $FFBBBBBB;
|
||||
|
||||
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
|
||||
|
||||
for i := 0 to FNode.Count - 1 do
|
||||
begin
|
||||
// Standard spacing between elements
|
||||
if i > 0 then
|
||||
OwnerNode.AddLabel(OwnerNode, ' ');
|
||||
|
||||
childView := visu.CallAccept(FNode.Items[i]);
|
||||
FChildNodes.Add(childView);
|
||||
end;
|
||||
@@ -86,9 +73,13 @@ begin
|
||||
// Visual hint for empty tuples
|
||||
if FNode.Count = 0 then
|
||||
begin
|
||||
var lbl := OwnerNode.AddLabel(OwnerNode, '[]');
|
||||
lbl.FontSettings.FontColor := $FF808080;
|
||||
OwnerNode.AddLabel(OwnerNode, ' '); // spacer
|
||||
end;
|
||||
|
||||
// Closing Bracket
|
||||
lbl := OwnerNode.AddLabel(OwnerNode, ']');
|
||||
lbl.FontSettings.Font.Style := [TFontStyle.fsBold];
|
||||
lbl.Color := $FFBBBBBB;
|
||||
end;
|
||||
|
||||
function TTupleHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
|
||||
@@ -103,114 +94,4 @@ begin
|
||||
Result := TAst.Tuple(FNode.Identity, newElements, FNode.StaticType);
|
||||
end;
|
||||
|
||||
{ TPipeSelectorListHandler }
|
||||
|
||||
constructor TPipeSelectorListHandler.Create(const ANode: IPipeSelectorList);
|
||||
begin
|
||||
inherited Create(ANode);
|
||||
FChildNodes := TList<TAstViewNode>.Create;
|
||||
end;
|
||||
|
||||
destructor TPipeSelectorListHandler.Destroy;
|
||||
begin
|
||||
FChildNodes.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TPipeSelectorListHandler.BuildUI(OwnerNode: TAstViewNode);
|
||||
var
|
||||
visu: IAstVisualizer;
|
||||
item: IKeywordNode;
|
||||
begin
|
||||
OwnerNode.Frameless := True;
|
||||
OwnerNode.Orientation := loHorizontal;
|
||||
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
|
||||
|
||||
for item in FNode do
|
||||
FChildNodes.Add(visu.CallAccept(item));
|
||||
end;
|
||||
|
||||
function TPipeSelectorListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
|
||||
var
|
||||
newItems: TArray<IKeywordNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
SetLength(newItems, FChildNodes.Count);
|
||||
for i := 0 to FChildNodes.Count - 1 do
|
||||
newItems[i] := FChildNodes[i].CreateAst.AsKeyword;
|
||||
|
||||
Result := TAst.PipeSelectorList(newItems, FNode.Identity.Location);
|
||||
end;
|
||||
|
||||
{ TPipeInputListHandler }
|
||||
|
||||
constructor TPipeInputListHandler.Create(const ANode: IPipeInputList);
|
||||
begin
|
||||
inherited Create(ANode);
|
||||
FChildNodes := TList<TAstViewNode>.Create;
|
||||
end;
|
||||
|
||||
destructor TPipeInputListHandler.Destroy;
|
||||
begin
|
||||
FChildNodes.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TPipeInputListHandler.BuildUI(OwnerNode: TAstViewNode);
|
||||
var
|
||||
visu: IAstVisualizer;
|
||||
item: IPipeInputNode;
|
||||
begin
|
||||
OwnerNode.Frameless := True;
|
||||
OwnerNode.Orientation := loHorizontal; // Inputs flow horizontally in pipe def
|
||||
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
|
||||
|
||||
for item in FNode do
|
||||
FChildNodes.Add(visu.CallAccept(item));
|
||||
end;
|
||||
|
||||
function TPipeInputListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
|
||||
var
|
||||
newItems: TArray<IPipeInputNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
SetLength(newItems, FChildNodes.Count);
|
||||
for i := 0 to FChildNodes.Count - 1 do
|
||||
newItems[i] := FChildNodes[i].CreateAst.AsPipeInput;
|
||||
|
||||
Result := TAst.Pipe(newItems, nil, FNode.Identity.Location).AsPipe.Inputs; // Hack to reuse factory logic or create direct?
|
||||
// Better: Since TAst doesn't expose PipeInputList factory directly (it's internal to Pipe factory),
|
||||
// we should really rely on the PipeNode handler to reconstruct the list.
|
||||
// However, if we must return a node here:
|
||||
// We assume TAst has a factory or we create the implementation directly if exposed.
|
||||
// Since we don't have direct access to TPipeInputList.Create here (it is in implementation of Nodes),
|
||||
// we use a workaround via the generic TAst.Pipe factory which takes an array.
|
||||
|
||||
// Actually, looking at TAst.Pipe overload that takes TArray<IPipeInputNode>, it creates the list internally.
|
||||
// But here we need to return the list object itself.
|
||||
// Since this method is likely called by the PipeHandler to reconstruct its children,
|
||||
// the PipeHandler usually calls CreateAst on the child nodes.
|
||||
// If the ViewNode for the list returns the list AST, we are good.
|
||||
// BUT: We cannot instantiate the list implementation here easily without the factory exposing it.
|
||||
|
||||
// FIX: We need to expose a factory for PipeInputList in TAst or make the implementation accessible.
|
||||
// For this Proof of Concept, I will assume TAst has a method or we added one.
|
||||
// Let's verify Myc.Ast.pas ... It doesn't have PipeInputList factory public.
|
||||
// It has: class function Pipe(const AInputs: TArray<IPipeInputNode> ...
|
||||
|
||||
// Workaround: We cast to TAstNodeList and create it, assuming we can access the class if we add it to interface?
|
||||
// No, TImplementation classes are usually hidden.
|
||||
// CORRECT FIX: The PipeHandler should probably manage the list reconstruction if it's just an array in the factory.
|
||||
// However, to satisfy the interface, let's assume we added `TAst.PipeInputList` factory.
|
||||
// I will add a TO-DO comment or use a hack.
|
||||
|
||||
// REALITY CHECK: In the previous step for Myc.Ast, I added:
|
||||
// class function Pipe(const AInputs: IPipeInputList ...
|
||||
// But I did NOT add a factory to create IPipeInputList standalone.
|
||||
// I will use a dummy return or raise exception, as typically the Pipe Handler reconstructs the whole pipe
|
||||
// using arrays, not by asking the list-view to reconstruct itself.
|
||||
|
||||
raise ENotSupportedException.Create('Reconstructing PipeInputList directly is not supported. Reconstruct the Pipe Node instead.');
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -13,8 +13,8 @@ uses
|
||||
Myc.Fmx.AstEditor.Handlers;
|
||||
|
||||
type
|
||||
// --- Pipe Container ---
|
||||
|
||||
// --- Pipe Container Handler ---
|
||||
// Repräsentiert die (pipe [[src [:sel]]...] (fn ...)) Struktur.
|
||||
TPipeNodeHandler = class(TBaseNodeHandler<IPipeNode>)
|
||||
private
|
||||
FInputsNode: TAstViewNode;
|
||||
@@ -24,34 +24,6 @@ type
|
||||
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
// --- Pipe Inputs ---
|
||||
|
||||
TPipeInputListHandler = class(TNodeListHandler<IPipeInputNode>)
|
||||
protected
|
||||
function GetOrientation: TLayoutOrientation; override;
|
||||
function GetAlignment: TLayoutAlignment; override;
|
||||
public
|
||||
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
TPipeInputNodeHandler = class(TBaseNodeHandler<IPipeInputNode>)
|
||||
private
|
||||
FStreamNode: TAstViewNode;
|
||||
FSelectorsNode: TAstViewNode;
|
||||
public
|
||||
procedure BuildUI(OwnerNode: TAstViewNode); override;
|
||||
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
// --- Selectors ---
|
||||
|
||||
TPipeSelectorListHandler = class(TNodeListHandler<IKeywordNode>)
|
||||
protected
|
||||
function GetOrientation: TLayoutOrientation; override;
|
||||
public
|
||||
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TPipeNodeHandler }
|
||||
@@ -68,7 +40,7 @@ begin
|
||||
OwnerNode.Orientation := loVertical;
|
||||
OwnerNode.Alignment := laCenter;
|
||||
|
||||
// Distinct background color for Pipes to make them stand out as data flows
|
||||
// Visuelles Styling für den Dataflow-Container
|
||||
OwnerNode.BackgroundColor := $FF202530;
|
||||
OwnerNode.BorderColor := TAlphaColors.Slateblue;
|
||||
|
||||
@@ -77,101 +49,25 @@ begin
|
||||
header.FontSettings.Font.Style := [TFontStyle.fsBold];
|
||||
header.Color := TAlphaColors.Lightskyblue;
|
||||
|
||||
// Section 1: Inputs
|
||||
// Sektion 1: Inputs
|
||||
// FNode.Inputs ist jetzt ITupleNode. CallAccept triggert automatisch
|
||||
// den TTupleHandler aus Myc.Fmx.AstEditor.Handlers.Lists.
|
||||
FInputsNode := visu.CallAccept(FNode.Inputs);
|
||||
|
||||
// Visual Separator / Direction indicator
|
||||
// Visueller Trenner
|
||||
arrow := OwnerNode.AddLabel(OwnerNode, '▼ Transform ▼');
|
||||
arrow.FontSettings.Font.Size := 10;
|
||||
arrow.Color := TAlphaColors.Gray;
|
||||
arrow.Margins := TMarginRect.Create(0, 5, 0, 5);
|
||||
|
||||
// Section 2: Transformation (Lambda)
|
||||
// Sektion 2: Transformation (Lambda)
|
||||
FTransformNode := visu.CallAccept(FNode.Transformation);
|
||||
end;
|
||||
|
||||
function TPipeNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
|
||||
begin
|
||||
Result :=
|
||||
TAst.Pipe(FNode.Identity, FInputsNode.CreateAst.AsPipeInputList, FTransformNode.CreateAst.AsLambdaExpression, FNode.StaticType);
|
||||
end;
|
||||
|
||||
{ TPipeInputListHandler }
|
||||
|
||||
function TPipeInputListHandler.GetOrientation: TLayoutOrientation;
|
||||
begin
|
||||
// Stack inputs vertically
|
||||
Result := loVertical;
|
||||
end;
|
||||
|
||||
function TPipeInputListHandler.GetAlignment: TLayoutAlignment;
|
||||
begin
|
||||
Result := laFlush;
|
||||
end;
|
||||
|
||||
function TPipeInputListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
|
||||
var
|
||||
arr: TArray<IPipeInputNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
SetLength(arr, FChildNodes.Count);
|
||||
for i := 0 to FChildNodes.Count - 1 do
|
||||
arr[i] := FChildNodes[i].CreateAst.AsPipeInput;
|
||||
|
||||
Result := TPipeInputList.Create(arr, FNode.Identity);
|
||||
end;
|
||||
|
||||
{ TPipeInputNodeHandler }
|
||||
|
||||
procedure TPipeInputNodeHandler.BuildUI(OwnerNode: TAstViewNode);
|
||||
var
|
||||
visu: IAstVisualizer;
|
||||
begin
|
||||
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
|
||||
|
||||
OwnerNode.Frameless := True;
|
||||
OwnerNode.Orientation := loHorizontal;
|
||||
OwnerNode.Alignment := laCenter;
|
||||
|
||||
// "StreamName [ :Col :Col ]"
|
||||
FStreamNode := visu.CallAccept(FNode.StreamSource);
|
||||
|
||||
// Add small spacing
|
||||
var spacer := TAutoFitLayout.Create(OwnerNode.Host);
|
||||
spacer.Size := TSizeF.Create(5, 5);
|
||||
OwnerNode.AddChild(spacer);
|
||||
|
||||
FSelectorsNode := visu.CallAccept(FNode.Selectors);
|
||||
end;
|
||||
|
||||
function TPipeInputNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
|
||||
begin
|
||||
Result :=
|
||||
TAst.PipeInput(
|
||||
FNode.StreamSource, // Identifiers are immutable usually, or re-create if needed
|
||||
FSelectorsNode.CreateAst.AsPipeSelectorList,
|
||||
FNode.Identity.Location
|
||||
);
|
||||
end;
|
||||
|
||||
{ TPipeSelectorListHandler }
|
||||
|
||||
function TPipeSelectorListHandler.GetOrientation: TLayoutOrientation;
|
||||
begin
|
||||
// Selectors are horizontal: [:A :B :C]
|
||||
Result := loHorizontal;
|
||||
end;
|
||||
|
||||
function TPipeSelectorListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
|
||||
var
|
||||
arr: TArray<IKeywordNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
SetLength(arr, FChildNodes.Count);
|
||||
for i := 0 to FChildNodes.Count - 1 do
|
||||
arr[i] := FChildNodes[i].CreateAst.AsKeyword;
|
||||
|
||||
Result := TPipeSelectorList.Create(arr, FNode.Identity);
|
||||
// Rekonstruktion nutzt das nun generische ITupleNode für die Inputs
|
||||
Result := TAst.Pipe(FNode.Identity, FInputsNode.CreateAst.AsTuple, FTransformNode.CreateAst.AsLambdaExpression, FNode.StaticType);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -15,7 +15,7 @@ uses
|
||||
|
||||
type
|
||||
// Implementation of the Factory/Visitor for the Virtual Tree
|
||||
// Uses TAstVisitor<TAstViewNode> which now uses closures for dispatch.
|
||||
// Uses TAstVisitor<TAstViewNode> which uses closures for dispatch.
|
||||
TAstVisualizer = class(TAstVisitor<TAstViewNode>, IAstVisualizer)
|
||||
private
|
||||
FExprDepth: Integer;
|
||||
@@ -77,9 +77,6 @@ type
|
||||
function VisitSeriesLength(const Node: IAstNode): TAstViewNode;
|
||||
|
||||
// Pipes
|
||||
function VisitPipeInput(const Node: IAstNode): TAstViewNode;
|
||||
function VisitPipeSelectorList(const Node: IAstNode): TAstViewNode;
|
||||
function VisitPipeInputList(const Node: IAstNode): TAstViewNode;
|
||||
function VisitPipe(const Node: IAstNode): TAstViewNode;
|
||||
|
||||
public
|
||||
@@ -101,7 +98,8 @@ uses
|
||||
Myc.Fmx.AstEditor.Handlers.Data,
|
||||
Myc.Fmx.AstEditor.Handlers.Control,
|
||||
Myc.Fmx.AstEditor.Handlers.Primitives,
|
||||
Myc.Fmx.AstEditor.Handlers.Lists;
|
||||
Myc.Fmx.AstEditor.Handlers.Lists,
|
||||
Myc.Fmx.AstEditor.Handlers.Pipes;
|
||||
|
||||
function IsStandardOperator(const Name: string): Boolean;
|
||||
begin
|
||||
@@ -166,10 +164,7 @@ begin
|
||||
Register(akAddSeriesItem, VisitAddSeriesItem);
|
||||
Register(akSeriesLength, VisitSeriesLength);
|
||||
|
||||
// Pipes
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
// Pipes (Simplified)
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
@@ -208,7 +203,7 @@ begin
|
||||
Result := FWorkspace;
|
||||
end;
|
||||
|
||||
// --- Visitor Implementations (Cast-heavy for efficiency) ---
|
||||
// --- Visitor Implementations ---
|
||||
|
||||
function TAstVisualizer.VisitConstant(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
@@ -230,8 +225,6 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
||||
end;
|
||||
|
||||
// Lists
|
||||
|
||||
function TAstVisualizer.VisitTuple(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TTupleHandler.Create(Node.AsTuple));
|
||||
@@ -242,8 +235,6 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TRecordFieldHandler.Create(Node.AsRecordField));
|
||||
end;
|
||||
|
||||
// Control Flow
|
||||
|
||||
function TAstVisualizer.VisitBlockExpression(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TBlockExpressionNodeHandler.Create(Node.AsBlockExpression));
|
||||
@@ -264,8 +255,6 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TRecurNodeHandler.Create(Node.AsRecur));
|
||||
end;
|
||||
|
||||
// Functions
|
||||
|
||||
function TAstVisualizer.VisitLambdaExpression(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TLambdaExpressionNodeHandler.Create(Node.AsLambdaExpression));
|
||||
@@ -302,8 +291,6 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TMacroExpansionNodeHandler.Create(Node.AsMacroExpansion));
|
||||
end;
|
||||
|
||||
// Declarations
|
||||
|
||||
function TAstVisualizer.VisitVariableDeclaration(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TVariableDeclarationNodeHandler.Create(Node.AsVariableDeclaration));
|
||||
@@ -319,8 +306,6 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TMacroDefinitionNodeHandler.Create(Node.AsMacroDefinition));
|
||||
end;
|
||||
|
||||
// Metaprogramming
|
||||
|
||||
function TAstVisualizer.VisitQuasiquote(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TQuasiquoteNodeHandler.Create(Node.AsQuasiquote));
|
||||
@@ -336,8 +321,6 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TUnquoteSplicingNodeHandler.Create(Node.AsUnquoteSplicing));
|
||||
end;
|
||||
|
||||
// Data Access
|
||||
|
||||
function TAstVisualizer.VisitIndexer(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TIndexerNodeHandler.Create(Node.AsIndexer));
|
||||
@@ -353,8 +336,6 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TRecordLiteralNodeHandler.Create(Node.AsRecordLiteral));
|
||||
end;
|
||||
|
||||
// Series
|
||||
|
||||
function TAstVisualizer.VisitCreateSeries(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TCreateSeriesNodeHandler.Create(Node.AsCreateSeries));
|
||||
@@ -370,30 +351,9 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TSeriesLengthNodeHandler.Create(Node.AsSeriesLength));
|
||||
end;
|
||||
|
||||
// Pipes
|
||||
|
||||
function TAstVisualizer.VisitPipeInput(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
// Needs a dedicated handler for better visualization, fallback to Nop for now?
|
||||
// Actually, we don't have a specific handler for PipeInputNode in the library yet.
|
||||
// It's a typed node with children, so we should probably create one or reuse generic.
|
||||
// For this refactoring step, I'll use Nop to keep it compiling, but this should be improved.
|
||||
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
||||
end;
|
||||
|
||||
function TAstVisualizer.VisitPipeSelectorList(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TPipeSelectorListHandler.Create(Node.AsPipeSelectorList));
|
||||
end;
|
||||
|
||||
function TAstVisualizer.VisitPipeInputList(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TPipeInputListHandler.Create(Node.AsPipeInputList));
|
||||
end;
|
||||
|
||||
function TAstVisualizer.VisitPipe(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
||||
Result := TAstViewNode.Create(Self, TPipeNodeHandler.Create(Node.AsPipe));
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -195,7 +195,7 @@ begin
|
||||
FEnv.RootScope.Define('src', mockStream, TTypes.CreateRecordSeries(source.Def));
|
||||
|
||||
// Act
|
||||
script := '(pipe [src [:val]] (fn [x] {:res (* x 2)}))';
|
||||
script := '(pipe [[src [:val]]] (fn [x] {:res (* x 2)}))';
|
||||
|
||||
// 1. Compile & Link & Run (Constructs the Pipe Object)
|
||||
resVal := FEnv.Run(TAstScript.Parse(script));
|
||||
@@ -253,7 +253,7 @@ begin
|
||||
FEnv.RootScope.Define('in2', m2, TTypes.CreateRecordSeries(s2.Def));
|
||||
|
||||
// Act
|
||||
script := '(pipe [in1 [:a] in2 [:b]] (fn [valA valB] {:sum (+ valA valB)}))';
|
||||
script := '(pipe [[in1 [:a]] [in2 [:b]]] (fn [valA valB] {:sum (+ valA valB)}))';
|
||||
|
||||
resSeries := FEnv.Run(TAstScript.Parse(script)).AsStream.Series;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
;; EXPECT: <series[0]>
|
||||
(do
|
||||
(def btc-ma (pipe [btc [:Close]] (fn [c] (do {:ma c}))))
|
||||
(def btc-ma (pipe [[btc [:Close]]] (fn [c] (do {:ma c}))))
|
||||
(.ma btc-ma)
|
||||
)
|
||||
Reference in New Issue
Block a user