Generic Visitors
This commit is contained in:
@@ -52,28 +52,31 @@ type
|
||||
function PrepareBaseType(const BaseNode: IAstNode; out IsOptional: Boolean): IStaticType;
|
||||
function ApplyOptionality(const AType: IStaticType; IsOptional: Boolean): IStaticType;
|
||||
|
||||
protected
|
||||
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
|
||||
function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
|
||||
function VisitNop(const Node: INopNode): IAstNode; override;
|
||||
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
|
||||
|
||||
function VisitConstant(const Node: IConstantNode): IAstNode; override;
|
||||
function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
|
||||
strict private
|
||||
// Typed Handlers (non-virtual, IAstNode signature)
|
||||
function VisitIdentifier(const Node: IAstNode): IAstNode;
|
||||
function VisitVariableDeclaration(const Node: IAstNode): IAstNode;
|
||||
function VisitAssignment(const Node: IAstNode): IAstNode;
|
||||
function VisitLambdaExpression(const Node: IAstNode): IAstNode;
|
||||
function VisitFunctionCall(const Node: IAstNode): IAstNode;
|
||||
function VisitBlockExpression(const Node: IAstNode): IAstNode;
|
||||
function VisitIfExpression(const Node: IAstNode): IAstNode;
|
||||
function VisitMemberAccess(const Node: IAstNode): IAstNode;
|
||||
function VisitIndexer(const Node: IAstNode): IAstNode;
|
||||
function VisitCreateSeries(const Node: IAstNode): IAstNode;
|
||||
function VisitSeriesLength(const Node: IAstNode): IAstNode;
|
||||
function VisitRecurNode(const Node: IAstNode): IAstNode;
|
||||
function VisitNop(const Node: IAstNode): IAstNode;
|
||||
function VisitRecordLiteral(const Node: IAstNode): IAstNode;
|
||||
function VisitConstant(const Node: IAstNode): IAstNode;
|
||||
function VisitKeyword(const Node: IAstNode): IAstNode;
|
||||
|
||||
// Pipe Support
|
||||
function VisitPipeInput(const Node: IPipeInputNode): IAstNode; override;
|
||||
function VisitPipe(const Node: IPipeNode): IAstNode; override;
|
||||
function VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||
function VisitPipe(const Node: IAstNode): IAstNode;
|
||||
|
||||
protected
|
||||
procedure SetupHandlers; override;
|
||||
|
||||
public
|
||||
constructor Create(const RootLayout: IScopeLayout; const RootScope: IExecutionScope; const ALog: ICompilerLog);
|
||||
@@ -215,6 +218,32 @@ begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TTypeChecker.SetupHandlers;
|
||||
begin
|
||||
inherited SetupHandlers; // Load Defaults
|
||||
|
||||
Register(akIdentifier, VisitIdentifier);
|
||||
Register(akVariableDeclaration, VisitVariableDeclaration);
|
||||
Register(akAssignment, VisitAssignment);
|
||||
Register(akLambdaExpression, VisitLambdaExpression);
|
||||
Register(akFunctionCall, VisitFunctionCall);
|
||||
Register(akBlockExpression, VisitBlockExpression);
|
||||
Register(akIfExpression, VisitIfExpression);
|
||||
Register(akMemberAccess, VisitMemberAccess);
|
||||
Register(akIndexer, VisitIndexer);
|
||||
Register(akCreateSeries, VisitCreateSeries);
|
||||
Register(akSeriesLength, VisitSeriesLength);
|
||||
Register(akRecur, VisitRecurNode);
|
||||
Register(akNop, VisitNop);
|
||||
Register(akRecordLiteral, VisitRecordLiteral);
|
||||
Register(akConstant, VisitConstant);
|
||||
Register(akKeyword, VisitKeyword);
|
||||
|
||||
// Pipe Support
|
||||
Register(akPipeInput, VisitPipeInput);
|
||||
Register(akPipe, VisitPipe);
|
||||
end;
|
||||
|
||||
class function TTypeChecker.CheckTypes(
|
||||
const RootNode: IAstNode;
|
||||
const Layout: IScopeLayout;
|
||||
@@ -261,24 +290,27 @@ end;
|
||||
|
||||
// --- Visits ---
|
||||
|
||||
function TTypeChecker.VisitConstant(const Node: IConstantNode): IAstNode;
|
||||
function TTypeChecker.VisitConstant(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
// Base implementation already returns Node, but here we explicitly confirm identity for clarity
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitKeyword(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitKeyword(const Node: IKeywordNode): IAstNode;
|
||||
begin
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
|
||||
function TTypeChecker.VisitIdentifier(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
I: IIdentifierNode;
|
||||
typ: IStaticType;
|
||||
adr: TResolvedAddress;
|
||||
identity: INamedIdentity;
|
||||
begin
|
||||
adr := Node.Address;
|
||||
identity := Node.Identity.AsNamed;
|
||||
I := Node.AsIdentifier;
|
||||
adr := I.Address;
|
||||
identity := I.Identity.AsNamed;
|
||||
|
||||
if adr.Kind = akUnresolved then
|
||||
begin
|
||||
@@ -290,40 +322,45 @@ begin
|
||||
Result := TAst.Identifier(identity, adr, typ);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode;
|
||||
function TTypeChecker.VisitRecurNode(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newArgs: TArray<IAstNode>;
|
||||
i: Integer;
|
||||
R: IRecurNode;
|
||||
args: IArgumentList;
|
||||
begin
|
||||
SetLength(newArgs, Node.Arguments.Count);
|
||||
for i := 0 to Node.Arguments.Count - 1 do
|
||||
newArgs[i] := Accept(Node.Arguments[i]);
|
||||
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.
|
||||
|
||||
var argList := TArgumentList.Create(newArgs, Node.Arguments.Identity);
|
||||
Result := TAst.Recur(Node.Identity, argList, TTypes.Void);
|
||||
// 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;
|
||||
|
||||
function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
||||
function TTypeChecker.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
V: IVariableDeclarationNode;
|
||||
initType: IStaticType;
|
||||
newInitializer, newIdent: IAstNode;
|
||||
adr: TResolvedAddress;
|
||||
identNode: IIdentifierNode;
|
||||
begin
|
||||
identNode := Node.Target.AsIdentifier;
|
||||
V := Node.AsVariableDeclaration;
|
||||
identNode := V.Target.AsIdentifier;
|
||||
adr := identNode.Address;
|
||||
initType := TTypes.Unknown;
|
||||
|
||||
if adr.Kind = akUnresolved then
|
||||
begin
|
||||
if Assigned(Node.Initializer) then
|
||||
Accept(Node.Initializer);
|
||||
if Assigned(V.Initializer) then
|
||||
Accept(V.Initializer);
|
||||
Result := Node;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if Assigned(Node.Initializer) then
|
||||
newInitializer := Accept(Node.Initializer)
|
||||
if Assigned(V.Initializer) then
|
||||
newInitializer := Accept(V.Initializer)
|
||||
else
|
||||
newInitializer := nil;
|
||||
|
||||
@@ -334,29 +371,31 @@ begin
|
||||
FCurrentContext.SetType(adr.SlotIndex, initType);
|
||||
|
||||
newIdent := TAst.Identifier(identNode.Identity.AsNamed, adr, initType);
|
||||
Result := TAst.VarDecl(Node.Identity, newIdent, newInitializer, initType, Node.IsBoxed);
|
||||
Result := TAst.VarDecl(Node.Identity, newIdent, newInitializer, initType, V.IsBoxed);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
|
||||
function TTypeChecker.VisitAssignment(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
A: IAssignmentNode;
|
||||
targetType, sourceType: IStaticType;
|
||||
newIdent, newValue: IAstNode;
|
||||
adr: TResolvedAddress;
|
||||
identNode: IIdentifierNode;
|
||||
begin
|
||||
identNode := Node.Target.AsIdentifier;
|
||||
newIdent := Accept(Node.Target);
|
||||
A := Node.AsAssignment;
|
||||
identNode := A.Target.AsIdentifier;
|
||||
newIdent := Accept(A.Target);
|
||||
targetType := newIdent.AsTypedNode.StaticType;
|
||||
adr := identNode.Address;
|
||||
|
||||
if adr.Kind = akUnresolved then
|
||||
begin
|
||||
Accept(Node.Value);
|
||||
Accept(A.Value);
|
||||
Result := Node;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
newValue := Accept(Node.Value);
|
||||
newValue := Accept(A.Value);
|
||||
sourceType := newValue.AsTypedNode.StaticType;
|
||||
|
||||
if (targetType.Kind <> stUnknown) and (sourceType.Kind <> stUnknown) then
|
||||
@@ -378,27 +417,28 @@ begin
|
||||
Result := TAst.Assign(Node.Identity, newIdent, newValue, targetType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
||||
function TTypeChecker.VisitBlockExpression(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newBlock: IBlockExpressionNode;
|
||||
blockType: IStaticType;
|
||||
newExprs: TArray<IAstNode>;
|
||||
i: Integer;
|
||||
exprs: IExpressionList;
|
||||
begin
|
||||
SetLength(newExprs, Node.Expressions.Count);
|
||||
for i := 0 to Node.Expressions.Count - 1 do
|
||||
newExprs[i] := Accept(Node.Expressions[i]);
|
||||
// Inherited logic transforms all expressions in the list
|
||||
newBlock := inherited VisitBlockExpression(Node).AsBlockExpression;
|
||||
exprs := newBlock.Expressions;
|
||||
|
||||
if Length(newExprs) > 0 then
|
||||
blockType := newExprs[High(newExprs)].AsTypedNode.StaticType
|
||||
if exprs.Count > 0 then
|
||||
blockType := exprs[exprs.Count - 1].AsTypedNode.StaticType
|
||||
else
|
||||
blockType := TTypes.Void;
|
||||
|
||||
var exprList := TExpressionList.Create(newExprs, Node.Expressions.Identity);
|
||||
Result := TAst.Block(Node.Identity, exprList, blockType);
|
||||
// Return new block with calculated type
|
||||
Result := TAst.Block(Node.Identity, exprs, blockType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
function TTypeChecker.VisitLambdaExpression(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
L: ILambdaExpressionNode;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
bodyType, methodType: IStaticType;
|
||||
@@ -409,8 +449,10 @@ var
|
||||
paramIdent: IIdentifierNode;
|
||||
injectedType: IStaticType;
|
||||
begin
|
||||
L := Node.AsLambdaExpression;
|
||||
|
||||
// 1. Resolve Upvalue Types
|
||||
var upvalueAddrs := Node.Upvalues;
|
||||
var upvalueAddrs := L.Upvalues;
|
||||
SetLength(upvalueTypes, Length(upvalueAddrs));
|
||||
|
||||
for i := 0 to High(upvalueAddrs) do
|
||||
@@ -425,14 +467,14 @@ begin
|
||||
end;
|
||||
|
||||
// 2. Enter New Scope
|
||||
FCurrentContext := TTypeContext.Create(FCurrentContext, Node.Layout, upvalueTypes, nil);
|
||||
FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil);
|
||||
try
|
||||
SetLength(newParams, Node.Parameters.Count);
|
||||
SetLength(paramTypes, Node.Parameters.Count);
|
||||
SetLength(newParams, L.Parameters.Count);
|
||||
SetLength(paramTypes, L.Parameters.Count);
|
||||
|
||||
for i := 0 to Node.Parameters.Count - 1 do
|
||||
for i := 0 to L.Parameters.Count - 1 do
|
||||
begin
|
||||
paramIdent := Node.Parameters[i];
|
||||
paramIdent := L.Parameters[i];
|
||||
|
||||
// Check if there is already a type assigned (e.g. injected by Pipe Visitor)
|
||||
injectedType := paramIdent.AsTypedNode.StaticType;
|
||||
@@ -448,52 +490,45 @@ begin
|
||||
newParams[i] := TAst.Identifier(paramIdent.Identity.AsNamed, paramIdent.Address, paramTypes[i]);
|
||||
end;
|
||||
|
||||
newBody := Accept(Node.Body);
|
||||
newBody := Accept(L.Body);
|
||||
|
||||
bodyType := newBody.AsTypedNode.StaticType;
|
||||
methodType := TTypes.CreateMethod(paramTypes, bodyType);
|
||||
|
||||
finalDescriptor := TScope.CreateDescriptor(Node.Layout, FCurrentContext.Types);
|
||||
finalDescriptor := TScope.CreateDescriptor(L.Layout, FCurrentContext.Types);
|
||||
finally
|
||||
var temp := FCurrentContext;
|
||||
FCurrentContext := FCurrentContext.FParent;
|
||||
temp.Free;
|
||||
end;
|
||||
|
||||
var paramList := TParameterList.Create(newParams, Node.Parameters.Identity);
|
||||
var paramList := TParameterList.Create(newParams, L.Parameters.Identity);
|
||||
Result :=
|
||||
TAst.LambdaExpr(
|
||||
Node.Identity,
|
||||
paramList,
|
||||
newBody,
|
||||
Node.Layout,
|
||||
finalDescriptor,
|
||||
Node.Upvalues,
|
||||
Node.HasNestedLambdas,
|
||||
Node.IsPure,
|
||||
methodType
|
||||
);
|
||||
TAst.LambdaExpr(Node.Identity, paramList, newBody, L.Layout, finalDescriptor, L.Upvalues, L.HasNestedLambdas, L.IsPure, methodType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
function TTypeChecker.VisitFunctionCall(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newCall: IFunctionCallNode;
|
||||
calleeType, retType: IStaticType;
|
||||
i, j: Integer;
|
||||
newCallee: IAstNode;
|
||||
newArgs: TArray<IAstNode>;
|
||||
argTypes: TArray<IStaticType>;
|
||||
hasUnknownArgs: Boolean;
|
||||
bestSig: IMethodSignature;
|
||||
match: Boolean;
|
||||
begin
|
||||
newCallee := Accept(Node.Callee);
|
||||
SetLength(newArgs, Node.Arguments.Count);
|
||||
SetLength(argTypes, Node.Arguments.Count);
|
||||
// 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;
|
||||
|
||||
SetLength(argTypes, newArgs.Count);
|
||||
hasUnknownArgs := False;
|
||||
|
||||
for i := 0 to Node.Arguments.Count - 1 do
|
||||
for i := 0 to newArgs.Count - 1 do
|
||||
begin
|
||||
newArgs[i] := Accept(Node.Arguments[i]);
|
||||
argTypes[i] := newArgs[i].AsTypedNode.StaticType;
|
||||
if argTypes[i].Kind = stUnknown then
|
||||
hasUnknownArgs := True;
|
||||
@@ -535,36 +570,35 @@ begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(Format('Cannot invoke type %s as a function.', [calleeType.ToString]), Node);
|
||||
|
||||
var argList := TArgumentList.Create(newArgs, Node.Arguments.Identity);
|
||||
Result := TAst.FunctionCall(Node.Identity, newCallee, argList, retType, Node.IsTailCall, nil, False);
|
||||
// Return new node with Types
|
||||
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgs, retType, newCall.IsTailCall, nil, False);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
|
||||
function TTypeChecker.VisitIfExpression(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newCond, newThen, newElse: IAstNode;
|
||||
newIf: IIfExpressionNode;
|
||||
resType: IStaticType;
|
||||
begin
|
||||
newCond := Accept(Node.Condition);
|
||||
newThen := Accept(Node.ThenBranch);
|
||||
newElse := Accept(Node.ElseBranch);
|
||||
newIf := inherited VisitIfExpression(Node).AsIfExpression;
|
||||
|
||||
// Simple promotion logic
|
||||
if (newElse <> nil) then
|
||||
resType := TTypeRules.Promote(newThen.AsTypedNode.StaticType, newElse.AsTypedNode.StaticType)
|
||||
if (newIf.ElseBranch <> nil) then
|
||||
resType := TTypeRules.Promote(newIf.ThenBranch.AsTypedNode.StaticType, newIf.ElseBranch.AsTypedNode.StaticType)
|
||||
else
|
||||
resType := TTypes.MakeOptional(newThen.AsTypedNode.StaticType);
|
||||
resType := TTypes.MakeOptional(newIf.ThenBranch.AsTypedNode.StaticType);
|
||||
|
||||
Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, resType);
|
||||
Result := TAst.IfExpr(Node.Identity, newIf.Condition, newIf.ThenBranch, newIf.ElseBranch, resType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitIndexer(const Node: IIndexerNode): IAstNode;
|
||||
function TTypeChecker.VisitIndexer(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
I: IIndexerNode;
|
||||
newBase, newIndex: IAstNode;
|
||||
baseType, elemType: IStaticType;
|
||||
isOpt: Boolean;
|
||||
begin
|
||||
newBase := Accept(Node.Base);
|
||||
newIndex := Accept(Node.Index);
|
||||
I := Node.AsIndexer;
|
||||
newBase := Accept(I.Base);
|
||||
newIndex := Accept(I.Index);
|
||||
baseType := PrepareBaseType(newBase, isOpt);
|
||||
|
||||
elemType := TTypes.Unknown;
|
||||
@@ -577,20 +611,22 @@ begin
|
||||
Result := TAst.Indexer(Node.Identity, newBase, newIndex, elemType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
|
||||
function TTypeChecker.VisitMemberAccess(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
M: IMemberAccessNode;
|
||||
newBase: IAstNode;
|
||||
baseType, resType: IStaticType;
|
||||
idx: Integer;
|
||||
isOpt: Boolean;
|
||||
begin
|
||||
newBase := Accept(Node.Base);
|
||||
M := Node.AsMemberAccess;
|
||||
newBase := Accept(M.Base);
|
||||
baseType := PrepareBaseType(newBase, isOpt);
|
||||
resType := TTypes.Unknown;
|
||||
|
||||
if (baseType.Kind = stRecord) or (baseType.Kind = stRecordSeries) then
|
||||
begin
|
||||
idx := baseType.Definition.IndexOf(Node.Member.Value);
|
||||
idx := baseType.Definition.IndexOf(M.Member.Value);
|
||||
if idx >= 0 then
|
||||
begin
|
||||
var fieldType := TTypes.FromScalarKind(baseType.Definition.Items[idx].Value);
|
||||
@@ -604,45 +640,40 @@ begin
|
||||
end;
|
||||
|
||||
resType := ApplyOptionality(resType, isOpt);
|
||||
Result := TAst.MemberAccess(Node.Identity, newBase, Node.Member, resType);
|
||||
Result := TAst.MemberAccess(Node.Identity, newBase, M.Member, resType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
|
||||
function TTypeChecker.VisitRecordLiteral(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
R: IRecordLiteralNode;
|
||||
i: Integer;
|
||||
newFields: TArray<IRecordFieldNode>;
|
||||
fieldTypes: TArray<TPair<IKeyword, IStaticType>>;
|
||||
scalarFieldTypes: TArray<TPair<IKeyword, TScalar.TKind>>;
|
||||
isScalar: Boolean;
|
||||
valType: IStaticType;
|
||||
key: IKeyword;
|
||||
visitedValue: IAstNode;
|
||||
newFieldList: IRecordFieldList;
|
||||
begin
|
||||
SetLength(newFields, Node.Fields.Count);
|
||||
SetLength(fieldTypes, Node.Fields.Count);
|
||||
SetLength(scalarFieldTypes, Node.Fields.Count);
|
||||
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);
|
||||
isScalar := True;
|
||||
|
||||
for i := 0 to Node.Fields.Count - 1 do
|
||||
for i := 0 to count - 1 do
|
||||
begin
|
||||
var oldField := Node.Fields[i];
|
||||
visitedValue := Accept(oldField.Value);
|
||||
|
||||
// Keys are guaranteed to be keywords by parser
|
||||
key := oldField.Key.Value;
|
||||
|
||||
// Recreate the field node with the typed value
|
||||
newFields[i] := TAst.RecordField(oldField.Identity, oldField.Key, visitedValue);
|
||||
|
||||
// Analyze Type
|
||||
valType := visitedValue.AsTypedNode.StaticType;
|
||||
var field := newFieldList[i];
|
||||
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;
|
||||
// Map StaticType Kind to Scalar Kind
|
||||
case valType.Kind of
|
||||
stOrdinal: kind := TScalar.TKind.Ordinal;
|
||||
stFloat: kind := TScalar.TKind.Float;
|
||||
@@ -650,7 +681,7 @@ begin
|
||||
stDateTime: kind := TScalar.TKind.DateTime;
|
||||
stKeyword: kind := TScalar.TKind.Keyword;
|
||||
else
|
||||
kind := TScalar.TKind.Ordinal; // Should not happen given check above
|
||||
kind := TScalar.TKind.Ordinal;
|
||||
end;
|
||||
scalarFieldTypes[i] := TPair<IKeyword, TScalar.TKind>.Create(key, kind);
|
||||
end
|
||||
@@ -667,7 +698,7 @@ begin
|
||||
var genericDef: IGenericRecordDefinition := nil;
|
||||
var resultType: IStaticType;
|
||||
|
||||
if isScalar and (Length(newFields) > 0) then
|
||||
if isScalar and (count > 0) then
|
||||
begin
|
||||
scalarDef := TKeywordMappingRegistry<TScalar.TKind>.Intern(scalarFieldTypes);
|
||||
resultType := TTypes.CreateRecord(scalarDef);
|
||||
@@ -678,16 +709,17 @@ begin
|
||||
resultType := TTypes.CreateGenericRecord(genericDef);
|
||||
end;
|
||||
|
||||
newFieldList := TRecordFieldList.Create(newFields, Node.Fields.Identity);
|
||||
Result := TAst.RecordLiteral(Node.Identity, newFieldList, scalarDef, genericDef, resultType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
|
||||
function TTypeChecker.VisitCreateSeries(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
C: ICreateSeriesNode;
|
||||
elemType: IStaticType;
|
||||
def: string;
|
||||
begin
|
||||
def := Node.Definition;
|
||||
C := Node.AsCreateSeries;
|
||||
def := C.Definition;
|
||||
// Simple heuristic for type
|
||||
if def.StartsWith('[') then
|
||||
elemType := TTypes.CreateRecord(nil) // Placeholder, normally parses JSON
|
||||
@@ -697,12 +729,12 @@ begin
|
||||
Result := TAst.CreateSeries(Node.Identity.AsDefinition, TTypes.CreateSeries(elemType));
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
|
||||
function TTypeChecker.VisitSeriesLength(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
Result := TAst.SeriesLength(Node.Identity, Accept(Node.Series).AsIdentifier, TTypes.Ordinal);
|
||||
Result := TAst.SeriesLength(Node.Identity, Accept(Node.AsSeriesLength.Series).AsIdentifier, TTypes.Ordinal);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitNop(const Node: INopNode): IAstNode;
|
||||
function TTypeChecker.VisitNop(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
Result := TAst.Nop(Node.Identity, TTypes.Void);
|
||||
end;
|
||||
@@ -711,14 +743,15 @@ end;
|
||||
// PIPE IMPLEMENTATION (Type Checking)
|
||||
// =============================================================================
|
||||
|
||||
function TTypeChecker.VisitPipeInput(const Node: IPipeInputNode): IAstNode;
|
||||
function TTypeChecker.VisitPipeInput(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeInputNode;
|
||||
newSource: IIdentifierNode;
|
||||
sourceType: IStaticType;
|
||||
newSelectors: TArray<IKeywordNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
newSource := Accept(Node.StreamSource).AsIdentifier;
|
||||
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
|
||||
@@ -736,7 +769,7 @@ begin
|
||||
begin
|
||||
// 2. Verify Selectors exist in Record Definition
|
||||
var def := sourceType.Definition;
|
||||
for var sel in Node.Selectors do
|
||||
for var sel in P.Selectors do
|
||||
begin
|
||||
if def.IndexOf(sel.Value) < 0 then
|
||||
begin
|
||||
@@ -747,16 +780,13 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
// Reconstruct list simply to pass through
|
||||
SetLength(newSelectors, Node.Selectors.Count);
|
||||
for i := 0 to Node.Selectors.Count - 1 do
|
||||
newSelectors[i] := Node.Selectors[i];
|
||||
|
||||
Result := TAst.PipeInput(newSource, TAst.PipeSelectorList(newSelectors, Node.Selectors.Identity.Location), Node.Identity.Location);
|
||||
// Reuse selectors (they are just keywords, no type checking needed)
|
||||
Result := TAst.PipeInput(newSource, P.Selectors, Node.Identity.Location);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitPipe(const Node: IPipeNode): IAstNode;
|
||||
function TTypeChecker.VisitPipe(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
P: IPipeNode;
|
||||
i, k: Integer;
|
||||
inputNode: IPipeInputNode;
|
||||
newInputs: TArray<IPipeInputNode>;
|
||||
@@ -766,13 +796,15 @@ var
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
inferredType: IStaticType;
|
||||
begin
|
||||
SetLength(newInputs, Node.Inputs.Count);
|
||||
P := Node.AsPipe;
|
||||
SetLength(newInputs, P.Inputs.Count);
|
||||
paramTypes := TList<IStaticType>.Create;
|
||||
try
|
||||
// 1. Visit Inputs and collect types for Lambda parameters
|
||||
for i := 0 to Node.Inputs.Count - 1 do
|
||||
for i := 0 to P.Inputs.Count - 1 do
|
||||
begin
|
||||
inputNode := Accept(Node.Inputs[i]).AsPipeInput;
|
||||
// Recurse on inputs to resolve their sources
|
||||
inputNode := Accept(P.Inputs[i]).AsPipeInput;
|
||||
newInputs[i] := inputNode;
|
||||
|
||||
streamType := inputNode.StreamSource.AsTypedNode.StaticType;
|
||||
@@ -803,7 +835,7 @@ begin
|
||||
end;
|
||||
|
||||
// 2. Prepare Lambda with Inferred Types
|
||||
lambda := Node.Transformation;
|
||||
lambda := P.Transformation;
|
||||
|
||||
if lambda.Parameters.Count <> paramTypes.Count then
|
||||
begin
|
||||
@@ -871,7 +903,7 @@ begin
|
||||
pipeType := TTypes.Unknown;
|
||||
end;
|
||||
|
||||
Result := TAst.Pipe(Node.Identity, TPipeInputList.Create(newInputs, Node.Inputs.Identity), typedLambda, pipeType);
|
||||
Result := TAst.Pipe(Node.Identity, TPipeInputList.Create(newInputs, P.Inputs.Identity), typedLambda, pipeType);
|
||||
finally
|
||||
paramTypes.Free;
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user