AST list refactoring

This commit is contained in:
Michael Schimmel
2026-01-04 22:41:44 +01:00
parent 2a7e6626ad
commit 242ec9a56e
27 changed files with 815 additions and 1076 deletions
+58 -24
View File
@@ -318,7 +318,7 @@ var
finalType: IStaticType;
begin
T := Node.AsTuple;
var count := Length(T.Elements);
var count := T.Count;
SetLength(newElements, count);
SetLength(elementTypes, count);
@@ -327,8 +327,13 @@ begin
// 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;
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
elementTypes[i] := TTypes.Unknown;
end;
// 2. Inference Logic: Tuple vs. Vector vs. Matrix
@@ -343,6 +348,8 @@ begin
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
@@ -352,7 +359,7 @@ begin
end;
end;
if isHomogeneous then
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).
@@ -418,10 +425,10 @@ end;
function TTypeChecker.VisitRecurNode(const Node: IAstNode): IAstNode;
var
R: IRecurNode;
args: IArgumentList;
args: ITupleNode;
begin
R := Node.AsRecur;
args := Accept(R.Arguments).AsArgumentList;
args := Accept(R.Arguments).AsTuple;
Result := TAst.Recur(Node.Identity, args, TTypes.Void);
end;
@@ -508,10 +515,12 @@ function TTypeChecker.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
newBlock: IBlockExpressionNode;
blockType: IStaticType;
exprs: IExpressionList;
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?
@@ -527,13 +536,13 @@ 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
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[exprs.Count - 1].AsTypedNode.StaticType;
blockType := exprs.Items[exprs.Count - 1].AsTypedNode.StaticType;
end
else
begin
@@ -555,6 +564,8 @@ var
finalDescriptor: IScopeDescriptor;
paramIdent: IIdentifierNode;
injectedType: IStaticType;
paramsTuple: ITupleNode;
paramsAsNodes: TArray<IAstNode>;
begin
L := Node.AsLambdaExpression;
@@ -574,12 +585,13 @@ begin
FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil);
try
SetLength(newParams, L.Parameters.Count);
SetLength(paramTypes, L.Parameters.Count);
paramsTuple := L.Parameters;
SetLength(newParams, paramsTuple.Count);
SetLength(paramTypes, paramsTuple.Count);
for i := 0 to L.Parameters.Count - 1 do
for i := 0 to paramsTuple.Count - 1 do
begin
paramIdent := L.Parameters[i];
paramIdent := paramsTuple.Items[i].AsIdentifier;
injectedType := paramIdent.AsTypedNode.StaticType;
if injectedType.Kind = stUnknown then
@@ -605,7 +617,12 @@ begin
temp.Free;
end;
var paramList := TParameterList.Create(newParams, L.Parameters.Identity);
SetLength(paramsAsNodes, Length(newParams));
for i := 0 to High(newParams) do
paramsAsNodes[i] := newParams[i];
var paramList := TAst.Tuple(L.Parameters.Identity, paramsAsNodes);
Result :=
TAst.LambdaExpr(Node.Identity, paramList, newBody, L.Layout, finalDescriptor, L.Upvalues, L.HasNestedLambdas, L.IsPure, methodType);
end;
@@ -619,17 +636,18 @@ var
hasUnknownArgs: Boolean;
bestSig: IMethodSignature;
match: Boolean;
newArgs: ITupleNode;
begin
newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
var newCallee := newCall.Callee;
var newArgs := newCall.Arguments;
newArgs := newCall.Arguments;
SetLength(argTypes, newArgs.Count);
hasUnknownArgs := False;
for i := 0 to newArgs.Count - 1 do
begin
argTypes[i] := newArgs[i].AsTypedNode.StaticType;
argTypes[i] := newArgs.Items[i].AsTypedNode.StaticType;
if argTypes[i].Kind = stUnknown then
hasUnknownArgs := True;
end;
@@ -707,7 +725,7 @@ begin
elemType := TTypes.CreateRecord(baseType.AsRecord.Definition)
else if baseType.Kind = stTuple then
begin
// NEW: Tuple Indexing (requires constant index for strong typing!)
// 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;
@@ -762,19 +780,29 @@ var
isScalar: Boolean;
valType: IStaticType;
key: IKeyword;
newFieldList: IRecordFieldList;
newFields: ITupleNode;
begin
R := Node.AsRecordLiteral;
newFieldList := inherited VisitRecordFieldList(R.Fields).AsRecordFieldList;
var count := newFieldList.Count;
// 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;
SetLength(fieldTypes, count);
SetLength(scalarFieldTypes, count);
isScalar := True;
for i := 0 to count - 1 do
begin
var field := newFieldList[i];
// 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;
@@ -815,7 +843,7 @@ begin
resultType := TTypes.CreateGenericRecord(genericDef);
end;
Result := TAst.RecordLiteral(Node.Identity, newFieldList, scalarDef, genericDef, resultType);
Result := TAst.RecordLiteral(Node.Identity, newFields, scalarDef, genericDef, resultType);
end;
function TTypeChecker.VisitCreateSeries(const Node: IAstNode): IAstNode;
@@ -895,6 +923,7 @@ var
paramTypes: TList<IStaticType>;
lambda: ILambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newParamsAsNodes: TArray<IAstNode>;
inferredType: IStaticType;
begin
P := Node.AsPipe;
@@ -947,7 +976,7 @@ begin
SetLength(newParams, lambda.Parameters.Count);
for k := 0 to lambda.Parameters.Count - 1 do
begin
var oldP := lambda.Parameters[k];
var oldP := lambda.Parameters.Items[k].AsIdentifier;
var typeToInject :=
if k < paramTypes.Count then paramTypes[k]
else TTypes.Unknown;
@@ -955,10 +984,15 @@ begin
newParams[k] := TAst.Identifier(oldP.Identity.AsNamed, oldP.Address, typeToInject);
end;
// Convert for Tuple Factory
SetLength(newParamsAsNodes, Length(newParams));
for k := 0 to High(newParams) do
newParamsAsNodes[k] := newParams[k];
var preTypedLambda :=
TAst.LambdaExpr(
lambda.Identity,
TParameterList.Create(newParams, lambda.Parameters.Identity),
TAst.Tuple(lambda.Parameters.Identity, newParamsAsNodes),
lambda.Body,
lambda.Layout,
lambda.Descriptor,