Pipe Parameter is now a Tuple
This commit is contained in:
@@ -73,7 +73,6 @@ type
|
||||
function VisitTuple(const Node: IAstNode): IAstNode;
|
||||
|
||||
// Pipe Support
|
||||
function VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||
function VisitPipe(const Node: IAstNode): IAstNode;
|
||||
|
||||
protected
|
||||
@@ -242,7 +241,6 @@ begin
|
||||
Register(akTuple, VisitTuple);
|
||||
|
||||
// Pipe Support
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
@@ -328,8 +326,6 @@ begin
|
||||
for i := 0 to count - 1 do
|
||||
begin
|
||||
newElements[i] := Accept(T.Items[i]);
|
||||
// FIX: Ensure the node is typed before accessing StaticType.
|
||||
// Structural nodes (like RecordField) are NOT typed and might crash or return garbage.
|
||||
if newElements[i].IsTyped then
|
||||
elementTypes[i] := newElements[i].AsTypedNode.StaticType
|
||||
else
|
||||
@@ -347,9 +343,6 @@ begin
|
||||
firstType := elementTypes[0];
|
||||
isHomogeneous := True;
|
||||
|
||||
// Check for Homogeneity (Exact type equality of all elements)
|
||||
// Note: If any element is Unknown, we assume Heterogeneous (Tuple) unless all are unknown?
|
||||
// For now, Unknown breaks homogeneity.
|
||||
for i := 1 to count - 1 do
|
||||
begin
|
||||
if not firstType.IsEqual(elementTypes[i]) then
|
||||
@@ -361,20 +354,15 @@ begin
|
||||
|
||||
if isHomogeneous and (firstType.Kind <> stUnknown) then
|
||||
begin
|
||||
// It is at least a Vector.
|
||||
// Check if it should be promoted to a Matrix (i.e., elements are Vectors or Matrices).
|
||||
|
||||
if firstType.Kind = stVector then
|
||||
begin
|
||||
// Vector of Vectors -> Matrix (2D)
|
||||
// New Dimensions = [OuterCount, InnerCount]
|
||||
newDim := [count, firstType.AsVector.Count];
|
||||
finalType := TTypes.CreateMatrix(firstType.AsVector.ElementType, newDim);
|
||||
end
|
||||
else if firstType.Kind = stMatrix then
|
||||
begin
|
||||
// Vector of Matrices -> Higher dimensional Matrix (N+1)
|
||||
// New Dimensions = [OuterCount, Dim0, Dim1...]
|
||||
// Vector of Matrices -> Higher dimensional Matrix
|
||||
commonDim := firstType.AsMatrix.Dimensions;
|
||||
SetLength(newDim, Length(commonDim) + 1);
|
||||
|
||||
@@ -518,30 +506,22 @@ var
|
||||
exprs: ITupleNode;
|
||||
i: Integer;
|
||||
begin
|
||||
// 1. Transform children via inherited logic
|
||||
// This delegates to TAstTransformer.VisitBlockExpression, which calls Visit(B.Expressions).
|
||||
// B.Expressions is a Tuple, so it routes to TTypeChecker.VisitTuple.
|
||||
var transformedNode := inherited VisitBlockExpression(Node);
|
||||
|
||||
// Safety check: Did the transformer return a node?
|
||||
if not Assigned(transformedNode) then
|
||||
raise ECompilationFailed.Create([TCompilerError.Create(elError, 'Internal Error: Block transformation returned nil.', Node)]);
|
||||
|
||||
newBlock := transformedNode.AsBlockExpression;
|
||||
exprs := newBlock.Expressions;
|
||||
|
||||
// 2. Validate expressions
|
||||
if exprs.Count > 0 then
|
||||
begin
|
||||
// Check for NIL entries which indicate failed transformation of children
|
||||
for i := 0 to exprs.Count - 1 do
|
||||
begin
|
||||
if exprs.Items[i] = nil then
|
||||
raise ECompilationFailed.Create(
|
||||
[TCompilerError.Create(elError, Format('Internal Error: Block expression #%d transformed to nil.', [i]), Node)]);
|
||||
end;
|
||||
|
||||
// The type of the block is the type of the last expression
|
||||
blockType := exprs.Items[exprs.Count - 1].AsTypedNode.StaticType;
|
||||
end
|
||||
else
|
||||
@@ -783,12 +763,6 @@ var
|
||||
newFields: ITupleNode;
|
||||
begin
|
||||
R := Node.AsRecordLiteral;
|
||||
|
||||
// FIX: Do NOT use inherited VisitRecordLiteral() here, because it expects an IRecordLiteralNode
|
||||
// but R.Fields is an ITupleNode. Using inherited would dispatch to VisitRecordLiteral with wrong type
|
||||
// or trigger recursive confusion.
|
||||
// Instead, directly visit the fields Tuple. This routes to TTypeChecker.VisitTuple.
|
||||
|
||||
newFields := Visit(R.Fields).AsTuple;
|
||||
|
||||
var count := newFields.Count;
|
||||
@@ -798,10 +772,6 @@ begin
|
||||
|
||||
for i := 0 to count - 1 do
|
||||
begin
|
||||
// Note: Elements of the fields tuple are RecordFieldNodes.
|
||||
// RecordFieldNodes are NOT Typed (IAstTypedNode), they are structural.
|
||||
// So we cannot check .StaticType on the field itself.
|
||||
// We must check the .Value of the field.
|
||||
var field := newFields.Items[i].AsRecordField;
|
||||
key := field.Key.Value;
|
||||
valType := field.Value.AsTypedNode.StaticType;
|
||||
@@ -876,89 +846,133 @@ end;
|
||||
// PIPE IMPLEMENTATION (Type Checking)
|
||||
// =============================================================================
|
||||
|
||||
function TTypeChecker.VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeInputNode;
|
||||
newSource: IIdentifierNode;
|
||||
sourceType: IStaticType;
|
||||
begin
|
||||
P := Node.AsPipeInput;
|
||||
newSource := Accept(P.StreamSource).AsIdentifier;
|
||||
sourceType := newSource.AsTypedNode.StaticType;
|
||||
|
||||
if (sourceType.Kind <> stUnknown) then
|
||||
begin
|
||||
if not ((sourceType.Kind = stSeries) or (sourceType.Kind = stRecordSeries)) then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(
|
||||
Format('Pipe input "%s" must be a Series or RecordSeries, but got %s.', [newSource.Name, sourceType.ToString]),
|
||||
Node
|
||||
);
|
||||
end
|
||||
else if (sourceType.Kind = stRecordSeries) then
|
||||
begin
|
||||
var def := sourceType.AsRecord.Definition;
|
||||
for var sel in P.Selectors do
|
||||
begin
|
||||
if def.IndexOf(sel.Value) < 0 then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(Format('Field ":%s" not found in stream "%s".', [sel.Value.Name, newSource.Name]), sel);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := TAst.PipeInput(newSource, P.Selectors, Node.Identity.Location);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitPipe(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeNode;
|
||||
rawInputs: ITupleNode;
|
||||
i, k: Integer;
|
||||
inputNode: IPipeInputNode;
|
||||
newInputs: TArray<IPipeInputNode>;
|
||||
streamType: IStaticType;
|
||||
newInputs: TList<IAstNode>;
|
||||
paramTypes: TList<IStaticType>;
|
||||
lambda: ILambdaExpressionNode;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newParamsAsNodes: TArray<IAstNode>;
|
||||
inferredType: IStaticType;
|
||||
|
||||
// Iteration vars
|
||||
inputPair: IAstNode;
|
||||
rawTuple: ITupleNode;
|
||||
streamNode: IIdentifierNode;
|
||||
selectorsNode: ITupleNode;
|
||||
newSelectors: TList<IAstNode>;
|
||||
streamType: IStaticType;
|
||||
begin
|
||||
P := Node.AsPipe;
|
||||
SetLength(newInputs, P.Inputs.Count);
|
||||
rawInputs := P.Inputs; // This is the Tuple of Tuples [[id [sel]] ...]
|
||||
|
||||
newInputs := TList<IAstNode>.Create;
|
||||
paramTypes := TList<IStaticType>.Create;
|
||||
try
|
||||
for i := 0 to P.Inputs.Count - 1 do
|
||||
// 1. Iterate over input definitions
|
||||
for i := 0 to rawInputs.Count - 1 do
|
||||
begin
|
||||
inputNode := Accept(P.Inputs[i]).AsPipeInput;
|
||||
newInputs[i] := inputNode;
|
||||
inputPair := rawInputs.Items[i];
|
||||
|
||||
streamType := inputNode.StreamSource.AsTypedNode.StaticType;
|
||||
|
||||
for var sel in inputNode.Selectors do
|
||||
// Validate Structure: [StreamSource, [Selectors]]
|
||||
if inputPair.Kind <> akTuple then
|
||||
begin
|
||||
inferredType := TTypes.Unknown;
|
||||
if streamType.Kind = stRecordSeries then
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe input must be a vector: [Source [Selectors]].', inputPair);
|
||||
continue;
|
||||
end;
|
||||
|
||||
rawTuple := inputPair.AsTuple;
|
||||
if rawTuple.Count <> 2 then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe input vector must have exactly 2 elements.', inputPair);
|
||||
continue;
|
||||
end;
|
||||
|
||||
// Element 0: Stream Identifier
|
||||
if rawTuple.Items[0].Kind <> akIdentifier then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe source must be an identifier.', rawTuple.Items[0]);
|
||||
continue;
|
||||
end;
|
||||
|
||||
// Resolve Stream Identifier (Type Binding)
|
||||
streamNode := Accept(rawTuple.Items[0]).AsIdentifier;
|
||||
streamType := streamNode.StaticType;
|
||||
|
||||
// Validate Stream Type
|
||||
if (streamType.Kind <> stUnknown) then
|
||||
begin
|
||||
if not ((streamType.Kind = stSeries) or (streamType.Kind = stRecordSeries)) then
|
||||
begin
|
||||
var def := streamType.AsRecord.Definition;
|
||||
var idx := def.IndexOf(sel.Value);
|
||||
if idx >= 0 then
|
||||
inferredType := TTypes.FromScalarKind(def[idx]);
|
||||
end
|
||||
else if streamType.Kind = stSeries then
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(
|
||||
Format('Pipe input "%s" must be a Series or RecordSeries, but got %s.', [streamNode.Name, streamType.ToString]),
|
||||
streamNode
|
||||
);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Element 1: Selector Vector
|
||||
if rawTuple.Items[1].Kind <> akTuple then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Pipe selectors must be a vector of keywords.', rawTuple.Items[1]);
|
||||
continue;
|
||||
end;
|
||||
selectorsNode := rawTuple.Items[1].AsTuple;
|
||||
newSelectors := TList<IAstNode>.Create;
|
||||
try
|
||||
// Iterate Selectors
|
||||
for k := 0 to selectorsNode.Count - 1 do
|
||||
begin
|
||||
if Assigned(streamType.AsSeries.ElementType) then
|
||||
inferredType := streamType.AsSeries.ElementType
|
||||
else
|
||||
inferredType := TTypes.Ordinal;
|
||||
var selItem := selectorsNode.Items[k];
|
||||
if selItem.Kind <> akKeyword then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError('Selector must be a keyword.', selItem);
|
||||
continue;
|
||||
end;
|
||||
|
||||
var kw := selItem.AsKeyword;
|
||||
newSelectors.Add(kw); // Keywords don't need 'Accept' as they are constant, but we keep them structurally
|
||||
|
||||
// Infer Type for Lambda Parameter
|
||||
inferredType := TTypes.Unknown;
|
||||
if streamType.Kind = stRecordSeries then
|
||||
begin
|
||||
var def := streamType.AsRecord.Definition;
|
||||
var idx := def.IndexOf(kw.Value);
|
||||
if idx >= 0 then
|
||||
inferredType := TTypes.FromScalarKind(def[idx])
|
||||
else if Assigned(FLog) then
|
||||
FLog.AddError(Format('Field ":%s" not found in stream "%s".', [kw.Value.Name, streamNode.Name]), selItem);
|
||||
end
|
||||
else if streamType.Kind = stSeries then
|
||||
begin
|
||||
if Assigned(streamType.AsSeries.ElementType) then
|
||||
inferredType := streamType.AsSeries.ElementType
|
||||
else
|
||||
inferredType := TTypes.Ordinal;
|
||||
end;
|
||||
paramTypes.Add(inferredType);
|
||||
end;
|
||||
|
||||
paramTypes.Add(inferredType);
|
||||
// Reconstruct the typed Input Tuple [StreamIdent, [Selectors]]
|
||||
var typedSelectorTuple := TAst.Tuple(selectorsNode.Identity, newSelectors.ToArray, TTypes.Unknown);
|
||||
var typedInputPair := TAst.Tuple(rawTuple.Identity, [streamNode, typedSelectorTuple], TTypes.Unknown);
|
||||
newInputs.Add(typedInputPair);
|
||||
finally
|
||||
newSelectors.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
// 2. Process Transformation Lambda
|
||||
lambda := P.Transformation;
|
||||
|
||||
if lambda.Parameters.Count <> paramTypes.Count then
|
||||
@@ -1004,6 +1018,7 @@ begin
|
||||
|
||||
var typedLambda := Accept(preTypedLambda).AsLambdaExpression;
|
||||
|
||||
// 3. Determine Result Type
|
||||
var lambdaRetType := typedLambda.AsTypedNode.StaticType.AsMethod.Signatures[0].ReturnType;
|
||||
var pipeType: IStaticType;
|
||||
|
||||
@@ -1024,8 +1039,12 @@ begin
|
||||
pipeType := TTypes.Unknown;
|
||||
end;
|
||||
|
||||
Result := TAst.Pipe(Node.Identity, TPipeInputList.Create(newInputs, P.Inputs.Identity), typedLambda, pipeType);
|
||||
// Reconstruct the Pipe Node with typed Inputs tuple and typed Lambda
|
||||
var typedInputsTuple := TAst.Tuple(rawInputs.Identity, newInputs.ToArray, TTypes.Unknown);
|
||||
Result := TAst.Pipe(Node.Identity, typedInputsTuple, typedLambda, pipeType);
|
||||
|
||||
finally
|
||||
newInputs.Free;
|
||||
paramTypes.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user