Generic Visitors
This commit is contained in:
@@ -13,47 +13,39 @@ uses
|
||||
|
||||
type
|
||||
// Removes a specific node from the AST.
|
||||
// - If the node is part of a list, it is removed from the list.
|
||||
// - If the node is a mandatory child of a structure (e.g. If-Condition),
|
||||
// it is replaced by a Nop node to maintain tree integrity.
|
||||
TAstNodeRemover = class(TAstTransformer)
|
||||
private
|
||||
FTarget: IAstNode;
|
||||
FRemoved: Boolean;
|
||||
|
||||
// Generic helper to filter nil items from lists during transformation
|
||||
function FilterList<T: IAstNode>(const Source: INodeList<T>): TArray<T>;
|
||||
|
||||
// Helper to ensure a mandatory slot is never nil (replaces with Nop)
|
||||
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;
|
||||
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;
|
||||
|
||||
protected
|
||||
procedure SetupHandlers; override;
|
||||
function Accept(const Node: IAstNode): IAstNode; override;
|
||||
|
||||
// --- List Containers (Filter Logic) ---
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
|
||||
function VisitExpressionList(const Node: IExpressionList): IAstNode; override;
|
||||
function VisitArgumentList(const Node: IArgumentList): IAstNode; override;
|
||||
function VisitParameterList(const Node: IParameterList): IAstNode; override;
|
||||
function VisitRecordFieldList(const Node: IRecordFieldList): IAstNode; override;
|
||||
|
||||
// --- Structural Nodes (Replace Logic) ---
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
|
||||
function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override; // Replaces Ternary
|
||||
function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
|
||||
function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
|
||||
|
||||
public
|
||||
constructor Create(const ATarget: IAstNode);
|
||||
|
||||
// Tries to remove TargetNode from RootNode.
|
||||
// Returns True if the node was found and removed/replaced.
|
||||
// If successful, NewRootNode contains the modified AST.
|
||||
// If not found, NewRootNode returns the original RootNode.
|
||||
class function TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean; static;
|
||||
end;
|
||||
|
||||
@@ -68,14 +60,35 @@ begin
|
||||
FRemoved := False;
|
||||
end;
|
||||
|
||||
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);
|
||||
Register(akAssignment, VisitAssignment);
|
||||
Register(akVariableDeclaration, VisitVariableDeclaration);
|
||||
Register(akLambdaExpression, VisitLambdaExpression);
|
||||
Register(akFunctionCall, VisitFunctionCall);
|
||||
Register(akIndexer, VisitIndexer);
|
||||
Register(akMemberAccess, VisitMemberAccess);
|
||||
Register(akAddSeriesItem, VisitAddSeriesItem);
|
||||
end;
|
||||
|
||||
class function TAstNodeRemover.TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean;
|
||||
var
|
||||
Remover: TAstNodeRemover;
|
||||
begin
|
||||
// Edge case: Removing the root itself
|
||||
if (RootNode = TargetNode) then
|
||||
begin
|
||||
// We replace the root with a Nop, as we cannot return 'nil' for a valid AST root expectation usually
|
||||
NewRootNode := TAst.Nop(RootNode.Identity);
|
||||
Result := True;
|
||||
Exit;
|
||||
@@ -87,15 +100,9 @@ begin
|
||||
Result := Remover.FRemoved;
|
||||
|
||||
if not Result then
|
||||
begin
|
||||
// If nothing was removed, return the original node to ensure reference stability
|
||||
NewRootNode := RootNode;
|
||||
end
|
||||
NewRootNode := RootNode
|
||||
else if (NewRootNode = nil) then
|
||||
begin
|
||||
// Safety fallback: if traversal somehow resulted in nil (should be caught by EnsureNode logic usually)
|
||||
NewRootNode := TAst.Nop(RootNode.Identity);
|
||||
end;
|
||||
finally
|
||||
Remover.Free;
|
||||
end;
|
||||
@@ -103,14 +110,11 @@ end;
|
||||
|
||||
function TAstNodeRemover.Accept(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
// If we matched the target, flag it as removed and return nil.
|
||||
// The parent visitor is responsible for handling the nil result (Filter or Replace).
|
||||
if Node = FTarget then
|
||||
begin
|
||||
FRemoved := True;
|
||||
Exit(nil);
|
||||
end;
|
||||
|
||||
Result := inherited Accept(Node);
|
||||
end;
|
||||
|
||||
@@ -119,7 +123,6 @@ begin
|
||||
if Assigned(Node) then
|
||||
Result := Node
|
||||
else
|
||||
// Mandatory child was deleted -> Replace with Nop using parent's identity location
|
||||
Result := TAst.Nop(ContextNode.Identity);
|
||||
end;
|
||||
|
||||
@@ -136,9 +139,6 @@ begin
|
||||
begin
|
||||
item := Source[i];
|
||||
transformed := Accept(item);
|
||||
|
||||
// If transformed is not nil, keep it.
|
||||
// If it is nil, it was the target, so we skip (delete) it.
|
||||
if Assigned(transformed) then
|
||||
list.Add(T(transformed));
|
||||
end;
|
||||
@@ -152,107 +152,114 @@ end;
|
||||
// List Visitors (Filtering)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function TAstNodeRemover.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
||||
function TAstNodeRemover.VisitBlockExpression(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
B: IBlockExpressionNode;
|
||||
newExprs: TArray<IAstNode>;
|
||||
newList: IExpressionList;
|
||||
changed: Boolean;
|
||||
i: Integer;
|
||||
begin
|
||||
newExprs := FilterList<IAstNode>(Node.Expressions);
|
||||
B := Node.AsBlockExpression;
|
||||
newExprs := FilterList<IAstNode>(B.Expressions);
|
||||
|
||||
// If counts differ, something was removed
|
||||
if Length(newExprs) = Node.Expressions.Count then
|
||||
if Length(newExprs) = B.Expressions.Count then
|
||||
begin
|
||||
// If not marked as removed, we assume no change in this branch
|
||||
if not FRemoved then
|
||||
Exit(Node);
|
||||
|
||||
// Deep check for replacement
|
||||
var changed := False;
|
||||
for var i := 0 to High(newExprs) do
|
||||
if newExprs[i] <> Node.Expressions[i] then
|
||||
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, Node.Expressions.Identity);
|
||||
Result := TAst.Block(Node.Identity, newList, Node.StaticType);
|
||||
newList := TExpressionList.Create(newExprs, B.Expressions.Identity);
|
||||
Result := TAst.Block(Node.Identity, newList, B.StaticType);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitExpressionList(const Node: IExpressionList): IAstNode;
|
||||
function TAstNodeRemover.VisitExpressionList(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
L: IExpressionList;
|
||||
newExprs: TArray<IAstNode>;
|
||||
changed: Boolean;
|
||||
i: Integer;
|
||||
begin
|
||||
newExprs := FilterList<IAstNode>(Node);
|
||||
|
||||
if Length(newExprs) = Node.Count then
|
||||
L := Node.AsExpressionList;
|
||||
newExprs := FilterList<IAstNode>(L);
|
||||
if Length(newExprs) = L.Count then
|
||||
begin
|
||||
var changed := False;
|
||||
for var i := 0 to High(newExprs) do
|
||||
if newExprs[i] <> Node[i] then
|
||||
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: IArgumentList): IAstNode;
|
||||
function TAstNodeRemover.VisitArgumentList(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
L: IArgumentList;
|
||||
newArgs: TArray<IAstNode>;
|
||||
changed: Boolean;
|
||||
i: Integer;
|
||||
begin
|
||||
newArgs := FilterList<IAstNode>(Node);
|
||||
|
||||
if Length(newArgs) = Node.Count then
|
||||
L := Node.AsArgumentList;
|
||||
newArgs := FilterList<IAstNode>(L);
|
||||
if Length(newArgs) = L.Count then
|
||||
begin
|
||||
var changed := False;
|
||||
for var i := 0 to High(newArgs) do
|
||||
if newArgs[i] <> Node[i] then
|
||||
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: IParameterList): IAstNode;
|
||||
function TAstNodeRemover.VisitParameterList(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
L: IParameterList;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
changed: Boolean;
|
||||
i: Integer;
|
||||
begin
|
||||
newParams := FilterList<IIdentifierNode>(Node);
|
||||
|
||||
if Length(newParams) = Node.Count then
|
||||
L := Node.AsParameterList;
|
||||
newParams := FilterList<IIdentifierNode>(L);
|
||||
if Length(newParams) = L.Count then
|
||||
begin
|
||||
var changed := False;
|
||||
for var i := 0 to High(newParams) do
|
||||
if newParams[i] <> Node[i] then
|
||||
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: IRecordFieldList): IAstNode;
|
||||
function TAstNodeRemover.VisitRecordFieldList(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
L: IRecordFieldList;
|
||||
newFields: TArray<IRecordFieldNode>;
|
||||
changed: Boolean;
|
||||
i: Integer;
|
||||
begin
|
||||
newFields := FilterList<IRecordFieldNode>(Node);
|
||||
|
||||
if Length(newFields) = Node.Count then
|
||||
L := Node.AsRecordFieldList;
|
||||
newFields := FilterList<IRecordFieldNode>(L);
|
||||
if Length(newFields) = L.Count then
|
||||
begin
|
||||
var changed := False;
|
||||
for var i := 0 to High(newFields) do
|
||||
if newFields[i] <> Node[i] then
|
||||
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;
|
||||
|
||||
@@ -260,190 +267,166 @@ end;
|
||||
// Structural Visitors (Replacement with Nop/Void)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function TAstNodeRemover.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
|
||||
function TAstNodeRemover.VisitIfExpression(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
E: IIfExpressionNode;
|
||||
newCond, newThen, newElse: IAstNode;
|
||||
begin
|
||||
newCond := Accept(Node.Condition);
|
||||
newThen := Accept(Node.ThenBranch);
|
||||
newElse := Accept(Node.ElseBranch);
|
||||
E := Node.AsIfExpression;
|
||||
newCond := EnsureNode(Accept(E.Condition), Node);
|
||||
newThen := EnsureNode(Accept(E.ThenBranch), Node);
|
||||
newElse := Accept(E.ElseBranch); // Optional
|
||||
|
||||
// If condition or Then-branch is deleted, replace with Nop
|
||||
newCond := EnsureNode(newCond, Node);
|
||||
newThen := EnsureNode(newThen, Node);
|
||||
|
||||
// Else branch is optional
|
||||
if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
|
||||
if (newCond = E.Condition) and (newThen = E.ThenBranch) and (newElse = E.ElseBranch) then
|
||||
Exit(Node);
|
||||
|
||||
Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
|
||||
Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, E.StaticType);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitCondExpression(const Node: ICondExpressionNode): IAstNode;
|
||||
function TAstNodeRemover.VisitCondExpression(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
E: ICondExpressionNode;
|
||||
i: Integer;
|
||||
newPairs: TArray<TCondPair>;
|
||||
newElse: IAstNode;
|
||||
newCond, newBranch: IAstNode;
|
||||
hasChanged: Boolean;
|
||||
begin
|
||||
SetLength(newPairs, Length(Node.Pairs));
|
||||
E := Node.AsCondExpression;
|
||||
SetLength(newPairs, Length(E.Pairs));
|
||||
hasChanged := False;
|
||||
|
||||
for i := 0 to High(Node.Pairs) do
|
||||
for i := 0 to High(E.Pairs) do
|
||||
begin
|
||||
// Ensure mandatory children exist (replace with Nop if removed)
|
||||
newCond := EnsureNode(Accept(Node.Pairs[i].Condition), Node);
|
||||
newBranch := EnsureNode(Accept(Node.Pairs[i].Branch), Node);
|
||||
|
||||
newCond := EnsureNode(Accept(E.Pairs[i].Condition), Node);
|
||||
newBranch := EnsureNode(Accept(E.Pairs[i].Branch), Node);
|
||||
newPairs[i] := TCondPair.Create(newCond, newBranch);
|
||||
|
||||
if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then
|
||||
if (newCond <> E.Pairs[i].Condition) or (newBranch <> E.Pairs[i].Branch) then
|
||||
hasChanged := True;
|
||||
end;
|
||||
|
||||
// Else branch is effectively mandatory in the structure, even if it's Nop/Void
|
||||
newElse := EnsureNode(Accept(Node.ElseBranch), Node);
|
||||
if newElse <> Node.ElseBranch then
|
||||
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, Node.StaticType);
|
||||
Result := TAst.CondExpr(Node.Identity, newPairs, newElse, E.StaticType);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitAssignment(const Node: IAssignmentNode): IAstNode;
|
||||
function TAstNodeRemover.VisitAssignment(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
A: IAssignmentNode;
|
||||
newTarget, newValue: IAstNode;
|
||||
begin
|
||||
newTarget := EnsureNode(Accept(Node.Target), Node);
|
||||
newValue := EnsureNode(Accept(Node.Value), Node);
|
||||
|
||||
if (newTarget = Node.Target) and (newValue = Node.Value) then
|
||||
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, Node.StaticType);
|
||||
Result := TAst.Assign(Node.Identity, newTarget, newValue, A.StaticType);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
||||
function TAstNodeRemover.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
V: IVariableDeclarationNode;
|
||||
newTarget, newInit: IAstNode;
|
||||
begin
|
||||
newTarget := EnsureNode(Accept(Node.Target), Node);
|
||||
newInit := Accept(Node.Initializer); // Initializer is optional
|
||||
|
||||
if (newTarget = Node.Target) and (newInit = Node.Initializer) then
|
||||
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, Node.StaticType, Node.IsBoxed);
|
||||
Result := TAst.VarDecl(Node.Identity, newTarget, newInit, V.StaticType, V.IsBoxed);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
function TAstNodeRemover.VisitLambdaExpression(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newParams: IAstNode;
|
||||
newBody: IAstNode;
|
||||
L: ILambdaExpressionNode;
|
||||
newParams, newBody: IAstNode;
|
||||
begin
|
||||
newParams := Accept(Node.Parameters);
|
||||
// If the parameter list itself was the target, we replace it with an empty list
|
||||
L := Node.AsLambdaExpression;
|
||||
newParams := Accept(L.Parameters);
|
||||
if newParams = nil then
|
||||
newParams := TParameterList.Create([], Node.Parameters.Identity);
|
||||
newParams := TParameterList.Create([], L.Parameters.Identity);
|
||||
newBody := EnsureNode(Accept(L.Body), Node);
|
||||
|
||||
newBody := EnsureNode(Accept(Node.Body), Node);
|
||||
|
||||
if (newParams = Node.Parameters) and (newBody = Node.Body) then
|
||||
if (newParams = L.Parameters) and (newBody = L.Body) then
|
||||
Exit(Node);
|
||||
|
||||
Result :=
|
||||
TAst.LambdaExpr(
|
||||
Node.Identity,
|
||||
newParams.AsParameterList,
|
||||
newBody,
|
||||
Node.Layout,
|
||||
Node.Descriptor,
|
||||
Node.Upvalues,
|
||||
Node.HasNestedLambdas,
|
||||
Node.IsPure,
|
||||
Node.StaticType
|
||||
L.Layout,
|
||||
L.Descriptor,
|
||||
L.Upvalues,
|
||||
L.HasNestedLambdas,
|
||||
L.IsPure,
|
||||
L.StaticType
|
||||
);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
function TAstNodeRemover.VisitFunctionCall(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newCallee: IAstNode;
|
||||
newArgs: IAstNode;
|
||||
C: IFunctionCallNode;
|
||||
newCallee, newArgs: IAstNode;
|
||||
begin
|
||||
newCallee := EnsureNode(Accept(Node.Callee), Node);
|
||||
|
||||
newArgs := Accept(Node.Arguments);
|
||||
C := Node.AsFunctionCall;
|
||||
newCallee := EnsureNode(Accept(C.Callee), Node);
|
||||
newArgs := Accept(C.Arguments);
|
||||
if newArgs = nil then
|
||||
newArgs := TArgumentList.Create([], Node.Arguments.Identity);
|
||||
newArgs := TArgumentList.Create([], C.Arguments.Identity);
|
||||
|
||||
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
|
||||
if (newCallee = C.Callee) and (newArgs = C.Arguments) then
|
||||
Exit(Node);
|
||||
|
||||
Result :=
|
||||
TAst.FunctionCall(
|
||||
Node.Identity,
|
||||
newCallee,
|
||||
newArgs.AsArgumentList,
|
||||
Node.StaticType,
|
||||
Node.IsTailCall,
|
||||
Node.StaticTarget,
|
||||
Node.IsTargetPure
|
||||
);
|
||||
TAst.FunctionCall(Node.Identity, newCallee, newArgs.AsArgumentList, C.StaticType, C.IsTailCall, C.StaticTarget, C.IsTargetPure);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitIndexer(const Node: IIndexerNode): IAstNode;
|
||||
function TAstNodeRemover.VisitIndexer(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
I: IIndexerNode;
|
||||
newBase, newIndex: IAstNode;
|
||||
begin
|
||||
newBase := EnsureNode(Accept(Node.Base), Node);
|
||||
newIndex := EnsureNode(Accept(Node.Index), Node);
|
||||
|
||||
if (newBase = Node.Base) and (newIndex = Node.Index) then
|
||||
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, Node.StaticType);
|
||||
Result := TAst.Indexer(Node.Identity, newBase, newIndex, I.StaticType);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
|
||||
function TAstNodeRemover.VisitMemberAccess(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newBase: IAstNode;
|
||||
newMember: IAstNode;
|
||||
M: IMemberAccessNode;
|
||||
newBase, newMember: IAstNode;
|
||||
begin
|
||||
newBase := EnsureNode(Accept(Node.Base), Node);
|
||||
|
||||
newMember := Accept(Node.Member);
|
||||
M := Node.AsMemberAccess;
|
||||
newBase := EnsureNode(Accept(M.Base), Node);
|
||||
newMember := Accept(M.Member);
|
||||
if newMember = nil then
|
||||
begin
|
||||
// If member name is deleted, the access is invalid. Replace whole node with Nop.
|
||||
Exit(TAst.Nop(Node.Identity));
|
||||
end;
|
||||
Exit(TAst.Nop(Node.Identity)); // Member missing -> invalid access
|
||||
|
||||
if (newBase = Node.Base) and (newMember = Node.Member) then
|
||||
if (newBase = M.Base) and (newMember = M.Member) then
|
||||
Exit(Node);
|
||||
|
||||
Result := TAst.MemberAccess(Node.Identity, newBase, newMember.AsKeyword, Node.StaticType);
|
||||
Result := TAst.MemberAccess(Node.Identity, newBase, newMember.AsKeyword, M.StaticType);
|
||||
end;
|
||||
|
||||
function TAstNodeRemover.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
|
||||
function TAstNodeRemover.VisitAddSeriesItem(const Node: IAstNode): IAstNode;
|
||||
var
|
||||
newSeries: IAstNode;
|
||||
newValue: IAstNode;
|
||||
newLookback: IAstNode;
|
||||
A: IAddSeriesItemNode;
|
||||
newSeries, newValue, newLookback: IAstNode;
|
||||
begin
|
||||
newSeries := Accept(Node.Series);
|
||||
A := Node.AsAddSeriesItem;
|
||||
newSeries := Accept(A.Series);
|
||||
if newSeries = nil then
|
||||
Exit(TAst.Nop(Node.Identity)); // Cannot exist without series identifier
|
||||
Exit(TAst.Nop(Node.Identity));
|
||||
newValue := EnsureNode(Accept(A.Value), Node);
|
||||
newLookback := Accept(A.Lookback);
|
||||
|
||||
newValue := EnsureNode(Accept(Node.Value), Node);
|
||||
newLookback := Accept(Node.Lookback); // Optional
|
||||
|
||||
if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then
|
||||
if (newSeries = A.Series) and (newValue = A.Value) and (newLookback = A.Lookback) then
|
||||
Exit(Node);
|
||||
|
||||
Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, Node.StaticType);
|
||||
Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, A.StaticType);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user