368 lines
11 KiB
ObjectPascal
368 lines
11 KiB
ObjectPascal
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<IAstNode>;
|
|
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<IAstNode>;
|
|
var
|
|
i: Integer;
|
|
item, transformed: IAstNode;
|
|
list: TList<IAstNode>;
|
|
begin
|
|
list := TList<IAstNode>.Create;
|
|
try
|
|
for i := 0 to Source.Count - 1 do
|
|
begin
|
|
item := Source.Items[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<TCondPair>;
|
|
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<IAstNode>;
|
|
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<IAstNode>;
|
|
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<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;
|
|
newElements: TArray<IAstNode>;
|
|
list: TList<IAstNode>;
|
|
item, transformed: IAstNode;
|
|
i: Integer;
|
|
hasChanged: Boolean;
|
|
begin
|
|
T := Node.AsTuple;
|
|
list := TList<IAstNode>.Create;
|
|
hasChanged := False;
|
|
try
|
|
for i := 0 to T.Count - 1 do
|
|
begin
|
|
item := T.Items[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.
|