From 2e0db2682f0d90b0850bf8d5b0f0c340d4cc4fa6 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Fri, 2 Jan 2026 14:18:46 +0100 Subject: [PATCH] Generic Visitors --- Src/AST/Myc.Ast.Evaluator.pas | 14 ------ Src/AST/Myc.Ast.Nodes.pas | 1 - Src/AST/Myc.Ast.Visitor.pas | 95 ++++++++++++++++++----------------- 3 files changed, 49 insertions(+), 61 deletions(-) diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 8bb7194..dbba02a 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -23,9 +23,6 @@ type FScope: IExecutionScope; protected - // Setup the registry-based handlers - procedure SetupHandlers; override; - // --- Core Logic Implementation (Legacy Virtual Overrides) --- // These are kept as virtual methods so TDebugEvaluatorVisitor can still override them. function VisitConstant(const Node: IConstantNode): TDataValue; override; @@ -117,17 +114,6 @@ begin Assert(Assigned(AScope)); end; -procedure TEvaluatorVisitor.SetupHandlers; -begin - // The base SetupHandlers registers delegates that call the virtual methods (VisitConstant, etc.). - // Since TEvaluatorVisitor still implements its logic by overriding these virtual methods (for now), - // we just call inherited. - // - // In Phase 2/3 of refactoring, we would move the logic from VisitConstant directly into - // anonymous methods or separate handler functions registered here, bypassing the virtual table. - inherited; -end; - function TEvaluatorVisitor.Execute(const RootNode: IAstNode): TDataValue; begin if not Assigned(RootNode) then diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index 3d27c5c..4e824db 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -513,7 +513,6 @@ type end; IAstVisitor = interface - // --- Central Dispatch Method (New) --- function Visit(const Node: IAstNode): TDataValue; end; diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index ebea59f..816802b 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -16,6 +16,7 @@ type // Delegate for handling a specific node kind within the visitor context TNodeHandler = reference to function(const Node: IAstNode): T; + TRegisterHandler = reference to procedure(Kind: TAstNodeKind; const Handler: TNodeHandler); TAstVisitor = class abstract(TInterfacedObject, IAstVisitor) strict private @@ -29,13 +30,9 @@ type function VisitBridge(const Node: IAstNode): TDataValue; protected - // Registers a handler for a specific node kind. - // Subclasses can call this in their constructor/SetupHandlers to override behavior without implementing VisitXYZ. - procedure RegisterHandler(Kind: TAstNodeKind; Handler: TNodeHandler); - // Initial setup of the dispatch table. // The base implementation maps all kinds to the legacy VisitXYZ virtual methods. - procedure SetupHandlers; virtual; + procedure SetupHandlers(const Register: TRegisterHandler); virtual; // The central dispatch method. Looks up the handler in the registry. function Visit(const Node: IAstNode): T; virtual; @@ -84,7 +81,8 @@ type function Accept(const Node: IAstNode): T; virtual; public - constructor Create; + procedure AfterConstruction; override; + procedure BeforeDestruction; override; end; // Helper for transformation visitors (returns IAstNode) @@ -137,74 +135,79 @@ implementation { TAstVisitor } -constructor TAstVisitor.Create; +procedure TAstVisitor.AfterConstruction; begin - inherited Create; - SetupHandlers; + inherited; + SetupHandlers(procedure(Kind: TAstNodeKind; const Handler: TNodeHandler) begin FHandlers[Kind] := Handler; end); end; -procedure TAstVisitor.RegisterHandler(Kind: TAstNodeKind; Handler: TNodeHandler); +procedure TAstVisitor.BeforeDestruction; begin - FHandlers[Kind] := Handler; + for var i := Low(TAstNodeKind) to High(TAstNodeKind) do + FHandlers[i] := nil; + inherited; end; -procedure TAstVisitor.SetupHandlers; +procedure TAstVisitor.SetupHandlers(const Register: TRegisterHandler); begin // Map all kinds to the legacy virtual methods. // This provides the compatibility layer for existing subclasses. // In Phase 2, subclasses will register their own handlers directly here. // Core - FHandlers[akConstant] := function(const Node: IAstNode): T begin Result := VisitConstant(Node.AsConstant); end; - FHandlers[akIdentifier] := function(const Node: IAstNode): T begin Result := VisitIdentifier(Node.AsIdentifier); end; - FHandlers[akKeyword] := function(const Node: IAstNode): T begin Result := VisitKeyword(Node.AsKeyword); end; + Register(akConstant, function(const Node: IAstNode): T begin Result := VisitConstant(Node.AsConstant); end); + Register(akIdentifier, function(const Node: IAstNode): T begin Result := VisitIdentifier(Node.AsIdentifier); end); + Register(akKeyword, function(const Node: IAstNode): T begin Result := VisitKeyword(Node.AsKeyword); end); // Lists - FHandlers[akParameterList] := function(const Node: IAstNode): T begin Result := VisitParameterList(Node.AsParameterList); end; - FHandlers[akArgumentList] := function(const Node: IAstNode): T begin Result := VisitArgumentList(Node.AsArgumentList); end; - FHandlers[akExpressionList] := function(const Node: IAstNode): T begin Result := VisitExpressionList(Node.AsExpressionList); end; - FHandlers[akRecordFieldList] := function(const Node: IAstNode): T begin Result := VisitRecordFieldList(Node.AsRecordFieldList); end; - FHandlers[akRecordField] := function(const Node: IAstNode): T begin Result := VisitRecordField(Node.AsRecordField); end; + Register(akParameterList, function(const Node: IAstNode): T begin Result := VisitParameterList(Node.AsParameterList); end); + Register(akArgumentList, function(const Node: IAstNode): T begin Result := VisitArgumentList(Node.AsArgumentList); end); + Register(akExpressionList, function(const Node: IAstNode): T begin Result := VisitExpressionList(Node.AsExpressionList); end); + Register(akRecordFieldList, function(const Node: IAstNode): T begin Result := VisitRecordFieldList(Node.AsRecordFieldList); end); + Register(akRecordField, function(const Node: IAstNode): T begin Result := VisitRecordField(Node.AsRecordField); end); // Structural - FHandlers[akIfExpression] := function(const Node: IAstNode): T begin Result := VisitIfExpression(Node.AsIfExpression); end; - FHandlers[akCondExpression] := function(const Node: IAstNode): T begin Result := VisitCondExpression(Node.AsCondExpression); end; - FHandlers[akLambdaExpression] := function(const Node: IAstNode): T begin Result := VisitLambdaExpression(Node.AsLambdaExpression); end; - FHandlers[akFunctionCall] := function(const Node: IAstNode): T begin Result := VisitFunctionCall(Node.AsFunctionCall); end; - FHandlers[akMacroExpansion] := function(const Node: IAstNode): T begin Result := VisitMacroExpansionNode(Node.AsMacroExpansion); end; - FHandlers[akBlockExpression] := function(const Node: IAstNode): T begin Result := VisitBlockExpression(Node.AsBlockExpression); end; - FHandlers[akVariableDeclaration] := - function(const Node: IAstNode): T begin Result := VisitVariableDeclaration(Node.AsVariableDeclaration); end; - FHandlers[akAssignment] := function(const Node: IAstNode): T begin Result := VisitAssignment(Node.AsAssignment); end; - FHandlers[akMacroDefinition] := function(const Node: IAstNode): T begin Result := VisitMacroDefinition(Node.AsMacroDefinition); end; - FHandlers[akQuasiquote] := function(const Node: IAstNode): T begin Result := VisitQuasiquote(Node.AsQuasiquote); end; - FHandlers[akUnquote] := function(const Node: IAstNode): T begin Result := VisitUnquote(Node.AsUnquote); end; - FHandlers[akUnquoteSplicing] := function(const Node: IAstNode): T begin Result := VisitUnquoteSplicing(Node.AsUnquoteSplicing); end; - FHandlers[akIndexer] := function(const Node: IAstNode): T begin Result := VisitIndexer(Node.AsIndexer); end; - FHandlers[akMemberAccess] := function(const Node: IAstNode): T begin Result := VisitMemberAccess(Node.AsMemberAccess); end; - FHandlers[akRecordLiteral] := function(const Node: IAstNode): T begin Result := VisitRecordLiteral(Node.AsRecordLiteral); end; - FHandlers[akCreateSeries] := function(const Node: IAstNode): T begin Result := VisitCreateSeries(Node.AsCreateSeries); end; - FHandlers[akAddSeriesItem] := function(const Node: IAstNode): T begin Result := VisitAddSeriesItem(Node.AsAddSeriesItem); end; - FHandlers[akSeriesLength] := function(const Node: IAstNode): T begin Result := VisitSeriesLength(Node.AsSeriesLength); end; - FHandlers[akRecur] := function(const Node: IAstNode): T begin Result := VisitRecurNode(Node.AsRecur); end; - FHandlers[akNop] := function(const Node: IAstNode): T begin Result := VisitNop(Node.AsNop); end; + Register(akIfExpression, function(const Node: IAstNode): T begin Result := VisitIfExpression(Node.AsIfExpression); end); + Register(akCondExpression, function(const Node: IAstNode): T begin Result := VisitCondExpression(Node.AsCondExpression); end); + Register(akLambdaExpression, function(const Node: IAstNode): T begin Result := VisitLambdaExpression(Node.AsLambdaExpression); end); + Register(akFunctionCall, function(const Node: IAstNode): T begin Result := VisitFunctionCall(Node.AsFunctionCall); end); + Register(akMacroExpansion, function(const Node: IAstNode): T begin Result := VisitMacroExpansionNode(Node.AsMacroExpansion); end); + Register(akBlockExpression, function(const Node: IAstNode): T begin Result := VisitBlockExpression(Node.AsBlockExpression); end); + Register( + akVariableDeclaration, + function(const Node: IAstNode): T begin Result := VisitVariableDeclaration(Node.AsVariableDeclaration); end + ); + Register(akAssignment, function(const Node: IAstNode): T begin Result := VisitAssignment(Node.AsAssignment); end); + Register(akMacroDefinition, function(const Node: IAstNode): T begin Result := VisitMacroDefinition(Node.AsMacroDefinition); end); + Register(akQuasiquote, function(const Node: IAstNode): T begin Result := VisitQuasiquote(Node.AsQuasiquote); end); + Register(akUnquote, function(const Node: IAstNode): T begin Result := VisitUnquote(Node.AsUnquote); end); + Register(akUnquoteSplicing, function(const Node: IAstNode): T begin Result := VisitUnquoteSplicing(Node.AsUnquoteSplicing); end); + Register(akIndexer, function(const Node: IAstNode): T begin Result := VisitIndexer(Node.AsIndexer); end); + Register(akMemberAccess, function(const Node: IAstNode): T begin Result := VisitMemberAccess(Node.AsMemberAccess); end); + Register(akRecordLiteral, function(const Node: IAstNode): T begin Result := VisitRecordLiteral(Node.AsRecordLiteral); end); + Register(akCreateSeries, function(const Node: IAstNode): T begin Result := VisitCreateSeries(Node.AsCreateSeries); end); + Register(akAddSeriesItem, function(const Node: IAstNode): T begin Result := VisitAddSeriesItem(Node.AsAddSeriesItem); end); + Register(akSeriesLength, function(const Node: IAstNode): T begin Result := VisitSeriesLength(Node.AsSeriesLength); end); + Register(akRecur, function(const Node: IAstNode): T begin Result := VisitRecurNode(Node.AsRecur); end); + Register(akNop, function(const Node: IAstNode): T begin Result := VisitNop(Node.AsNop); end); // Pipes - FHandlers[akPipeInput] := function(const Node: IAstNode): T begin Result := VisitPipeInput(Node.AsPipeInput); end; - FHandlers[akPipeSelectorList] := function(const Node: IAstNode): T begin Result := VisitPipeSelectorList(Node.AsPipeSelectorList); end; - FHandlers[akPipeInputList] := function(const Node: IAstNode): T begin Result := VisitPipeInputList(Node.AsPipeInputList); end; - FHandlers[akPipe] := function(const Node: IAstNode): T begin Result := VisitPipe(Node.AsPipe); end; + Register(akPipeInput, function(const Node: IAstNode): T begin Result := VisitPipeInput(Node.AsPipeInput); end); + Register(akPipeSelectorList, function(const Node: IAstNode): T begin Result := VisitPipeSelectorList(Node.AsPipeSelectorList); end); + Register(akPipeInputList, function(const Node: IAstNode): T begin Result := VisitPipeInputList(Node.AsPipeInputList); end); + Register(akPipe, function(const Node: IAstNode): T begin Result := VisitPipe(Node.AsPipe); end); end; function TAstVisitor.Visit(const Node: IAstNode): T; var + [unsafe] handler: TNodeHandler; begin if not Assigned(Node) then Exit(Default(T)); // O(1) Lookup - handler := FHandlers[Node.Kind]; + handler := TNodeHandler(FHandlers[Node.Kind]); if Assigned(handler) then Result := handler(Node)