Files
MycLib/Src/AST/Myc.Ast.Visitor.pas
T
2025-11-01 13:36:10 +01:00

444 lines
19 KiB
ObjectPascal

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)
protected
function VisitConstant(const Node: IConstantNode): TDataValue; virtual; abstract;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual; abstract;
function VisitKeyword(const Node: IKeywordNode): 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 VisitMacroExpansionNode(const Node: IMacroExpansionNode): 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 VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; virtual; abstract;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; virtual; abstract;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; virtual; abstract;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; virtual; abstract;
function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual; abstract;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual; abstract;
function VisitRecordLiteral(const Node: IRecordLiteralNode): 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.
// Derived classes can override the virtual Visit... methods to customize behavior.
TAstTransformer = class abstract(TAstVisitor, IAstTransformer)
private
FDone: Boolean;
protected
// These virtual methods implement the default identity-transformation (cloning) behavior.
function VisitConstant(const Node: IConstantNode): TDataValue; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
function Accept(const Node: IAstNode): TDataValue; virtual;
// Helper to transform an array of nodes.
function AcceptNodes<T: IAstNode>(const Nodes: TArray<T>): TArray<T>;
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;
end;
// Generic traverser for managing state during AST walks.
TAstTraverser<T> = class(TAstTransformer)
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
{ TAstTransformer }
function TAstTransformer.Accept(const Node: IAstNode): TDataValue;
begin
if (not Assigned(Node)) or FDone then
exit;
Result := Node.Accept(Self);
end;
function TAstTransformer.AcceptNodes<T>(const Nodes: TArray<T>): TArray<T>;
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<T>;
if Result[i] <> Nodes[i] then
hasChanged := True;
end;
if not hasChanged then
Result := Nodes;
end;
function TAstTransformer.Execute(const RootNode: IAstNode): IAstNode;
begin
Result := Accept(RootNode).AsIntf<IAstNode>;
end;
function TAstTransformer.VisitConstant(const Node: IConstantNode): TDataValue;
begin
// The node itself is immutable, return it directly.
Result := TDataValue.FromIntf<IConstantNode>(Node);
end;
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
Result := TDataValue.FromIntf<IIdentifierNode>(Node);
end;
function TAstTransformer.VisitKeyword(const Node: IKeywordNode): TDataValue;
begin
// Keywords are interned and immutable
Result := TDataValue.FromIntf<IKeywordNode>(Node);
end;
function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
begin
var left := Accept(Node.Left).AsIntf<IAstNode>;
var right := Accept(Node.Right).AsIntf<IAstNode>;
if (left = Node.Left) and (right = Node.Right) then
Result := TDataValue.FromIntf<IBinaryExpressionNode>(Node)
else
Result := TDataValue.FromIntf<IBinaryExpressionNode>(TAst.BinaryExpr(left, Node.Operator, right));
end;
function TAstTransformer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
begin
var right := Accept(Node.Right).AsIntf<IAstNode>;
if right = Node.Right then
Result := TDataValue.FromIntf<IUnaryExpressionNode>(Node)
else
Result := TDataValue.FromIntf<IUnaryExpressionNode>(TAst.UnaryExpr(Node.Operator, right));
end;
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin
var condition := Accept(Node.Condition).AsIntf<IAstNode>;
var thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
var elseBranchValue := Accept(Node.ElseBranch);
var elseBranch :=
if elseBranchValue.IsVoid then nil
else elseBranchValue.AsIntf<IAstNode>;
if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then
Result := TDataValue.FromIntf<IIfExpressionNode>(Node)
else
Result := TDataValue.FromIntf<IIfExpressionNode>(TAst.IfExpr(condition, thenBranch, elseBranch));
end;
function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
begin
var condition := Accept(Node.Condition).AsIntf<IAstNode>;
var thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
var elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then
Result := TDataValue.FromIntf<ITernaryExpressionNode>(Node)
else
Result := TDataValue.FromIntf<ITernaryExpressionNode>(TAst.TernaryExpr(condition, thenBranch, elseBranch));
end;
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
begin
var parameters := AcceptNodes<IIdentifierNode>(Node.Parameters);
var body := Accept(Node.Body).AsIntf<IAstNode>;
if (parameters = Node.Parameters) and (body = Node.Body) then
Result := TDataValue.FromIntf<ILambdaExpressionNode>(Node)
else
Result := TDataValue.FromIntf<ILambdaExpressionNode>(TAst.LambdaExpr(parameters, body));
end;
function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
begin
var callee := Accept(Node.Callee).AsIntf<IAstNode>;
var args := AcceptNodes<IAstNode>(Node.Arguments);
if (callee = Node.Callee) and (args = Node.Arguments) then
Result := TDataValue.FromIntf<IFunctionCallNode>(Node)
else
Result := TDataValue.FromIntf<IFunctionCallNode>(TAst.FunctionCall(callee, args));
end;
function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
var
callee: IAstNode;
args: TArray<IAstNode>;
expandedBody: IAstNode;
begin
// Transform all children of the node
callee := Accept(Node.Callee).AsIntf<IAstNode>;
args := AcceptNodes<IAstNode>(Node.Arguments);
expandedBody := Accept(Node.ExpandedBody).AsIntf<IAstNode>;
if (callee = Node.Callee) and (args = Node.Arguments) and (expandedBody = Node.ExpandedBody) then
begin
// Nothing changed, return the original node.
Result := TDataValue.FromIntf<IMacroExpansionNode>(Node);
end
else
begin
// Children have changed, but the base transformer doesn't know how to
// create a new TBoundMacroExpansionNode. Derived classes must handle this.
raise ENotSupportedException.Create(
'Rebuilding an IMacroExpansionNode is not supported by the base TAstTransformer. Descendant classes must provide their own implementation.');
end;
end;
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
var
expressions: TArray<IAstNode>;
i: Integer;
hasChanged: Boolean;
begin
// Check if any child expression has changed before creating a new node.
hasChanged := false;
SetLength(expressions, Length(Node.Expressions));
for i := 0 to High(Node.Expressions) do
begin
expressions[i] := Accept(Node.Expressions[i]).AsIntf<IAstNode>;
if expressions[i] <> Node.Expressions[i] then
hasChanged := true;
end;
if hasChanged then
Result := TDataValue.FromIntf<IBlockExpressionNode>(TAst.Block(expressions))
else
Result := TDataValue.FromIntf<IBlockExpressionNode>(Node);
end;
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
begin
var identifier := Accept(Node.Identifier).AsIntf<IIdentifierNode>;
var initializer: IAstNode := nil;
if Node.Initializer <> nil then
initializer := Accept(Node.Initializer).AsIntf<IAstNode>;
if (identifier = Node.Identifier) and (initializer = Node.Initializer) then
Result := TDataValue.FromIntf<IVariableDeclarationNode>(Node)
else
Result := TDataValue.FromIntf<IVariableDeclarationNode>(TAst.VarDecl(identifier, initializer));
end;
function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): TDataValue;
begin
var identifier := Accept(Node.Identifier).AsIntf<IIdentifierNode>;
var value := Accept(Node.Value).AsIntf<IAstNode>;
if (identifier = Node.Identifier) and (value = Node.Value) then
Result := TDataValue.FromIntf<IAssignmentNode>(Node)
else
Result := TDataValue.FromIntf<IAssignmentNode>(TAst.Assign(identifier, value));
end;
function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin
var name := Accept(Node.Name).AsIntf<IIdentifierNode>;
var parameters := AcceptNodes<IIdentifierNode>(Node.Parameters);
var body := Accept(Node.Body).AsIntf<IQuasiquoteNode>;
if (name = Node.Name) and (parameters = Node.Parameters) and (body = Node.Body) then
Result := TDataValue.FromIntf<IMacroDefinitionNode>(Node)
else
Result := TDataValue.FromIntf<IMacroDefinitionNode>(TAst.MacroDef(name, parameters, body));
end;
function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
var expr := Accept(Node.Expression).AsIntf<IAstNode>;
if expr = Node.Expression then
Result := TDataValue.FromIntf<IQuasiquoteNode>(Node)
else
Result := TDataValue.FromIntf<IQuasiquoteNode>(TAst.Quasiquote(expr));
end;
function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
var expr := Accept(Node.Expression).AsIntf<IAstNode>;
if expr = Node.Expression then
Result := TDataValue.FromIntf<IUnquoteNode>(Node)
else
Result := TDataValue.FromIntf<IUnquoteNode>(TAst.Unquote(expr));
end;
function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
var expr := Accept(Node.Expression).AsIntf<IAstNode>;
if expr = Node.Expression then
Result := TDataValue.FromIntf<IUnquoteSplicingNode>(Node)
else
Result := TDataValue.FromIntf<IUnquoteSplicingNode>(TAst.UnquoteSplicing(expr));
end;
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): TDataValue;
begin
var base := Accept(Node.Base).AsIntf<IAstNode>;
var index := Accept(Node.Index).AsIntf<IAstNode>;
if (base = Node.Base) and (index = Node.Index) then
Result := TDataValue.FromIntf<IIndexerNode>(Node)
else
Result := TDataValue.FromIntf<IIndexerNode>(TAst.Indexer(base, index));
end;
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
begin
var base := Accept(Node.Base).AsIntf<IAstNode>;
var member := Accept(Node.Member).AsIntf<IKeywordNode>;
if (base = Node.Base) and (member = Node.Member) then
Result := TDataValue.FromIntf<IMemberAccessNode>(Node)
else
Result := TDataValue.FromIntf<IMemberAccessNode>(TAst.MemberAccess(base, member));
end;
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
var
i: Integer;
hasChanged: Boolean;
newFields: TArray<TRecordFieldLiteral>;
newKey: IKeywordNode;
newValue: IAstNode;
begin
hasChanged := False;
SetLength(newFields, Length(Node.Fields));
for i := 0 to High(Node.Fields) do
begin
newKey := Accept(Node.Fields[i].Key).AsIntf<IKeywordNode>;
newValue := Accept(Node.Fields[i].Value).AsIntf<IAstNode>;
if (newKey <> Node.Fields[i].Key) or (newValue <> Node.Fields[i].Value) then
hasChanged := True;
newFields[i] := TRecordFieldLiteral.Create(newKey, newValue);
end;
if hasChanged then
Result := TDataValue.FromIntf<IRecordLiteralNode>(TAst.RecordLiteral(newFields))
else
Result := TDataValue.FromIntf<IRecordLiteralNode>(Node);
end;
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
begin
Result := TDataValue.FromIntf<ICreateSeriesNode>(Node);
end;
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
begin
var series := Accept(Node.Series).AsIntf<IIdentifierNode>;
var value := Accept(Node.Value).AsIntf<IAstNode>;
var lookback := Accept(Node.Lookback).AsIntf<IAstNode>;
if (series = Node.Series) and (value = Node.Value) and (lookback = Node.Lookback) then
Result := TDataValue.FromIntf<IAddSeriesItemNode>(Node)
else
Result := TDataValue.FromIntf<IAddSeriesItemNode>(TAst.AddSeriesItem(series, value, lookback));
end;
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
begin
var series := Accept(Node.Series).AsIntf<IIdentifierNode>;
if series = Node.Series then
Result := TDataValue.FromIntf<ISeriesLengthNode>(Node)
else
Result := TDataValue.FromIntf<ISeriesLengthNode>(TAst.SeriesLength(series));
end;
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): TDataValue;
begin
var args := AcceptNodes<IAstNode>(Node.Arguments);
if args = Node.Arguments then
Result := TDataValue.FromIntf<IRecurNode>(Node)
else
Result := TDataValue.FromIntf<IRecurNode>(TAst.Recur(args));
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) 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<T>.ExitNode(const Node: IAstNode; const Data: T);
begin
end;
end.