unit Myc.Ast.Traverser; interface uses System.Classes, System.Generics.Collections, Myc.Data.Value, Myc.Ast.Nodes; type // TAstTraverser provides a default AST traversal implementation. TAstTraverser = class abstract(TInterfacedObject, IAstVisitor) private FDone: Boolean; protected function Accept(const Node: IAstNode): TDataValue; virtual; property Done: Boolean read FDone write FDone; public function Execute(const RootNode: IAstNode): TDataValue; function VisitConstant(const Node: IConstantNode): TDataValue; virtual; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; virtual; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; virtual; function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; virtual; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual; function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual; function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual; function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual; function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual; end; // Generic traverser for managing state during AST walks. TAstTraverser = class abstract(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 { TAstTraverser } function TAstTraverser.Accept(const Node: IAstNode): TDataValue; begin if not Assigned(Node) or FDone then exit; Result := Node.Accept(Self); end; function TAstTraverser.Execute(const RootNode: IAstNode): TDataValue; begin Result := RootNode.Accept(Self); end; function TAstTraverser.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; begin Accept(Node.Series); Accept(Node.Value); if Assigned(Node.Lookback) then Accept(Node.Lookback); end; function TAstTraverser.VisitAssignment(const Node: IAssignmentNode): TDataValue; begin Accept(Node.Value); Accept(Node.Identifier); end; function TAstTraverser.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; begin Accept(Node.Left); Accept(Node.Right); end; function TAstTraverser.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; var expr: IAstNode; begin for expr in Node.Expressions do begin if FDone then break; Accept(expr); end; end; function TAstTraverser.VisitConstant(const Node: IConstantNode): TDataValue; begin end; function TAstTraverser.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; begin end; function TAstTraverser.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; var arg: IAstNode; begin Accept(Node.Callee); for arg in Node.Arguments do begin if FDone then break; Accept(arg); end; end; function TAstTraverser.VisitRecurNode(const Node: IRecurNode): TDataValue; var arg: IAstNode; begin for arg in Node.Arguments do begin if FDone then break; Accept(arg); end; end; function TAstTraverser.VisitIdentifier(const Node: IIdentifierNode): TDataValue; begin end; function TAstTraverser.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; begin Accept(Node.Condition); Accept(Node.ThenBranch); if Assigned(Node.ElseBranch) then Accept(Node.ElseBranch); end; function TAstTraverser.VisitIndexer(const Node: IIndexerNode): TDataValue; begin Accept(Node.Base); Accept(Node.Index); end; function TAstTraverser.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; var param: IIdentifierNode; begin for param in Node.Parameters do begin if FDone then break; Accept(param); end; Accept(Node.Body); end; function TAstTraverser.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; begin // Do not visit the member identifier, as it's not a variable in the current scope. Accept(Node.Base); end; function TAstTraverser.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; begin Accept(Node.Series); end; function TAstTraverser.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; begin Accept(Node.Condition); Accept(Node.ThenBranch); Accept(Node.ElseBranch); end; function TAstTraverser.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; begin Accept(Node.Right); end; function TAstTraverser.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; begin if Assigned(Node.Initializer) then Accept(Node.Initializer); Accept(Node.Identifier); 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) or Done then exit; FData.Push(EnterNode(Node)); try 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.