Tuples
This commit is contained in:
@@ -55,6 +55,8 @@ type
|
||||
function VisitMacroExpansionNode(const Node: IAstNode): Boolean;
|
||||
function VisitNop(const Node: IAstNode): Boolean;
|
||||
|
||||
function VisitTuple(const Node: IAstNode): Boolean;
|
||||
|
||||
// Pipe Support (Structural Check)
|
||||
function VisitPipeInput(const Node: IAstNode): Boolean;
|
||||
function VisitPipeSelectorList(const Node: IAstNode): Boolean;
|
||||
@@ -132,6 +134,8 @@ begin
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akNop, VisitNop);
|
||||
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
// Pipes
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
@@ -378,4 +382,17 @@ begin
|
||||
Result := IsNodePure(P.Inputs) and IsNodePure(P.Transformation);
|
||||
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.
|
||||
|
||||
@@ -38,6 +38,7 @@ type
|
||||
function VisitRecurNode(const Node: IAstNode): IAstNode;
|
||||
function VisitMacroExpansionNode(const Node: IAstNode): IAstNode;
|
||||
function VisitFunctionCall(const Node: IAstNode): IAstNode;
|
||||
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||
|
||||
protected
|
||||
procedure SetupHandlers; override;
|
||||
@@ -81,6 +82,7 @@ begin
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akMacroExpansion, VisitMacroExpansionNode);
|
||||
Register(akFunctionCall, VisitFunctionCall);
|
||||
Register(akTuple, VisitTuple);
|
||||
end;
|
||||
|
||||
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);
|
||||
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.
|
||||
|
||||
@@ -70,6 +70,7 @@ type
|
||||
function VisitRecordLiteral(const Node: IAstNode): IAstNode;
|
||||
function VisitConstant(const Node: IAstNode): IAstNode;
|
||||
function VisitKeyword(const Node: IAstNode): IAstNode;
|
||||
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||
|
||||
// Pipe Support
|
||||
function VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||
@@ -238,6 +239,7 @@ begin
|
||||
Register(akRecordLiteral, VisitRecordLiteral);
|
||||
Register(akConstant, VisitConstant);
|
||||
Register(akKeyword, VisitKeyword);
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
// Pipe Support
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
@@ -292,7 +294,6 @@ end;
|
||||
|
||||
function TTypeChecker.VisitConstant(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
// Base implementation already returns Node, but here we explicitly confirm identity for clarity
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
@@ -301,6 +302,98 @@ begin
|
||||
Result := Node;
|
||||
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;
|
||||
var
|
||||
I: IIdentifierNode;
|
||||
@@ -328,12 +421,6 @@ var
|
||||
args: IArgumentList;
|
||||
begin
|
||||
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;
|
||||
Result := TAst.Recur(Node.Identity, args, TTypes.Void);
|
||||
end;
|
||||
@@ -422,17 +509,37 @@ var
|
||||
newBlock: IBlockExpressionNode;
|
||||
blockType: IStaticType;
|
||||
exprs: IExpressionList;
|
||||
i: Integer;
|
||||
begin
|
||||
// Inherited logic transforms all expressions in the list
|
||||
newBlock := inherited VisitBlockExpression(Node).AsBlockExpression;
|
||||
// 1. Transform children via inherited logic
|
||||
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
|
||||
blockType := exprs[exprs.Count - 1].AsTypedNode.StaticType
|
||||
else
|
||||
blockType := TTypes.Void;
|
||||
begin
|
||||
// Check for NIL entries which indicate failed transformation of children
|
||||
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);
|
||||
end;
|
||||
|
||||
@@ -451,7 +558,6 @@ var
|
||||
begin
|
||||
L := Node.AsLambdaExpression;
|
||||
|
||||
// 1. Resolve Upvalue Types
|
||||
var upvalueAddrs := L.Upvalues;
|
||||
SetLength(upvalueTypes, Length(upvalueAddrs));
|
||||
|
||||
@@ -466,7 +572,6 @@ begin
|
||||
upvalueTypes[i] := FCurrentContext.LookupType(lookupAddr);
|
||||
end;
|
||||
|
||||
// 2. Enter New Scope
|
||||
FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil);
|
||||
try
|
||||
SetLength(newParams, L.Parameters.Count);
|
||||
@@ -475,8 +580,6 @@ begin
|
||||
for i := 0 to L.Parameters.Count - 1 do
|
||||
begin
|
||||
paramIdent := L.Parameters[i];
|
||||
|
||||
// Check if there is already a type assigned (e.g. injected by Pipe Visitor)
|
||||
injectedType := paramIdent.AsTypedNode.StaticType;
|
||||
|
||||
if injectedType.Kind = stUnknown then
|
||||
@@ -517,10 +620,7 @@ var
|
||||
bestSig: IMethodSignature;
|
||||
match: Boolean;
|
||||
begin
|
||||
// Use inherited to visit Callee and Arguments first
|
||||
newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
|
||||
|
||||
// Now analyze types on the transformed children
|
||||
var newCallee := newCall.Callee;
|
||||
var newArgs := newCall.Arguments;
|
||||
|
||||
@@ -570,7 +670,6 @@ begin
|
||||
if Assigned(FLog) then
|
||||
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);
|
||||
end;
|
||||
|
||||
@@ -605,7 +704,18 @@ begin
|
||||
if baseType.Kind = stSeries then
|
||||
elemType := baseType.AsSeries.ElementType
|
||||
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);
|
||||
Result := TAst.Indexer(Node.Identity, newBase, newIndex, elemType);
|
||||
@@ -655,10 +765,8 @@ var
|
||||
newFieldList: IRecordFieldList;
|
||||
begin
|
||||
R := Node.AsRecordLiteral;
|
||||
// Transform fields using inherited recursion
|
||||
newFieldList := inherited VisitRecordFieldList(R.Fields).AsRecordFieldList;
|
||||
|
||||
// Now analyze the transformed fields
|
||||
var count := newFieldList.Count;
|
||||
SetLength(fieldTypes, count);
|
||||
SetLength(scalarFieldTypes, count);
|
||||
@@ -670,7 +778,6 @@ begin
|
||||
key := field.Key.Value;
|
||||
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
|
||||
begin
|
||||
var kind: TScalar.TKind;
|
||||
@@ -693,7 +800,6 @@ begin
|
||||
fieldTypes[i] := TPair<IKeyword, IStaticType>.Create(key, valType);
|
||||
end;
|
||||
|
||||
// Build Definition
|
||||
var scalarDef: IScalarRecordDefinition := nil;
|
||||
var genericDef: IGenericRecordDefinition := nil;
|
||||
var resultType: IStaticType;
|
||||
@@ -720,9 +826,8 @@ var
|
||||
begin
|
||||
C := Node.AsCreateSeries;
|
||||
def := C.Definition;
|
||||
// Simple heuristic for type
|
||||
if def.StartsWith('[') then
|
||||
elemType := TTypes.CreateRecord(nil) // Placeholder, normally parses JSON
|
||||
elemType := TTypes.CreateRecord(nil)
|
||||
else
|
||||
elemType := TTypes.FromScalarKind(TScalar.StringToKind(def));
|
||||
|
||||
@@ -750,11 +855,9 @@ var
|
||||
sourceType: IStaticType;
|
||||
begin
|
||||
P := Node.AsPipeInput;
|
||||
// Transform source identifier (resolves type)
|
||||
newSource := Accept(P.StreamSource).AsIdentifier;
|
||||
sourceType := newSource.AsTypedNode.StaticType;
|
||||
|
||||
// 1. Verify Source is a Series-compatible type
|
||||
if (sourceType.Kind <> stUnknown) then
|
||||
begin
|
||||
if not ((sourceType.Kind = stSeries) or (sourceType.Kind = stRecordSeries)) then
|
||||
@@ -767,7 +870,6 @@ begin
|
||||
end
|
||||
else if (sourceType.Kind = stRecordSeries) then
|
||||
begin
|
||||
// 2. Verify Selectors exist in Record Definition
|
||||
var def := sourceType.AsRecord.Definition;
|
||||
for var sel in P.Selectors do
|
||||
begin
|
||||
@@ -780,7 +882,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
// Reuse selectors (they are just keywords, no type checking needed)
|
||||
Result := TAst.PipeInput(newSource, P.Selectors, Node.Identity.Location);
|
||||
end;
|
||||
|
||||
@@ -800,22 +901,18 @@ begin
|
||||
SetLength(newInputs, P.Inputs.Count);
|
||||
paramTypes := TList<IStaticType>.Create;
|
||||
try
|
||||
// 1. Visit Inputs and collect types for Lambda parameters
|
||||
for i := 0 to P.Inputs.Count - 1 do
|
||||
begin
|
||||
// Recurse on inputs to resolve their sources
|
||||
inputNode := Accept(P.Inputs[i]).AsPipeInput;
|
||||
newInputs[i] := inputNode;
|
||||
|
||||
streamType := inputNode.StreamSource.AsTypedNode.StaticType;
|
||||
|
||||
// Flatten logic: One lambda param per selector
|
||||
for var sel in inputNode.Selectors do
|
||||
begin
|
||||
inferredType := TTypes.Unknown;
|
||||
if streamType.Kind = stRecordSeries then
|
||||
begin
|
||||
// Extract field type from definition
|
||||
var def := streamType.AsRecord.Definition;
|
||||
var idx := def.IndexOf(sel.Value);
|
||||
if idx >= 0 then
|
||||
@@ -823,18 +920,16 @@ begin
|
||||
end
|
||||
else if streamType.Kind = stSeries then
|
||||
begin
|
||||
// If simple series, use its element type
|
||||
if Assigned(streamType.AsSeries.ElementType) then
|
||||
inferredType := streamType.AsSeries.ElementType
|
||||
else
|
||||
inferredType := TTypes.Ordinal; // Fallback default
|
||||
inferredType := TTypes.Ordinal;
|
||||
end;
|
||||
|
||||
paramTypes.Add(inferredType);
|
||||
end;
|
||||
end;
|
||||
|
||||
// 2. Prepare Lambda with Inferred Types
|
||||
lambda := P.Transformation;
|
||||
|
||||
if lambda.Parameters.Count <> paramTypes.Count then
|
||||
@@ -857,11 +952,9 @@ begin
|
||||
if k < paramTypes.Count then paramTypes[k]
|
||||
else TTypes.Unknown;
|
||||
|
||||
// Create new identifier with the inferred type!
|
||||
newParams[k] := TAst.Identifier(oldP.Identity.AsNamed, oldP.Address, typeToInject);
|
||||
end;
|
||||
|
||||
// Recreate Lambda NODE with Typed Parameters
|
||||
var preTypedLambda :=
|
||||
TAst.LambdaExpr(
|
||||
lambda.Identity,
|
||||
@@ -872,24 +965,20 @@ begin
|
||||
lambda.Upvalues,
|
||||
lambda.HasNestedLambdas,
|
||||
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;
|
||||
|
||||
// 4. Infer Pipe Return Type & Validate Strictness
|
||||
var lambdaRetType := typedLambda.AsTypedNode.StaticType.AsMethod.Signatures[0].ReturnType;
|
||||
var pipeType: IStaticType;
|
||||
|
||||
if lambdaRetType.Kind = stRecord then
|
||||
begin
|
||||
// Valid: Record -> RecordSeries
|
||||
pipeType := TTypes.CreateRecordSeries(lambdaRetType.AsRecord.Definition);
|
||||
end
|
||||
else
|
||||
begin
|
||||
// STRICT CHECK: Scalars are NOT allowed.
|
||||
if (lambdaRetType.Kind <> stUnknown) and (lambdaRetType.Kind <> stVoid) then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
@@ -898,8 +987,6 @@ begin
|
||||
lambda
|
||||
);
|
||||
end;
|
||||
|
||||
// If Void or Unknown, or Error case:
|
||||
pipeType := TTypes.Unknown;
|
||||
end;
|
||||
|
||||
|
||||
@@ -151,6 +151,14 @@ begin
|
||||
akLambdaExpression: Result := 'FN';
|
||||
akRecordLiteral: Result := 'RECORD';
|
||||
akPipe: Result := 'PIPE';
|
||||
|
||||
// NEW:
|
||||
akIndexer: Result := 'INDEXER';
|
||||
akMemberAccess: Result := 'MEMBER';
|
||||
akTuple: Result := 'TUPLE';
|
||||
akCreateSeries: Result := 'NEW-SERIES';
|
||||
akAddSeriesItem: Result := 'ADD-ITEM';
|
||||
|
||||
else
|
||||
Result := ''; // Silence leaf nodes like Constants and Keywords to reduce noise
|
||||
end;
|
||||
|
||||
@@ -62,6 +62,8 @@ type
|
||||
function VisitRecordFieldList(const Node: IAstNode): TVoid;
|
||||
function VisitRecordField(const Node: IAstNode): TVoid;
|
||||
|
||||
function VisitTuple(const Node: IAstNode): TVoid;
|
||||
|
||||
// Pipe Visitors
|
||||
function VisitPipeInput(const Node: IAstNode): TVoid;
|
||||
function VisitPipeSelectorList(const Node: IAstNode): TVoid;
|
||||
@@ -143,6 +145,8 @@ begin
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akNop, VisitNop);
|
||||
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
@@ -625,4 +629,20 @@ begin
|
||||
Unindent;
|
||||
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.
|
||||
|
||||
@@ -21,13 +21,16 @@ type
|
||||
private
|
||||
FScope: IExecutionScope;
|
||||
protected
|
||||
// Haupt-Dispatch-Methode
|
||||
function Visit(const Node: IAstNode): TDataValue; virtual;
|
||||
function CreateVisitorFactory: TEvaluatorFactory; virtual;
|
||||
|
||||
// Nur noch die für die Ausführung relevanten Methoden
|
||||
// Besuchermethoden
|
||||
function VisitConstant(const N: IConstantNode): TDataValue; virtual;
|
||||
function VisitIdentifier(const N: IIdentifierNode): 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 VisitCondExpression(const N: ICondExpressionNode): TDataValue; virtual;
|
||||
function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; virtual;
|
||||
@@ -89,10 +92,12 @@ end;
|
||||
function TEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
// Der Hot-Path: Direkter Dispatch ohne Umweg über ungenutzte Methoden.
|
||||
// HIER fehlte wahrscheinlich akTuple!
|
||||
case Node.Kind of
|
||||
akConstant: Result := VisitConstant(Node.AsConstant);
|
||||
akIdentifier: Result := VisitIdentifier(Node.AsIdentifier);
|
||||
akKeyword: Result := VisitKeyword(Node.AsKeyword);
|
||||
akTuple: Result := VisitTuple(Node.AsTuple); // <--- KORREKTUR
|
||||
akIfExpression: Result := VisitIfExpression(Node.AsIfExpression);
|
||||
akCondExpression: Result := VisitCondExpression(Node.AsCondExpression);
|
||||
akLambdaExpression: Result := VisitLambdaExpression(Node.AsLambdaExpression);
|
||||
@@ -173,6 +178,21 @@ begin
|
||||
Result := FScope[N.Address];
|
||||
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;
|
||||
var
|
||||
capturedCells: TArray<IValueCell>;
|
||||
@@ -334,20 +354,30 @@ begin
|
||||
if base.IsVoid then
|
||||
exit(TDataValue.Void);
|
||||
idx := Visit(N.Index);
|
||||
if (base.Kind = vkSeries) then
|
||||
Result := TDataValue(base.AsSeries.Items[Integer(idx.AsScalar.Value.AsInt64)])
|
||||
else if (base.Kind = vkRecordSeries) then
|
||||
begin
|
||||
var rs := base.AsRecordSeries;
|
||||
var vals: TArray<TScalar.TValue>;
|
||||
SetLength(vals, rs.Def.Count);
|
||||
var i64 := idx.AsScalar.Value.AsInt64;
|
||||
for var k := 0 to rs.Def.Count - 1 do
|
||||
vals[k] := rs.Fields[rs.Def.Keywords[k]].Items[Integer(i64)].Value;
|
||||
Result := TScalarRecord.Create(rs.Def, vals);
|
||||
end
|
||||
|
||||
case base.Kind of
|
||||
vkSeries: Result := TDataValue(base.AsSeries.Items[Integer(idx.AsScalar.Value.AsInt64)]);
|
||||
vkRecordSeries:
|
||||
begin
|
||||
var rs := base.AsRecordSeries;
|
||||
var vals: TArray<TScalar.TValue>;
|
||||
SetLength(vals, rs.Def.Count);
|
||||
var i64 := idx.AsScalar.Value.AsInt64;
|
||||
for var k := 0 to rs.Def.Count - 1 do
|
||||
vals[k] := rs.Fields[rs.Def.Keywords[k]].Items[Integer(i64)].Value;
|
||||
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
|
||||
raise EEvaluatorException.Create('Indexer error');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitMemberAccess(const N: IMemberAccessNode): TDataValue;
|
||||
|
||||
@@ -51,6 +51,7 @@ type
|
||||
function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode;
|
||||
function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
|
||||
function JsonToNopNode(const AObj: TJSONObject): INopNode;
|
||||
function JsonToTupleNode(const AObj: TJSONObject): ITupleNode;
|
||||
|
||||
strict private
|
||||
// Serialization Visitors (IAstNode signature)
|
||||
@@ -85,6 +86,8 @@ type
|
||||
function VisitSeriesLength(const Node: IAstNode): TJSONObject;
|
||||
function VisitNop(const Node: IAstNode): TJSONObject;
|
||||
|
||||
function VisitTuple(const Node: IAstNode): TJSONObject;
|
||||
|
||||
function VisitPipeInput(const Node: IAstNode): TJSONObject;
|
||||
function VisitPipeSelectorList(const Node: IAstNode): TJSONObject;
|
||||
function VisitPipeInputList(const Node: IAstNode): TJSONObject;
|
||||
@@ -144,6 +147,8 @@ begin
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akNop, VisitNop);
|
||||
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
@@ -531,6 +536,24 @@ begin
|
||||
Result.AddPair('NodeType', TJSONString.Create('Nop'));
|
||||
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;
|
||||
begin
|
||||
Result := nil;
|
||||
@@ -548,6 +571,21 @@ begin
|
||||
Result := nil;
|
||||
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;
|
||||
begin
|
||||
raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
|
||||
@@ -660,6 +698,8 @@ begin
|
||||
Result := JsonToSeriesLengthNode(obj)
|
||||
else if nodeType = 'Nop' then
|
||||
Result := JsonToNopNode(obj)
|
||||
else if nodeType = 'Tuple' then
|
||||
Result := JsonToTupleNode(obj)
|
||||
else
|
||||
raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s".', [nodeType]);
|
||||
end;
|
||||
|
||||
+52
-13
@@ -64,12 +64,6 @@ type
|
||||
property Errors: TArray<TCompilerError> read FErrors;
|
||||
end;
|
||||
|
||||
// Exception for the new visitor registry dispatch
|
||||
ENoHandlerException = class(EAstException)
|
||||
public
|
||||
constructor Create(const KindName: string);
|
||||
end;
|
||||
|
||||
TCompiledFunction = record
|
||||
public
|
||||
Func: TDataValue.TFunc;
|
||||
@@ -94,6 +88,7 @@ type
|
||||
IExpressionList = interface;
|
||||
IRecordFieldList = interface;
|
||||
IRecordFieldNode = interface;
|
||||
ITupleNode = interface;
|
||||
|
||||
// Control Flow & Structure
|
||||
IIfExpressionNode = interface;
|
||||
@@ -134,6 +129,9 @@ type
|
||||
akExpressionList,
|
||||
akRecordFieldList,
|
||||
akRecordField,
|
||||
// Tuple
|
||||
akTuple,
|
||||
// Control Flow
|
||||
akIfExpression,
|
||||
akCondExpression,
|
||||
akLambdaExpression,
|
||||
@@ -195,6 +193,7 @@ type
|
||||
function AsExpressionList: IExpressionList;
|
||||
function AsRecordFieldList: IRecordFieldList;
|
||||
function AsRecordField: IRecordFieldNode;
|
||||
function AsTuple: ITupleNode;
|
||||
|
||||
function AsIfExpression: IIfExpressionNode;
|
||||
function AsCondExpression: ICondExpressionNode;
|
||||
@@ -298,6 +297,13 @@ type
|
||||
property Value: IKeyword read GetValue;
|
||||
end;
|
||||
|
||||
ITupleNode = interface(IAstTypedNode)
|
||||
{$region 'private'}
|
||||
function GetElements: TArray<IAstNode>;
|
||||
{$endregion}
|
||||
property Elements: TArray<IAstNode> read GetElements;
|
||||
end;
|
||||
|
||||
IIfExpressionNode = interface(IAstTypedNode)
|
||||
{$region 'private'}
|
||||
function GetCondition: IAstNode;
|
||||
@@ -543,6 +549,7 @@ type
|
||||
function AsExpressionList: IExpressionList; virtual;
|
||||
function AsRecordFieldList: IRecordFieldList; virtual;
|
||||
function AsRecordField: IRecordFieldNode; virtual;
|
||||
function AsTuple: ITupleNode; virtual;
|
||||
|
||||
function AsIfExpression: IIfExpressionNode; virtual;
|
||||
function AsCondExpression: ICondExpressionNode; virtual;
|
||||
@@ -696,6 +703,17 @@ type
|
||||
function AsKeyword: IKeywordNode; override;
|
||||
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)
|
||||
private
|
||||
FDefIdentity: IDefinitionIdentity;
|
||||
@@ -1207,13 +1225,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ ENoHandlerException }
|
||||
|
||||
constructor ENoHandlerException.Create(const KindName: string);
|
||||
begin
|
||||
inherited CreateFmt('No visitor handler registered for AST node kind: %s', [KindName]);
|
||||
end;
|
||||
|
||||
{ TAstNode }
|
||||
|
||||
constructor TAstNode.Create(const AIdentity: IAstIdentity);
|
||||
@@ -1280,6 +1291,11 @@ begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TAstNode.AsTuple: ITupleNode;
|
||||
begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TAstNode.AsIfExpression: IIfExpressionNode;
|
||||
begin
|
||||
Result := nil;
|
||||
@@ -1608,6 +1624,29 @@ begin
|
||||
Result := FKeywordIdentity.Value;
|
||||
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 }
|
||||
|
||||
constructor TCreateSeriesNode.Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
|
||||
|
||||
@@ -39,6 +39,7 @@ type
|
||||
function VisitIndexer(const Node: IAstNode): IAstNode;
|
||||
function VisitMemberAccess(const Node: IAstNode): IAstNode;
|
||||
function VisitAddSeriesItem(const Node: IAstNode): IAstNode;
|
||||
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||
|
||||
protected
|
||||
procedure SetupHandlers; override;
|
||||
@@ -81,6 +82,8 @@ begin
|
||||
Register(akIndexer, VisitIndexer);
|
||||
Register(akMemberAccess, VisitMemberAccess);
|
||||
Register(akAddSeriesItem, VisitAddSeriesItem);
|
||||
|
||||
Register(akTuple, VisitTuple);
|
||||
end;
|
||||
|
||||
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);
|
||||
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.
|
||||
|
||||
+23
-17
@@ -437,8 +437,8 @@ begin
|
||||
items.Add(ParseExpression.Node);
|
||||
end;
|
||||
|
||||
var id := TIdentities.List('[', ']', ' ', startLoc);
|
||||
Result := TArgumentList.Create(items.ToArray, id);
|
||||
// CHANGED: Use Tuple factory instead of ArgumentList
|
||||
Result := TAst.Tuple(items.ToArray, startLoc);
|
||||
finally
|
||||
items.Free;
|
||||
end;
|
||||
@@ -447,15 +447,19 @@ end;
|
||||
|
||||
function TParser.ExtractIdentifiers(const Node: IAstNode; const Context: string): TArray<IIdentifierNode>;
|
||||
var
|
||||
list: IArgumentList;
|
||||
list: TArray<IAstNode>;
|
||||
i: Integer;
|
||||
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]);
|
||||
|
||||
list := Node.AsArgumentList;
|
||||
SetLength(Result, list.Count);
|
||||
for i := 0 to list.Count - 1 do
|
||||
SetLength(Result, Length(list));
|
||||
for i := 0 to High(list) do
|
||||
begin
|
||||
if list[i].Kind <> akIdentifier then
|
||||
ErrorFmt('%s vector must contain only identifiers.', [Context]);
|
||||
@@ -536,16 +540,18 @@ begin
|
||||
if Length(tailNodes) <> 2 then
|
||||
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 [...].');
|
||||
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]]).');
|
||||
|
||||
var pipeInputs := TList<IPipeInputNode>.Create;
|
||||
try
|
||||
var k := 0;
|
||||
while k < rawInputs.Count do
|
||||
while k < Length(rawInputs) do
|
||||
begin
|
||||
if rawInputs[k].Kind <> akIdentifier then
|
||||
Error('Syntax Error: Pipe input stream must be an identifier.');
|
||||
@@ -554,16 +560,16 @@ begin
|
||||
var streamId := rawInputs[k].AsIdentifier;
|
||||
var selKeywords: TArray<IKeywordNode>;
|
||||
|
||||
// Strict format: Selector must be a vector [...] containing keywords
|
||||
if selNode.Kind <> akArgumentList then
|
||||
// Strict format: Selector must be a vector [...] (TupleNode) containing keywords
|
||||
if selNode.Kind <> akTuple then
|
||||
Error('Syntax Error: Pipe selector must be a vector of keywords (e.g. [:Close :High]).');
|
||||
|
||||
var selList := selNode.AsArgumentList;
|
||||
if selList.Count = 0 then
|
||||
var selList := selNode.AsTuple.Elements;
|
||||
if Length(selList) = 0 then
|
||||
Error('Syntax Error: Selector list cannot be empty.');
|
||||
|
||||
SetLength(selKeywords, selList.Count);
|
||||
for var m := 0 to selList.Count - 1 do
|
||||
SetLength(selKeywords, Length(selList));
|
||||
for var m := 0 to High(selList) do
|
||||
begin
|
||||
if selList[m].Kind <> akKeyword then
|
||||
Error('Syntax Error: Selector vector must contain only keywords.');
|
||||
|
||||
@@ -558,7 +558,7 @@ end;
|
||||
|
||||
function TSeriesType.ToString: string;
|
||||
begin
|
||||
Result := 'Series<' + FElementType.ToString + '>';
|
||||
Result := inherited + '<' + FElementType.ToString + '>';
|
||||
end;
|
||||
|
||||
// --- Tuple ---
|
||||
@@ -651,14 +651,14 @@ var
|
||||
begin
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('(');
|
||||
sb.Append('[');
|
||||
for i := 0 to High(FElements) do
|
||||
begin
|
||||
sb.Append(FElements[i].ToString);
|
||||
if i < High(FElements) then
|
||||
sb.Append(', ');
|
||||
end;
|
||||
sb.Append(')');
|
||||
sb.Append(']');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
@@ -740,7 +740,7 @@ end;
|
||||
|
||||
function TVectorType.ToString: string;
|
||||
begin
|
||||
Result := Format('[%s; %d]', [FElementType.ToString, FCount]);
|
||||
Result := inherited + Format('[%s; %d]', [FElementType.ToString, FCount]);
|
||||
end;
|
||||
|
||||
// --- Matrix ---
|
||||
@@ -846,7 +846,7 @@ begin
|
||||
sb.Append('x');
|
||||
end;
|
||||
sb.Append(']');
|
||||
Result := sb.ToString;
|
||||
Result := inherited + sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
|
||||
@@ -83,6 +83,9 @@ type
|
||||
function VisitSeriesLength(const N: IAstNode): IAstNode;
|
||||
function VisitRecurNode(const N: IAstNode): IAstNode;
|
||||
|
||||
// Tuples
|
||||
function VisitTuple(const N: IAstNode): IAstNode;
|
||||
|
||||
// Pipes
|
||||
function VisitPipeInput(const N: IAstNode): IAstNode;
|
||||
function VisitPipeSelectorList(const N: IAstNode): IAstNode;
|
||||
@@ -125,6 +128,8 @@ type
|
||||
function WalkSeriesLength(const N: IAstNode): TVoid;
|
||||
function WalkRecurNode(const N: IAstNode): TVoid;
|
||||
|
||||
function WalkTuple(const N: IAstNode): TVoid;
|
||||
|
||||
function WalkPipeInput(const N: IAstNode): TVoid;
|
||||
function WalkPipeSelectorList(const N: IAstNode): TVoid;
|
||||
function WalkPipeInputList(const N: IAstNode): TVoid;
|
||||
@@ -160,8 +165,8 @@ end;
|
||||
|
||||
function TAstVisitor<T>.DefaultSentinel(const Node: IAstNode): T;
|
||||
begin
|
||||
// Controlled crash for missing handlers.
|
||||
raise ENoHandlerException.Create(Node.Kind.ToString);
|
||||
Assert(false, 'No visitor handler registered for AST node kind: ' + Node.Kind.ToString + ' in class ' + ClassName);
|
||||
exit(Default(T));
|
||||
end;
|
||||
|
||||
procedure TAstVisitor<T>.SetupHandlers;
|
||||
@@ -235,6 +240,8 @@ begin
|
||||
Register(akSeriesLength, VisitSeriesLength);
|
||||
Register(akRecur, VisitRecurNode);
|
||||
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipeSelectorList, VisitPipeSelectorList);
|
||||
Register(akPipeInputList, VisitPipeInputList);
|
||||
@@ -716,6 +723,33 @@ begin
|
||||
Result := TAst.Pipe(N.Identity, inp, trans, P.StaticType);
|
||||
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) }
|
||||
|
||||
procedure TAstVisitor.SetupHandlers;
|
||||
@@ -756,6 +790,8 @@ begin
|
||||
Register(akSeriesLength, WalkSeriesLength);
|
||||
Register(akRecur, WalkRecurNode);
|
||||
|
||||
Register(akTuple, WalkTuple);
|
||||
|
||||
// Pipes
|
||||
Register(akPipeInput, WalkPipeInput);
|
||||
Register(akPipeSelectorList, WalkPipeSelectorList);
|
||||
@@ -937,4 +973,12 @@ begin
|
||||
Visit(P.Transformation);
|
||||
end;
|
||||
|
||||
function TAstVisitor.WalkTuple(const N: IAstNode): TVoid;
|
||||
var
|
||||
item: IAstNode;
|
||||
begin
|
||||
for item in N.AsTuple.Elements do
|
||||
Visit(item);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+25
-4
@@ -32,7 +32,6 @@ type
|
||||
ARegisterLibraries: Boolean = False
|
||||
): IExecutionScope; static;
|
||||
|
||||
// ... (Keep existing methods: Identifier, Constant, Keyword, CreateSeries, Nop, IfExpr, CondExpr, LambdaExpr, MacroDef, Quotes, Call, etc.) ...
|
||||
// [Creation] Raw Name -> New INamedIdentity
|
||||
class function Identifier(const AName: string; const Loc: ISourceLocation = nil): IIdentifierNode; overload; static;
|
||||
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 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 Identity: IDefinitionIdentity;
|
||||
@@ -261,12 +267,10 @@ type
|
||||
const AStaticType: IStaticType = nil
|
||||
): 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;
|
||||
|
||||
// Updated PipeInput: Accepts IPipeSelectorList instead of single Keyword
|
||||
class function PipeInput(
|
||||
const AStream: IIdentifierNode;
|
||||
const ASelectors: IPipeSelectorList;
|
||||
@@ -389,6 +393,23 @@ begin
|
||||
Result := TKeywordNode.Create(Identity);
|
||||
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;
|
||||
begin
|
||||
var id := TIdentities.Definition(ADefinition, Loc);
|
||||
|
||||
@@ -228,6 +228,7 @@ end;
|
||||
|
||||
procedure TVisualNode.AddChild(Child: TVisualNode);
|
||||
begin
|
||||
Assert(Child <> nil);
|
||||
if Child.Parent <> nil then
|
||||
Child.Parent.RemoveChild(Child);
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ type
|
||||
function VisitRecordFieldList(const Node: IAstNode): TAstViewNode;
|
||||
function VisitRecordField(const Node: IAstNode): TAstViewNode;
|
||||
|
||||
// Tuple
|
||||
function VisitTuple(const Node: IAstNode): TAstViewNode;
|
||||
|
||||
// Control Flow
|
||||
function VisitIfExpression(const Node: IAstNode): TAstViewNode;
|
||||
function VisitCondExpression(const Node: IAstNode): TAstViewNode;
|
||||
@@ -138,6 +141,7 @@ begin
|
||||
Register(akExpressionList, VisitExpressionList);
|
||||
Register(akRecordFieldList, VisitRecordFieldList);
|
||||
Register(akRecordField, VisitRecordField);
|
||||
Register(akTuple, VisitTuple); // <--- NEW
|
||||
|
||||
// Control Flow
|
||||
Register(akIfExpression, VisitIfExpression);
|
||||
@@ -261,6 +265,11 @@ begin
|
||||
Result := TAstViewNode.Create(Self, TRecordFieldHandler.Create(Node.AsRecordField));
|
||||
end;
|
||||
|
||||
function TAstVisualizer.VisitTuple(const Node: IAstNode): TAstViewNode;
|
||||
begin
|
||||
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
||||
end;
|
||||
|
||||
// Control Flow
|
||||
|
||||
function TAstVisualizer.VisitBlockExpression(const Node: IAstNode): TAstViewNode;
|
||||
|
||||
Reference in New Issue
Block a user