unit Myc.Ast.Refactoring.Remove; interface uses System.SysUtils, System.Classes, System.Generics.Collections, Myc.Ast, Myc.Ast.Nodes, Myc.Ast.Visitor, Myc.Ast.Types; type // Removes a specific node from the AST. TAstNodeRemover = class(TAstTransformer) private FTarget: IAstNode; FRemoved: Boolean; function FilterTuple(const Source: ITupleNode): TArray; function EnsureNode(const Node: IAstNode; const ContextNode: IAstNode): IAstNode; strict private // Structural Nodes (Replace Logic) function VisitIfExpression(const Node: IAstNode): IAstNode; function VisitCondExpression(const Node: IAstNode): IAstNode; function VisitAssignment(const Node: IAstNode): IAstNode; function VisitVariableDeclaration(const Node: IAstNode): IAstNode; function VisitLambdaExpression(const Node: IAstNode): IAstNode; function VisitFunctionCall(const Node: IAstNode): IAstNode; 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; function Accept(const Node: IAstNode): IAstNode; override; public constructor Create(const ATarget: IAstNode); class function TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean; static; end; implementation { TAstNodeRemover } constructor TAstNodeRemover.Create(const ATarget: IAstNode); begin inherited Create; FTarget := ATarget; FRemoved := False; end; procedure TAstNodeRemover.SetupHandlers; begin inherited SetupHandlers; // Defaults // Replace Logic Register(akIfExpression, VisitIfExpression); Register(akCondExpression, VisitCondExpression); Register(akAssignment, VisitAssignment); Register(akVariableDeclaration, VisitVariableDeclaration); Register(akLambdaExpression, VisitLambdaExpression); Register(akFunctionCall, VisitFunctionCall); Register(akIndexer, VisitIndexer); Register(akMemberAccess, VisitMemberAccess); Register(akAddSeriesItem, VisitAddSeriesItem); Register(akBlockExpression, VisitBlockExpression); Register(akRecur, VisitRecurNode); // List Logic Register(akTuple, VisitTuple); end; class function TAstNodeRemover.TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean; var Remover: TAstNodeRemover; begin if (RootNode = TargetNode) then begin NewRootNode := TAst.Nop(RootNode.Identity); Result := True; Exit; end; Remover := TAstNodeRemover.Create(TargetNode); try NewRootNode := Remover.Accept(RootNode); Result := Remover.FRemoved; if not Result then NewRootNode := RootNode else if (NewRootNode = nil) then NewRootNode := TAst.Nop(RootNode.Identity); finally Remover.Free; end; end; function TAstNodeRemover.Accept(const Node: IAstNode): IAstNode; begin if Node = FTarget then begin FRemoved := True; Exit(nil); end; Result := inherited Accept(Node); end; function TAstNodeRemover.EnsureNode(const Node: IAstNode; const ContextNode: IAstNode): IAstNode; begin if Assigned(Node) then Result := Node else Result := TAst.Nop(ContextNode.Identity); end; function TAstNodeRemover.FilterTuple(const Source: ITupleNode): TArray; var i: Integer; item, transformed: IAstNode; list: TList; elements: TArray; begin list := TList.Create; try elements := Source.Elements; for i := 0 to High(elements) do begin item := elements[i]; transformed := Accept(item); if Assigned(transformed) then list.Add(transformed); end; Result := list.ToArray; finally list.Free; end; end; // ----------------------------------------------------------------------------- // Structural Visitors (Replacement with Nop/Void) // ----------------------------------------------------------------------------- function TAstNodeRemover.VisitIfExpression(const Node: IAstNode): IAstNode; var E: IIfExpressionNode; newCond, newThen, newElse: IAstNode; begin E := Node.AsIfExpression; newCond := EnsureNode(Accept(E.Condition), Node); newThen := EnsureNode(Accept(E.ThenBranch), Node); newElse := Accept(E.ElseBranch); // Optional if (newCond = E.Condition) and (newThen = E.ThenBranch) and (newElse = E.ElseBranch) then Exit(Node); Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, E.StaticType); end; function TAstNodeRemover.VisitCondExpression(const Node: IAstNode): IAstNode; var E: ICondExpressionNode; i: Integer; newPairs: TArray; newElse: IAstNode; newCond, newBranch: IAstNode; hasChanged: Boolean; begin E := Node.AsCondExpression; SetLength(newPairs, Length(E.Pairs)); hasChanged := False; for i := 0 to High(E.Pairs) do begin newCond := EnsureNode(Accept(E.Pairs[i].Condition), Node); newBranch := EnsureNode(Accept(E.Pairs[i].Branch), Node); newPairs[i] := TCondPair.Create(newCond, newBranch); if (newCond <> E.Pairs[i].Condition) or (newBranch <> E.Pairs[i].Branch) then hasChanged := True; end; newElse := EnsureNode(Accept(E.ElseBranch), Node); if newElse <> E.ElseBranch then hasChanged := True; if not hasChanged then Exit(Node); Result := TAst.CondExpr(Node.Identity, newPairs, newElse, E.StaticType); end; function TAstNodeRemover.VisitAssignment(const Node: IAstNode): IAstNode; var A: IAssignmentNode; newTarget, newValue: IAstNode; begin A := Node.AsAssignment; newTarget := EnsureNode(Accept(A.Target), Node); newValue := EnsureNode(Accept(A.Value), Node); if (newTarget = A.Target) and (newValue = A.Value) then Exit(Node); Result := TAst.Assign(Node.Identity, newTarget, newValue, A.StaticType); end; function TAstNodeRemover.VisitVariableDeclaration(const Node: IAstNode): IAstNode; var V: IVariableDeclarationNode; newTarget, newInit: IAstNode; begin V := Node.AsVariableDeclaration; newTarget := EnsureNode(Accept(V.Target), Node); newInit := Accept(V.Initializer); if (newTarget = V.Target) and (newInit = V.Initializer) then Exit(Node); Result := TAst.VarDecl(Node.Identity, newTarget, newInit, V.StaticType, V.IsBoxed); end; function TAstNodeRemover.VisitLambdaExpression(const Node: IAstNode): IAstNode; var L: ILambdaExpressionNode; newParams: TArray; newBody: IAstNode; paramTuple: ITupleNode; begin L := Node.AsLambdaExpression; // Filter parameters (removing a parameter is semantically risky but consistent with "Remove" logic) newParams := FilterTuple(L.Parameters); newBody := EnsureNode(Accept(L.Body), Node); // Factory creates new Tuple from Array paramTuple := TAst.Tuple(L.Parameters.Identity, newParams); Result := 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: IAstNode; newArgs: TArray; argsTuple: ITupleNode; begin C := Node.AsFunctionCall; newCallee := EnsureNode(Accept(C.Callee), Node); newArgs := FilterTuple(C.Arguments); 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; var I: IIndexerNode; newBase, newIndex: IAstNode; begin I := Node.AsIndexer; newBase := EnsureNode(Accept(I.Base), Node); newIndex := EnsureNode(Accept(I.Index), Node); if (newBase = I.Base) and (newIndex = I.Index) then Exit(Node); Result := TAst.Indexer(Node.Identity, newBase, newIndex, I.StaticType); end; function TAstNodeRemover.VisitMemberAccess(const Node: IAstNode): IAstNode; var M: IMemberAccessNode; newBase, newMember: IAstNode; begin M := Node.AsMemberAccess; newBase := EnsureNode(Accept(M.Base), Node); newMember := Accept(M.Member); if newMember = nil then Exit(TAst.Nop(Node.Identity)); // Member missing -> invalid access if (newBase = M.Base) and (newMember = M.Member) then Exit(Node); Result := TAst.MemberAccess(Node.Identity, newBase, newMember.AsKeyword, M.StaticType); end; function TAstNodeRemover.VisitAddSeriesItem(const Node: IAstNode): IAstNode; var A: IAddSeriesItemNode; newSeries, newValue, newLookback: IAstNode; begin A := Node.AsAddSeriesItem; newSeries := Accept(A.Series); if newSeries = nil then Exit(TAst.Nop(Node.Identity)); newValue := EnsureNode(Accept(A.Value), Node); newLookback := Accept(A.Lookback); if (newSeries = A.Series) and (newValue = A.Value) and (newLookback = A.Lookback) then Exit(Node); Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, A.StaticType); end; function TAstNodeRemover.VisitBlockExpression(const Node: IAstNode): IAstNode; var B: IBlockExpressionNode; newExprs: TArray; 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; 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; newElements: TArray; list: TList; item, transformed: IAstNode; i: Integer; hasChanged: Boolean; elements: TArray; begin T := Node.AsTuple; list := TList.Create; hasChanged := False; try elements := T.Elements; for i := 0 to High(elements) do begin item := elements[i]; transformed := Accept(item); if transformed <> item then hasChanged := True; if Assigned(transformed) then list.Add(transformed) else hasChanged := True; // Item removed end; if not hasChanged then Exit(Node); newElements := list.ToArray; Result := TAst.Tuple(Node.Identity, newElements, T.StaticType); finally list.Free; end; end; end.