unit Myc.Ast.Visitor; interface uses System.SysUtils, System.Generics.Collections, Myc.Data.Value, Myc.Ast, Myc.Ast.Nodes; type // A fully abstract base class for any visitor of the AST. TAstVisitor = class abstract(TInterfacedObject, IAstVisitor) public function VisitConstant(const Node: IConstantNode): TDataValue; virtual; abstract; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual; abstract; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; virtual; abstract; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; virtual; abstract; function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual; abstract; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; virtual; abstract; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual; abstract; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual; abstract; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual; abstract; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual; abstract; function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual; abstract; function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual; abstract; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual; abstract; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual; abstract; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual; abstract; function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual; abstract; function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual; abstract; end; IAstTransformer = interface(IAstVisitor) function Execute(const RootNode: IAstNode): IAstNode; end; // A base visitor that walks the AST and rebuilds it node by node. // This class uses the Template Method pattern: // - The Visit... methods are the template and should not be overridden. // - The virtual Transform... methods are the hooks for derived classes to customize behavior. TAstTransformer = class abstract(TAstVisitor, IAstTransformer) private FDone: Boolean; protected // Dispatch methods for each node type. Derived classes override these to change the transformation. function TransformConstant(const Node: IConstantNode): IConstantNode; virtual; function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; virtual; function TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode; virtual; function TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode; virtual; function TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode; virtual; function TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; virtual; function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; virtual; function TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; virtual; function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; virtual; function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; virtual; function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; virtual; function TransformIndexer(const Node: IIndexerNode): IIndexerNode; virtual; function TransformMemberAccess(const Node: IMemberAccessNode): IMemberAccessNode; virtual; function TransformCreateSeries(const Node: ICreateSeriesNode): ICreateSeriesNode; virtual; function TransformAddSeriesItem(const Node: IAddSeriesItemNode): IAddSeriesItemNode; virtual; function TransformSeriesLength(const Node: ISeriesLengthNode): ISeriesLengthNode; virtual; function TransformRecur(const Node: IRecurNode): IRecurNode; virtual; // Helper to transform an array of nodes. function TransformNodes(const Nodes: TArray): TArray; function Accept(const Node: IAstNode): TDataValue; virtual; property Done: Boolean read FDone write FDone; public // This is the main entry point for the transformer, returning a transformed node. function Execute(const RootNode: IAstNode): IAstNode; // Final Visit methods - these should not be overridden. They call the virtual Transform methods. function VisitConstant(const Node: IConstantNode): TDataValue; override; final; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; final; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; final; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; final; function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; final; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; final; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; final; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; final; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; final; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; final; function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; final; function VisitIndexer(const Node: IIndexerNode): TDataValue; override; final; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; final; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; final; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; final; function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; final; function VisitRecurNode(const Node: IRecurNode): TDataValue; override; final; end; // TAstTraverser simply inherits the identity-transform (cloning) behavior from TAstTransformer. TAstTraverser = class(TAstTransformer); // Generic traverser for managing state during AST walks. TAstTraverser = class(TAstTraverser) private FData: TStack; protected function Accept(const Node: IAstNode): TDataValue; override; // Called before a node is visited. The returned value is pushed onto the state stack. function EnterNode(const Node: IAstNode): T; virtual; abstract; // Called after a node has been visited. procedure ExitNode(const Node: IAstNode; const Data: T); virtual; property Data: TStack read FData; public constructor Create; destructor Destroy; override; end; implementation { TAstTransformer } function TAstTransformer.Accept(const Node: IAstNode): TDataValue; begin if (not Assigned(Node)) or FDone then exit; Result := Node.Accept(Self); end; function TAstTransformer.Execute(const RootNode: IAstNode): IAstNode; begin Result := Accept(RootNode).AsIntf; end; // --- Default implementations for Transform... methods (Identity Transformation / Cloning) --- function TAstTransformer.TransformConstant(const Node: IConstantNode): IConstantNode; begin // The node itself is immutable, return it directly. Result := Node; end; function TAstTransformer.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; begin Result := Node; end; function TAstTransformer.TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode; begin var left := Accept(Node.Left).AsIntf; var right := Accept(Node.Right).AsIntf; if (left = Node.Left) and (right = Node.Right) then Result := Node else Result := TAst.BinaryExpr(left, Node.Operator, right); end; function TAstTransformer.TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode; begin var right := Accept(Node.Right).AsIntf; if right = Node.Right then Result := Node else Result := TAst.UnaryExpr(Node.Operator, right); end; function TAstTransformer.TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode; begin var condition := Accept(Node.Condition).AsIntf; var thenBranch := Accept(Node.ThenBranch).AsIntf; var elseBranch := Accept(Node.ElseBranch).AsIntf; if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then Result := Node else Result := TAst.IfExpr(condition, thenBranch, elseBranch); end; function TAstTransformer.TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; begin var condition := Accept(Node.Condition).AsIntf; var thenBranch := Accept(Node.ThenBranch).AsIntf; var elseBranch := Accept(Node.ElseBranch).AsIntf; if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then Result := Node else Result := TAst.TernaryExpr(condition, thenBranch, elseBranch); end; function TAstTransformer.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; begin var parameters := TransformNodes(Node.Parameters); var body := Accept(Node.Body).AsIntf; if (parameters = Node.Parameters) and (body = Node.Body) then Result := Node else Result := TAst.LambdaExpr(parameters, body); end; function TAstTransformer.TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; begin var callee := Accept(Node.Callee).AsIntf; var args := TransformNodes(Node.Arguments); if (callee = Node.Callee) and (args = Node.Arguments) then Result := Node else Result := TAst.FunctionCall(callee, args); end; function TAstTransformer.TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; begin // TList is mutable, so we always transform. var exprs: TArray; var i: Integer; SetLength(exprs, Node.Expressions.Count); for i := 0 to Node.Expressions.Count - 1 do exprs[i] := Accept(Node.Expressions[i]).AsIntf; Result := TAst.Block(exprs); end; function TAstTransformer.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; begin var identifier := Accept(Node.Identifier).AsIntf; var initializer := Accept(Node.Initializer).AsIntf; if (identifier = Node.Identifier) and (initializer = Node.Initializer) then Result := Node else Result := TAst.VarDecl(identifier, initializer); end; function TAstTransformer.TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; begin var identifier := Accept(Node.Identifier).AsIntf; var value := Accept(Node.Value).AsIntf; if (identifier = Node.Identifier) and (value = Node.Value) then Result := Node else Result := TAst.Assign(identifier, value); end; function TAstTransformer.TransformIndexer(const Node: IIndexerNode): IIndexerNode; begin var base := Accept(Node.Base).AsIntf; var index := Accept(Node.Index).AsIntf; if (base = Node.Base) and (index = Node.Index) then Result := Node else Result := TAst.Indexer(base, index); end; function TAstTransformer.TransformMemberAccess(const Node: IMemberAccessNode): IMemberAccessNode; begin var base := Accept(Node.Base).AsIntf; var member := Node.Member; if (base = Node.Base) and (member = Node.Member) then Result := Node else Result := TAst.MemberAccess(base, member); end; function TAstTransformer.TransformCreateSeries(const Node: ICreateSeriesNode): ICreateSeriesNode; begin Result := Node; end; function TAstTransformer.TransformAddSeriesItem(const Node: IAddSeriesItemNode): IAddSeriesItemNode; begin var series := Accept(Node.Series).AsIntf; var value := Accept(Node.Value).AsIntf; var lookback := Accept(Node.Lookback).AsIntf; if (series = Node.Series) and (value = Node.Value) and (lookback = Node.Lookback) then Result := Node else Result := TAst.AddSeriesItem(series, value, lookback); end; function TAstTransformer.TransformSeriesLength(const Node: ISeriesLengthNode): ISeriesLengthNode; begin var series := Accept(Node.Series).AsIntf; if series = Node.Series then Result := Node else Result := TAst.SeriesLength(series); end; function TAstTransformer.TransformRecur(const Node: IRecurNode): IRecurNode; begin var args := TransformNodes(Node.Arguments); if args = Node.Arguments then Result := Node else Result := TAst.Recur(args); end; function TAstTransformer.TransformNodes(const Nodes: TArray): TArray; var i: Integer; hasChanged: boolean; begin hasChanged := False; SetLength(Result, Length(Nodes)); for i := 0 to High(Nodes) do begin Result[i] := Accept(Nodes[i]).AsIntf; if Result[i] <> Nodes[i] then hasChanged := True; end; if not hasChanged then Result := Nodes; end; function TAstTransformer.VisitConstant(const Node: IConstantNode): TDataValue; begin Result := TDataValue.FromIntf(TransformConstant(Node)); end; function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): TDataValue; begin Result := TDataValue.FromIntf(TransformIdentifier(Node)); end; function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; begin Result := TDataValue.FromIntf(TransformBinaryExpression(Node)); end; function TAstTransformer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; begin Result := TDataValue.FromIntf(TransformUnaryExpression(Node)); end; function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; begin Result := TDataValue.FromIntf(TransformIfExpression(Node)); end; function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; begin Result := TDataValue.FromIntf(TransformTernaryExpression(Node)); end; function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; begin Result := TDataValue.FromIntf(TransformLambdaExpression(Node)); end; function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; begin Result := TDataValue.FromIntf(TransformFunctionCall(Node)); end; function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; begin Result := TDataValue.FromIntf(TransformBlockExpression(Node)); end; function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; begin Result := TDataValue.FromIntf(TransformVariableDeclaration(Node)); end; function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): TDataValue; begin Result := TDataValue.FromIntf(TransformAssignment(Node)); end; function TAstTransformer.VisitIndexer(const Node: IIndexerNode): TDataValue; begin Result := TDataValue.FromIntf(TransformIndexer(Node)); end; function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; begin Result := TDataValue.FromIntf(TransformMemberAccess(Node)); end; function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; begin Result := TDataValue.FromIntf(TransformCreateSeries(Node)); end; function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; begin Result := TDataValue.FromIntf(TransformAddSeriesItem(Node)); end; function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; begin Result := TDataValue.FromIntf(TransformSeriesLength(Node)); end; function TAstTransformer.VisitRecurNode(const Node: IRecurNode): TDataValue; begin Result := TDataValue.FromIntf(TransformRecur(Node)); end; { TAstTraverser } constructor TAstTraverser.Create; begin inherited Create; FData := TStack.Create; end; destructor TAstTraverser.Destroy; begin FData.Free; inherited; end; function TAstTraverser.Accept(const Node: IAstNode): TDataValue; begin if not Assigned(Node) then exit; FData.Push(EnterNode(Node)); try // Call the inherited Accept, which will dispatch to the virtual Visit... methods // in the base TAstTransformer, performing the transformation/cloning. Result := inherited Accept(Node); finally var data := FData.Pop; ExitNode(Node, data); end; end; procedure TAstTraverser.ExitNode(const Node: IAstNode; const Data: T); begin end; end.