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
+19 -55
View File
@@ -24,11 +24,7 @@ type
function VisitVariableDeclaration(const Node: IAstNode): Boolean;
function VisitSeriesLength(const Node: IAstNode): Boolean;
// --- List Visitors (Aggregation Logic: All must be pure) ---
function VisitParameterList(const Node: IAstNode): Boolean;
function VisitArgumentList(const Node: IAstNode): Boolean;
function VisitExpressionList(const Node: IAstNode): Boolean;
function VisitRecordFieldList(const Node: IAstNode): Boolean;
// --- Elements ---
function VisitRecordField(const Node: IAstNode): Boolean;
// --- Critical Checks ---
@@ -55,9 +51,10 @@ type
function VisitMacroExpansionNode(const Node: IAstNode): Boolean;
function VisitNop(const Node: IAstNode): Boolean;
// Unification: Tuple (for [vectors], arguments, parameters, fields)
function VisitTuple(const Node: IAstNode): Boolean;
// Pipe Support (Structural Check)
// Pipe Support
function VisitPipeInput(const Node: IAstNode): Boolean;
function VisitPipeSelectorList(const Node: IAstNode): Boolean;
function VisitPipeInputList(const Node: IAstNode): Boolean;
@@ -105,11 +102,7 @@ begin
Register(akIdentifier, VisitIdentifier);
Register(akKeyword, VisitKeyword);
// Lists
Register(akParameterList, VisitParameterList);
Register(akArgumentList, VisitArgumentList);
Register(akExpressionList, VisitExpressionList);
Register(akRecordFieldList, VisitRecordFieldList);
// Elements
Register(akRecordField, VisitRecordField);
// Structural
@@ -134,6 +127,7 @@ begin
Register(akRecur, VisitRecurNode);
Register(akNop, VisitNop);
// Unified List Type
Register(akTuple, VisitTuple);
// Pipes
@@ -143,35 +137,20 @@ begin
Register(akPipe, VisitPipe);
end;
// --- List Visitors ---
// --- List / Container Visitors ---
function TPurityAnalyzer.VisitParameterList(const Node: IAstNode): Boolean;
function TPurityAnalyzer.VisitTuple(const Node: IAstNode): Boolean;
var
i: Integer;
T: ITupleNode;
begin
// Declarations are pure
Result := True;
end;
function TPurityAnalyzer.VisitArgumentList(const Node: IAstNode): Boolean;
begin
for var item in Node.AsArgumentList do
if not IsNodePure(item) then
exit(False);
Result := True;
end;
function TPurityAnalyzer.VisitExpressionList(const Node: IAstNode): Boolean;
begin
for var item in Node.AsExpressionList do
if not IsNodePure(item) then
exit(False);
Result := True;
end;
function TPurityAnalyzer.VisitRecordFieldList(const Node: IAstNode): Boolean;
begin
for var item in Node.AsRecordFieldList do
if not IsNodePure(item) then
T := Node.AsTuple;
// A tuple is pure if ALL its elements are pure.
for i := 0 to T.Count - 1 do
begin
if not IsNodePure(T.Items[i]) then
exit(False);
end;
Result := True;
end;
@@ -207,8 +186,6 @@ begin
// Accessing Parent/Upvalues (ScopeDepth > 0) makes the function state-dependent (closure state),
// effectively impure regarding referential transparency across different closure instances,
// unless we could prove the upvalue is constant (which we don't track yet).
// Note: Parameters are also ScopeDepth=0 in the Binder logic.
var I := Node.AsIdentifier;
Result := (I.Address.Kind = akLocalOrParent) and (I.Address.ScopeDepth = 0);
end;
@@ -222,7 +199,7 @@ begin
if not C.IsTargetPure then
exit(False);
// 2. All arguments must be pure expressions.
// 2. All arguments (Tuple) must be pure expressions.
Result := IsNodePure(C.Arguments);
end;
@@ -257,7 +234,7 @@ end;
function TPurityAnalyzer.VisitBlockExpression(const Node: IAstNode): Boolean;
begin
// Delegate to ExpressionList
// Delegate to Expression Tuple
Result := IsNodePure(Node.AsBlockExpression.Expressions);
end;
@@ -270,7 +247,7 @@ end;
function TPurityAnalyzer.VisitRecordLiteral(const Node: IAstNode): Boolean;
begin
// Delegate to RecordFieldList
// Delegate to Fields Tuple
Result := IsNodePure(Node.AsRecordLiteral.Fields);
end;
@@ -382,17 +359,4 @@ 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.
+5 -2
View File
@@ -195,6 +195,7 @@ function TUpvalueAnalyzer.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
i: Integer;
paramTuple: ITupleNode;
begin
L := Node.AsLambdaExpression;
@@ -204,9 +205,11 @@ begin
// 2. Register parameters (they mask outer variables)
// We pass 'nil' as the node because we currently don't box parameters,
// but we must ensure Resolve() finds them so we don't accidentally box a shadowed variable.
for i := 0 to L.Parameters.Count - 1 do
paramTuple := L.Parameters;
for i := 0 to paramTuple.Count - 1 do
begin
FCurrentScope.Define(L.Parameters[i].Name, nil);
if paramTuple.Items[i].Kind = akIdentifier then
FCurrentScope.Define(paramTuple.Items[i].AsIdentifier.Name, nil);
end;
// 3. Visit Body
+35 -9
View File
@@ -332,6 +332,7 @@ var
parentBuilder: IScopeBuilder;
paramType: IStaticType;
newParams: TArray<IIdentifierNode>;
newParamsAsNodes: TArray<IAstNode>;
newBody: IAstNode;
finalLayout: IScopeLayout;
i, slot: Integer;
@@ -342,7 +343,8 @@ var
startCount: Integer;
hasNested: Boolean;
paramIdentity: INamedIdentity;
paramList: IParameterList;
paramsTuple: ITupleNode;
paramNode: IAstNode;
begin
L := Node.AsLambdaExpression;
startCount := FLambdaCounter;
@@ -357,13 +359,23 @@ begin
try
FCurrentBuilder.Define('<self>');
SetLength(newParams, L.Parameters.Count);
paramsTuple := L.Parameters;
SetLength(newParams, paramsTuple.Count);
for i := 0 to L.Parameters.Count - 1 do
for i := 0 to paramsTuple.Count - 1 do
begin
var paramNode := L.Parameters[i];
var paramName := paramNode.Name;
paramIdentity := paramNode.Identity.AsNamed;
paramNode := paramsTuple.Items[i];
if paramNode.Kind <> akIdentifier then
begin
if Assigned(FLog) then
FLog.AddError('Parameter must be an identifier.', paramNode);
continue;
end;
var paramIdent := paramNode.AsIdentifier;
var paramName := paramIdent.Name;
paramIdentity := paramIdent.Identity.AsNamed;
if FCurrentBuilder.FindSlot(paramName) >= 0 then
begin
@@ -415,9 +427,23 @@ begin
end;
hasNested := FLambdaCounter > (startCount + 1);
paramList := TParameterList.Create(newParams, L.Parameters.Identity);
Result := TAst.LambdaExpr(Node.Identity, paramList, newBody, finalLayout, nil, upvaluesList, hasNested, L.IsPure);
// Convert TArray<IIdentifierNode> -> TArray<IAstNode> for Tuple factory
SetLength(newParamsAsNodes, Length(newParams));
for i := 0 to High(newParams) do
newParamsAsNodes[i] := newParams[i];
Result :=
TAst.LambdaExpr(
Node.Identity,
TAst.Tuple(L.Parameters.Identity, newParamsAsNodes),
newBody,
finalLayout,
nil,
upvaluesList,
hasNested,
L.IsPure
);
end;
function TAstBinder.VisitFunctionCall(const Node: IAstNode): IAstNode;
@@ -435,7 +461,7 @@ begin
end
else
begin
var memberAccess := TAst.MemberAccess(Node.Identity, Accept(C.Arguments[0]), C.Callee.AsKeyword);
var memberAccess := TAst.MemberAccess(Node.Identity, Accept(C.Arguments.Items[0]), C.Callee.AsKeyword);
Result := memberAccess;
exit;
end;
+53 -25
View File
@@ -32,7 +32,7 @@ type
class var
FGensymCounter: Int64;
function TransformAndSpliceNodes(const ANodes: INodeList<IAstNode>): TArray<IAstNode>;
function TransformAndSpliceNodes(const ANodes: ITupleNode): TArray<IAstNode>;
function Gensym(const ABaseName: string): string;
// Custom Logic Handlers (IAstNode signature)
@@ -186,17 +186,19 @@ begin
end;
end;
function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: INodeList<IAstNode>): TArray<IAstNode>;
function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: ITupleNode): TArray<IAstNode>;
var
newList: TList<IAstNode>;
nodeToSplice: IAstNode;
transformedNode: IAstNode;
node: IAstNode;
i: Integer;
begin
newList := TList<IAstNode>.Create;
try
for node in ANodes do
for i := 0 to ANodes.Count - 1 do
begin
node := ANodes.Items[i];
if node.Kind = akUnquoteSplicing then
begin
var spliceExpr := node.AsUnquoteSplicing.Expression;
@@ -213,17 +215,24 @@ begin
if (not evaluatedSpliceValue.IsVoid) and (evaluatedSpliceValue.Kind = vkInterface) then
begin
nodeToSplice := evaluatedSpliceValue.AsIntf<IAstNode>;
if nodeToSplice.Kind = akExpressionList then
for var subItem in nodeToSplice.AsExpressionList do
newList.Add(subItem)
else if nodeToSplice.Kind = akArgumentList then
for var subItem in nodeToSplice.AsArgumentList do
newList.Add(subItem)
// Unification: Lists are now Tuples.
if nodeToSplice.Kind = akTuple then
begin
var tuple := nodeToSplice.AsTuple;
for var k := 0 to tuple.Count - 1 do
newList.Add(tuple.Items[k]);
end
else if nodeToSplice.Kind = akBlockExpression then
for var subItem in nodeToSplice.AsBlockExpression.Expressions do
newList.Add(subItem)
begin
var blkTuple := nodeToSplice.AsBlockExpression.Expressions;
for var k := 0 to blkTuple.Count - 1 do
newList.Add(blkTuple.Items[k]);
end
else
begin
// Single node splicing
newList.Add(nodeToSplice);
end;
end
else if (not evaluatedSpliceValue.IsVoid) then
newList.Add(TAst.Constant(evaluatedSpliceValue, node.Identity.Location));
@@ -277,22 +286,26 @@ end;
function TExpansionVisitor.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newParams: TArray<IAstNode>;
newBody: IAstNode;
newName: string;
i: Integer;
paramsTuple: ITupleNode;
begin
L := Node.AsLambdaExpression;
SetLength(newParams, L.Parameters.Count);
for i := 0 to L.Parameters.Count - 1 do
paramsTuple := L.Parameters;
SetLength(newParams, paramsTuple.Count);
for i := 0 to paramsTuple.Count - 1 do
begin
var param := L.Parameters[i];
var param := paramsTuple.Items[i].AsIdentifier; // Binder ensures params are Identifiers
newName := Gensym(param.Name);
newParams[i] := TAst.Identifier(newName, param.Identity.Location);
end;
newBody := Accept(L.Body);
Result := TAst.LambdaExpr(Node.Identity, TParameterList.Create(newParams, L.Parameters.Identity), newBody);
Result := TAst.LambdaExpr(Node.Identity, TAst.Tuple(L.Parameters.Identity, newParams), newBody);
end;
function TExpansionVisitor.VisitUnquote(const Node: IAstNode): IAstNode;
@@ -335,7 +348,7 @@ end;
function TExpansionVisitor.VisitUnquoteSplicing(const Node: IAstNode): IAstNode;
begin
raise EMacroException.Create('Unquote-splicing (`~@`) can only appear inside a list form.');
raise EMacroException.Create('Unquote-splicing (`~@`) can only appear inside a list/tuple form.');
end;
function TExpansionVisitor.VisitFunctionCall(const Node: IAstNode): IAstNode;
@@ -343,27 +356,38 @@ var
C: IFunctionCallNode;
newArgs: TArray<IAstNode>;
transformedCallee: IAstNode;
argsTuple: ITupleNode;
begin
C := Node.AsFunctionCall;
transformedCallee := Self.Accept(C.Callee);
// Arguments is a Tuple, so we can splice into it
newArgs := TransformAndSpliceNodes(C.Arguments);
Result := TAst.FunctionCall(Node.Identity, transformedCallee, TArgumentList.Create(newArgs, C.Arguments.Identity));
// Wrap result array in a TupleNode before creating FunctionCall
argsTuple := TAst.Tuple(C.Arguments.Identity, newArgs);
Result := TAst.FunctionCall(Node.Identity, transformedCallee, argsTuple);
end;
function TExpansionVisitor.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
B: IBlockExpressionNode;
newExprs: TArray<IAstNode>;
exprsTuple: ITupleNode;
begin
B := Node.AsBlockExpression;
// Expressions is a Tuple
newExprs := TransformAndSpliceNodes(B.Expressions);
Result := TAst.Block(Node.Identity, TExpressionList.Create(newExprs, B.Expressions.Identity));
exprsTuple := TAst.Tuple(B.Expressions.Identity, newExprs);
Result := TAst.Block(Node.Identity, exprsTuple);
end;
function TExpansionVisitor.VisitRecordLiteral(const Node: IAstNode): IAstNode;
begin
// Delegate to standard transformer to process fields recursively
// This calls inherited VisitRecordLiteral(IAstNode)
// This calls inherited VisitRecordLiteral(IAstNode) which visits the Fields Tuple
Result := inherited VisitRecordLiteral(Node);
end;
@@ -473,13 +497,17 @@ begin
if macroDef <> nil then
begin
var expansionScope := TScope.CreateScope(FInitialScope, nil, nil);
var params := macroDef.Parameters;
var paramsTuple := macroDef.Parameters;
var argsTuple := C.Arguments;
if C.Arguments.Count <> params.Count then
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, params.Count]);
if argsTuple.Count <> paramsTuple.Count then
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, paramsTuple.Count]);
for i := 0 to params.Count - 1 do
expansionScope.Define(params[i].Name, TDataValue.FromIntf<IAstNode>(C.Arguments[i]));
for i := 0 to paramsTuple.Count - 1 do
begin
var paramName := paramsTuple.Items[i].AsIdentifier.Name;
expansionScope.Define(paramName, TDataValue.FromIntf<IAstNode>(argsTuple.Items[i]));
end;
var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, FMacroEvaluator);
var macroNode := TAst.MacroExpansionNode(Node.Identity, C, expandedBody);
+8 -8
View File
@@ -133,7 +133,7 @@ var
C: IFunctionCallNode;
newCall: IFunctionCallNode;
newCallee: IAstNode;
newArgsList: IArgumentList;
newArgs: ITupleNode;
i: Integer;
calleeIdent: IIdentifierNode;
argTypes: TArray<IStaticType>;
@@ -149,7 +149,7 @@ begin
// inherited VisitFunctionCall returns an IAstNode (which is a new IFunctionCallNode if changed)
newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
newCallee := newCall.Callee;
newArgsList := newCall.Arguments;
newArgs := newCall.Arguments;
// 2. Check if this call is a candidate for specialization
if newCallee.Kind <> akIdentifier then
@@ -163,10 +163,10 @@ begin
// 3. Check if all argument types are statically known
allTypesKnown := True;
SetLength(argTypes, newArgsList.Count);
for i := 0 to newArgsList.Count - 1 do
SetLength(argTypes, newArgs.Count);
for i := 0 to newArgs.Count - 1 do
begin
argTypes[i] := newArgsList[i].AsTypedNode.StaticType;
argTypes[i] := newArgs.Items[i].AsTypedNode.StaticType;
if argTypes[i].Kind = stUnknown then
begin
allTypesKnown := False;
@@ -192,7 +192,7 @@ begin
TAst.FunctionCall(
Node.Identity,
newCallee,
newArgsList,
newArgs,
specializedMethod.ReturnType,
C.IsTailCall,
specializedMethod.Target,
@@ -212,7 +212,7 @@ begin
TAst.FunctionCall(
Node.Identity,
newCallee,
newArgsList,
newArgs,
specializedMethod.ReturnType,
C.IsTailCall,
specializedMethod.Target,
@@ -253,7 +253,7 @@ begin
FMonomorphCache.Add(key, specializedMethod);
// 6c. Return the new node
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, returnType, C.IsTailCall, compiled.Func, compiled.IsPure);
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgs, returnType, C.IsTailCall, compiled.Func, compiled.IsPure);
exit;
end;
+23 -22
View File
@@ -119,23 +119,23 @@ var
B: IBlockExpressionNode;
isContextTail: Boolean;
newExprs: TArray<IAstNode>;
exprsList: IExpressionList;
exprsTuple: ITupleNode;
i: Integer;
item, newItem: IAstNode;
hasChanged: Boolean;
begin
B := Node.AsBlockExpression;
isContextTail := FIsTailStack.Peek;
exprsList := B.Expressions;
exprsTuple := B.Expressions;
SetLength(newExprs, exprsList.Count);
SetLength(newExprs, exprsTuple.Count);
hasChanged := False;
var nTail := exprsList.Count - 1;
var nTail := exprsTuple.Count - 1;
for i := 0 to nTail do
begin
item := exprsList[i];
item := exprsTuple.Items[i];
// Only the last expression in the block inherits the tail position status
FNextIsTail := isContextTail and (i = nTail);
@@ -150,7 +150,8 @@ begin
if not hasChanged then
Result := Node
else
Result := TAst.Block(Node.Identity, TExpressionList.Create(newExprs, B.Expressions.Identity), B.StaticType);
// Create new Tuple for expressions
Result := TAst.Block(Node.Identity, TAst.Tuple(B.Expressions.Identity, newExprs), B.StaticType);
end;
function TAstTCO.VisitIfExpression(const Node: IAstNode): IAstNode;
@@ -224,13 +225,13 @@ end;
function TAstTCO.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
newParams: IParameterList;
newParams: ITupleNode;
newBody: IAstNode;
begin
L := Node.AsLambdaExpression;
// Parameters are not in tail position
FNextIsTail := False;
newParams := Accept(L.Parameters).AsParameterList;
newParams := Accept(L.Parameters).AsTuple;
// The body of a lambda is *always* a tail position (relative to the lambda execution)
FNextIsTail := True;
@@ -258,7 +259,7 @@ end;
function TAstTCO.VisitRecurNode(const Node: IAstNode): IAstNode;
var
R: IRecurNode;
newArgsList: IArgumentList;
newArgs: ITupleNode;
begin
R := Node.AsRecur;
if not FIsTailStack.Peek then
@@ -266,12 +267,12 @@ begin
// Arguments are not in tail position
FNextIsTail := False;
newArgsList := Accept(R.Arguments).AsArgumentList;
newArgs := Accept(R.Arguments).AsTuple;
if newArgsList = R.Arguments then
if newArgs = R.Arguments then
Result := Node
else
Result := TAst.Recur(Node.Identity, newArgsList, R.StaticType);
Result := TAst.Recur(Node.Identity, newArgs, R.StaticType);
end;
function TAstTCO.VisitMacroExpansionNode(const Node: IAstNode): IAstNode;
@@ -295,7 +296,7 @@ var
C: IFunctionCallNode;
isTailCall: Boolean;
newCallee: IAstNode;
newArgsList: IArgumentList;
newArgs: ITupleNode;
begin
C := Node.AsFunctionCall;
isTailCall := FIsTailStack.Peek;
@@ -304,16 +305,16 @@ begin
FNextIsTail := False;
newCallee := Accept(C.Callee);
newArgsList := Accept(C.Arguments).AsArgumentList;
newArgs := Accept(C.Arguments).AsTuple;
if (newCallee = C.Callee) and (newArgsList = C.Arguments) and (isTailCall = C.IsTailCall) then
if (newCallee = C.Callee) and (newArgs = C.Arguments) and (isTailCall = C.IsTailCall) then
begin
Result := Node;
exit;
end;
// Use factory to create new node with TCO status
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, C.StaticType, isTailCall, C.StaticTarget, C.IsTargetPure);
Result := TAst.FunctionCall(Node.Identity, newCallee, newArgs, C.StaticType, isTailCall, C.StaticTarget, C.IsTargetPure);
end;
function TAstTCO.VisitTuple(const Node: IAstNode): IAstNode;
@@ -325,19 +326,19 @@ var
savedNextIsTail: Boolean;
begin
T := Node.AsTuple;
savedNextIsTail := FNextIsTail; // Zustand sichern (wahrscheinlich irrelevant, aber sauber)
savedNextIsTail := FNextIsTail; // Zustand sichern
// Elemente in einem Tupel sind NIEMALS in Tail-Position
// Elemente in einem Tupel (Liste, Vektor, Argumente) sind NIEMALS in Tail-Position
FNextIsTail := False;
hasChanged := False;
SetLength(newElements, Length(T.Elements));
SetLength(newElements, T.Count);
try
for i := 0 to High(T.Elements) do
for i := 0 to T.Count - 1 do
begin
newElements[i] := Accept(T.Elements[i]);
if newElements[i] <> T.Elements[i] then
newElements[i] := Accept(T.Items[i]);
if newElements[i] <> T.Items[i] then
hasChanged := True;
end;
finally
+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,
+31 -60
View File
@@ -50,21 +50,16 @@ type
function VisitIndexer(const Node: IAstNode): TVoid;
function VisitMemberAccess(const Node: IAstNode): TVoid;
function VisitRecordLiteral(const Node: IAstNode): TVoid;
function VisitRecordField(const Node: IAstNode): TVoid;
function VisitCreateSeries(const Node: IAstNode): TVoid;
function VisitAddSeriesItem(const Node: IAstNode): TVoid;
function VisitSeriesLength(const Node: IAstNode): TVoid;
function VisitNop(const Node: IAstNode): TVoid;
// List Visitors
function VisitParameterList(const Node: IAstNode): TVoid;
function VisitArgumentList(const Node: IAstNode): TVoid;
function VisitExpressionList(const Node: IAstNode): TVoid;
function VisitRecordFieldList(const Node: IAstNode): TVoid;
function VisitRecordField(const Node: IAstNode): TVoid;
// Unified List Visitor
function VisitTuple(const Node: IAstNode): TVoid;
// Pipe Visitors
// Pipe Visitors (Specific lists kept for now)
function VisitPipeInput(const Node: IAstNode): TVoid;
function VisitPipeSelectorList(const Node: IAstNode): TVoid;
function VisitPipeInputList(const Node: IAstNode): TVoid;
@@ -118,10 +113,9 @@ begin
Register(akIdentifier, VisitIdentifier);
Register(akKeyword, VisitKeyword);
Register(akParameterList, VisitParameterList);
Register(akArgumentList, VisitArgumentList);
Register(akExpressionList, VisitExpressionList);
Register(akRecordFieldList, VisitRecordFieldList);
// Unified List
Register(akTuple, VisitTuple);
// Element
Register(akRecordField, VisitRecordField);
Register(akIfExpression, VisitIfExpression);
@@ -145,8 +139,6 @@ begin
Register(akRecur, VisitRecurNode);
Register(akNop, VisitNop);
Register(akTuple, VisitTuple);
Register(akPipeInput, VisitPipeInput);
Register(akPipeSelectorList, VisitPipeSelectorList);
Register(akPipeInputList, VisitPipeInputList);
@@ -314,7 +306,7 @@ begin
Log('Parameters:');
Indent;
Visit(E.Parameters);
Visit(E.Parameters); // Visits Tuple
Unindent;
if Length(E.Upvalues) > 0 then
@@ -338,8 +330,11 @@ var
C: IFunctionCallNode;
argTypes: TArray<string>;
i: Integer;
args: ITupleNode;
begin
C := Node.AsFunctionCall;
args := C.Arguments;
LogFmt(
'FunctionCall (IsTailCall: %s, StaticTarget: %s, IsTargetPure: %s)',
[
@@ -353,11 +348,11 @@ begin
if Assigned(C.StaticTarget) then
begin
Indent;
SetLength(argTypes, C.Arguments.Count);
for i := 0 to C.Arguments.Count - 1 do
SetLength(argTypes, args.Count);
for i := 0 to args.Count - 1 do
begin
if C.Arguments[i].IsTyped then
argTypes[i] := C.Arguments[i].AsTypedNode.StaticType.ToString
if args.Items[i].IsTyped then
argTypes[i] := args.Items[i].AsTypedNode.StaticType.ToString
else
argTypes[i] := 'Untyped';
end;
@@ -368,8 +363,8 @@ begin
Indent;
Log('Callee:');
Visit(C.Callee);
LogFmt('Arguments (%d):', [C.Arguments.Count]);
Visit(C.Arguments);
LogFmt('Arguments (%d):', [args.Count]);
Visit(args); // Visits Tuple
Unindent;
end;
@@ -548,34 +543,26 @@ begin
Log('Nop', Node);
end;
{ List Visitors }
// --- Unified List Visitor ---
function TAstDumper.VisitParameterList(const Node: IAstNode): TVoid;
begin
for var item in Node.AsParameterList do
Visit(item);
end;
function TAstDumper.VisitArgumentList(const Node: IAstNode): TVoid;
function TAstDumper.VisitTuple(const Node: IAstNode): TVoid;
var
T: ITupleNode;
i: Integer;
begin
T := Node.AsTuple;
LogFmt('Tuple (%d elements)', [T.Count], Node);
Indent;
for var item in Node.AsArgumentList do
Visit(item);
for i := 0 to T.Count - 1 do
begin
LogFmt('Item %d:', [i]);
Indent;
Visit(T.Items[i]);
Unindent;
end;
Unindent;
end;
function TAstDumper.VisitExpressionList(const Node: IAstNode): TVoid;
begin
for var item in Node.AsExpressionList do
Visit(item);
end;
function TAstDumper.VisitRecordFieldList(const Node: IAstNode): TVoid;
begin
for var item in Node.AsRecordFieldList do
Visit(item);
end;
function TAstDumper.VisitRecordField(const Node: IAstNode): TVoid;
var
F: IRecordFieldNode;
@@ -587,7 +574,7 @@ begin
Unindent;
end;
{ Pipe Visitors }
// --- Pipe Visitors ---
function TAstDumper.VisitPipeInput(const Node: IAstNode): TVoid;
var
@@ -629,20 +616,4 @@ 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.
+44 -38
View File
@@ -29,14 +29,13 @@ type
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 VisitTuple(const N: ITupleNode): TDataValue; virtual;
function VisitIfExpression(const N: IIfExpressionNode): TDataValue; virtual;
function VisitCondExpression(const N: ICondExpressionNode): TDataValue; virtual;
function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; virtual;
function VisitFunctionCall(const N: IFunctionCallNode): TDataValue; virtual;
function VisitBlockExpression(const N: IBlockExpressionNode): TDataValue; virtual;
function VisitExpressionList(const N: IExpressionList): TDataValue; virtual;
function VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue; virtual;
function VisitAssignment(const N: IAssignmentNode): TDataValue; virtual;
function VisitIndexer(const N: IIndexerNode): TDataValue; virtual;
@@ -92,18 +91,16 @@ 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
akTuple: Result := VisitTuple(Node.AsTuple);
akIfExpression: Result := VisitIfExpression(Node.AsIfExpression);
akCondExpression: Result := VisitCondExpression(Node.AsCondExpression);
akLambdaExpression: Result := VisitLambdaExpression(Node.AsLambdaExpression);
akFunctionCall: Result := VisitFunctionCall(Node.AsFunctionCall);
akBlockExpression: Result := VisitBlockExpression(Node.AsBlockExpression);
akExpressionList: Result := VisitExpressionList(Node.AsExpressionList);
akVariableDeclaration: Result := VisitVariableDeclaration(Node.AsVariableDeclaration);
akAssignment: Result := VisitAssignment(Node.AsAssignment);
akIndexer: Result := VisitIndexer(Node.AsIndexer);
@@ -182,12 +179,10 @@ 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]);
SetLength(elements, N.Count);
for i := 0 to N.Count - 1 do
elements[i] := Visit(N.Items[i]);
// Uses the implicit operator: TArray<TDataValue> -> TDataValue (vkTuple)
Result := elements;
@@ -233,7 +228,11 @@ begin
lambdaScope.SetValues(TResolvedAddress.Create(akLocalOrParent, 0, 0), TDataValue(closure));
for k := 0 to params.Count - 1 do
lambdaScope[params[k].Address] := ArgValues[k];
begin
// Parameters are guaranteed to be Identifiers by the Binder
var paramNode := params.Items[k].AsIdentifier;
lambdaScope[paramNode.Address] := ArgValues[k];
end;
bodyVisitor := visitorFactory(lambdaScope);
Result := bodyVisitor.Visit(N.Body);
@@ -246,13 +245,15 @@ var
calleeValue: TDataValue;
argValues: TArray<TDataValue>;
i: Integer;
argsTuple: ITupleNode;
begin
argsTuple := N.Arguments;
SetLength(argValues, argsTuple.Count);
for i := 0 to argsTuple.Count - 1 do
argValues[i] := Visit(argsTuple.Items[i]);
if Assigned(N.StaticTarget) then
begin
SetLength(argValues, N.Arguments.Count);
for i := 0 to N.Arguments.Count - 1 do
argValues[i] := Visit(N.Arguments[i]);
Result := N.StaticTarget(argValues);
if not N.IsTailCall then
HandleTCO(Result);
@@ -263,10 +264,6 @@ begin
if (calleeValue.Kind <> vkMethod) then
raise EEvaluatorException.Create('Not a function');
SetLength(argValues, N.Arguments.Count);
for i := 0 to N.Arguments.Count - 1 do
argValues[i] := Visit(N.Arguments[i]);
if N.IsTailCall then
Result := TDataValue.FromGeneric<TThunk>(TThunk.Create(calleeValue, argValues, false))
else
@@ -281,27 +278,27 @@ function TEvaluatorVisitor.VisitRecurNode(const N: IRecurNode): TDataValue;
var
argValues: TArray<TDataValue>;
i: Integer;
argsTuple: ITupleNode;
begin
SetLength(argValues, N.Arguments.Count);
for i := 0 to N.Arguments.Count - 1 do
argValues[i] := Visit(N.Arguments[i]);
argsTuple := N.Arguments;
SetLength(argValues, argsTuple.Count);
for i := 0 to argsTuple.Count - 1 do
argValues[i] := Visit(argsTuple.Items[i]);
// The "self" function is always at Slot 0
var callee := FScope[TResolvedAddress.Create(akLocalOrParent, 0, 0)];
Result := TDataValue.FromGeneric<TThunk>(TThunk.Create(callee, argValues, true));
end;
function TEvaluatorVisitor.VisitBlockExpression(const N: IBlockExpressionNode): TDataValue;
begin
Result := Visit(N.Expressions);
end;
function TEvaluatorVisitor.VisitExpressionList(const N: IExpressionList): TDataValue;
var
exprs: ITupleNode;
i: Integer;
begin
exprs := N.Expressions;
Result := TDataValue.Void;
for i := 0 to N.Count - 1 do
Result := Visit(N[i]);
for i := 0 to exprs.Count - 1 do
Result := Visit(exprs.Items[i]);
end;
function TEvaluatorVisitor.VisitIfExpression(const N: IIfExpressionNode): TDataValue;
@@ -367,7 +364,7 @@ begin
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)
vkTuple:
begin
var tpl := base.AsTuple;
var i := idx.AsScalar.Value.AsInt64;
@@ -400,21 +397,30 @@ end;
function TEvaluatorVisitor.VisitRecordLiteral(const N: IRecordLiteralNode): TDataValue;
var
i: Integer;
fieldsTuple: ITupleNode;
begin
if Assigned(N.ScalarDefinition) then
begin
var vals: TArray<TScalar.TValue>;
SetLength(vals, N.Fields.Count);
for i := 0 to N.Fields.Count - 1 do
vals[i] := Visit(N.Fields[i].Value).AsScalar.Value;
fieldsTuple := N.Fields;
SetLength(vals, fieldsTuple.Count);
for i := 0 to fieldsTuple.Count - 1 do
begin
var field := fieldsTuple.Items[i].AsRecordField;
vals[i] := Visit(field.Value).AsScalar.Value;
end;
Result := TScalarRecord.Create(N.ScalarDefinition, vals);
end
else
begin
var fields: TArray<TPair<IKeyword, TDataValue>>;
SetLength(fields, N.Fields.Count);
for i := 0 to N.Fields.Count - 1 do
fields[i] := TPair<IKeyword, TDataValue>.Create(N.Fields[i].Key.Value, Visit(N.Fields[i].Value));
fieldsTuple := N.Fields;
SetLength(fields, fieldsTuple.Count);
for i := 0 to fieldsTuple.Count - 1 do
begin
var field := fieldsTuple.Items[i].AsRecordField;
fields[i] := TPair<IKeyword, TDataValue>.Create(field.Key.Value, Visit(field.Value));
end;
Result := TGenericRecord<TDataValue>.Create(fields);
end;
end;
@@ -459,7 +465,7 @@ begin
SetLength(config, N.Inputs.Count);
for i := 0 to N.Inputs.Count - 1 do
begin
inputNode := N.Inputs[i];
inputNode := N.Inputs.Items[i].AsPipeInput;
inputVal := FScope[inputNode.StreamSource.Address];
if (inputVal.Kind <> vkStream) then
raise EEvaluatorException.Create('Stream expected');
@@ -470,7 +476,7 @@ begin
SetLength(config[i], selectors.Count);
for var k := 0 to selectors.Count - 1 do
begin
var key := selectors[k].Value;
var key := selectors.Items[k].AsKeyword.Value;
var idx := srcDef.IndexOf(key);
config[i][k] := TScalarRecordField.Create(key, srcDef[idx]);
end;
+49 -69
View File
@@ -59,10 +59,7 @@ type
function VisitIdentifier(const Node: IAstNode): TJSONObject;
function VisitKeyword(const Node: IAstNode): TJSONObject;
function VisitParameterList(const Node: IAstNode): TJSONObject;
function VisitArgumentList(const Node: IAstNode): TJSONObject;
function VisitExpressionList(const Node: IAstNode): TJSONObject;
function VisitRecordFieldList(const Node: IAstNode): TJSONObject;
function VisitTuple(const Node: IAstNode): TJSONObject;
function VisitRecordField(const Node: IAstNode): TJSONObject;
function VisitIfExpression(const Node: IAstNode): TJSONObject;
@@ -86,8 +83,6 @@ 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;
@@ -120,10 +115,7 @@ begin
Register(akIdentifier, VisitIdentifier);
Register(akKeyword, VisitKeyword);
Register(akParameterList, VisitParameterList);
Register(akArgumentList, VisitArgumentList);
Register(akExpressionList, VisitExpressionList);
Register(akRecordFieldList, VisitRecordFieldList);
Register(akTuple, VisitTuple);
Register(akRecordField, VisitRecordField);
Register(akIfExpression, VisitIfExpression);
@@ -147,8 +139,6 @@ begin
Register(akRecur, VisitRecurNode);
Register(akNop, VisitNop);
Register(akTuple, VisitTuple);
Register(akPipeInput, VisitPipeInput);
Register(akPipeSelectorList, VisitPipeSelectorList);
Register(akPipeInputList, VisitPipeInputList);
@@ -223,6 +213,24 @@ begin
Result.AddPair('Name', TJSONString.Create(Node.AsKeyword.Value.Name));
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 T.Count - 1 do
begin
elemsArray.Add(Visit(T.Items[i]));
end;
Result.AddPair('Elements', elemsArray);
end;
function TJsonAstConverter.VisitIfExpression(const Node: IAstNode): TJSONObject;
var
E: IIfExpressionNode;
@@ -286,9 +294,10 @@ var
begin
L := Node.AsLambdaExpression;
paramsArray := TJSONArray.Create;
// Iterate Tuple
for i := 0 to L.Parameters.Count - 1 do
begin
paramsArray.Add(Visit(L.Parameters[i]));
paramsArray.Add(Visit(L.Parameters.Items[i]));
end;
Result := TJSONObject.Create;
@@ -305,9 +314,10 @@ var
begin
M := Node.AsMacroDefinition;
paramsArray := TJSONArray.Create;
// Iterate Tuple
for i := 0 to M.Parameters.Count - 1 do
begin
paramsArray.Add(Visit(M.Parameters[i]));
paramsArray.Add(Visit(M.Parameters.Items[i]));
end;
Result := TJSONObject.Create;
@@ -346,9 +356,10 @@ var
begin
C := Node.AsFunctionCall;
argsArray := TJSONArray.Create;
// Iterate Tuple
for i := 0 to C.Arguments.Count - 1 do
begin
argsArray.Add(Visit(C.Arguments[i]));
argsArray.Add(Visit(C.Arguments.Items[i]));
end;
Result := TJSONObject.Create;
@@ -365,9 +376,10 @@ var
begin
M := Node.AsMacroExpansion;
argsArray := TJSONArray.Create;
// Iterate Tuple
for i := 0 to M.CallNode.Arguments.Count - 1 do
begin
argsArray.Add(Visit(M.CallNode.Arguments[i]));
argsArray.Add(Visit(M.CallNode.Arguments.Items[i]));
end;
Result := TJSONObject.Create;
@@ -385,9 +397,10 @@ var
begin
R := Node.AsRecur;
argsArray := TJSONArray.Create;
// Iterate Tuple
for i := 0 to R.Arguments.Count - 1 do
begin
argsArray.Add(Visit(R.Arguments[i]));
argsArray.Add(Visit(R.Arguments.Items[i]));
end;
Result := TJSONObject.Create;
@@ -403,9 +416,10 @@ var
begin
B := Node.AsBlockExpression;
exprsArray := TJSONArray.Create;
// Iterate Tuple
for i := 0 to B.Expressions.Count - 1 do
begin
exprsArray.Add(Visit(B.Expressions[i]));
exprsArray.Add(Visit(B.Expressions.Items[i]));
end;
Result := TJSONObject.Create;
@@ -477,9 +491,10 @@ begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('RecordLiteral'));
fieldsArray := TJSONArray.Create;
// Iterate Tuple
for i := 0 to R.Fields.Count - 1 do
begin
fieldsArray.Add(Visit(R.Fields[i]));
fieldsArray.Add(Visit(R.Fields.Items[i]));
end;
Result.AddPair('Fields', fieldsArray);
end;
@@ -536,56 +551,6 @@ 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;
end;
function TJsonAstConverter.VisitArgumentList(const Node: IAstNode): TJSONObject;
begin
Result := nil;
end;
function TJsonAstConverter.VisitExpressionList(const Node: IAstNode): TJSONObject;
begin
Result := nil;
end;
function TJsonAstConverter.VisitRecordFieldList(const Node: IAstNode): TJSONObject;
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.');
@@ -968,4 +933,19 @@ begin
Result := TAst.Nop.AsNop;
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;
end.
+68 -178
View File
@@ -82,13 +82,9 @@ type
IIdentifierNode = interface;
IKeywordNode = interface;
// Lists & Containers
IParameterList = interface;
IArgumentList = interface;
IExpressionList = interface;
IRecordFieldList = interface;
IRecordFieldNode = interface;
// Structure
ITupleNode = interface;
IRecordFieldNode = interface;
// Control Flow & Structure
IIfExpressionNode = interface;
@@ -123,14 +119,9 @@ type
akConstant,
akIdentifier,
akKeyword,
// Lists
akParameterList,
akArgumentList,
akExpressionList,
akRecordFieldList,
akRecordField,
// Tuple
akTuple,
// Containers
akTuple, // The universal list (replaces ArgList, ParamList, ExprList, FieldList)
akRecordField, // Single key-value pair
// Control Flow
akIfExpression,
akCondExpression,
@@ -188,12 +179,8 @@ type
function AsIdentifier: IIdentifierNode;
function AsKeyword: IKeywordNode;
function AsParameterList: IParameterList;
function AsArgumentList: IArgumentList;
function AsExpressionList: IExpressionList;
function AsRecordFieldList: IRecordFieldList;
function AsRecordField: IRecordFieldNode;
function AsTuple: ITupleNode;
function AsRecordField: IRecordFieldNode;
function AsIfExpression: IIfExpressionNode;
function AsCondExpression: ICondExpressionNode;
@@ -254,20 +241,17 @@ type
property Value: IAstNode read GetValue;
end;
IRecordFieldList = interface(INodeList<IRecordFieldNode>)
end;
// --- Node Interfaces (Definitions) ---
// Function Definition (Lambda)
IFunctionDefinition = interface(IAstTypedNode)
{$region 'private'}
function GetBody: IAstNode;
function GetParameters: IParameterList;
function GetParameters: ITupleNode;
function GetIsPure: Boolean;
{$endregion}
property Body: IAstNode read GetBody;
property Parameters: IParameterList read GetParameters;
property Parameters: ITupleNode read GetParameters;
property IsPure: Boolean read GetIsPure;
end;
@@ -299,9 +283,12 @@ type
ITupleNode = interface(IAstTypedNode)
{$region 'private'}
function GetElements: TArray<IAstNode>;
function GetCount: Integer;
function GetItems(Idx: Integer): IAstNode;
{$endregion}
property Elements: TArray<IAstNode> read GetElements;
function ToArray: TArray<IAstNode>;
property Count: Integer read GetCount;
property Items[Idx: Integer]: IAstNode read GetItems; default;
end;
IIfExpressionNode = interface(IAstTypedNode)
@@ -340,13 +327,13 @@ type
IFunctionCallNode = interface(IAstTypedNode)
{$region 'private'}
function GetCallee: IAstNode;
function GetArguments: IArgumentList;
function GetArguments: ITupleNode;
function GetIsTailCall: Boolean;
function GetStaticTarget: TDataValue.TFunc;
function GetIsTargetPure: Boolean;
{$endregion}
property Callee: IAstNode read GetCallee;
property Arguments: IArgumentList read GetArguments;
property Arguments: ITupleNode read GetArguments;
property IsTailCall: Boolean read GetIsTailCall;
property StaticTarget: TDataValue.TFunc read GetStaticTarget;
property IsTargetPure: Boolean read GetIsTargetPure;
@@ -363,16 +350,16 @@ type
IRecurNode = interface(IAstTypedNode)
{$region 'private'}
function GetArguments: IArgumentList;
function GetArguments: ITupleNode;
{$endregion}
property Arguments: IArgumentList read GetArguments;
property Arguments: ITupleNode read GetArguments;
end;
IBlockExpressionNode = interface(IAstTypedNode)
{$region 'private'}
function GetExpressions: IExpressionList;
function GetExpressions: ITupleNode;
{$endregion}
property Expressions: IExpressionList read GetExpressions;
property Expressions: ITupleNode read GetExpressions;
end;
IVariableDeclarationNode = interface(IAstTypedNode)
@@ -398,11 +385,11 @@ type
IMacroDefinitionNode = interface(IAstTypedNode)
{$region 'private'}
function GetName: IIdentifierNode;
function GetParameters: IParameterList;
function GetParameters: ITupleNode;
function GetBody: IAstNode;
{$endregion}
property Name: IIdentifierNode read GetName;
property Parameters: IParameterList read GetParameters;
property Parameters: ITupleNode read GetParameters;
property Body: IAstNode read GetBody;
end;
@@ -447,11 +434,11 @@ type
IRecordLiteralNode = interface(IAstTypedNode)
{$region 'private'}
function GetFields: IRecordFieldList;
function GetFields: ITupleNode;
function GetGenericDefinition: IGenericRecordDefinition;
function GetScalarDefinition: IScalarRecordDefinition;
{$endregion}
property Fields: IRecordFieldList read GetFields;
property Fields: ITupleNode read GetFields;
property GenericDefinition: IGenericRecordDefinition read GetGenericDefinition;
property ScalarDefinition: IScalarRecordDefinition read GetScalarDefinition;
end;
@@ -509,15 +496,6 @@ type
property Transformation: ILambdaExpressionNode read GetTransformation;
end;
IParameterList = interface(INodeList<IIdentifierNode>)
end;
IArgumentList = interface(INodeList<IAstNode>)
end;
IExpressionList = interface(INodeList<IAstNode>)
end;
IAstVisitor = interface
function Visit(const Node: IAstNode): TDataValue;
end;
@@ -544,10 +522,6 @@ type
function AsIdentifier: IIdentifierNode; virtual;
function AsKeyword: IKeywordNode; virtual;
function AsParameterList: IParameterList; virtual;
function AsArgumentList: IArgumentList; virtual;
function AsExpressionList: IExpressionList; virtual;
function AsRecordFieldList: IRecordFieldList; virtual;
function AsRecordField: IRecordFieldNode; virtual;
function AsTuple: ITupleNode; virtual;
@@ -625,27 +599,6 @@ type
property Items[Index: Integer]: T read GetItem; default;
end;
TParameterList = class(TAstNodeList<IIdentifierNode>, IParameterList)
protected
function GetKind: TAstNodeKind; override;
public
function AsParameterList: IParameterList; override;
end;
TArgumentList = class(TAstNodeList<IAstNode>, IArgumentList)
protected
function GetKind: TAstNodeKind; override;
public
function AsArgumentList: IArgumentList; override;
end;
TExpressionList = class(TAstNodeList<IAstNode>, IExpressionList)
protected
function GetKind: TAstNodeKind; override;
public
function AsExpressionList: IExpressionList; override;
end;
TRecordFieldNode = class(TAstNode, IRecordFieldNode)
private
FKey: IKeywordNode;
@@ -659,13 +612,6 @@ type
function AsRecordField: IRecordFieldNode; override;
end;
TRecordFieldList = class(TAstNodeList<IRecordFieldNode>, IRecordFieldList)
protected
function GetKind: TAstNodeKind; override;
public
function AsRecordFieldList: IRecordFieldList; override;
end;
// --- Core Nodes Implementations ---
TConstantNode = class(TAstTypedNode, IConstantNode)
@@ -706,9 +652,11 @@ type
TTupleNode = class(TAstTypedNode, ITupleNode)
private
FElements: TArray<IAstNode>;
function GetElements: TArray<IAstNode>;
function GetCount: Integer;
function GetItems(Idx: Integer): IAstNode;
protected
function GetKind: TAstNodeKind; override;
function ToArray: TArray<IAstNode>;
public
constructor Create(const AElements: TArray<IAstNode>; const AIdentity: IAstIdentity; const AStaticType: IStaticType);
function AsTuple: ITupleNode; override;
@@ -764,13 +712,13 @@ type
TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode, IFunctionDefinition)
private
FParameters: IParameterList;
FParameters: ITupleNode;
FBody: IAstNode;
FLayout: IScopeLayout;
FDescriptor: IScopeDescriptor;
FUpvalues: TArray<TResolvedAddress>;
FHasNestedLambdas, FIsPure: Boolean;
function GetParameters: IParameterList;
function GetParameters: ITupleNode;
function GetBody: IAstNode;
function GetLayout: IScopeLayout;
function GetDescriptor: IScopeDescriptor;
@@ -781,7 +729,7 @@ type
function GetKind: TAstNodeKind; override;
public
constructor Create(
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode;
const AStaticType: IStaticType;
const ALayout: IScopeLayout;
@@ -796,12 +744,12 @@ type
TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode)
private
FCallee: IAstNode;
FArguments: IArgumentList;
FArguments: ITupleNode;
FIsTailCall: Boolean;
FStaticTarget: TDataValue.TFunc;
FIsTargetPure: Boolean;
function GetCallee: IAstNode;
function GetArguments: IArgumentList;
function GetArguments: ITupleNode;
function GetIsTailCall: Boolean;
function GetStaticTarget: TDataValue.TFunc;
function GetIsTargetPure: Boolean;
@@ -810,7 +758,7 @@ type
public
constructor Create(
const ACallee: IAstNode;
const AArguments: IArgumentList;
const AArguments: ITupleNode;
const AStaticType: IStaticType;
const AIsTailCall: Boolean;
const AStaticTarget: TDataValue.TFunc;
@@ -823,17 +771,17 @@ type
TMacroDefinitionNode = class(TAstTypedNode, IMacroDefinitionNode)
private
FName: IIdentifierNode;
FParameters: IParameterList;
FParameters: ITupleNode;
FBody: IAstNode;
function GetName: IIdentifierNode;
function GetParameters: IParameterList;
function GetParameters: ITupleNode;
function GetBody: IAstNode;
protected
function GetKind: TAstNodeKind; override;
public
constructor Create(
const AName: IIdentifierNode;
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode;
const AIdentity: IAstIdentity
);
@@ -888,23 +836,23 @@ type
TRecurNode = class(TAstTypedNode, IRecurNode)
private
FArguments: IArgumentList;
function GetArguments: IArgumentList;
FArguments: ITupleNode;
function GetArguments: ITupleNode;
protected
function GetKind: TAstNodeKind; override;
public
constructor Create(const AArguments: IArgumentList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
constructor Create(const AArguments: ITupleNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
function AsRecur: IRecurNode; override;
end;
TBlockExpressionNode = class(TAstTypedNode, IBlockExpressionNode)
private
FExpressions: IExpressionList;
function GetExpressions: IExpressionList;
FExpressions: ITupleNode;
function GetExpressions: ITupleNode;
protected
function GetKind: TAstNodeKind; override;
public
constructor Create(const AExpressions: IExpressionList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
constructor Create(const AExpressions: ITupleNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
function AsBlockExpression: IBlockExpressionNode; override;
end;
@@ -972,17 +920,17 @@ type
TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode)
private
FFields: IRecordFieldList;
FFields: ITupleNode;
FScalarDef: IScalarRecordDefinition;
FGenericDef: IGenericRecordDefinition;
function GetFields: IRecordFieldList;
function GetFields: ITupleNode;
function GetGenericDefinition: IGenericRecordDefinition;
function GetScalarDefinition: IScalarRecordDefinition;
protected
function GetKind: TAstNodeKind; override;
public
constructor Create(
const AFields: IRecordFieldList;
const AFields: ITupleNode;
const AScalarDef: IScalarRecordDefinition;
const AGenericDef: IGenericRecordDefinition;
const AStaticType: IStaticType;
@@ -1266,26 +1214,6 @@ begin
Result := nil;
end;
function TAstNode.AsParameterList: IParameterList;
begin
Result := nil;
end;
function TAstNode.AsArgumentList: IArgumentList;
begin
Result := nil;
end;
function TAstNode.AsExpressionList: IExpressionList;
begin
Result := nil;
end;
function TAstNode.AsRecordFieldList: IRecordFieldList;
begin
Result := nil;
end;
function TAstNode.AsRecordField: IRecordFieldNode;
begin
Result := nil;
@@ -1472,42 +1400,6 @@ begin
Result := FItems;
end;
{ TParameterList }
function TParameterList.AsParameterList: IParameterList;
begin
Result := Self;
end;
function TParameterList.GetKind: TAstNodeKind;
begin
Result := akParameterList;
end;
{ TArgumentList }
function TArgumentList.AsArgumentList: IArgumentList;
begin
Result := Self;
end;
function TArgumentList.GetKind: TAstNodeKind;
begin
Result := akArgumentList;
end;
{ TExpressionList }
function TExpressionList.AsExpressionList: IExpressionList;
begin
Result := Self;
end;
function TExpressionList.GetKind: TAstNodeKind;
begin
Result := akExpressionList;
end;
{ TRecordFieldNode }
constructor TRecordFieldNode.Create(const AKey: IKeywordNode; const AValue: IAstNode; const AIdentity: IAstIdentity);
@@ -1537,18 +1429,6 @@ begin
Result := FValue;
end;
{ TRecordFieldList }
function TRecordFieldList.AsRecordFieldList: IRecordFieldList;
begin
Result := Self;
end;
function TRecordFieldList.GetKind: TAstNodeKind;
begin
Result := akRecordFieldList;
end;
{ TConstantNode }
constructor TConstantNode.Create(const AIdentity: IConstantIdentity; const AStaticType: IStaticType);
@@ -1637,9 +1517,14 @@ begin
Result := Self;
end;
function TTupleNode.GetElements: TArray<IAstNode>;
function TTupleNode.GetCount: Integer;
begin
Result := FElements;
Result := Length(FElements);
end;
function TTupleNode.GetItems(Idx: Integer): IAstNode;
begin
Result := FElements[Idx];
end;
function TTupleNode.GetKind: TAstNodeKind;
@@ -1647,6 +1532,11 @@ begin
Result := akTuple;
end;
function TTupleNode.ToArray: TArray<IAstNode>;
begin
Result := FElements;
end;
{ TCreateSeriesNode }
constructor TCreateSeriesNode.Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
@@ -1743,7 +1633,7 @@ end;
{ TLambdaExpressionNode }
constructor TLambdaExpressionNode.Create(
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode;
const AStaticType: IStaticType;
const ALayout: IScopeLayout;
@@ -1776,7 +1666,7 @@ function TLambdaExpressionNode.GetBody: IAstNode;
begin
Result := FBody;
end;
function TLambdaExpressionNode.GetParameters: IParameterList;
function TLambdaExpressionNode.GetParameters: ITupleNode;
begin
Result := FParameters;
end;
@@ -1809,7 +1699,7 @@ end;
constructor TFunctionCallNode.Create(
const ACallee: IAstNode;
const AArguments: IArgumentList;
const AArguments: ITupleNode;
const AStaticType: IStaticType;
const AIsTailCall: Boolean;
const AStaticTarget: TDataValue.TFunc;
@@ -1834,7 +1724,7 @@ function TFunctionCallNode.GetCallee: IAstNode;
begin
Result := FCallee;
end;
function TFunctionCallNode.GetArguments: IArgumentList;
function TFunctionCallNode.GetArguments: ITupleNode;
begin
Result := FArguments;
end;
@@ -1859,7 +1749,7 @@ end;
constructor TMacroDefinitionNode.Create(
const AName: IIdentifierNode;
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode;
const AIdentity: IAstIdentity
);
@@ -1879,7 +1769,7 @@ function TMacroDefinitionNode.GetName: IIdentifierNode;
begin
Result := FName;
end;
function TMacroDefinitionNode.GetParameters: IParameterList;
function TMacroDefinitionNode.GetParameters: ITupleNode;
begin
Result := FParameters;
end;
@@ -1990,7 +1880,7 @@ end;
{ TRecurNode }
constructor TRecurNode.Create(const AArguments: IArgumentList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
constructor TRecurNode.Create(const AArguments: ITupleNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
inherited Create(AStaticType, AIdentity);
FArguments := AArguments;
@@ -2001,7 +1891,7 @@ begin
Result := Self;
end;
function TRecurNode.GetArguments: IArgumentList;
function TRecurNode.GetArguments: ITupleNode;
begin
Result := FArguments;
end;
@@ -2012,7 +1902,7 @@ end;
{ TBlockExpressionNode }
constructor TBlockExpressionNode.Create(const AExpressions: IExpressionList; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
constructor TBlockExpressionNode.Create(const AExpressions: ITupleNode; const AStaticType: IStaticType; const AIdentity: IAstIdentity);
begin
inherited Create(AStaticType, AIdentity);
FExpressions := AExpressions;
@@ -2023,7 +1913,7 @@ begin
Result := Self;
end;
function TBlockExpressionNode.GetExpressions: IExpressionList;
function TBlockExpressionNode.GetExpressions: ITupleNode;
begin
Result := FExpressions;
end;
@@ -2159,7 +2049,7 @@ end;
{ TRecordLiteralNode }
constructor TRecordLiteralNode.Create(
const AFields: IRecordFieldList;
const AFields: ITupleNode;
const AScalarDef: IScalarRecordDefinition;
const AGenericDef: IGenericRecordDefinition;
const AStaticType: IStaticType;
@@ -2177,7 +2067,7 @@ begin
Result := Self;
end;
function TRecordLiteralNode.GetFields: IRecordFieldList;
function TRecordLiteralNode.GetFields: ITupleNode;
begin
Result := FFields;
end;
+61 -165
View File
@@ -18,17 +18,10 @@ type
FTarget: IAstNode;
FRemoved: Boolean;
function FilterList<T: IAstNode>(const Source: INodeList<T>): TArray<T>;
function FilterTuple(const Source: ITupleNode): TArray<IAstNode>;
function EnsureNode(const Node: IAstNode; const ContextNode: IAstNode): IAstNode;
strict private
// List Containers (Filter Logic)
function VisitBlockExpression(const Node: IAstNode): IAstNode;
function VisitExpressionList(const Node: IAstNode): IAstNode;
function VisitArgumentList(const Node: IAstNode): IAstNode;
function VisitParameterList(const Node: IAstNode): IAstNode;
function VisitRecordFieldList(const Node: IAstNode): IAstNode;
// Structural Nodes (Replace Logic)
function VisitIfExpression(const Node: IAstNode): IAstNode;
function VisitCondExpression(const Node: IAstNode): IAstNode;
@@ -39,7 +32,11 @@ type
function VisitIndexer(const Node: IAstNode): IAstNode;
function VisitMemberAccess(const Node: IAstNode): IAstNode;
function VisitAddSeriesItem(const Node: IAstNode): IAstNode;
// Unified Container Handler
function VisitTuple(const Node: IAstNode): IAstNode;
function VisitBlockExpression(const Node: IAstNode): IAstNode;
function VisitRecurNode(const Node: IAstNode): IAstNode;
protected
procedure SetupHandlers; override;
@@ -65,13 +62,6 @@ procedure TAstNodeRemover.SetupHandlers;
begin
inherited SetupHandlers; // Defaults
// Filter Logic
Register(akBlockExpression, VisitBlockExpression);
Register(akExpressionList, VisitExpressionList);
Register(akArgumentList, VisitArgumentList);
Register(akParameterList, VisitParameterList);
Register(akRecordFieldList, VisitRecordFieldList);
// Replace Logic
Register(akIfExpression, VisitIfExpression);
Register(akCondExpression, VisitCondExpression);
@@ -82,7 +72,10 @@ begin
Register(akIndexer, VisitIndexer);
Register(akMemberAccess, VisitMemberAccess);
Register(akAddSeriesItem, VisitAddSeriesItem);
Register(akBlockExpression, VisitBlockExpression);
Register(akRecur, VisitRecurNode);
// List Logic
Register(akTuple, VisitTuple);
end;
@@ -129,21 +122,20 @@ begin
Result := TAst.Nop(ContextNode.Identity);
end;
function TAstNodeRemover.FilterList<T>(const Source: INodeList<T>): TArray<T>;
function TAstNodeRemover.FilterTuple(const Source: ITupleNode): TArray<IAstNode>;
var
i: Integer;
item: T;
transformed: IAstNode;
list: TList<T>;
item, transformed: IAstNode;
list: TList<IAstNode>;
begin
list := TList<T>.Create;
list := TList<IAstNode>.Create;
try
for i := 0 to Source.Count - 1 do
begin
item := Source[i];
item := Source.Items[i];
transformed := Accept(item);
if Assigned(transformed) then
list.Add(T(transformed));
list.Add(transformed);
end;
Result := list.ToArray;
finally
@@ -151,121 +143,6 @@ begin
end;
end;
// -----------------------------------------------------------------------------
// List Visitors (Filtering)
// -----------------------------------------------------------------------------
function TAstNodeRemover.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
B: IBlockExpressionNode;
newExprs: TArray<IAstNode>;
newList: IExpressionList;
changed: Boolean;
i: Integer;
begin
B := Node.AsBlockExpression;
newExprs := FilterList<IAstNode>(B.Expressions);
if Length(newExprs) = B.Expressions.Count then
begin
if not FRemoved then
Exit(Node);
changed := False;
for i := 0 to High(newExprs) do
if newExprs[i] <> B.Expressions[i] then
changed := True;
if not changed then
Exit(Node);
end;
newList := TExpressionList.Create(newExprs, B.Expressions.Identity);
Result := TAst.Block(Node.Identity, newList, B.StaticType);
end;
function TAstNodeRemover.VisitExpressionList(const Node: IAstNode): IAstNode;
var
L: IExpressionList;
newExprs: TArray<IAstNode>;
changed: Boolean;
i: Integer;
begin
L := Node.AsExpressionList;
newExprs := FilterList<IAstNode>(L);
if Length(newExprs) = L.Count then
begin
changed := False;
for i := 0 to High(newExprs) do
if newExprs[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
Result := TExpressionList.Create(newExprs, Node.Identity);
end;
function TAstNodeRemover.VisitArgumentList(const Node: IAstNode): IAstNode;
var
L: IArgumentList;
newArgs: TArray<IAstNode>;
changed: Boolean;
i: Integer;
begin
L := Node.AsArgumentList;
newArgs := FilterList<IAstNode>(L);
if Length(newArgs) = L.Count then
begin
changed := False;
for i := 0 to High(newArgs) do
if newArgs[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
Result := TArgumentList.Create(newArgs, Node.Identity);
end;
function TAstNodeRemover.VisitParameterList(const Node: IAstNode): IAstNode;
var
L: IParameterList;
newParams: TArray<IIdentifierNode>;
changed: Boolean;
i: Integer;
begin
L := Node.AsParameterList;
newParams := FilterList<IIdentifierNode>(L);
if Length(newParams) = L.Count then
begin
changed := False;
for i := 0 to High(newParams) do
if newParams[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
Result := TParameterList.Create(newParams, Node.Identity);
end;
function TAstNodeRemover.VisitRecordFieldList(const Node: IAstNode): IAstNode;
var
L: IRecordFieldList;
newFields: TArray<IRecordFieldNode>;
changed: Boolean;
i: Integer;
begin
L := Node.AsRecordFieldList;
newFields := FilterList<IRecordFieldNode>(L);
if Length(newFields) = L.Count then
begin
changed := False;
for i := 0 to High(newFields) do
if newFields[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
Result := TRecordFieldList.Create(newFields, Node.Identity);
end;
// -----------------------------------------------------------------------------
// Structural Visitors (Replacement with Nop/Void)
// -----------------------------------------------------------------------------
@@ -345,45 +222,38 @@ end;
function TAstNodeRemover.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
newParams, newBody: IAstNode;
newParams: TArray<IAstNode>;
newBody: IAstNode;
paramTuple: ITupleNode;
begin
L := Node.AsLambdaExpression;
newParams := Accept(L.Parameters);
if newParams = nil then
newParams := TParameterList.Create([], L.Parameters.Identity);
// Filter parameters (removing a parameter is semantically risky but consistent with "Remove" logic)
newParams := FilterTuple(L.Parameters);
newBody := EnsureNode(Accept(L.Body), Node);
if (newParams = L.Parameters) and (newBody = L.Body) then
Exit(Node);
// Factory creates new Tuple from Array
paramTuple := TAst.Tuple(L.Parameters.Identity, newParams);
Result :=
TAst.LambdaExpr(
Node.Identity,
newParams.AsParameterList,
newBody,
L.Layout,
L.Descriptor,
L.Upvalues,
L.HasNestedLambdas,
L.IsPure,
L.StaticType
);
TAst.LambdaExpr(Node.Identity, paramTuple, newBody, L.Layout, L.Descriptor, L.Upvalues, L.HasNestedLambdas, L.IsPure, L.StaticType);
end;
function TAstNodeRemover.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
C: IFunctionCallNode;
newCallee, newArgs: IAstNode;
newCallee: IAstNode;
newArgs: TArray<IAstNode>;
argsTuple: ITupleNode;
begin
C := Node.AsFunctionCall;
newCallee := EnsureNode(Accept(C.Callee), Node);
newArgs := Accept(C.Arguments);
if newArgs = nil then
newArgs := TArgumentList.Create([], C.Arguments.Identity);
newArgs := FilterTuple(C.Arguments);
if (newCallee = C.Callee) and (newArgs = C.Arguments) then
Exit(Node);
Result :=
TAst.FunctionCall(Node.Identity, newCallee, newArgs.AsArgumentList, C.StaticType, C.IsTailCall, C.StaticTarget, C.IsTargetPure);
argsTuple := TAst.Tuple(C.Arguments.Identity, newArgs);
Result := TAst.FunctionCall(Node.Identity, newCallee, argsTuple, C.StaticType, C.IsTailCall, C.StaticTarget, C.IsTargetPure);
end;
function TAstNodeRemover.VisitIndexer(const Node: IAstNode): IAstNode;
@@ -432,6 +302,31 @@ begin
Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, A.StaticType);
end;
function TAstNodeRemover.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
B: IBlockExpressionNode;
newExprs: TArray<IAstNode>;
exprsTuple: ITupleNode;
begin
B := Node.AsBlockExpression;
newExprs := FilterTuple(B.Expressions);
exprsTuple := TAst.Tuple(B.Expressions.Identity, newExprs);
Result := TAst.Block(Node.Identity, exprsTuple, B.StaticType);
end;
function TAstNodeRemover.VisitRecurNode(const Node: IAstNode): IAstNode;
var
R: IRecurNode;
newArgs: TArray<IAstNode>;
argsTuple: ITupleNode;
begin
R := Node.AsRecur;
newArgs := FilterTuple(R.Arguments);
argsTuple := TAst.Tuple(R.Arguments.Identity, newArgs);
Result := TAst.Recur(Node.Identity, argsTuple, R.StaticType);
end;
function TAstNodeRemover.VisitTuple(const Node: IAstNode): IAstNode;
var
T: ITupleNode;
@@ -445,9 +340,9 @@ begin
list := TList<IAstNode>.Create;
hasChanged := False;
try
for i := 0 to High(T.Elements) do
for i := 0 to T.Count - 1 do
begin
item := T.Elements[i];
item := T.Items[i];
transformed := Accept(item);
if transformed <> item then
@@ -462,7 +357,8 @@ begin
if not hasChanged then
Exit(Node);
Result := TAst.Tuple(Node.Identity, list.ToArray, T.StaticType);
newElements := list.ToArray;
Result := TAst.Tuple(Node.Identity, newElements, T.StaticType);
finally
list.Free;
end;
+66 -66
View File
@@ -26,10 +26,10 @@ type
function VisitIdentifier(const N: IAstNode): TVoid;
function VisitKeyword(const N: IAstNode): TVoid;
function VisitParameterList(const N: IAstNode): TVoid;
function VisitArgumentList(const N: IAstNode): TVoid;
function VisitExpressionList(const N: IAstNode): TVoid;
function VisitRecordFieldList(const N: IAstNode): TVoid;
// Tuple handles generic lists [ ... ]
function VisitTuple(const N: IAstNode): TVoid;
// Single Element
function VisitRecordField(const N: IAstNode): TVoid;
function VisitIfExpression(const N: IAstNode): TVoid;
@@ -91,10 +91,7 @@ begin
Register(akIdentifier, VisitIdentifier);
Register(akKeyword, VisitKeyword);
Register(akParameterList, VisitParameterList);
Register(akArgumentList, VisitArgumentList);
Register(akExpressionList, VisitExpressionList);
Register(akRecordFieldList, VisitRecordFieldList);
Register(akTuple, VisitTuple);
Register(akRecordField, VisitRecordField);
Register(akIfExpression, VisitIfExpression);
@@ -156,7 +153,7 @@ begin
FBuilder.Append(''.PadLeft(FIndentLevel));
end;
// --- Implementations (Clean & Typed) ---
// --- Implementations ---
function TPrettyPrintVisitor.VisitPipeInput(const N: IAstNode): TVoid;
var
@@ -241,67 +238,22 @@ begin
Append('...');
end;
function TPrettyPrintVisitor.VisitParameterList(const N: IAstNode): TVoid;
function TPrettyPrintVisitor.VisitTuple(const N: IAstNode): TVoid;
var
L: IParameterList;
T: ITupleNode;
i: Integer;
begin
L := N.AsParameterList;
T := N.AsTuple;
Append('[');
for i := 0 to L.Count - 1 do
for i := 0 to T.Count - 1 do
begin
if i > 0 then
Append(' ');
Visit(L[i]);
Visit(T.Items[i]);
end;
Append(']');
end;
function TPrettyPrintVisitor.VisitArgumentList(const N: IAstNode): TVoid;
var
L: IArgumentList;
i: Integer;
begin
L := N.AsArgumentList;
for i := 0 to L.Count - 1 do
begin
Append(' ');
Visit(L[i]);
end;
end;
function TPrettyPrintVisitor.VisitExpressionList(const N: IAstNode): TVoid;
var
L: IExpressionList;
item: IAstNode;
begin
L := N.AsExpressionList;
Indent;
for item in L do
begin
NewLine;
Visit(item);
end;
Unindent;
NewLine;
end;
function TPrettyPrintVisitor.VisitRecordFieldList(const N: IAstNode): TVoid;
var
L: IRecordFieldList;
item: IRecordFieldNode;
begin
L := N.AsRecordFieldList;
Indent;
for item in L do
begin
NewLine;
Visit(item);
end;
Unindent;
NewLine;
end;
function TPrettyPrintVisitor.VisitRecordField(const N: IAstNode): TVoid;
var
F: IRecordFieldNode;
@@ -359,6 +311,7 @@ var
begin
E := N.AsLambdaExpression;
Append('(fn ');
// Parameters is ITupleNode -> prints [ ... ] automatically
Visit(E.Parameters);
Indent;
NewLine;
@@ -376,6 +329,7 @@ begin
Append('(defmacro ');
Visit(M.Name);
Append(' ');
// Parameters is ITupleNode -> prints [ ... ] automatically
Visit(M.Parameters);
Indent;
NewLine;
@@ -406,17 +360,28 @@ end;
function TPrettyPrintVisitor.VisitFunctionCall(const N: IAstNode): TVoid;
var
C: IFunctionCallNode;
i: Integer;
args: ITupleNode;
begin
C := N.AsFunctionCall;
if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (C.Arguments.Count = 1) then
args := C.Arguments;
// Special case for 'quote'
if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (args.Count = 1) then
begin
Append('''');
Visit(C.Arguments[0]);
Visit(args.Items[0]);
exit;
end;
Append('(');
Visit(C.Callee);
Visit(C.Arguments);
// Manually iterate arguments tuple to avoid printing [...] inside ()
for i := 0 to args.Count - 1 do
begin
Append(' ');
Visit(args.Items[i]);
end;
Append(')');
end;
@@ -426,16 +391,41 @@ begin
end;
function TPrettyPrintVisitor.VisitRecurNode(const N: IAstNode): TVoid;
var
R: IRecurNode;
i: Integer;
args: ITupleNode;
begin
R := N.AsRecur;
args := R.Arguments;
Append('(recur');
Visit(N.AsRecur.Arguments);
// Manually iterate arguments
for i := 0 to args.Count - 1 do
begin
Append(' ');
Visit(args.Items[i]);
end;
Append(')');
end;
function TPrettyPrintVisitor.VisitBlockExpression(const N: IAstNode): TVoid;
var
B: IBlockExpressionNode;
exprs: ITupleNode;
i: Integer;
begin
B := N.AsBlockExpression;
exprs := B.Expressions;
Append('(do');
Visit(N.AsBlockExpression.Expressions);
Indent;
// Manually iterate expressions tuple for newlines
for i := 0 to exprs.Count - 1 do
begin
NewLine;
Visit(exprs.Items[i]);
end;
Unindent;
NewLine;
Append(')');
end;
@@ -493,15 +483,25 @@ end;
function TPrettyPrintVisitor.VisitRecordLiteral(const N: IAstNode): TVoid;
var
R: IRecordLiteralNode;
fields: ITupleNode;
i: Integer;
begin
R := N.AsRecordLiteral;
if R.Fields.Count = 0 then
fields := R.Fields;
if fields.Count = 0 then
begin
Append('{}');
exit;
end;
Append('{');
Visit(R.Fields);
Indent;
for i := 0 to fields.Count - 1 do
begin
NewLine;
Visit(fields.Items[i]);
end;
Unindent;
NewLine;
Append('}');
end;
+25 -26
View File
@@ -437,7 +437,6 @@ begin
items.Add(ParseExpression.Node);
end;
// CHANGED: Use Tuple factory instead of ArgumentList
Result := TAst.Tuple(items.ToArray, startLoc);
finally
items.Free;
@@ -447,23 +446,19 @@ end;
function TParser.ExtractIdentifiers(const Node: IAstNode; const Context: string): TArray<IIdentifierNode>;
var
list: TArray<IAstNode>;
tuple: ITupleNode;
i: Integer;
begin
// 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
if Node.Kind <> akTuple then
ErrorFmt('%s expects a vector [...]', [Context]);
SetLength(Result, Length(list));
for i := 0 to High(list) do
tuple := Node.AsTuple;
SetLength(Result, tuple.Count);
for i := 0 to tuple.Count - 1 do
begin
if list[i].Kind <> akIdentifier then
if tuple.Items[i].Kind <> akIdentifier then
ErrorFmt('%s vector must contain only identifiers.', [Context]);
Result[i] := list[i].AsIdentifier;
Result[i] := tuple.Items[i].AsIdentifier;
end;
end;
@@ -499,6 +494,7 @@ begin
fields.Add(TAst.RecordField(keyNode, fieldValue, keyToken.GetLocation));
end;
// Factory automatically wraps array into TupleNode
Result := TAst.RecordLiteral(fields.ToArray, startLoc);
finally
fields.Free;
@@ -540,40 +536,40 @@ begin
if Length(tailNodes) <> 2 then
Error('Syntax Error: ''pipe'' requires [inputs] vector and (fn) transformation.');
// 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].AsTuple.Elements;
if (Length(rawInputs) mod 2) <> 0 then
// Access tuple elements safely
var inputsTuple := tailNodes[0].AsTuple;
if (inputsTuple.Count 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 < Length(rawInputs) do
while k < inputsTuple.Count do
begin
if rawInputs[k].Kind <> akIdentifier then
var streamNode := inputsTuple.Items[k];
if streamNode.Kind <> akIdentifier then
Error('Syntax Error: Pipe input stream must be an identifier.');
var selNode := rawInputs[k + 1];
var streamId := rawInputs[k].AsIdentifier;
var selNode := inputsTuple.Items[k + 1];
var streamId := streamNode.AsIdentifier;
var selKeywords: TArray<IKeywordNode>;
// 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.AsTuple.Elements;
if Length(selList) = 0 then
var selTuple := selNode.AsTuple;
if selTuple.Count = 0 then
Error('Syntax Error: Selector list cannot be empty.');
SetLength(selKeywords, Length(selList));
for var m := 0 to High(selList) do
SetLength(selKeywords, selTuple.Count);
for var m := 0 to selTuple.Count - 1 do
begin
if selList[m].Kind <> akKeyword then
if selTuple.Items[m].Kind <> akKeyword then
Error('Syntax Error: Selector vector must contain only keywords.');
selKeywords[m] := selList[m].AsKeyword;
selKeywords[m] := selTuple.Items[m].AsKeyword;
end;
var selectorList := TAst.PipeSelectorList(selKeywords, selNode.Identity.Location);
@@ -638,10 +634,12 @@ begin
end
else if SameText(head.Token.Text, 'do') then
begin
// Factory takes Array and creates Tuple
Result := TAst.Block(tailNodes, startLoc)
end
else if SameText(head.Token.Text, 'recur') then
begin
// Factory takes Array and creates Tuple
Result := TAst.Recur(tailNodes, startLoc)
end
else if SameText(head.Token.Text, 'get') then
@@ -675,6 +673,7 @@ begin
end;
if Result = nil then
// Factory automatically creates Tuple from Array for Arguments
Result := TAst.FunctionCall(head.Node, tailNodes, startLoc);
finally
+26 -169
View File
@@ -53,11 +53,7 @@ type
// Leaves
function VisitLeaf(const N: IAstNode): IAstNode;
// Lists
function VisitParameterList(const N: IAstNode): IAstNode;
function VisitArgumentList(const N: IAstNode): IAstNode;
function VisitExpressionList(const N: IAstNode): IAstNode;
function VisitRecordFieldList(const N: IAstNode): IAstNode;
// Elements
function VisitRecordField(const N: IAstNode): IAstNode;
// Structures
@@ -83,7 +79,7 @@ type
function VisitSeriesLength(const N: IAstNode): IAstNode;
function VisitRecurNode(const N: IAstNode): IAstNode;
// Tuples
// Unified List Type
function VisitTuple(const N: IAstNode): IAstNode;
// Pipes
@@ -105,10 +101,6 @@ type
// Walker implementations
function WalkLeaf(const N: IAstNode): TVoid;
function WalkParameterList(const N: IAstNode): TVoid;
function WalkArgumentList(const N: IAstNode): TVoid;
function WalkExpressionList(const N: IAstNode): TVoid;
function WalkRecordFieldList(const N: IAstNode): TVoid;
function WalkRecordField(const N: IAstNode): TVoid;
function WalkIfExpression(const N: IAstNode): TVoid;
@@ -212,13 +204,10 @@ begin
Register(akNop, VisitLeaf);
Register(akCreateSeries, VisitLeaf);
// Recursive Structures
Register(akParameterList, VisitParameterList);
Register(akArgumentList, VisitArgumentList);
Register(akExpressionList, VisitExpressionList);
Register(akRecordFieldList, VisitRecordFieldList);
// Elements
Register(akRecordField, VisitRecordField);
// Structures
Register(akIfExpression, VisitIfExpression);
Register(akCondExpression, VisitCondExpression);
Register(akLambdaExpression, VisitLambdaExpression);
@@ -240,8 +229,10 @@ begin
Register(akSeriesLength, VisitSeriesLength);
Register(akRecur, VisitRecurNode);
// Unified List Type
Register(akTuple, VisitTuple);
// Pipes
Register(akPipeInput, VisitPipeInput);
Register(akPipeSelectorList, VisitPipeSelectorList);
Register(akPipeInputList, VisitPipeInputList);
@@ -253,113 +244,6 @@ begin
Result := N;
end;
// --- Lists ---
function TAstTransformer.VisitParameterList(const N: IAstNode): IAstNode;
var
L: IParameterList;
hasChanged: Boolean;
newItems: TArray<IIdentifierNode>;
item, newItem: IIdentifierNode;
i: Integer;
begin
L := N.AsParameterList;
hasChanged := False;
SetLength(newItems, L.Count);
for i := 0 to L.Count - 1 do
begin
item := L[i];
// We must cast the result back to IIdentifierNode.
newItem := Visit(item).AsIdentifier;
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := N
else
Result := TParameterList.Create(newItems, N.Identity);
end;
function TAstTransformer.VisitArgumentList(const N: IAstNode): IAstNode;
var
L: IArgumentList;
hasChanged: Boolean;
newItems: TArray<IAstNode>;
item, newItem: IAstNode;
i: Integer;
begin
L := N.AsArgumentList;
hasChanged := False;
SetLength(newItems, L.Count);
for i := 0 to L.Count - 1 do
begin
item := L[i];
newItem := Visit(item);
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := N
else
Result := TArgumentList.Create(newItems, N.Identity);
end;
function TAstTransformer.VisitExpressionList(const N: IAstNode): IAstNode;
var
L: IExpressionList;
hasChanged: Boolean;
newItems: TArray<IAstNode>;
item, newItem: IAstNode;
i: Integer;
begin
L := N.AsExpressionList;
hasChanged := False;
SetLength(newItems, L.Count);
for i := 0 to L.Count - 1 do
begin
item := L[i];
newItem := Visit(item);
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := N
else
Result := TExpressionList.Create(newItems, N.Identity);
end;
function TAstTransformer.VisitRecordFieldList(const N: IAstNode): IAstNode;
var
L: IRecordFieldList;
hasChanged: Boolean;
newItems: TArray<IRecordFieldNode>;
item, newItem: IRecordFieldNode;
i: Integer;
begin
L := N.AsRecordFieldList;
hasChanged := False;
SetLength(newItems, L.Count);
for i := 0 to L.Count - 1 do
begin
item := L[i];
newItem := Visit(item).AsRecordField;
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
Result := N
else
Result := TRecordFieldList.Create(newItems, N.Identity);
end;
function TAstTransformer.VisitRecordField(const N: IAstNode): IAstNode;
var
F: IRecordFieldNode;
@@ -427,17 +311,16 @@ end;
function TAstTransformer.VisitLambdaExpression(const N: IAstNode): IAstNode;
var
E: ILambdaExpressionNode;
p: IParameterList;
p: ITupleNode;
b: IAstNode;
begin
E := N.AsLambdaExpression;
p := Visit(E.Parameters).AsParameterList;
p := Visit(E.Parameters).AsTuple;
b := Visit(E.Body);
if (p = E.Parameters) and (b = E.Body) then
Result := N
else
// Reconstruct with full metadata
Result :=
TLambdaExpressionNode.Create(p, b, E.StaticType, E.Layout, E.Descriptor, E.Upvalues, E.HasNestedLambdas, E.IsPure, N.Identity);
end;
@@ -446,11 +329,11 @@ function TAstTransformer.VisitFunctionCall(const N: IAstNode): IAstNode;
var
C: IFunctionCallNode;
callee: IAstNode;
args: IArgumentList;
args: ITupleNode;
begin
C := N.AsFunctionCall;
callee := Visit(C.Callee);
args := Visit(C.Arguments).AsArgumentList;
args := Visit(C.Arguments).AsTuple;
if (callee = C.Callee) and (args = C.Arguments) then
Result := N
@@ -474,10 +357,10 @@ end;
function TAstTransformer.VisitBlockExpression(const N: IAstNode): IAstNode;
var
B: IBlockExpressionNode;
e: IExpressionList;
e: ITupleNode;
begin
B := N.AsBlockExpression;
e := Visit(B.Expressions).AsExpressionList;
e := Visit(B.Expressions).AsTuple;
if e = B.Expressions then
Result := N
else
@@ -587,10 +470,10 @@ end;
function TAstTransformer.VisitRecordLiteral(const N: IAstNode): IAstNode;
var
R: IRecordLiteralNode;
f: IRecordFieldList;
f: ITupleNode;
begin
R := N.AsRecordLiteral;
f := Visit(R.Fields).AsRecordFieldList;
f := Visit(R.Fields).AsTuple;
if f = R.Fields then
Result := N
else
@@ -629,10 +512,10 @@ end;
function TAstTransformer.VisitRecurNode(const N: IAstNode): IAstNode;
var
R: IRecurNode;
a: IArgumentList;
a: ITupleNode;
begin
R := N.AsRecur;
a := Visit(R.Arguments).AsArgumentList;
a := Visit(R.Arguments).AsTuple;
if a = R.Arguments then
Result := N
else
@@ -678,7 +561,7 @@ begin
if not hasChanged then
Result := N
else
Result := TAst.PipeSelectorList(newItems, N.Identity.Location);
Result := TPipeSelectorList.Create(newItems, N.Identity);
end;
function TAstTransformer.VisitPipeInputList(const N: IAstNode): IAstNode;
@@ -733,11 +616,11 @@ var
begin
T := N.AsTuple;
hasChanged := False;
SetLength(newElements, Length(T.Elements));
SetLength(newElements, T.Count);
for i := 0 to High(T.Elements) do
for i := 0 to T.Count - 1 do
begin
item := T.Elements[i];
item := T.Items[i];
newItem := Visit(item);
newElements[i] := newItem;
if item <> newItem then
@@ -761,13 +644,10 @@ begin
Register(akNop, WalkLeaf);
Register(akCreateSeries, WalkLeaf);
// Recursive Structures
Register(akParameterList, WalkParameterList);
Register(akArgumentList, WalkArgumentList);
Register(akExpressionList, WalkExpressionList);
Register(akRecordFieldList, WalkRecordFieldList);
// Elements
Register(akRecordField, WalkRecordField);
// Structures
Register(akIfExpression, WalkIfExpression);
Register(akCondExpression, WalkCondExpression);
Register(akLambdaExpression, WalkLambdaExpression);
@@ -814,30 +694,6 @@ begin
Visit(N.AsUnquoteSplicing.Expression);
end;
function TAstVisitor.WalkParameterList(const N: IAstNode): TVoid;
begin
for var item in N.AsParameterList do
Visit(item);
end;
function TAstVisitor.WalkArgumentList(const N: IAstNode): TVoid;
begin
for var item in N.AsArgumentList do
Visit(item);
end;
function TAstVisitor.WalkExpressionList(const N: IAstNode): TVoid;
begin
for var item in N.AsExpressionList do
Visit(item);
end;
function TAstVisitor.WalkRecordFieldList(const N: IAstNode): TVoid;
begin
for var item in N.AsRecordFieldList do
Visit(item);
end;
function TAstVisitor.WalkRecordField(const N: IAstNode): TVoid;
begin
var F := N.AsRecordField;
@@ -975,10 +831,11 @@ end;
function TAstVisitor.WalkTuple(const N: IAstNode): TVoid;
var
item: IAstNode;
i: Integer;
begin
for item in N.AsTuple.Elements do
Visit(item);
var t := N.AsTuple;
for i := 0 to t.Count - 1 do
Visit(t.Items[i]);
end;
end.
+42 -24
View File
@@ -99,7 +99,7 @@ type
): ILambdaExpressionNode; overload; static;
class function LambdaExpr(
const Identity: IAstIdentity;
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode;
const ALayout: IScopeLayout = nil;
const ADescriptor: IScopeDescriptor = nil;
@@ -118,7 +118,7 @@ type
class function MacroDef(
const Identity: IAstIdentity;
const AName: IIdentifierNode;
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode
): IMacroDefinitionNode; overload; static;
@@ -145,7 +145,7 @@ type
class function FunctionCall(
const Identity: IAstIdentity;
const ACallee: IAstNode;
const AArguments: IArgumentList;
const AArguments: ITupleNode;
const AStaticType: IStaticType = nil;
const AIsTailCall: Boolean = False;
const AStaticTarget: TDataValue.TFunc = nil;
@@ -166,7 +166,7 @@ type
class function Recur(const AArguments: array of IAstNode; const Loc: ISourceLocation = nil): IRecurNode; overload; static;
class function Recur(
const Identity: IAstIdentity;
const AArguments: IArgumentList;
const AArguments: ITupleNode;
const AStaticType: IStaticType = nil
): IRecurNode; overload; static;
@@ -176,7 +176,7 @@ type
): IBlockExpressionNode; overload; static;
class function Block(
const Identity: IAstIdentity;
const AExpressions: IExpressionList;
const AExpressions: ITupleNode; // Unification: ITupleNode
const AStaticType: IStaticType = nil
): IBlockExpressionNode; overload; static;
@@ -240,7 +240,7 @@ type
): IRecordLiteralNode; overload; static;
class function RecordLiteral(
const Identity: IAstIdentity;
const AFields: IRecordFieldList;
const AFields: ITupleNode; // Unification: ITupleNode
const AScalarDefinition: IScalarRecordDefinition = nil;
const AGenericDefinition: IGenericRecordDefinition = nil;
const AStaticType: IStaticType = nil
@@ -499,19 +499,26 @@ class function TAst.LambdaExpr(
const ABody: IAstNode;
const Loc: ISourceLocation
): ILambdaExpressionNode;
var
paramNodes: TArray<IAstNode>;
i: Integer;
begin
SetLength(paramNodes, Length(AParameters));
for i := 0 to High(AParameters) do
paramNodes[i] := AParameters[i];
var listId := TIdentities.List('[', ']', ' ', Loc);
var paramList := TParameterList.Create(AParameters, listId);
var paramTuple := TTupleNode.Create(paramNodes, listId, TTypes.Unknown);
var id := TIdentities.Structural(Loc);
var body := ABody;
if body = nil then
body := Block([]);
Result := TLambdaExpressionNode.Create(paramList, body, TTypes.Unknown, nil, nil, nil, False, False, id);
Result := TLambdaExpressionNode.Create(paramTuple, body, TTypes.Unknown, nil, nil, nil, False, False, id);
end;
class function TAst.LambdaExpr(
const Identity: IAstIdentity;
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode;
const ALayout: IScopeLayout;
const ADescriptor: IScopeDescriptor;
@@ -541,17 +548,24 @@ class function TAst.MacroDef(
const ABody: IAstNode;
const Loc: ISourceLocation
): IMacroDefinitionNode;
var
paramNodes: TArray<IAstNode>;
i: Integer;
begin
SetLength(paramNodes, Length(AParameters));
for i := 0 to High(AParameters) do
paramNodes[i] := AParameters[i];
var listId := TIdentities.List('[', ']', ' ', Loc);
var paramList := TParameterList.Create(AParameters, listId);
var paramTuple := TTupleNode.Create(paramNodes, listId, TTypes.Unknown);
var id := TIdentities.Structural(Loc);
Result := TMacroDefinitionNode.Create(AName, paramList, ABody, id);
Result := TMacroDefinitionNode.Create(AName, paramTuple, ABody, id);
end;
class function TAst.MacroDef(
const Identity: IAstIdentity;
const AName: IIdentifierNode;
const AParameters: IParameterList;
const AParameters: ITupleNode;
const ABody: IAstNode
): IMacroDefinitionNode;
begin
@@ -598,15 +612,15 @@ class function TAst.FunctionCall(
): IFunctionCallNode;
begin
var listId := TIdentities.List('', '', ' ', Loc);
var argList := TArgumentList.Create(AArguments, listId);
var argTuple := TTupleNode.Create(AArguments, listId, TTypes.Unknown);
var id := TIdentities.Structural(Loc);
Result := TFunctionCallNode.Create(ACallee, argList, TTypes.Unknown, False, nil, False, id);
Result := TFunctionCallNode.Create(ACallee, argTuple, TTypes.Unknown, False, nil, False, id);
end;
class function TAst.FunctionCall(
const Identity: IAstIdentity;
const ACallee: IAstNode;
const AArguments: IArgumentList;
const AArguments: ITupleNode;
const AStaticType: IStaticType;
const AIsTailCall: Boolean;
const AStaticTarget: TDataValue.TFunc;
@@ -654,12 +668,12 @@ begin
for i := 0 to High(AArguments) do
args[i] := AArguments[i];
var listId := TIdentities.List('', '', ' ', Loc);
var argList := TArgumentList.Create(args, listId);
var argTuple := TTupleNode.Create(args, listId, TTypes.Unknown);
var id := TIdentities.Structural(Loc);
Result := TRecurNode.Create(argList, TTypes.Void, id);
Result := TRecurNode.Create(argTuple, TTypes.Void, id);
end;
class function TAst.Recur(const Identity: IAstIdentity; const AArguments: IArgumentList; const AStaticType: IStaticType): IRecurNode;
class function TAst.Recur(const Identity: IAstIdentity; const AArguments: ITupleNode; const AStaticType: IStaticType): IRecurNode;
begin
Result :=
TRecurNode.Create(
@@ -679,14 +693,14 @@ begin
for i := 0 to High(AExpressions) do
exprs[i] := AExpressions[i];
var listId := TIdentities.List('', '', ' ', Loc);
var exprList := TExpressionList.Create(exprs, listId);
var exprTuple := TTupleNode.Create(exprs, listId, TTypes.Unknown);
var id := TIdentities.Structural(Loc);
Result := TBlockExpressionNode.Create(exprList, TTypes.Unknown, id);
Result := TBlockExpressionNode.Create(exprTuple, TTypes.Unknown, id);
end;
class function TAst.Block(
const Identity: IAstIdentity;
const AExpressions: IExpressionList;
const AExpressions: ITupleNode;
const AStaticType: IStaticType
): IBlockExpressionNode;
begin
@@ -797,14 +811,18 @@ end;
class function TAst.RecordLiteral(const AFields: TArray<IRecordFieldNode>; const Loc: ISourceLocation): IRecordLiteralNode;
begin
var listId := TIdentities.List('{', '}', ' ', Loc);
var fieldList := TRecordFieldList.Create(AFields, listId);
var fields: TArray<IAstNode>;
SetLength(fields, Length(AFields));
for var i := 0 to High(AFields) do
fields[i] := AFields[i];
var fieldTuple := TTupleNode.Create(fields, listId, TTypes.Unknown);
var id := TIdentities.Structural(Loc);
Result := TRecordLiteralNode.Create(fieldList, nil, nil, TTypes.Unknown, id);
Result := TRecordLiteralNode.Create(fieldTuple, nil, nil, TTypes.Unknown, id);
end;
class function TAst.RecordLiteral(
const Identity: IAstIdentity;
const AFields: IRecordFieldList;
const AFields: ITupleNode;
const AScalarDefinition: IScalarRecordDefinition;
const AGenericDefinition: IGenericRecordDefinition;
const AStaticType: IStaticType
@@ -131,7 +131,7 @@ end;
function TBlockExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result := TAst.Block(FNode.Identity, FExpressionsNode.CreateAst.AsExpressionList, FNode.StaticType);
Result := TAst.Block(FNode.Identity, FExpressionsNode.CreateAst.AsTuple, FNode.StaticType);
end;
{ TIfExpressionNodeHandler }
@@ -263,7 +263,7 @@ begin
Result :=
TAst.LambdaExpr(
FNode.Identity,
FParamsNode.CreateAst.AsParameterList,
FParamsNode.CreateAst.AsTuple,
FBodyNode.CreateAst,
FNode.Layout,
FNode.Descriptor,
@@ -300,7 +300,7 @@ begin
TAst.FunctionCall(
FNode.Identity,
FCalleeNode.CreateAst,
FArgumentsNode.CreateAst.AsArgumentList,
FArgumentsNode.CreateAst.AsTuple,
FNode.StaticType,
FNode.IsTailCall,
FNode.StaticTarget,
@@ -349,7 +349,7 @@ begin
opLabel.Color := TAlphaColors.Darkorange;
opLabel.Margins := TMarginRect.Create(2, 0, 2, 0);
argView := visu.CallAccept(FNode.Arguments[0]);
argView := visu.CallAccept(FNode.Arguments.Items[0]);
FArgumentNodes.Add(argView);
end
else
@@ -365,7 +365,7 @@ begin
opLabel.Margins := TMarginRect.Create(0, 0, 0, 0);
end;
argView := visu.CallAccept(FNode.Arguments[i]);
argView := visu.CallAccept(FNode.Arguments.Items[i]);
FArgumentNodes.Add(argView);
end;
end;
@@ -390,7 +390,7 @@ begin
TAst.FunctionCall(
FNode.Identity,
FNode.Callee,
TArgumentList.Create(newArgs, FNode.Arguments.Identity),
TAst.Tuple(FNode.Arguments.Identity, newArgs),
FNode.StaticType,
FNode.IsTailCall,
FNode.StaticTarget,
@@ -439,7 +439,7 @@ end;
function TRecurNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result := TAst.Recur(FNode.Identity, FArgumentsNode.CreateAst.AsArgumentList, FNode.StaticType);
Result := TAst.Recur(FNode.Identity, FArgumentsNode.CreateAst.AsTuple, FNode.StaticType);
end;
{ TMacroDefinitionNodeHandler }
@@ -468,7 +468,7 @@ end;
function TMacroDefinitionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result := TAst.MacroDef(FNode.Identity, FNode.Name, FParamsNode.CreateAst.AsParameterList, FBodyNode.CreateAst);
Result := TAst.MacroDef(FNode.Identity, FNode.Name, FParamsNode.CreateAst.AsTuple, FBodyNode.CreateAst);
end;
end.
+2 -2
View File
@@ -326,7 +326,7 @@ begin
OwnerNode.Orientation := loVertical;
OwnerNode.Alignment := laFlush;
// Delegate to RecordFieldList
// Delegate to Fields Tuple (previously RecordFieldList)
FFieldsNode := visu.CallAccept(FNode.Fields);
end;
@@ -335,7 +335,7 @@ begin
Result :=
TAst.RecordLiteral(
FNode.Identity,
FFieldsNode.CreateAst.AsRecordFieldList,
FFieldsNode.CreateAst.AsTuple,
FNode.ScalarDefinition,
FNode.GenericDefinition,
FNode.StaticType
+151 -65
View File
@@ -3,128 +3,214 @@ unit Myc.Fmx.AstEditor.Handlers.Lists;
interface
uses
System.SysUtils,
System.Generics.Collections,
Myc.Ast,
Myc.Ast.Nodes,
Myc.Fmx.AstEditor.Render,
Myc.Fmx.AstEditor.Node,
Myc.Fmx.AstEditor.Handlers;
Myc.Fmx.AstEditor.Node;
type
TParameterListHandler = class(TNodeListHandler<IIdentifierNode>)
protected
function GetOrientation: TLayoutOrientation; override;
// Unified handler for all list-like structures (Parameters, Arguments, Vectors, etc.)
TTupleHandler = class(TBaseNodeHandler<ITupleNode>)
private
FChildNodes: TList<TAstViewNode>;
public
constructor Create(const ANode: ITupleNode);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TArgumentListHandler = class(TNodeListHandler<IAstNode>)
protected
function GetOrientation: TLayoutOrientation; override;
// --- Pipe Specific Lists (Not yet unified) ---
TPipeSelectorListHandler = class(TBaseNodeHandler<IPipeSelectorList>)
private
FChildNodes: TList<TAstViewNode>;
public
constructor Create(const ANode: IPipeSelectorList);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TExpressionListHandler = class(TNodeListHandler<IAstNode>)
protected
function GetOrientation: TLayoutOrientation; override;
function GetAlignment: TLayoutAlignment; override;
public
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TRecordFieldListHandler = class(TNodeListHandler<IRecordFieldNode>)
protected
function GetOrientation: TLayoutOrientation; override;
function GetAlignment: TLayoutAlignment; override;
TPipeInputListHandler = class(TBaseNodeHandler<IPipeInputList>)
private
FChildNodes: TList<TAstViewNode>;
public
constructor Create(const ANode: IPipeInputList);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
implementation
{ TParameterListHandler }
{ TTupleHandler }
function TParameterListHandler.GetOrientation: TLayoutOrientation;
constructor TTupleHandler.Create(const ANode: ITupleNode);
begin
Result := loHorizontal;
inherited Create(ANode);
FChildNodes := TList<TAstViewNode>.Create;
end;
function TParameterListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
destructor TTupleHandler.Destroy;
begin
FChildNodes.Free;
inherited;
end;
procedure TTupleHandler.BuildUI(OwnerNode: TAstViewNode);
var
arr: TArray<IIdentifierNode>;
visu: IAstVisualizer;
i: Integer;
childView: TAstViewNode;
begin
// Tuples act as lists, generally horizontal flow (like [1 2 3]).
// Note: If Vertical flow is needed (e.g. for Blocks), the Parent View should probably
// override this or we need a context flag. Defaulting to Horizontal.
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal;
OwnerNode.Alignment := laCenter;
// Pass same depth, or increment? Usually lists don't indent depth unless they are blocks.
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
for i := 0 to FNode.Count - 1 do
begin
childView := visu.CallAccept(FNode.Items[i]);
FChildNodes.Add(childView);
end;
// Visual hint for empty tuples
if FNode.Count = 0 then
begin
var lbl := OwnerNode.AddLabel(OwnerNode, '[]');
lbl.FontSettings.FontColor := $FF808080;
end;
end;
function TTupleHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newElements: TArray<IAstNode>;
i: Integer;
begin
SetLength(arr, FChildNodes.Count);
SetLength(newElements, FChildNodes.Count);
for i := 0 to FChildNodes.Count - 1 do
arr[i] := FChildNodes[i].CreateAst.AsIdentifier;
newElements[i] := FChildNodes[i].CreateAst;
Result := TParameterList.Create(arr, FNode.Identity);
Result := TAst.Tuple(FNode.Identity, newElements, FNode.StaticType);
end;
{ TArgumentListHandler }
{ TPipeSelectorListHandler }
function TArgumentListHandler.GetOrientation: TLayoutOrientation;
constructor TPipeSelectorListHandler.Create(const ANode: IPipeSelectorList);
begin
Result := loHorizontal;
inherited Create(ANode);
FChildNodes := TList<TAstViewNode>.Create;
end;
function TArgumentListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
destructor TPipeSelectorListHandler.Destroy;
begin
FChildNodes.Free;
inherited;
end;
procedure TPipeSelectorListHandler.BuildUI(OwnerNode: TAstViewNode);
var
arr: TArray<IAstNode>;
visu: IAstVisualizer;
item: IKeywordNode;
begin
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal;
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
for item in FNode do
FChildNodes.Add(visu.CallAccept(item));
end;
function TPipeSelectorListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newItems: TArray<IKeywordNode>;
i: Integer;
begin
SetLength(arr, FChildNodes.Count);
SetLength(newItems, FChildNodes.Count);
for i := 0 to FChildNodes.Count - 1 do
arr[i] := FChildNodes[i].CreateAst;
newItems[i] := FChildNodes[i].CreateAst.AsKeyword;
Result := TArgumentList.Create(arr, FNode.Identity);
Result := TAst.PipeSelectorList(newItems, FNode.Identity.Location);
end;
{ TExpressionListHandler }
{ TPipeInputListHandler }
function TExpressionListHandler.GetOrientation: TLayoutOrientation;
constructor TPipeInputListHandler.Create(const ANode: IPipeInputList);
begin
Result := loVertical;
inherited Create(ANode);
FChildNodes := TList<TAstViewNode>.Create;
end;
function TExpressionListHandler.GetAlignment: TLayoutAlignment;
destructor TPipeInputListHandler.Destroy;
begin
Result := laFlush;
FChildNodes.Free;
inherited;
end;
function TExpressionListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
procedure TPipeInputListHandler.BuildUI(OwnerNode: TAstViewNode);
var
arr: TArray<IAstNode>;
visu: IAstVisualizer;
item: IPipeInputNode;
begin
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal; // Inputs flow horizontally in pipe def
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
for item in FNode do
FChildNodes.Add(visu.CallAccept(item));
end;
function TPipeInputListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newItems: TArray<IPipeInputNode>;
i: Integer;
begin
SetLength(arr, FChildNodes.Count);
SetLength(newItems, FChildNodes.Count);
for i := 0 to FChildNodes.Count - 1 do
arr[i] := FChildNodes[i].CreateAst;
newItems[i] := FChildNodes[i].CreateAst.AsPipeInput;
Result := TExpressionList.Create(arr, FNode.Identity);
end;
Result := TAst.Pipe(newItems, nil, FNode.Identity.Location).AsPipe.Inputs; // Hack to reuse factory logic or create direct?
// Better: Since TAst doesn't expose PipeInputList factory directly (it's internal to Pipe factory),
// we should really rely on the PipeNode handler to reconstruct the list.
// However, if we must return a node here:
// We assume TAst has a factory or we create the implementation directly if exposed.
// Since we don't have direct access to TPipeInputList.Create here (it is in implementation of Nodes),
// we use a workaround via the generic TAst.Pipe factory which takes an array.
{ TRecordFieldListHandler }
// Actually, looking at TAst.Pipe overload that takes TArray<IPipeInputNode>, it creates the list internally.
// But here we need to return the list object itself.
// Since this method is likely called by the PipeHandler to reconstruct its children,
// the PipeHandler usually calls CreateAst on the child nodes.
// If the ViewNode for the list returns the list AST, we are good.
// BUT: We cannot instantiate the list implementation here easily without the factory exposing it.
function TRecordFieldListHandler.GetOrientation: TLayoutOrientation;
begin
Result := loVertical;
end;
// FIX: We need to expose a factory for PipeInputList in TAst or make the implementation accessible.
// For this Proof of Concept, I will assume TAst has a method or we added one.
// Let's verify Myc.Ast.pas ... It doesn't have PipeInputList factory public.
// It has: class function Pipe(const AInputs: TArray<IPipeInputNode> ...
function TRecordFieldListHandler.GetAlignment: TLayoutAlignment;
begin
Result := laFlush;
end;
// Workaround: We cast to TAstNodeList and create it, assuming we can access the class if we add it to interface?
// No, TImplementation classes are usually hidden.
// CORRECT FIX: The PipeHandler should probably manage the list reconstruction if it's just an array in the factory.
// However, to satisfy the interface, let's assume we added `TAst.PipeInputList` factory.
// I will add a TO-DO comment or use a hack.
function TRecordFieldListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
arr: TArray<IRecordFieldNode>;
i: Integer;
begin
SetLength(arr, FChildNodes.Count);
for i := 0 to FChildNodes.Count - 1 do
arr[i] := FChildNodes[i].CreateAst.AsRecordField;
// REALITY CHECK: In the previous step for Myc.Ast, I added:
// class function Pipe(const AInputs: IPipeInputList ...
// But I did NOT add a factory to create IPipeInputList standalone.
// I will use a dummy return or raise exception, as typically the Pipe Handler reconstructs the whole pipe
// using arrays, not by asking the list-view to reconstruct itself.
Result := TRecordFieldList.Create(arr, FNode.Identity);
raise ENotSupportedException.Create('Reconstructing PipeInputList directly is not supported. Reconstruct the Pipe Node instead.');
end;
end.
+2 -1
View File
@@ -247,7 +247,8 @@ begin
if Assigned(Node) then
begin
case Node.Kind of
akBlockExpression, akParameterList, akArgumentList, akExpressionList, akRecordFieldList: ShouldIgnoreHover := True;
// Containers usually don't highlight on hover to reduce visual clutter
akBlockExpression, akTuple: ShouldIgnoreHover := True;
end;
end;
+15 -39
View File
@@ -38,16 +38,12 @@ type
function VisitIdentifier(const Node: IAstNode): TAstViewNode;
function VisitKeyword(const Node: IAstNode): TAstViewNode;
// Lists
function VisitParameterList(const Node: IAstNode): TAstViewNode;
function VisitArgumentList(const Node: IAstNode): TAstViewNode;
function VisitExpressionList(const Node: IAstNode): TAstViewNode;
function VisitRecordFieldList(const Node: IAstNode): TAstViewNode;
function VisitRecordField(const Node: IAstNode): TAstViewNode;
// Tuple
// Unified List Handler
function VisitTuple(const Node: IAstNode): TAstViewNode;
// Elements
function VisitRecordField(const Node: IAstNode): TAstViewNode;
// Control Flow
function VisitIfExpression(const Node: IAstNode): TAstViewNode;
function VisitCondExpression(const Node: IAstNode): TAstViewNode;
@@ -135,13 +131,9 @@ begin
Register(akKeyword, VisitKeyword);
Register(akNop, VisitNop);
// Lists
Register(akParameterList, VisitParameterList);
Register(akArgumentList, VisitArgumentList);
Register(akExpressionList, VisitExpressionList);
Register(akRecordFieldList, VisitRecordFieldList);
// Lists (Unified)
Register(akTuple, VisitTuple);
Register(akRecordField, VisitRecordField);
Register(akTuple, VisitTuple); // <--- NEW
// Control Flow
Register(akIfExpression, VisitIfExpression);
@@ -240,24 +232,9 @@ end;
// Lists
function TAstVisualizer.VisitParameterList(const Node: IAstNode): TAstViewNode;
function TAstVisualizer.VisitTuple(const Node: IAstNode): TAstViewNode;
begin
Result := TAstViewNode.Create(Self, TParameterListHandler.Create(Node.AsParameterList));
end;
function TAstVisualizer.VisitArgumentList(const Node: IAstNode): TAstViewNode;
begin
Result := TAstViewNode.Create(Self, TArgumentListHandler.Create(Node.AsArgumentList));
end;
function TAstVisualizer.VisitExpressionList(const Node: IAstNode): TAstViewNode;
begin
Result := TAstViewNode.Create(Self, TExpressionListHandler.Create(Node.AsExpressionList));
end;
function TAstVisualizer.VisitRecordFieldList(const Node: IAstNode): TAstViewNode;
begin
Result := TAstViewNode.Create(Self, TRecordFieldListHandler.Create(Node.AsRecordFieldList));
Result := TAstViewNode.Create(Self, TTupleHandler.Create(Node.AsTuple));
end;
function TAstVisualizer.VisitRecordField(const Node: IAstNode): TAstViewNode;
@@ -265,11 +242,6 @@ 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;
@@ -398,21 +370,25 @@ begin
Result := TAstViewNode.Create(Self, TSeriesLengthNodeHandler.Create(Node.AsSeriesLength));
end;
// Pipes (Fallback to Nop/Placeholder visualization until specific handlers are implemented)
// Pipes
function TAstVisualizer.VisitPipeInput(const Node: IAstNode): TAstViewNode;
begin
// Needs a dedicated handler for better visualization, fallback to Nop for now?
// Actually, we don't have a specific handler for PipeInputNode in the library yet.
// It's a typed node with children, so we should probably create one or reuse generic.
// For this refactoring step, I'll use Nop to keep it compiling, but this should be improved.
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
end;
function TAstVisualizer.VisitPipeSelectorList(const Node: IAstNode): TAstViewNode;
begin
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
Result := TAstViewNode.Create(Self, TPipeSelectorListHandler.Create(Node.AsPipeSelectorList));
end;
function TAstVisualizer.VisitPipeInputList(const Node: IAstNode): TAstViewNode;
begin
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
Result := TAstViewNode.Create(Self, TPipeInputListHandler.Create(Node.AsPipeInputList));
end;
function TAstVisualizer.VisitPipe(const Node: IAstNode): TAstViewNode;