Files
MycLib/Src/AST/Myc.Ast.Traverser.pas
T
Michael Schimmel ea5879520a Refactoring Binder
2025-09-17 13:34:48 +02:00

214 lines
6.2 KiB
ObjectPascal

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 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;
end;
// Generic traverser for managing state during AST walks.
TAstTraverser<T> = class abstract(TAstTraverser)
private
FData: TStack<T>;
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<T> 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.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.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<T> }
constructor TAstTraverser<T>.Create;
begin
inherited Create;
FData := TStack<T>.Create;
end;
destructor TAstTraverser<T>.Destroy;
begin
FData.Free;
inherited;
end;
function TAstTraverser<T>.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<T>.ExitNode(const Node: IAstNode; const Data: T);
begin
end;
end.