Tuples
This commit is contained in:
@@ -55,6 +55,8 @@ type
|
|||||||
function VisitMacroExpansionNode(const Node: IAstNode): Boolean;
|
function VisitMacroExpansionNode(const Node: IAstNode): Boolean;
|
||||||
function VisitNop(const Node: IAstNode): Boolean;
|
function VisitNop(const Node: IAstNode): Boolean;
|
||||||
|
|
||||||
|
function VisitTuple(const Node: IAstNode): Boolean;
|
||||||
|
|
||||||
// Pipe Support (Structural Check)
|
// Pipe Support (Structural Check)
|
||||||
function VisitPipeInput(const Node: IAstNode): Boolean;
|
function VisitPipeInput(const Node: IAstNode): Boolean;
|
||||||
function VisitPipeSelectorList(const Node: IAstNode): Boolean;
|
function VisitPipeSelectorList(const Node: IAstNode): Boolean;
|
||||||
@@ -132,6 +134,8 @@ begin
|
|||||||
Register(akRecur, VisitRecurNode);
|
Register(akRecur, VisitRecurNode);
|
||||||
Register(akNop, VisitNop);
|
Register(akNop, VisitNop);
|
||||||
|
|
||||||
|
Register(akTuple, VisitTuple);
|
||||||
|
|
||||||
// Pipes
|
// Pipes
|
||||||
Register(akPipeInput, VisitPipeInput);
|
Register(akPipeInput, VisitPipeInput);
|
||||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||||
@@ -378,4 +382,17 @@ begin
|
|||||||
Result := IsNodePure(P.Inputs) and IsNodePure(P.Transformation);
|
Result := IsNodePure(P.Inputs) and IsNodePure(P.Transformation);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TPurityAnalyzer.VisitTuple(const Node: IAstNode): Boolean;
|
||||||
|
var
|
||||||
|
item: IAstNode;
|
||||||
|
begin
|
||||||
|
// Ein Tupel ist rein, wenn alle Elemente rein sind.
|
||||||
|
for item in Node.AsTuple.Elements do
|
||||||
|
begin
|
||||||
|
if not IsNodePure(item) then
|
||||||
|
exit(False);
|
||||||
|
end;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type
|
|||||||
function VisitRecurNode(const Node: IAstNode): IAstNode;
|
function VisitRecurNode(const Node: IAstNode): IAstNode;
|
||||||
function VisitMacroExpansionNode(const Node: IAstNode): IAstNode;
|
function VisitMacroExpansionNode(const Node: IAstNode): IAstNode;
|
||||||
function VisitFunctionCall(const Node: IAstNode): IAstNode;
|
function VisitFunctionCall(const Node: IAstNode): IAstNode;
|
||||||
|
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||||
|
|
||||||
protected
|
protected
|
||||||
procedure SetupHandlers; override;
|
procedure SetupHandlers; override;
|
||||||
@@ -81,6 +82,7 @@ begin
|
|||||||
Register(akRecur, VisitRecurNode);
|
Register(akRecur, VisitRecurNode);
|
||||||
Register(akMacroExpansion, VisitMacroExpansionNode);
|
Register(akMacroExpansion, VisitMacroExpansionNode);
|
||||||
Register(akFunctionCall, VisitFunctionCall);
|
Register(akFunctionCall, VisitFunctionCall);
|
||||||
|
Register(akTuple, VisitTuple);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TAstTCO.Optimize(const RootNode: IAstNode): IAstNode;
|
class function TAstTCO.Optimize(const RootNode: IAstNode): IAstNode;
|
||||||
@@ -314,4 +316,38 @@ begin
|
|||||||
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, C.StaticType, isTailCall, C.StaticTarget, C.IsTargetPure);
|
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, C.StaticType, isTailCall, C.StaticTarget, C.IsTargetPure);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstTCO.VisitTuple(const Node: IAstNode): IAstNode;
|
||||||
|
var
|
||||||
|
T: ITupleNode;
|
||||||
|
newElements: TArray<IAstNode>;
|
||||||
|
i: Integer;
|
||||||
|
hasChanged: Boolean;
|
||||||
|
savedNextIsTail: Boolean;
|
||||||
|
begin
|
||||||
|
T := Node.AsTuple;
|
||||||
|
savedNextIsTail := FNextIsTail; // Zustand sichern (wahrscheinlich irrelevant, aber sauber)
|
||||||
|
|
||||||
|
// Elemente in einem Tupel sind NIEMALS in Tail-Position
|
||||||
|
FNextIsTail := False;
|
||||||
|
|
||||||
|
hasChanged := False;
|
||||||
|
SetLength(newElements, Length(T.Elements));
|
||||||
|
|
||||||
|
try
|
||||||
|
for i := 0 to High(T.Elements) do
|
||||||
|
begin
|
||||||
|
newElements[i] := Accept(T.Elements[i]);
|
||||||
|
if newElements[i] <> T.Elements[i] then
|
||||||
|
hasChanged := True;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
FNextIsTail := savedNextIsTail;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if hasChanged then
|
||||||
|
Result := TAst.Tuple(Node.Identity, newElements, T.StaticType)
|
||||||
|
else
|
||||||
|
Result := Node;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ type
|
|||||||
function VisitRecordLiteral(const Node: IAstNode): IAstNode;
|
function VisitRecordLiteral(const Node: IAstNode): IAstNode;
|
||||||
function VisitConstant(const Node: IAstNode): IAstNode;
|
function VisitConstant(const Node: IAstNode): IAstNode;
|
||||||
function VisitKeyword(const Node: IAstNode): IAstNode;
|
function VisitKeyword(const Node: IAstNode): IAstNode;
|
||||||
|
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||||
|
|
||||||
// Pipe Support
|
// Pipe Support
|
||||||
function VisitPipeInput(const Node: IAstNode): IAstNode;
|
function VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||||
@@ -238,6 +239,7 @@ begin
|
|||||||
Register(akRecordLiteral, VisitRecordLiteral);
|
Register(akRecordLiteral, VisitRecordLiteral);
|
||||||
Register(akConstant, VisitConstant);
|
Register(akConstant, VisitConstant);
|
||||||
Register(akKeyword, VisitKeyword);
|
Register(akKeyword, VisitKeyword);
|
||||||
|
Register(akTuple, VisitTuple);
|
||||||
|
|
||||||
// Pipe Support
|
// Pipe Support
|
||||||
Register(akPipeInput, VisitPipeInput);
|
Register(akPipeInput, VisitPipeInput);
|
||||||
@@ -292,7 +294,6 @@ end;
|
|||||||
|
|
||||||
function TTypeChecker.VisitConstant(const Node: IAstNode): IAstNode;
|
function TTypeChecker.VisitConstant(const Node: IAstNode): IAstNode;
|
||||||
begin
|
begin
|
||||||
// Base implementation already returns Node, but here we explicitly confirm identity for clarity
|
|
||||||
Result := Node;
|
Result := Node;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -301,6 +302,98 @@ begin
|
|||||||
Result := Node;
|
Result := Node;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TTypeChecker.VisitTuple(const Node: IAstNode): IAstNode;
|
||||||
|
var
|
||||||
|
T: ITupleNode;
|
||||||
|
newElements: TArray<IAstNode>;
|
||||||
|
elementTypes: TArray<IStaticType>;
|
||||||
|
i: Integer;
|
||||||
|
|
||||||
|
// Inference variables
|
||||||
|
firstType: IStaticType;
|
||||||
|
isHomogeneous: Boolean;
|
||||||
|
commonDim: TArray<Integer>;
|
||||||
|
newDim: TArray<Integer>;
|
||||||
|
|
||||||
|
finalType: IStaticType;
|
||||||
|
begin
|
||||||
|
T := Node.AsTuple;
|
||||||
|
var count := Length(T.Elements);
|
||||||
|
|
||||||
|
SetLength(newElements, count);
|
||||||
|
SetLength(elementTypes, count);
|
||||||
|
|
||||||
|
// 1. Visit Children
|
||||||
|
// Recursively type-check all elements first to determine their static types.
|
||||||
|
for i := 0 to count - 1 do
|
||||||
|
begin
|
||||||
|
newElements[i] := Accept(T.Elements[i]);
|
||||||
|
elementTypes[i] := newElements[i].AsTypedNode.StaticType;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 2. Inference Logic: Tuple vs. Vector vs. Matrix
|
||||||
|
if count = 0 then
|
||||||
|
begin
|
||||||
|
// Empty Tuple -> stTuple (safest fallback, effectively Void)
|
||||||
|
finalType := TTypes.CreateTuple([]);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
firstType := elementTypes[0];
|
||||||
|
isHomogeneous := True;
|
||||||
|
|
||||||
|
// Check for Homogeneity (Exact type equality of all elements)
|
||||||
|
for i := 1 to count - 1 do
|
||||||
|
begin
|
||||||
|
if not firstType.IsEqual(elementTypes[i]) then
|
||||||
|
begin
|
||||||
|
isHomogeneous := False;
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if isHomogeneous 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...]
|
||||||
|
commonDim := firstType.AsMatrix.Dimensions;
|
||||||
|
SetLength(newDim, Length(commonDim) + 1);
|
||||||
|
|
||||||
|
newDim[0] := count;
|
||||||
|
for i := 0 to High(commonDim) do
|
||||||
|
newDim[i + 1] := commonDim[i];
|
||||||
|
|
||||||
|
finalType := TTypes.CreateMatrix(firstType.AsMatrix.ElementType, newDim);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// Base case: Homogeneous Scalars/Records -> Vector (1D)
|
||||||
|
finalType := TTypes.CreateVector(firstType, count);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// Heterogeneous types -> Standard Tuple
|
||||||
|
finalType := TTypes.CreateTuple(elementTypes);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 3. Return the new node with the inferred type definition
|
||||||
|
Result := TAst.Tuple(Node.Identity, newElements, finalType);
|
||||||
|
end;
|
||||||
|
|
||||||
function TTypeChecker.VisitIdentifier(const Node: IAstNode): IAstNode;
|
function TTypeChecker.VisitIdentifier(const Node: IAstNode): IAstNode;
|
||||||
var
|
var
|
||||||
I: IIdentifierNode;
|
I: IIdentifierNode;
|
||||||
@@ -328,12 +421,6 @@ var
|
|||||||
args: IArgumentList;
|
args: IArgumentList;
|
||||||
begin
|
begin
|
||||||
R := Node.AsRecur;
|
R := Node.AsRecur;
|
||||||
// Use inherited to transform arguments, then reconstruct with type Void
|
|
||||||
// Note: inherited VisitRecurNode returns IAstNode which is a RecurNode.
|
|
||||||
// We can call Accept on arguments list directly to avoid intermediate node creation if desired,
|
|
||||||
// but relying on inherited logic keeps it consistent.
|
|
||||||
|
|
||||||
// Efficient approach: Accept the arguments list directly (it's a child).
|
|
||||||
args := Accept(R.Arguments).AsArgumentList;
|
args := Accept(R.Arguments).AsArgumentList;
|
||||||
Result := TAst.Recur(Node.Identity, args, TTypes.Void);
|
Result := TAst.Recur(Node.Identity, args, TTypes.Void);
|
||||||
end;
|
end;
|
||||||
@@ -422,17 +509,37 @@ var
|
|||||||
newBlock: IBlockExpressionNode;
|
newBlock: IBlockExpressionNode;
|
||||||
blockType: IStaticType;
|
blockType: IStaticType;
|
||||||
exprs: IExpressionList;
|
exprs: IExpressionList;
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
// Inherited logic transforms all expressions in the list
|
// 1. Transform children via inherited logic
|
||||||
newBlock := inherited VisitBlockExpression(Node).AsBlockExpression;
|
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;
|
exprs := newBlock.Expressions;
|
||||||
|
|
||||||
|
// 2. Validate expressions
|
||||||
if exprs.Count > 0 then
|
if exprs.Count > 0 then
|
||||||
blockType := exprs[exprs.Count - 1].AsTypedNode.StaticType
|
begin
|
||||||
else
|
// Check for NIL entries which indicate failed transformation of children
|
||||||
blockType := TTypes.Void;
|
for i := 0 to exprs.Count - 1 do
|
||||||
|
begin
|
||||||
|
if exprs[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[exprs.Count - 1].AsTypedNode.StaticType;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
blockType := TTypes.Void;
|
||||||
|
end;
|
||||||
|
|
||||||
// Return new block with calculated type
|
|
||||||
Result := TAst.Block(Node.Identity, exprs, blockType);
|
Result := TAst.Block(Node.Identity, exprs, blockType);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -451,7 +558,6 @@ var
|
|||||||
begin
|
begin
|
||||||
L := Node.AsLambdaExpression;
|
L := Node.AsLambdaExpression;
|
||||||
|
|
||||||
// 1. Resolve Upvalue Types
|
|
||||||
var upvalueAddrs := L.Upvalues;
|
var upvalueAddrs := L.Upvalues;
|
||||||
SetLength(upvalueTypes, Length(upvalueAddrs));
|
SetLength(upvalueTypes, Length(upvalueAddrs));
|
||||||
|
|
||||||
@@ -466,7 +572,6 @@ begin
|
|||||||
upvalueTypes[i] := FCurrentContext.LookupType(lookupAddr);
|
upvalueTypes[i] := FCurrentContext.LookupType(lookupAddr);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// 2. Enter New Scope
|
|
||||||
FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil);
|
FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil);
|
||||||
try
|
try
|
||||||
SetLength(newParams, L.Parameters.Count);
|
SetLength(newParams, L.Parameters.Count);
|
||||||
@@ -475,8 +580,6 @@ begin
|
|||||||
for i := 0 to L.Parameters.Count - 1 do
|
for i := 0 to L.Parameters.Count - 1 do
|
||||||
begin
|
begin
|
||||||
paramIdent := L.Parameters[i];
|
paramIdent := L.Parameters[i];
|
||||||
|
|
||||||
// Check if there is already a type assigned (e.g. injected by Pipe Visitor)
|
|
||||||
injectedType := paramIdent.AsTypedNode.StaticType;
|
injectedType := paramIdent.AsTypedNode.StaticType;
|
||||||
|
|
||||||
if injectedType.Kind = stUnknown then
|
if injectedType.Kind = stUnknown then
|
||||||
@@ -517,10 +620,7 @@ var
|
|||||||
bestSig: IMethodSignature;
|
bestSig: IMethodSignature;
|
||||||
match: Boolean;
|
match: Boolean;
|
||||||
begin
|
begin
|
||||||
// Use inherited to visit Callee and Arguments first
|
|
||||||
newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
|
newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
|
||||||
|
|
||||||
// Now analyze types on the transformed children
|
|
||||||
var newCallee := newCall.Callee;
|
var newCallee := newCall.Callee;
|
||||||
var newArgs := newCall.Arguments;
|
var newArgs := newCall.Arguments;
|
||||||
|
|
||||||
@@ -570,7 +670,6 @@ begin
|
|||||||
if Assigned(FLog) then
|
if Assigned(FLog) then
|
||||||
FLog.AddError(Format('Cannot invoke type %s as a function.', [calleeType.ToString]), Node);
|
FLog.AddError(Format('Cannot invoke type %s as a function.', [calleeType.ToString]), Node);
|
||||||
|
|
||||||
// Return new node with Types
|
|
||||||
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgs, retType, newCall.IsTailCall, nil, False);
|
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgs, retType, newCall.IsTailCall, nil, False);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -605,7 +704,18 @@ begin
|
|||||||
if baseType.Kind = stSeries then
|
if baseType.Kind = stSeries then
|
||||||
elemType := baseType.AsSeries.ElementType
|
elemType := baseType.AsSeries.ElementType
|
||||||
else if baseType.Kind = stRecordSeries then
|
else if baseType.Kind = stRecordSeries then
|
||||||
elemType := TTypes.CreateRecord(baseType.AsRecord.Definition);
|
elemType := TTypes.CreateRecord(baseType.AsRecord.Definition)
|
||||||
|
else if baseType.Kind = stTuple then
|
||||||
|
begin
|
||||||
|
// NEW: Tuple Indexing (requires constant index for strong typing!)
|
||||||
|
if (newIndex.Kind = akConstant) and (newIndex.AsConstant.Value.Kind = vkScalar) then
|
||||||
|
begin
|
||||||
|
var idx := newIndex.AsConstant.Value.AsScalar.Value.AsInt64;
|
||||||
|
var tpl := baseType.AsTuple;
|
||||||
|
if (idx >= 0) and (idx < tpl.Count) then
|
||||||
|
elemType := tpl.Elements[Integer(idx)];
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
elemType := ApplyOptionality(elemType, isOpt);
|
elemType := ApplyOptionality(elemType, isOpt);
|
||||||
Result := TAst.Indexer(Node.Identity, newBase, newIndex, elemType);
|
Result := TAst.Indexer(Node.Identity, newBase, newIndex, elemType);
|
||||||
@@ -655,10 +765,8 @@ var
|
|||||||
newFieldList: IRecordFieldList;
|
newFieldList: IRecordFieldList;
|
||||||
begin
|
begin
|
||||||
R := Node.AsRecordLiteral;
|
R := Node.AsRecordLiteral;
|
||||||
// Transform fields using inherited recursion
|
|
||||||
newFieldList := inherited VisitRecordFieldList(R.Fields).AsRecordFieldList;
|
newFieldList := inherited VisitRecordFieldList(R.Fields).AsRecordFieldList;
|
||||||
|
|
||||||
// Now analyze the transformed fields
|
|
||||||
var count := newFieldList.Count;
|
var count := newFieldList.Count;
|
||||||
SetLength(fieldTypes, count);
|
SetLength(fieldTypes, count);
|
||||||
SetLength(scalarFieldTypes, count);
|
SetLength(scalarFieldTypes, count);
|
||||||
@@ -670,7 +778,6 @@ begin
|
|||||||
key := field.Key.Value;
|
key := field.Key.Value;
|
||||||
valType := field.Value.AsTypedNode.StaticType;
|
valType := field.Value.AsTypedNode.StaticType;
|
||||||
|
|
||||||
// Check if strict scalar (no optionals allowed in packed ScalarRecord)
|
|
||||||
if (valType.Kind in [stOrdinal, stFloat, stBoolean, stDateTime, stKeyword]) and (not valType.IsOptional) then
|
if (valType.Kind in [stOrdinal, stFloat, stBoolean, stDateTime, stKeyword]) and (not valType.IsOptional) then
|
||||||
begin
|
begin
|
||||||
var kind: TScalar.TKind;
|
var kind: TScalar.TKind;
|
||||||
@@ -693,7 +800,6 @@ begin
|
|||||||
fieldTypes[i] := TPair<IKeyword, IStaticType>.Create(key, valType);
|
fieldTypes[i] := TPair<IKeyword, IStaticType>.Create(key, valType);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Build Definition
|
|
||||||
var scalarDef: IScalarRecordDefinition := nil;
|
var scalarDef: IScalarRecordDefinition := nil;
|
||||||
var genericDef: IGenericRecordDefinition := nil;
|
var genericDef: IGenericRecordDefinition := nil;
|
||||||
var resultType: IStaticType;
|
var resultType: IStaticType;
|
||||||
@@ -720,9 +826,8 @@ var
|
|||||||
begin
|
begin
|
||||||
C := Node.AsCreateSeries;
|
C := Node.AsCreateSeries;
|
||||||
def := C.Definition;
|
def := C.Definition;
|
||||||
// Simple heuristic for type
|
|
||||||
if def.StartsWith('[') then
|
if def.StartsWith('[') then
|
||||||
elemType := TTypes.CreateRecord(nil) // Placeholder, normally parses JSON
|
elemType := TTypes.CreateRecord(nil)
|
||||||
else
|
else
|
||||||
elemType := TTypes.FromScalarKind(TScalar.StringToKind(def));
|
elemType := TTypes.FromScalarKind(TScalar.StringToKind(def));
|
||||||
|
|
||||||
@@ -750,11 +855,9 @@ var
|
|||||||
sourceType: IStaticType;
|
sourceType: IStaticType;
|
||||||
begin
|
begin
|
||||||
P := Node.AsPipeInput;
|
P := Node.AsPipeInput;
|
||||||
// Transform source identifier (resolves type)
|
|
||||||
newSource := Accept(P.StreamSource).AsIdentifier;
|
newSource := Accept(P.StreamSource).AsIdentifier;
|
||||||
sourceType := newSource.AsTypedNode.StaticType;
|
sourceType := newSource.AsTypedNode.StaticType;
|
||||||
|
|
||||||
// 1. Verify Source is a Series-compatible type
|
|
||||||
if (sourceType.Kind <> stUnknown) then
|
if (sourceType.Kind <> stUnknown) then
|
||||||
begin
|
begin
|
||||||
if not ((sourceType.Kind = stSeries) or (sourceType.Kind = stRecordSeries)) then
|
if not ((sourceType.Kind = stSeries) or (sourceType.Kind = stRecordSeries)) then
|
||||||
@@ -767,7 +870,6 @@ begin
|
|||||||
end
|
end
|
||||||
else if (sourceType.Kind = stRecordSeries) then
|
else if (sourceType.Kind = stRecordSeries) then
|
||||||
begin
|
begin
|
||||||
// 2. Verify Selectors exist in Record Definition
|
|
||||||
var def := sourceType.AsRecord.Definition;
|
var def := sourceType.AsRecord.Definition;
|
||||||
for var sel in P.Selectors do
|
for var sel in P.Selectors do
|
||||||
begin
|
begin
|
||||||
@@ -780,7 +882,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Reuse selectors (they are just keywords, no type checking needed)
|
|
||||||
Result := TAst.PipeInput(newSource, P.Selectors, Node.Identity.Location);
|
Result := TAst.PipeInput(newSource, P.Selectors, Node.Identity.Location);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -800,22 +901,18 @@ begin
|
|||||||
SetLength(newInputs, P.Inputs.Count);
|
SetLength(newInputs, P.Inputs.Count);
|
||||||
paramTypes := TList<IStaticType>.Create;
|
paramTypes := TList<IStaticType>.Create;
|
||||||
try
|
try
|
||||||
// 1. Visit Inputs and collect types for Lambda parameters
|
|
||||||
for i := 0 to P.Inputs.Count - 1 do
|
for i := 0 to P.Inputs.Count - 1 do
|
||||||
begin
|
begin
|
||||||
// Recurse on inputs to resolve their sources
|
|
||||||
inputNode := Accept(P.Inputs[i]).AsPipeInput;
|
inputNode := Accept(P.Inputs[i]).AsPipeInput;
|
||||||
newInputs[i] := inputNode;
|
newInputs[i] := inputNode;
|
||||||
|
|
||||||
streamType := inputNode.StreamSource.AsTypedNode.StaticType;
|
streamType := inputNode.StreamSource.AsTypedNode.StaticType;
|
||||||
|
|
||||||
// Flatten logic: One lambda param per selector
|
|
||||||
for var sel in inputNode.Selectors do
|
for var sel in inputNode.Selectors do
|
||||||
begin
|
begin
|
||||||
inferredType := TTypes.Unknown;
|
inferredType := TTypes.Unknown;
|
||||||
if streamType.Kind = stRecordSeries then
|
if streamType.Kind = stRecordSeries then
|
||||||
begin
|
begin
|
||||||
// Extract field type from definition
|
|
||||||
var def := streamType.AsRecord.Definition;
|
var def := streamType.AsRecord.Definition;
|
||||||
var idx := def.IndexOf(sel.Value);
|
var idx := def.IndexOf(sel.Value);
|
||||||
if idx >= 0 then
|
if idx >= 0 then
|
||||||
@@ -823,18 +920,16 @@ begin
|
|||||||
end
|
end
|
||||||
else if streamType.Kind = stSeries then
|
else if streamType.Kind = stSeries then
|
||||||
begin
|
begin
|
||||||
// If simple series, use its element type
|
|
||||||
if Assigned(streamType.AsSeries.ElementType) then
|
if Assigned(streamType.AsSeries.ElementType) then
|
||||||
inferredType := streamType.AsSeries.ElementType
|
inferredType := streamType.AsSeries.ElementType
|
||||||
else
|
else
|
||||||
inferredType := TTypes.Ordinal; // Fallback default
|
inferredType := TTypes.Ordinal;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
paramTypes.Add(inferredType);
|
paramTypes.Add(inferredType);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// 2. Prepare Lambda with Inferred Types
|
|
||||||
lambda := P.Transformation;
|
lambda := P.Transformation;
|
||||||
|
|
||||||
if lambda.Parameters.Count <> paramTypes.Count then
|
if lambda.Parameters.Count <> paramTypes.Count then
|
||||||
@@ -857,11 +952,9 @@ begin
|
|||||||
if k < paramTypes.Count then paramTypes[k]
|
if k < paramTypes.Count then paramTypes[k]
|
||||||
else TTypes.Unknown;
|
else TTypes.Unknown;
|
||||||
|
|
||||||
// Create new identifier with the inferred type!
|
|
||||||
newParams[k] := TAst.Identifier(oldP.Identity.AsNamed, oldP.Address, typeToInject);
|
newParams[k] := TAst.Identifier(oldP.Identity.AsNamed, oldP.Address, typeToInject);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Recreate Lambda NODE with Typed Parameters
|
|
||||||
var preTypedLambda :=
|
var preTypedLambda :=
|
||||||
TAst.LambdaExpr(
|
TAst.LambdaExpr(
|
||||||
lambda.Identity,
|
lambda.Identity,
|
||||||
@@ -872,24 +965,20 @@ begin
|
|||||||
lambda.Upvalues,
|
lambda.Upvalues,
|
||||||
lambda.HasNestedLambdas,
|
lambda.HasNestedLambdas,
|
||||||
lambda.IsPure,
|
lambda.IsPure,
|
||||||
TTypes.Unknown // Will be recalculated in Accept
|
TTypes.Unknown
|
||||||
);
|
);
|
||||||
|
|
||||||
// 3. Visit the Lambda (This will now type-check the Body using the types we just injected)
|
|
||||||
var typedLambda := Accept(preTypedLambda).AsLambdaExpression;
|
var typedLambda := Accept(preTypedLambda).AsLambdaExpression;
|
||||||
|
|
||||||
// 4. Infer Pipe Return Type & Validate Strictness
|
|
||||||
var lambdaRetType := typedLambda.AsTypedNode.StaticType.AsMethod.Signatures[0].ReturnType;
|
var lambdaRetType := typedLambda.AsTypedNode.StaticType.AsMethod.Signatures[0].ReturnType;
|
||||||
var pipeType: IStaticType;
|
var pipeType: IStaticType;
|
||||||
|
|
||||||
if lambdaRetType.Kind = stRecord then
|
if lambdaRetType.Kind = stRecord then
|
||||||
begin
|
begin
|
||||||
// Valid: Record -> RecordSeries
|
|
||||||
pipeType := TTypes.CreateRecordSeries(lambdaRetType.AsRecord.Definition);
|
pipeType := TTypes.CreateRecordSeries(lambdaRetType.AsRecord.Definition);
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
// STRICT CHECK: Scalars are NOT allowed.
|
|
||||||
if (lambdaRetType.Kind <> stUnknown) and (lambdaRetType.Kind <> stVoid) then
|
if (lambdaRetType.Kind <> stUnknown) and (lambdaRetType.Kind <> stVoid) then
|
||||||
begin
|
begin
|
||||||
if Assigned(FLog) then
|
if Assigned(FLog) then
|
||||||
@@ -898,8 +987,6 @@ begin
|
|||||||
lambda
|
lambda
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// If Void or Unknown, or Error case:
|
|
||||||
pipeType := TTypes.Unknown;
|
pipeType := TTypes.Unknown;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,14 @@ begin
|
|||||||
akLambdaExpression: Result := 'FN';
|
akLambdaExpression: Result := 'FN';
|
||||||
akRecordLiteral: Result := 'RECORD';
|
akRecordLiteral: Result := 'RECORD';
|
||||||
akPipe: Result := 'PIPE';
|
akPipe: Result := 'PIPE';
|
||||||
|
|
||||||
|
// NEW:
|
||||||
|
akIndexer: Result := 'INDEXER';
|
||||||
|
akMemberAccess: Result := 'MEMBER';
|
||||||
|
akTuple: Result := 'TUPLE';
|
||||||
|
akCreateSeries: Result := 'NEW-SERIES';
|
||||||
|
akAddSeriesItem: Result := 'ADD-ITEM';
|
||||||
|
|
||||||
else
|
else
|
||||||
Result := ''; // Silence leaf nodes like Constants and Keywords to reduce noise
|
Result := ''; // Silence leaf nodes like Constants and Keywords to reduce noise
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ type
|
|||||||
function VisitRecordFieldList(const Node: IAstNode): TVoid;
|
function VisitRecordFieldList(const Node: IAstNode): TVoid;
|
||||||
function VisitRecordField(const Node: IAstNode): TVoid;
|
function VisitRecordField(const Node: IAstNode): TVoid;
|
||||||
|
|
||||||
|
function VisitTuple(const Node: IAstNode): TVoid;
|
||||||
|
|
||||||
// Pipe Visitors
|
// Pipe Visitors
|
||||||
function VisitPipeInput(const Node: IAstNode): TVoid;
|
function VisitPipeInput(const Node: IAstNode): TVoid;
|
||||||
function VisitPipeSelectorList(const Node: IAstNode): TVoid;
|
function VisitPipeSelectorList(const Node: IAstNode): TVoid;
|
||||||
@@ -143,6 +145,8 @@ begin
|
|||||||
Register(akRecur, VisitRecurNode);
|
Register(akRecur, VisitRecurNode);
|
||||||
Register(akNop, VisitNop);
|
Register(akNop, VisitNop);
|
||||||
|
|
||||||
|
Register(akTuple, VisitTuple);
|
||||||
|
|
||||||
Register(akPipeInput, VisitPipeInput);
|
Register(akPipeInput, VisitPipeInput);
|
||||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||||
Register(akPipeInputList, VisitPipeInputList);
|
Register(akPipeInputList, VisitPipeInputList);
|
||||||
@@ -625,4 +629,20 @@ begin
|
|||||||
Unindent;
|
Unindent;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstDumper.VisitTuple(const Node: IAstNode): TVoid;
|
||||||
|
var
|
||||||
|
T: ITupleNode;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
T := Node.AsTuple;
|
||||||
|
LogFmt('Tuple (%d elements)', [Length(T.Elements)], Node);
|
||||||
|
Indent;
|
||||||
|
for i := 0 to High(T.Elements) do
|
||||||
|
begin
|
||||||
|
LogFmt('Element %d:', [i]);
|
||||||
|
Visit(T.Elements[i]);
|
||||||
|
end;
|
||||||
|
Unindent;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -21,13 +21,16 @@ type
|
|||||||
private
|
private
|
||||||
FScope: IExecutionScope;
|
FScope: IExecutionScope;
|
||||||
protected
|
protected
|
||||||
|
// Haupt-Dispatch-Methode
|
||||||
function Visit(const Node: IAstNode): TDataValue; virtual;
|
function Visit(const Node: IAstNode): TDataValue; virtual;
|
||||||
function CreateVisitorFactory: TEvaluatorFactory; virtual;
|
function CreateVisitorFactory: TEvaluatorFactory; virtual;
|
||||||
|
|
||||||
// Nur noch die für die Ausführung relevanten Methoden
|
// Besuchermethoden
|
||||||
function VisitConstant(const N: IConstantNode): TDataValue; virtual;
|
function VisitConstant(const N: IConstantNode): TDataValue; virtual;
|
||||||
function VisitIdentifier(const N: IIdentifierNode): TDataValue; virtual;
|
function VisitIdentifier(const N: IIdentifierNode): TDataValue; virtual;
|
||||||
function VisitKeyword(const N: IKeywordNode): TDataValue; virtual;
|
function VisitKeyword(const N: IKeywordNode): TDataValue; virtual;
|
||||||
|
function VisitTuple(const N: ITupleNode): TDataValue; virtual; // <--- WICHTIG
|
||||||
|
|
||||||
function VisitIfExpression(const N: IIfExpressionNode): TDataValue; virtual;
|
function VisitIfExpression(const N: IIfExpressionNode): TDataValue; virtual;
|
||||||
function VisitCondExpression(const N: ICondExpressionNode): TDataValue; virtual;
|
function VisitCondExpression(const N: ICondExpressionNode): TDataValue; virtual;
|
||||||
function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; virtual;
|
function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; virtual;
|
||||||
@@ -89,10 +92,12 @@ end;
|
|||||||
function TEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
|
function TEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
// Der Hot-Path: Direkter Dispatch ohne Umweg über ungenutzte Methoden.
|
// Der Hot-Path: Direkter Dispatch ohne Umweg über ungenutzte Methoden.
|
||||||
|
// HIER fehlte wahrscheinlich akTuple!
|
||||||
case Node.Kind of
|
case Node.Kind of
|
||||||
akConstant: Result := VisitConstant(Node.AsConstant);
|
akConstant: Result := VisitConstant(Node.AsConstant);
|
||||||
akIdentifier: Result := VisitIdentifier(Node.AsIdentifier);
|
akIdentifier: Result := VisitIdentifier(Node.AsIdentifier);
|
||||||
akKeyword: Result := VisitKeyword(Node.AsKeyword);
|
akKeyword: Result := VisitKeyword(Node.AsKeyword);
|
||||||
|
akTuple: Result := VisitTuple(Node.AsTuple); // <--- KORREKTUR
|
||||||
akIfExpression: Result := VisitIfExpression(Node.AsIfExpression);
|
akIfExpression: Result := VisitIfExpression(Node.AsIfExpression);
|
||||||
akCondExpression: Result := VisitCondExpression(Node.AsCondExpression);
|
akCondExpression: Result := VisitCondExpression(Node.AsCondExpression);
|
||||||
akLambdaExpression: Result := VisitLambdaExpression(Node.AsLambdaExpression);
|
akLambdaExpression: Result := VisitLambdaExpression(Node.AsLambdaExpression);
|
||||||
@@ -173,6 +178,21 @@ begin
|
|||||||
Result := FScope[N.Address];
|
Result := FScope[N.Address];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TEvaluatorVisitor.VisitTuple(const N: ITupleNode): TDataValue;
|
||||||
|
var
|
||||||
|
elements: TArray<TDataValue>;
|
||||||
|
i: Integer;
|
||||||
|
nodes: TArray<IAstNode>;
|
||||||
|
begin
|
||||||
|
nodes := N.Elements;
|
||||||
|
SetLength(elements, Length(nodes));
|
||||||
|
for i := 0 to High(nodes) do
|
||||||
|
elements[i] := Visit(nodes[i]);
|
||||||
|
|
||||||
|
// Uses the implicit operator: TArray<TDataValue> -> TDataValue (vkTuple)
|
||||||
|
Result := elements;
|
||||||
|
end;
|
||||||
|
|
||||||
function TEvaluatorVisitor.VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue;
|
function TEvaluatorVisitor.VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue;
|
||||||
var
|
var
|
||||||
capturedCells: TArray<IValueCell>;
|
capturedCells: TArray<IValueCell>;
|
||||||
@@ -334,20 +354,30 @@ begin
|
|||||||
if base.IsVoid then
|
if base.IsVoid then
|
||||||
exit(TDataValue.Void);
|
exit(TDataValue.Void);
|
||||||
idx := Visit(N.Index);
|
idx := Visit(N.Index);
|
||||||
if (base.Kind = vkSeries) then
|
|
||||||
Result := TDataValue(base.AsSeries.Items[Integer(idx.AsScalar.Value.AsInt64)])
|
case base.Kind of
|
||||||
else if (base.Kind = vkRecordSeries) then
|
vkSeries: Result := TDataValue(base.AsSeries.Items[Integer(idx.AsScalar.Value.AsInt64)]);
|
||||||
begin
|
vkRecordSeries:
|
||||||
var rs := base.AsRecordSeries;
|
begin
|
||||||
var vals: TArray<TScalar.TValue>;
|
var rs := base.AsRecordSeries;
|
||||||
SetLength(vals, rs.Def.Count);
|
var vals: TArray<TScalar.TValue>;
|
||||||
var i64 := idx.AsScalar.Value.AsInt64;
|
SetLength(vals, rs.Def.Count);
|
||||||
for var k := 0 to rs.Def.Count - 1 do
|
var i64 := idx.AsScalar.Value.AsInt64;
|
||||||
vals[k] := rs.Fields[rs.Def.Keywords[k]].Items[Integer(i64)].Value;
|
for var k := 0 to rs.Def.Count - 1 do
|
||||||
Result := TScalarRecord.Create(rs.Def, vals);
|
vals[k] := rs.Fields[rs.Def.Keywords[k]].Items[Integer(i64)].Value;
|
||||||
end
|
Result := TScalarRecord.Create(rs.Def, vals);
|
||||||
|
end;
|
||||||
|
vkTuple: // <--- WICHTIG FÜR (get x 3)
|
||||||
|
begin
|
||||||
|
var tpl := base.AsTuple;
|
||||||
|
var i := idx.AsScalar.Value.AsInt64;
|
||||||
|
if (i < 0) or (i >= tpl.Count) then
|
||||||
|
raise EEvaluatorException.Create('Tuple index out of bounds');
|
||||||
|
Result := tpl.Items[Integer(i)];
|
||||||
|
end;
|
||||||
else
|
else
|
||||||
raise EEvaluatorException.Create('Indexer error');
|
raise EEvaluatorException.Create('Indexer error');
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TEvaluatorVisitor.VisitMemberAccess(const N: IMemberAccessNode): TDataValue;
|
function TEvaluatorVisitor.VisitMemberAccess(const N: IMemberAccessNode): TDataValue;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ type
|
|||||||
function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode;
|
function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode;
|
||||||
function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
|
function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
|
||||||
function JsonToNopNode(const AObj: TJSONObject): INopNode;
|
function JsonToNopNode(const AObj: TJSONObject): INopNode;
|
||||||
|
function JsonToTupleNode(const AObj: TJSONObject): ITupleNode;
|
||||||
|
|
||||||
strict private
|
strict private
|
||||||
// Serialization Visitors (IAstNode signature)
|
// Serialization Visitors (IAstNode signature)
|
||||||
@@ -85,6 +86,8 @@ type
|
|||||||
function VisitSeriesLength(const Node: IAstNode): TJSONObject;
|
function VisitSeriesLength(const Node: IAstNode): TJSONObject;
|
||||||
function VisitNop(const Node: IAstNode): TJSONObject;
|
function VisitNop(const Node: IAstNode): TJSONObject;
|
||||||
|
|
||||||
|
function VisitTuple(const Node: IAstNode): TJSONObject;
|
||||||
|
|
||||||
function VisitPipeInput(const Node: IAstNode): TJSONObject;
|
function VisitPipeInput(const Node: IAstNode): TJSONObject;
|
||||||
function VisitPipeSelectorList(const Node: IAstNode): TJSONObject;
|
function VisitPipeSelectorList(const Node: IAstNode): TJSONObject;
|
||||||
function VisitPipeInputList(const Node: IAstNode): TJSONObject;
|
function VisitPipeInputList(const Node: IAstNode): TJSONObject;
|
||||||
@@ -144,6 +147,8 @@ begin
|
|||||||
Register(akRecur, VisitRecurNode);
|
Register(akRecur, VisitRecurNode);
|
||||||
Register(akNop, VisitNop);
|
Register(akNop, VisitNop);
|
||||||
|
|
||||||
|
Register(akTuple, VisitTuple);
|
||||||
|
|
||||||
Register(akPipeInput, VisitPipeInput);
|
Register(akPipeInput, VisitPipeInput);
|
||||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||||
Register(akPipeInputList, VisitPipeInputList);
|
Register(akPipeInputList, VisitPipeInputList);
|
||||||
@@ -531,6 +536,24 @@ begin
|
|||||||
Result.AddPair('NodeType', TJSONString.Create('Nop'));
|
Result.AddPair('NodeType', TJSONString.Create('Nop'));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TJsonAstConverter.VisitTuple(const Node: IAstNode): TJSONObject;
|
||||||
|
var
|
||||||
|
T: ITupleNode;
|
||||||
|
elemsArray: TJSONArray;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
T := Node.AsTuple;
|
||||||
|
Result := TJSONObject.Create;
|
||||||
|
Result.AddPair('NodeType', TJSONString.Create('Tuple'));
|
||||||
|
|
||||||
|
elemsArray := TJSONArray.Create;
|
||||||
|
for i := 0 to High(T.Elements) do
|
||||||
|
begin
|
||||||
|
elemsArray.Add(Visit(T.Elements[i]));
|
||||||
|
end;
|
||||||
|
Result.AddPair('Elements', elemsArray);
|
||||||
|
end;
|
||||||
|
|
||||||
function TJsonAstConverter.VisitParameterList(const Node: IAstNode): TJSONObject;
|
function TJsonAstConverter.VisitParameterList(const Node: IAstNode): TJSONObject;
|
||||||
begin
|
begin
|
||||||
Result := nil;
|
Result := nil;
|
||||||
@@ -548,6 +571,21 @@ begin
|
|||||||
Result := nil;
|
Result := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TJsonAstConverter.JsonToTupleNode(const AObj: TJSONObject): ITupleNode;
|
||||||
|
var
|
||||||
|
elems: TArray<IAstNode>;
|
||||||
|
elemsArray: TJSONArray;
|
||||||
|
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]);
|
||||||
|
end;
|
||||||
|
Result := TAst.Tuple(elems); // Factory handles identity/type defaults
|
||||||
|
end;
|
||||||
|
|
||||||
function TJsonAstConverter.VisitPipeInput(const Node: IAstNode): TJSONObject;
|
function TJsonAstConverter.VisitPipeInput(const Node: IAstNode): TJSONObject;
|
||||||
begin
|
begin
|
||||||
raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
|
raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
|
||||||
@@ -660,6 +698,8 @@ begin
|
|||||||
Result := JsonToSeriesLengthNode(obj)
|
Result := JsonToSeriesLengthNode(obj)
|
||||||
else if nodeType = 'Nop' then
|
else if nodeType = 'Nop' then
|
||||||
Result := JsonToNopNode(obj)
|
Result := JsonToNopNode(obj)
|
||||||
|
else if nodeType = 'Tuple' then
|
||||||
|
Result := JsonToTupleNode(obj)
|
||||||
else
|
else
|
||||||
raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s".', [nodeType]);
|
raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s".', [nodeType]);
|
||||||
end;
|
end;
|
||||||
|
|||||||
+52
-13
@@ -64,12 +64,6 @@ type
|
|||||||
property Errors: TArray<TCompilerError> read FErrors;
|
property Errors: TArray<TCompilerError> read FErrors;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Exception for the new visitor registry dispatch
|
|
||||||
ENoHandlerException = class(EAstException)
|
|
||||||
public
|
|
||||||
constructor Create(const KindName: string);
|
|
||||||
end;
|
|
||||||
|
|
||||||
TCompiledFunction = record
|
TCompiledFunction = record
|
||||||
public
|
public
|
||||||
Func: TDataValue.TFunc;
|
Func: TDataValue.TFunc;
|
||||||
@@ -94,6 +88,7 @@ type
|
|||||||
IExpressionList = interface;
|
IExpressionList = interface;
|
||||||
IRecordFieldList = interface;
|
IRecordFieldList = interface;
|
||||||
IRecordFieldNode = interface;
|
IRecordFieldNode = interface;
|
||||||
|
ITupleNode = interface;
|
||||||
|
|
||||||
// Control Flow & Structure
|
// Control Flow & Structure
|
||||||
IIfExpressionNode = interface;
|
IIfExpressionNode = interface;
|
||||||
@@ -134,6 +129,9 @@ type
|
|||||||
akExpressionList,
|
akExpressionList,
|
||||||
akRecordFieldList,
|
akRecordFieldList,
|
||||||
akRecordField,
|
akRecordField,
|
||||||
|
// Tuple
|
||||||
|
akTuple,
|
||||||
|
// Control Flow
|
||||||
akIfExpression,
|
akIfExpression,
|
||||||
akCondExpression,
|
akCondExpression,
|
||||||
akLambdaExpression,
|
akLambdaExpression,
|
||||||
@@ -195,6 +193,7 @@ type
|
|||||||
function AsExpressionList: IExpressionList;
|
function AsExpressionList: IExpressionList;
|
||||||
function AsRecordFieldList: IRecordFieldList;
|
function AsRecordFieldList: IRecordFieldList;
|
||||||
function AsRecordField: IRecordFieldNode;
|
function AsRecordField: IRecordFieldNode;
|
||||||
|
function AsTuple: ITupleNode;
|
||||||
|
|
||||||
function AsIfExpression: IIfExpressionNode;
|
function AsIfExpression: IIfExpressionNode;
|
||||||
function AsCondExpression: ICondExpressionNode;
|
function AsCondExpression: ICondExpressionNode;
|
||||||
@@ -298,6 +297,13 @@ type
|
|||||||
property Value: IKeyword read GetValue;
|
property Value: IKeyword read GetValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
ITupleNode = interface(IAstTypedNode)
|
||||||
|
{$region 'private'}
|
||||||
|
function GetElements: TArray<IAstNode>;
|
||||||
|
{$endregion}
|
||||||
|
property Elements: TArray<IAstNode> read GetElements;
|
||||||
|
end;
|
||||||
|
|
||||||
IIfExpressionNode = interface(IAstTypedNode)
|
IIfExpressionNode = interface(IAstTypedNode)
|
||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
function GetCondition: IAstNode;
|
function GetCondition: IAstNode;
|
||||||
@@ -543,6 +549,7 @@ type
|
|||||||
function AsExpressionList: IExpressionList; virtual;
|
function AsExpressionList: IExpressionList; virtual;
|
||||||
function AsRecordFieldList: IRecordFieldList; virtual;
|
function AsRecordFieldList: IRecordFieldList; virtual;
|
||||||
function AsRecordField: IRecordFieldNode; virtual;
|
function AsRecordField: IRecordFieldNode; virtual;
|
||||||
|
function AsTuple: ITupleNode; virtual;
|
||||||
|
|
||||||
function AsIfExpression: IIfExpressionNode; virtual;
|
function AsIfExpression: IIfExpressionNode; virtual;
|
||||||
function AsCondExpression: ICondExpressionNode; virtual;
|
function AsCondExpression: ICondExpressionNode; virtual;
|
||||||
@@ -696,6 +703,17 @@ type
|
|||||||
function AsKeyword: IKeywordNode; override;
|
function AsKeyword: IKeywordNode; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TTupleNode = class(TAstTypedNode, ITupleNode)
|
||||||
|
private
|
||||||
|
FElements: TArray<IAstNode>;
|
||||||
|
function GetElements: TArray<IAstNode>;
|
||||||
|
protected
|
||||||
|
function GetKind: TAstNodeKind; override;
|
||||||
|
public
|
||||||
|
constructor Create(const AElements: TArray<IAstNode>; const AIdentity: IAstIdentity; const AStaticType: IStaticType);
|
||||||
|
function AsTuple: ITupleNode; override;
|
||||||
|
end;
|
||||||
|
|
||||||
TCreateSeriesNode = class(TAstTypedNode, ICreateSeriesNode)
|
TCreateSeriesNode = class(TAstTypedNode, ICreateSeriesNode)
|
||||||
private
|
private
|
||||||
FDefIdentity: IDefinitionIdentity;
|
FDefIdentity: IDefinitionIdentity;
|
||||||
@@ -1207,13 +1225,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ ENoHandlerException }
|
|
||||||
|
|
||||||
constructor ENoHandlerException.Create(const KindName: string);
|
|
||||||
begin
|
|
||||||
inherited CreateFmt('No visitor handler registered for AST node kind: %s', [KindName]);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TAstNode }
|
{ TAstNode }
|
||||||
|
|
||||||
constructor TAstNode.Create(const AIdentity: IAstIdentity);
|
constructor TAstNode.Create(const AIdentity: IAstIdentity);
|
||||||
@@ -1280,6 +1291,11 @@ begin
|
|||||||
Result := nil;
|
Result := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstNode.AsTuple: ITupleNode;
|
||||||
|
begin
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
function TAstNode.AsIfExpression: IIfExpressionNode;
|
function TAstNode.AsIfExpression: IIfExpressionNode;
|
||||||
begin
|
begin
|
||||||
Result := nil;
|
Result := nil;
|
||||||
@@ -1608,6 +1624,29 @@ begin
|
|||||||
Result := FKeywordIdentity.Value;
|
Result := FKeywordIdentity.Value;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TTupleNode }
|
||||||
|
|
||||||
|
constructor TTupleNode.Create(const AElements: TArray<IAstNode>; const AIdentity: IAstIdentity; const AStaticType: IStaticType);
|
||||||
|
begin
|
||||||
|
inherited Create(AStaticType, AIdentity);
|
||||||
|
FElements := AElements;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TTupleNode.AsTuple: ITupleNode;
|
||||||
|
begin
|
||||||
|
Result := Self;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TTupleNode.GetElements: TArray<IAstNode>;
|
||||||
|
begin
|
||||||
|
Result := FElements;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TTupleNode.GetKind: TAstNodeKind;
|
||||||
|
begin
|
||||||
|
Result := akTuple;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TCreateSeriesNode }
|
{ TCreateSeriesNode }
|
||||||
|
|
||||||
constructor TCreateSeriesNode.Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
|
constructor TCreateSeriesNode.Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type
|
|||||||
function VisitIndexer(const Node: IAstNode): IAstNode;
|
function VisitIndexer(const Node: IAstNode): IAstNode;
|
||||||
function VisitMemberAccess(const Node: IAstNode): IAstNode;
|
function VisitMemberAccess(const Node: IAstNode): IAstNode;
|
||||||
function VisitAddSeriesItem(const Node: IAstNode): IAstNode;
|
function VisitAddSeriesItem(const Node: IAstNode): IAstNode;
|
||||||
|
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||||
|
|
||||||
protected
|
protected
|
||||||
procedure SetupHandlers; override;
|
procedure SetupHandlers; override;
|
||||||
@@ -81,6 +82,8 @@ begin
|
|||||||
Register(akIndexer, VisitIndexer);
|
Register(akIndexer, VisitIndexer);
|
||||||
Register(akMemberAccess, VisitMemberAccess);
|
Register(akMemberAccess, VisitMemberAccess);
|
||||||
Register(akAddSeriesItem, VisitAddSeriesItem);
|
Register(akAddSeriesItem, VisitAddSeriesItem);
|
||||||
|
|
||||||
|
Register(akTuple, VisitTuple);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TAstNodeRemover.TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean;
|
class function TAstNodeRemover.TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean;
|
||||||
@@ -429,4 +432,40 @@ begin
|
|||||||
Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, A.StaticType);
|
Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, A.StaticType);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstNodeRemover.VisitTuple(const Node: IAstNode): IAstNode;
|
||||||
|
var
|
||||||
|
T: ITupleNode;
|
||||||
|
newElements: TArray<IAstNode>;
|
||||||
|
list: TList<IAstNode>;
|
||||||
|
item, transformed: IAstNode;
|
||||||
|
i: Integer;
|
||||||
|
hasChanged: Boolean;
|
||||||
|
begin
|
||||||
|
T := Node.AsTuple;
|
||||||
|
list := TList<IAstNode>.Create;
|
||||||
|
hasChanged := False;
|
||||||
|
try
|
||||||
|
for i := 0 to High(T.Elements) do
|
||||||
|
begin
|
||||||
|
item := T.Elements[i];
|
||||||
|
transformed := Accept(item);
|
||||||
|
|
||||||
|
if transformed <> item then
|
||||||
|
hasChanged := True;
|
||||||
|
|
||||||
|
if Assigned(transformed) then
|
||||||
|
list.Add(transformed)
|
||||||
|
else
|
||||||
|
hasChanged := True; // Item removed
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not hasChanged then
|
||||||
|
Exit(Node);
|
||||||
|
|
||||||
|
Result := TAst.Tuple(Node.Identity, list.ToArray, T.StaticType);
|
||||||
|
finally
|
||||||
|
list.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+23
-17
@@ -437,8 +437,8 @@ begin
|
|||||||
items.Add(ParseExpression.Node);
|
items.Add(ParseExpression.Node);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var id := TIdentities.List('[', ']', ' ', startLoc);
|
// CHANGED: Use Tuple factory instead of ArgumentList
|
||||||
Result := TArgumentList.Create(items.ToArray, id);
|
Result := TAst.Tuple(items.ToArray, startLoc);
|
||||||
finally
|
finally
|
||||||
items.Free;
|
items.Free;
|
||||||
end;
|
end;
|
||||||
@@ -447,15 +447,19 @@ end;
|
|||||||
|
|
||||||
function TParser.ExtractIdentifiers(const Node: IAstNode; const Context: string): TArray<IIdentifierNode>;
|
function TParser.ExtractIdentifiers(const Node: IAstNode; const Context: string): TArray<IIdentifierNode>;
|
||||||
var
|
var
|
||||||
list: IArgumentList;
|
list: TArray<IAstNode>;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
if Node.Kind <> akArgumentList then
|
// Support both Tuple (new syntax [...]) and ArgumentList (internal legacy)
|
||||||
|
if Node.Kind = akTuple then
|
||||||
|
list := Node.AsTuple.Elements
|
||||||
|
else if Node.Kind = akArgumentList then
|
||||||
|
list := Node.AsArgumentList.ToArray
|
||||||
|
else
|
||||||
ErrorFmt('%s expects a vector [...]', [Context]);
|
ErrorFmt('%s expects a vector [...]', [Context]);
|
||||||
|
|
||||||
list := Node.AsArgumentList;
|
SetLength(Result, Length(list));
|
||||||
SetLength(Result, list.Count);
|
for i := 0 to High(list) do
|
||||||
for i := 0 to list.Count - 1 do
|
|
||||||
begin
|
begin
|
||||||
if list[i].Kind <> akIdentifier then
|
if list[i].Kind <> akIdentifier then
|
||||||
ErrorFmt('%s vector must contain only identifiers.', [Context]);
|
ErrorFmt('%s vector must contain only identifiers.', [Context]);
|
||||||
@@ -536,16 +540,18 @@ begin
|
|||||||
if Length(tailNodes) <> 2 then
|
if Length(tailNodes) <> 2 then
|
||||||
Error('Syntax Error: ''pipe'' requires [inputs] vector and (fn) transformation.');
|
Error('Syntax Error: ''pipe'' requires [inputs] vector and (fn) transformation.');
|
||||||
|
|
||||||
if tailNodes[0].Kind <> akArgumentList then
|
// Input vector is now a TupleNode
|
||||||
|
if tailNodes[0].Kind <> akTuple then
|
||||||
Error('Syntax Error: ''pipe'' first argument must be a vector [...].');
|
Error('Syntax Error: ''pipe'' first argument must be a vector [...].');
|
||||||
var rawInputs := tailNodes[0].AsArgumentList;
|
|
||||||
if (rawInputs.Count mod 2) <> 0 then
|
var rawInputs := tailNodes[0].AsTuple.Elements;
|
||||||
|
if (Length(rawInputs) mod 2) <> 0 then
|
||||||
Error('Syntax Error: Pipe inputs must be pairs of Identifier and Selector Vector (e.g. [btc [:Close]]).');
|
Error('Syntax Error: Pipe inputs must be pairs of Identifier and Selector Vector (e.g. [btc [:Close]]).');
|
||||||
|
|
||||||
var pipeInputs := TList<IPipeInputNode>.Create;
|
var pipeInputs := TList<IPipeInputNode>.Create;
|
||||||
try
|
try
|
||||||
var k := 0;
|
var k := 0;
|
||||||
while k < rawInputs.Count do
|
while k < Length(rawInputs) do
|
||||||
begin
|
begin
|
||||||
if rawInputs[k].Kind <> akIdentifier then
|
if rawInputs[k].Kind <> akIdentifier then
|
||||||
Error('Syntax Error: Pipe input stream must be an identifier.');
|
Error('Syntax Error: Pipe input stream must be an identifier.');
|
||||||
@@ -554,16 +560,16 @@ begin
|
|||||||
var streamId := rawInputs[k].AsIdentifier;
|
var streamId := rawInputs[k].AsIdentifier;
|
||||||
var selKeywords: TArray<IKeywordNode>;
|
var selKeywords: TArray<IKeywordNode>;
|
||||||
|
|
||||||
// Strict format: Selector must be a vector [...] containing keywords
|
// Strict format: Selector must be a vector [...] (TupleNode) containing keywords
|
||||||
if selNode.Kind <> akArgumentList then
|
if selNode.Kind <> akTuple then
|
||||||
Error('Syntax Error: Pipe selector must be a vector of keywords (e.g. [:Close :High]).');
|
Error('Syntax Error: Pipe selector must be a vector of keywords (e.g. [:Close :High]).');
|
||||||
|
|
||||||
var selList := selNode.AsArgumentList;
|
var selList := selNode.AsTuple.Elements;
|
||||||
if selList.Count = 0 then
|
if Length(selList) = 0 then
|
||||||
Error('Syntax Error: Selector list cannot be empty.');
|
Error('Syntax Error: Selector list cannot be empty.');
|
||||||
|
|
||||||
SetLength(selKeywords, selList.Count);
|
SetLength(selKeywords, Length(selList));
|
||||||
for var m := 0 to selList.Count - 1 do
|
for var m := 0 to High(selList) do
|
||||||
begin
|
begin
|
||||||
if selList[m].Kind <> akKeyword then
|
if selList[m].Kind <> akKeyword then
|
||||||
Error('Syntax Error: Selector vector must contain only keywords.');
|
Error('Syntax Error: Selector vector must contain only keywords.');
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ end;
|
|||||||
|
|
||||||
function TSeriesType.ToString: string;
|
function TSeriesType.ToString: string;
|
||||||
begin
|
begin
|
||||||
Result := 'Series<' + FElementType.ToString + '>';
|
Result := inherited + '<' + FElementType.ToString + '>';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- Tuple ---
|
// --- Tuple ---
|
||||||
@@ -651,14 +651,14 @@ var
|
|||||||
begin
|
begin
|
||||||
sb := TStringBuilder.Create;
|
sb := TStringBuilder.Create;
|
||||||
try
|
try
|
||||||
sb.Append('(');
|
sb.Append('[');
|
||||||
for i := 0 to High(FElements) do
|
for i := 0 to High(FElements) do
|
||||||
begin
|
begin
|
||||||
sb.Append(FElements[i].ToString);
|
sb.Append(FElements[i].ToString);
|
||||||
if i < High(FElements) then
|
if i < High(FElements) then
|
||||||
sb.Append(', ');
|
sb.Append(', ');
|
||||||
end;
|
end;
|
||||||
sb.Append(')');
|
sb.Append(']');
|
||||||
Result := sb.ToString;
|
Result := sb.ToString;
|
||||||
finally
|
finally
|
||||||
sb.Free;
|
sb.Free;
|
||||||
@@ -740,7 +740,7 @@ end;
|
|||||||
|
|
||||||
function TVectorType.ToString: string;
|
function TVectorType.ToString: string;
|
||||||
begin
|
begin
|
||||||
Result := Format('[%s; %d]', [FElementType.ToString, FCount]);
|
Result := inherited + Format('[%s; %d]', [FElementType.ToString, FCount]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- Matrix ---
|
// --- Matrix ---
|
||||||
@@ -846,7 +846,7 @@ begin
|
|||||||
sb.Append('x');
|
sb.Append('x');
|
||||||
end;
|
end;
|
||||||
sb.Append(']');
|
sb.Append(']');
|
||||||
Result := sb.ToString;
|
Result := inherited + sb.ToString;
|
||||||
finally
|
finally
|
||||||
sb.Free;
|
sb.Free;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ type
|
|||||||
function VisitSeriesLength(const N: IAstNode): IAstNode;
|
function VisitSeriesLength(const N: IAstNode): IAstNode;
|
||||||
function VisitRecurNode(const N: IAstNode): IAstNode;
|
function VisitRecurNode(const N: IAstNode): IAstNode;
|
||||||
|
|
||||||
|
// Tuples
|
||||||
|
function VisitTuple(const N: IAstNode): IAstNode;
|
||||||
|
|
||||||
// Pipes
|
// Pipes
|
||||||
function VisitPipeInput(const N: IAstNode): IAstNode;
|
function VisitPipeInput(const N: IAstNode): IAstNode;
|
||||||
function VisitPipeSelectorList(const N: IAstNode): IAstNode;
|
function VisitPipeSelectorList(const N: IAstNode): IAstNode;
|
||||||
@@ -125,6 +128,8 @@ type
|
|||||||
function WalkSeriesLength(const N: IAstNode): TVoid;
|
function WalkSeriesLength(const N: IAstNode): TVoid;
|
||||||
function WalkRecurNode(const N: IAstNode): TVoid;
|
function WalkRecurNode(const N: IAstNode): TVoid;
|
||||||
|
|
||||||
|
function WalkTuple(const N: IAstNode): TVoid;
|
||||||
|
|
||||||
function WalkPipeInput(const N: IAstNode): TVoid;
|
function WalkPipeInput(const N: IAstNode): TVoid;
|
||||||
function WalkPipeSelectorList(const N: IAstNode): TVoid;
|
function WalkPipeSelectorList(const N: IAstNode): TVoid;
|
||||||
function WalkPipeInputList(const N: IAstNode): TVoid;
|
function WalkPipeInputList(const N: IAstNode): TVoid;
|
||||||
@@ -160,8 +165,8 @@ end;
|
|||||||
|
|
||||||
function TAstVisitor<T>.DefaultSentinel(const Node: IAstNode): T;
|
function TAstVisitor<T>.DefaultSentinel(const Node: IAstNode): T;
|
||||||
begin
|
begin
|
||||||
// Controlled crash for missing handlers.
|
Assert(false, 'No visitor handler registered for AST node kind: ' + Node.Kind.ToString + ' in class ' + ClassName);
|
||||||
raise ENoHandlerException.Create(Node.Kind.ToString);
|
exit(Default(T));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TAstVisitor<T>.SetupHandlers;
|
procedure TAstVisitor<T>.SetupHandlers;
|
||||||
@@ -235,6 +240,8 @@ begin
|
|||||||
Register(akSeriesLength, VisitSeriesLength);
|
Register(akSeriesLength, VisitSeriesLength);
|
||||||
Register(akRecur, VisitRecurNode);
|
Register(akRecur, VisitRecurNode);
|
||||||
|
|
||||||
|
Register(akTuple, VisitTuple);
|
||||||
|
|
||||||
Register(akPipeInput, VisitPipeInput);
|
Register(akPipeInput, VisitPipeInput);
|
||||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||||
Register(akPipeInputList, VisitPipeInputList);
|
Register(akPipeInputList, VisitPipeInputList);
|
||||||
@@ -716,6 +723,33 @@ begin
|
|||||||
Result := TAst.Pipe(N.Identity, inp, trans, P.StaticType);
|
Result := TAst.Pipe(N.Identity, inp, trans, P.StaticType);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstTransformer.VisitTuple(const N: IAstNode): IAstNode;
|
||||||
|
var
|
||||||
|
T: ITupleNode;
|
||||||
|
hasChanged: Boolean;
|
||||||
|
newElements: TArray<IAstNode>;
|
||||||
|
item, newItem: IAstNode;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
T := N.AsTuple;
|
||||||
|
hasChanged := False;
|
||||||
|
SetLength(newElements, Length(T.Elements));
|
||||||
|
|
||||||
|
for i := 0 to High(T.Elements) do
|
||||||
|
begin
|
||||||
|
item := T.Elements[i];
|
||||||
|
newItem := Visit(item);
|
||||||
|
newElements[i] := newItem;
|
||||||
|
if item <> newItem then
|
||||||
|
hasChanged := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not hasChanged then
|
||||||
|
Result := N
|
||||||
|
else
|
||||||
|
Result := TAst.Tuple(N.Identity, newElements, N.AsTypedNode.StaticType);
|
||||||
|
end;
|
||||||
|
|
||||||
{ TAstVisitor (Walker) }
|
{ TAstVisitor (Walker) }
|
||||||
|
|
||||||
procedure TAstVisitor.SetupHandlers;
|
procedure TAstVisitor.SetupHandlers;
|
||||||
@@ -756,6 +790,8 @@ begin
|
|||||||
Register(akSeriesLength, WalkSeriesLength);
|
Register(akSeriesLength, WalkSeriesLength);
|
||||||
Register(akRecur, WalkRecurNode);
|
Register(akRecur, WalkRecurNode);
|
||||||
|
|
||||||
|
Register(akTuple, WalkTuple);
|
||||||
|
|
||||||
// Pipes
|
// Pipes
|
||||||
Register(akPipeInput, WalkPipeInput);
|
Register(akPipeInput, WalkPipeInput);
|
||||||
Register(akPipeSelectorList, WalkPipeSelectorList);
|
Register(akPipeSelectorList, WalkPipeSelectorList);
|
||||||
@@ -937,4 +973,12 @@ begin
|
|||||||
Visit(P.Transformation);
|
Visit(P.Transformation);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstVisitor.WalkTuple(const N: IAstNode): TVoid;
|
||||||
|
var
|
||||||
|
item: IAstNode;
|
||||||
|
begin
|
||||||
|
for item in N.AsTuple.Elements do
|
||||||
|
Visit(item);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+25
-4
@@ -32,7 +32,6 @@ type
|
|||||||
ARegisterLibraries: Boolean = False
|
ARegisterLibraries: Boolean = False
|
||||||
): IExecutionScope; static;
|
): IExecutionScope; static;
|
||||||
|
|
||||||
// ... (Keep existing methods: Identifier, Constant, Keyword, CreateSeries, Nop, IfExpr, CondExpr, LambdaExpr, MacroDef, Quotes, Call, etc.) ...
|
|
||||||
// [Creation] Raw Name -> New INamedIdentity
|
// [Creation] Raw Name -> New INamedIdentity
|
||||||
class function Identifier(const AName: string; const Loc: ISourceLocation = nil): IIdentifierNode; overload; static;
|
class function Identifier(const AName: string; const Loc: ISourceLocation = nil): IIdentifierNode; overload; static;
|
||||||
class function Identifier(
|
class function Identifier(
|
||||||
@@ -48,6 +47,13 @@ type
|
|||||||
class function Keyword(const AName: string; const Loc: ISourceLocation = nil): IKeywordNode; overload; static;
|
class function Keyword(const AName: string; const Loc: ISourceLocation = nil): IKeywordNode; overload; static;
|
||||||
class function Keyword(const Identity: IKeywordIdentity): IKeywordNode; overload; static;
|
class function Keyword(const Identity: IKeywordIdentity): IKeywordNode; overload; static;
|
||||||
|
|
||||||
|
class function Tuple(const Elements: TArray<IAstNode>; const Loc: ISourceLocation = nil): ITupleNode; overload; static;
|
||||||
|
class function Tuple(
|
||||||
|
const Identity: IAstIdentity;
|
||||||
|
const Elements: TArray<IAstNode>;
|
||||||
|
const AStaticType: IStaticType = nil
|
||||||
|
): ITupleNode; overload; static;
|
||||||
|
|
||||||
class function CreateSeries(const ADefinition: String; const Loc: ISourceLocation = nil): ICreateSeriesNode; overload; static;
|
class function CreateSeries(const ADefinition: String; const Loc: ISourceLocation = nil): ICreateSeriesNode; overload; static;
|
||||||
class function CreateSeries(
|
class function CreateSeries(
|
||||||
const Identity: IDefinitionIdentity;
|
const Identity: IDefinitionIdentity;
|
||||||
@@ -261,12 +267,10 @@ type
|
|||||||
const AStaticType: IStaticType = nil
|
const AStaticType: IStaticType = nil
|
||||||
): ISeriesLengthNode; overload; static;
|
): ISeriesLengthNode; overload; static;
|
||||||
|
|
||||||
// --- PIPES (Updated) ---
|
// --- PIPES ---
|
||||||
|
|
||||||
// Helper: Create a SelectorList from an array of KeywordNodes
|
|
||||||
class function PipeSelectorList(const AKeywords: TArray<IKeywordNode>; const Loc: ISourceLocation = nil): IPipeSelectorList; static;
|
class function PipeSelectorList(const AKeywords: TArray<IKeywordNode>; const Loc: ISourceLocation = nil): IPipeSelectorList; static;
|
||||||
|
|
||||||
// Updated PipeInput: Accepts IPipeSelectorList instead of single Keyword
|
|
||||||
class function PipeInput(
|
class function PipeInput(
|
||||||
const AStream: IIdentifierNode;
|
const AStream: IIdentifierNode;
|
||||||
const ASelectors: IPipeSelectorList;
|
const ASelectors: IPipeSelectorList;
|
||||||
@@ -389,6 +393,23 @@ begin
|
|||||||
Result := TKeywordNode.Create(Identity);
|
Result := TKeywordNode.Create(Identity);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class function TAst.Tuple(const Elements: TArray<IAstNode>; const Loc: ISourceLocation): ITupleNode;
|
||||||
|
begin
|
||||||
|
var id := TIdentities.List('[', ']', ' ', Loc);
|
||||||
|
Result := TTupleNode.Create(Elements, id, TTypes.Unknown);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TAst.Tuple(const Identity: IAstIdentity; const Elements: TArray<IAstNode>; const AStaticType: IStaticType): ITupleNode;
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
TTupleNode.Create(
|
||||||
|
Elements,
|
||||||
|
Identity,
|
||||||
|
if AStaticType <> nil then AStaticType
|
||||||
|
else TTypes.Unknown
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
class function TAst.CreateSeries(const ADefinition: String; const Loc: ISourceLocation): ICreateSeriesNode;
|
class function TAst.CreateSeries(const ADefinition: String; const Loc: ISourceLocation): ICreateSeriesNode;
|
||||||
begin
|
begin
|
||||||
var id := TIdentities.Definition(ADefinition, Loc);
|
var id := TIdentities.Definition(ADefinition, Loc);
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ end;
|
|||||||
|
|
||||||
procedure TVisualNode.AddChild(Child: TVisualNode);
|
procedure TVisualNode.AddChild(Child: TVisualNode);
|
||||||
begin
|
begin
|
||||||
|
Assert(Child <> nil);
|
||||||
if Child.Parent <> nil then
|
if Child.Parent <> nil then
|
||||||
Child.Parent.RemoveChild(Child);
|
Child.Parent.RemoveChild(Child);
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ type
|
|||||||
function VisitRecordFieldList(const Node: IAstNode): TAstViewNode;
|
function VisitRecordFieldList(const Node: IAstNode): TAstViewNode;
|
||||||
function VisitRecordField(const Node: IAstNode): TAstViewNode;
|
function VisitRecordField(const Node: IAstNode): TAstViewNode;
|
||||||
|
|
||||||
|
// Tuple
|
||||||
|
function VisitTuple(const Node: IAstNode): TAstViewNode;
|
||||||
|
|
||||||
// Control Flow
|
// Control Flow
|
||||||
function VisitIfExpression(const Node: IAstNode): TAstViewNode;
|
function VisitIfExpression(const Node: IAstNode): TAstViewNode;
|
||||||
function VisitCondExpression(const Node: IAstNode): TAstViewNode;
|
function VisitCondExpression(const Node: IAstNode): TAstViewNode;
|
||||||
@@ -138,6 +141,7 @@ begin
|
|||||||
Register(akExpressionList, VisitExpressionList);
|
Register(akExpressionList, VisitExpressionList);
|
||||||
Register(akRecordFieldList, VisitRecordFieldList);
|
Register(akRecordFieldList, VisitRecordFieldList);
|
||||||
Register(akRecordField, VisitRecordField);
|
Register(akRecordField, VisitRecordField);
|
||||||
|
Register(akTuple, VisitTuple); // <--- NEW
|
||||||
|
|
||||||
// Control Flow
|
// Control Flow
|
||||||
Register(akIfExpression, VisitIfExpression);
|
Register(akIfExpression, VisitIfExpression);
|
||||||
@@ -261,6 +265,11 @@ begin
|
|||||||
Result := TAstViewNode.Create(Self, TRecordFieldHandler.Create(Node.AsRecordField));
|
Result := TAstViewNode.Create(Self, TRecordFieldHandler.Create(Node.AsRecordField));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstVisualizer.VisitTuple(const Node: IAstNode): TAstViewNode;
|
||||||
|
begin
|
||||||
|
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
||||||
|
end;
|
||||||
|
|
||||||
// Control Flow
|
// Control Flow
|
||||||
|
|
||||||
function TAstVisualizer.VisitBlockExpression(const Node: IAstNode): TAstViewNode;
|
function TAstVisualizer.VisitBlockExpression(const Node: IAstNode): TAstViewNode;
|
||||||
|
|||||||
@@ -12,6 +12,34 @@ type
|
|||||||
property Count: Integer read GetCount;
|
property Count: Integer read GetCount;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Concrete generic implementation
|
||||||
|
TTupleImpl<T> = class(TInterfacedObject, ITuple<T>)
|
||||||
|
private
|
||||||
|
FItems: TArray<T>;
|
||||||
|
function GetItems(Idx: Integer): T;
|
||||||
|
function GetCount: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(const AItems: TArray<T>);
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
{ TTupleImpl<T> }
|
||||||
|
|
||||||
|
constructor TTupleImpl<T>.Create(const AItems: TArray<T>);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FItems := AItems;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TTupleImpl<T>.GetCount: Integer;
|
||||||
|
begin
|
||||||
|
Result := Length(FItems);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TTupleImpl<T>.GetItems(Idx: Integer): T;
|
||||||
|
begin
|
||||||
|
Result := FItems[Idx];
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ type
|
|||||||
class operator Implicit(const AValue: String): TDataValue; overload; inline;
|
class operator Implicit(const AValue: String): TDataValue; overload; inline;
|
||||||
class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline;
|
class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline;
|
||||||
class operator Implicit(const AValue: ITuple<TDataValue>): TDataValue; overload; inline;
|
class operator Implicit(const AValue: ITuple<TDataValue>): TDataValue; overload; inline;
|
||||||
|
class operator Implicit(const AValue: TArray<TDataValue>): TDataValue; overload; inline;
|
||||||
class operator Implicit(const AValue: IKeywordMapping<TScalar>): TDataValue; overload; inline;
|
class operator Implicit(const AValue: IKeywordMapping<TScalar>): TDataValue; overload; inline;
|
||||||
class operator Implicit(const AValue: IKeywordMapping<TDataValue>): TDataValue; overload; inline;
|
class operator Implicit(const AValue: IKeywordMapping<TDataValue>): TDataValue; overload; inline;
|
||||||
class operator Implicit(const AValue: IWriteableScalarRecordSeries): TDataValue; overload; inline;
|
class operator Implicit(const AValue: IWriteableScalarRecordSeries): TDataValue; overload; inline;
|
||||||
@@ -507,6 +508,12 @@ begin
|
|||||||
Result.FInterface := AValue;
|
Result.FInterface := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class operator TDataValue.Implicit(const AValue: TArray<TDataValue>): TDataValue;
|
||||||
|
begin
|
||||||
|
Result.FKind := vkTuple;
|
||||||
|
Result.FInterface := TTupleImpl<TDataValue>.Create(AValue) as ITuple<TDataValue>;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TMapSeries }
|
{ TMapSeries }
|
||||||
|
|
||||||
constructor TMapSeries.Create(const ASourceSeries: ISeries; const AMapperFunc: TDataValue.TFunc);
|
constructor TMapSeries.Create(const ASourceSeries: ISeries; const AMapperFunc: TDataValue.TFunc);
|
||||||
|
|||||||
Reference in New Issue
Block a user