diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index bb421ad..8bb7194 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -10,56 +10,59 @@ uses Myc.Data.Value, Myc.Data.Keyword, Myc.Ast.Nodes, - Myc.Ast.Scope; + Myc.Ast.Scope, + Myc.Ast.Visitor; type // Exception for runtime errors during script evaluation EEvaluatorException = class(EAstException); // The standard AST evaluator for production use. - TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor, IEvaluatorVisitor) + TEvaluatorVisitor = class(TAstVisitor, IEvaluatorVisitor) private FScope: IExecutionScope; protected - // IAstVisitor methods made virtual for TDebugEvaluatorVisitor to override - function VisitConstant(const Node: IConstantNode): TDataValue; virtual; - function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual; - function VisitKeyword(const Node: IKeywordNode): TDataValue; virtual; + // Setup the registry-based handlers + procedure SetupHandlers; override; - // List Visitors - function VisitParameterList(const Node: IParameterList): TDataValue; virtual; - function VisitArgumentList(const Node: IArgumentList): TDataValue; virtual; - function VisitExpressionList(const Node: IExpressionList): TDataValue; virtual; - function VisitRecordFieldList(const Node: IRecordFieldList): TDataValue; virtual; - function VisitRecordField(const Node: IRecordFieldNode): TDataValue; virtual; + // --- 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; + function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; + function VisitKeyword(const Node: IKeywordNode): TDataValue; override; - function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual; - function VisitCondExpression(const Node: ICondExpressionNode): TDataValue; virtual; - function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual; - function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; virtual; - function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; virtual; - function VisitUnquote(const Node: IUnquoteNode): TDataValue; virtual; - function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; virtual; - function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual; - function VisitMacroExpansionNode(const Node: IMacroExpansionNode): 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 VisitRecordLiteral(const Node: IRecordLiteralNode): 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; - function VisitNop(const Node: INopNode): TDataValue; virtual; + function VisitParameterList(const Node: IParameterList): TDataValue; override; + function VisitArgumentList(const Node: IArgumentList): TDataValue; override; + function VisitExpressionList(const Node: IExpressionList): TDataValue; override; + function VisitRecordFieldList(const Node: IRecordFieldList): TDataValue; override; + function VisitRecordField(const Node: IRecordFieldNode): TDataValue; override; - // Pipe Support - function VisitPipeInput(const Node: IPipeInputNode): TDataValue; virtual; - function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; virtual; - function VisitPipeInputList(const Node: IPipeInputList): TDataValue; virtual; - function VisitPipe(const Node: IPipeNode): TDataValue; virtual; + function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; + function VisitCondExpression(const Node: ICondExpressionNode): 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 VisitNop(const Node: INopNode): TDataValue; override; + + function VisitPipeInput(const Node: IPipeInputNode): TDataValue; override; + function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; override; + function VisitPipeInputList(const Node: IPipeInputList): TDataValue; override; + function VisitPipe(const Node: IPipeNode): TDataValue; override; function IsTruthy(const AValue: TDataValue): Boolean; inline; @@ -108,9 +111,21 @@ end; constructor TEvaluatorVisitor.Create(const AScope: IExecutionScope); begin + FScope := AScope; + // Base constructor calls SetupHandlers automatically inherited Create; Assert(Assigned(AScope)); - FScope := 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; @@ -119,7 +134,8 @@ begin exit(TDataValue.Void); try - Result := RootNode.Accept(Self); + // Call the central dispatch method from TAstVisitor + Result := Visit(RootNode); HandleTCO(Result); except on E: EAstException do @@ -171,6 +187,23 @@ begin end; end; +// --- Visits Implementation --- + +function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue; +begin + Result := Node.Value; +end; + +function TEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue; +begin + Result := TDataValue(TScalar.FromKeyword(Node.Value)); +end; + +function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; +begin + Result := FScope[Node.Address]; +end; + function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; var capturedCells: TArray; @@ -232,8 +265,9 @@ begin end; // Create a visitor with the new scope and execute the lambda's body. + // Use the generic Visit method for dispatch bodyVisitor := visitorFactory(lambdaScope); - Result := cNode.Body.Accept(bodyVisitor); + Result := bodyVisitor.Visit(cNode.Body); end; Result := TDataValue(closure); @@ -272,7 +306,7 @@ begin argList := Node.Arguments; SetLength(argValues, argList.Count); for i := 0 to argList.Count - 1 do - argValues[i] := argList[i].Accept(Self); + argValues[i] := Visit(argList[i]); // Use central dispatch // Call static target. Exceptions here are caught by Execute/HandleTCO. Result := Node.StaticTarget(argValues); @@ -281,14 +315,14 @@ begin else begin // --- Dynamic Path (Default) --- - calleeValue := Node.Callee.Accept(Self); + calleeValue := Visit(Node.Callee); if calleeValue.Kind <> vkMethod then raise EEvaluatorException.Create('Expression is not invokable in this context.'); argList := Node.Arguments; SetLength(argValues, argList.Count); for i := 0 to argList.Count - 1 do - argValues[i] := argList[i].Accept(Self); + argValues[i] := Visit(argList[i]); if Node.IsTailCall then begin @@ -304,7 +338,7 @@ end; function TEvaluatorVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; begin - Result := Node.ExpandedBody.Accept(Self); + Result := Visit(Node.ExpandedBody); end; function TEvaluatorVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue; @@ -316,7 +350,7 @@ var begin SetLength(argValues, Node.Arguments.Count); for i := 0 to Node.Arguments.Count - 1 do - argValues[i] := Node.Arguments[i].Accept(Self); + argValues[i] := Visit(Node.Arguments[i]); calleeAddress.Kind := akLocalOrParent; calleeAddress.ScopeDepth := 0; @@ -332,12 +366,12 @@ var lookback: Int64; begin seriesVar := FScope[Node.Series.Address]; - itemValue := Node.Value.Accept(Self); + itemValue := Visit(Node.Value); lookback := -1; if Assigned(Node.Lookback) then begin - lookbackValue := Node.Lookback.Accept(Self); + lookbackValue := Visit(Node.Lookback); if (lookbackValue.Kind <> vkScalar) or (lookbackValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then raise EEvaluatorException.Create('Lookback parameter must be an integer.'); lookback := lookbackValue.AsScalar.Value.AsInt64; @@ -362,20 +396,10 @@ begin if Node.Target.Kind <> akIdentifier then raise EEvaluatorException.Create('Runtime Error: Assignment target must be an identifier.'); - Result := Node.Value.Accept(Self); + Result := Visit(Node.Value); FScope[Node.Target.AsIdentifier.Address] := Result; end; -function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue; -begin - Result := Node.Value; -end; - -function TEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue; -begin - Result := TDataValue(TScalar.FromKeyword(Node.Value)); -end; - function TEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; var def: string; @@ -403,11 +427,6 @@ begin end; end; -function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; -begin - Result := FScope[Node.Address]; -end; - function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; var baseValue, indexValue: TDataValue; @@ -420,12 +439,12 @@ var memberSeries: ISeries; scalarValue: TScalar; begin - baseValue := Node.Base.Accept(Self); + baseValue := Visit(Node.Base); if baseValue.IsVoid then exit(TDataValue.Void); - indexValue := Node.Index.Accept(Self); + indexValue := Visit(Node.Index); if (indexValue.Kind <> vkScalar) or (indexValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then raise EEvaluatorException.Create('Indexer `[]` requires an integer argument.'); @@ -471,7 +490,7 @@ function TEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDa var baseValue: TDataValue; begin - baseValue := Node.Base.Accept(Self); + baseValue := Visit(Node.Base); if baseValue.IsVoid then exit(TDataValue.Void); @@ -498,7 +517,7 @@ begin for i := 0 to Node.Fields.Count - 1 do begin var field := Node.Fields[i]; - genFields[i] := TPair.Create(field.Key.Value, field.Value.Accept(Self)); + genFields[i] := TPair.Create(field.Key.Value, Visit(field.Value)); end; Result := TDataValue.FromGenericRecord(TKeywordMapping.Create(genFields)); @@ -511,7 +530,7 @@ begin for i := 0 to Node.Fields.Count - 1 do begin var field := Node.Fields[i]; - var valData := field.Value.Accept(Self); + var valData := Visit(field.Value); values[i] := valData.AsScalar.Value; end; @@ -529,7 +548,7 @@ begin raise EEvaluatorException.Create('Runtime Error: Variable declaration target must be an identifier.'); if Assigned(Node.Initializer) then - Result := Node.Initializer.Accept(Self) + Result := Visit(Node.Initializer) else Result := TDataValue.Void; @@ -548,10 +567,10 @@ end; function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; begin - if IsTruthy(Node.Condition.Accept(Self)) then - Result := Node.ThenBranch.Accept(Self) + if IsTruthy(Visit(Node.Condition)) then + Result := Visit(Node.ThenBranch) else if Assigned(Node.ElseBranch) then - Result := Node.ElseBranch.Accept(Self) + Result := Visit(Node.ElseBranch) else Result := TDataValue.Void; end; @@ -562,20 +581,20 @@ var begin for pair in Node.Pairs do begin - if IsTruthy(pair.Condition.Accept(Self)) then + if IsTruthy(Visit(pair.Condition)) then begin - Result := pair.Branch.Accept(Self); + Result := Visit(pair.Branch); Exit; end; end; // Else branch is mandatory in AST (though can be Nop/Void in source if omitted and normalized) - Result := Node.ElseBranch.Accept(Self); + Result := Visit(Node.ElseBranch); end; function TEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; begin - // Delegate to ExpressionList visitor - Result := Node.Expressions.Accept(Self); + // Delegate to ExpressionList visitor via central Visit dispatch (default handler maps to VisitExpressionList) + Result := Visit(Node.Expressions); end; function TEvaluatorVisitor.VisitExpressionList(const Node: IExpressionList): TDataValue; @@ -584,7 +603,7 @@ var begin Result := TDataValue.Void; for i := 0 to Node.Count - 1 do - Result := Node[i].Accept(Self); + Result := Visit(Node[i]); end; function TEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue; @@ -610,7 +629,7 @@ begin Result := TDataValue(TScalar.FromInt64(len)); end; -// --- List Visitor Stubs (Mostly Unused directly, but required by Interface) --- +// --- List Visitor Stubs (Default to Void if not implemented specifically) --- function TEvaluatorVisitor.VisitParameterList(const Node: IParameterList): TDataValue; begin @@ -619,20 +638,16 @@ end; function TEvaluatorVisitor.VisitArgumentList(const Node: IArgumentList): TDataValue; begin - // Could evaluate arguments here and return an array-wrapped TDataValue, - // but VisitFunctionCall handles iteration manually for efficiency. Result := TDataValue.Void; end; function TEvaluatorVisitor.VisitRecordFieldList(const Node: IRecordFieldList): TDataValue; begin - // Handled by VisitRecordLiteral Result := TDataValue.Void; end; function TEvaluatorVisitor.VisitRecordField(const Node: IRecordFieldNode): TDataValue; begin - // Handled by manual iteration in VisitRecordLiteral Result := TDataValue.Void; end; @@ -711,7 +726,7 @@ begin outputDef := Node.StaticType.Definition; // 3. Compile Lambda - lambdaFunc := Node.Transformation.Accept(Self).AsMethod(); + lambdaFunc := Visit(Node.Transformation).AsMethod(); // 4. Create Adapter pipeAdapter := diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index 36fb057..7a5294c 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -64,6 +64,12 @@ type property Errors: TArray read FErrors; end; + // Exception for the new visitor registry dispatch + ENoHandlerException = class(EAstException) + public + constructor Create(const KindName: string); + end; + TCompiledFunction = record public Func: TDataValue.TFunc; @@ -507,6 +513,10 @@ type end; IAstVisitor = interface + // --- Central Dispatch Method (New) --- + function Visit(const Node: IAstNode): TDataValue; + + // --- Specific Methods (Legacy / Hybrid Support) --- function VisitConstant(const Node: IConstantNode): TDataValue; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; function VisitKeyword(const Node: IKeywordNode): TDataValue; @@ -1268,6 +1278,13 @@ begin end; end; +{ ENoHandlerException } + +constructor ENoHandlerException.Create(const KindName: string); +begin + inherited CreateFmt('No visitor handler registered for AST node kind: %s', [KindName]); +end; + { TAstNode } constructor TAstNode.Create(const AIdentity: IAstIdentity); diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index 41aa091..2281718 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -12,9 +12,20 @@ uses Myc.Ast.Identities; type + TAstVisitor = class; + + // Delegate for handling a specific node kind within the visitor context + TNodeHandler = reference to function(const Node: IAstNode): T; + TAstVisitor = class abstract(TInterfacedObject, IAstVisitor) strict private + // The registry for O(1) dispatch based on TAstNodeKind + FHandlers: array[TAstNodeKind] of TNodeHandler; + // IAstVisitor explicit implementation (bridge methods) + function IAstVisitor.Visit = VisitBridge; + + // Legacy bridge methods for IAstVisitor interface compliance function IAstVisitor.VisitConstant = DoVisitConstant; function IAstVisitor.VisitIdentifier = DoVisitIdentifier; function IAstVisitor.VisitKeyword = DoVisitKeyword; @@ -46,23 +57,21 @@ type function IAstVisitor.VisitRecurNode = DoVisitRecurNode; function IAstVisitor.VisitNop = DoVisitNop; - // Pipe Support function IAstVisitor.VisitPipeInput = DoVisitPipeInput; function IAstVisitor.VisitPipeSelectorList = DoVisitPipeSelectorList; function IAstVisitor.VisitPipeInputList = DoVisitPipeInputList; function IAstVisitor.VisitPipe = DoVisitPipe; - // Private bridge method implementations + // Private helpers to wrap T into TDataValue for the non-generic interface + function VisitBridge(const Node: IAstNode): TDataValue; function DoVisitConstant(const Node: IConstantNode): TDataValue; function DoVisitIdentifier(const Node: IIdentifierNode): TDataValue; function DoVisitKeyword(const Node: IKeywordNode): TDataValue; - function DoVisitParameterList(const Node: IParameterList): TDataValue; function DoVisitArgumentList(const Node: IArgumentList): TDataValue; function DoVisitExpressionList(const Node: IExpressionList): TDataValue; function DoVisitRecordFieldList(const Node: IRecordFieldList): TDataValue; function DoVisitRecordField(const Node: IRecordFieldNode): TDataValue; - function DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue; function DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue; function DoVisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; @@ -83,17 +92,26 @@ type function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function DoVisitRecurNode(const Node: IRecurNode): TDataValue; function DoVisitNop(const Node: INopNode): TDataValue; - function DoVisitPipeInput(const Node: IPipeInputNode): TDataValue; function DoVisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; function DoVisitPipeInputList(const Node: IPipeInputList): TDataValue; function DoVisitPipe(const Node: IPipeNode): TDataValue; protected - // Visit a node. - function Accept(const Node: IAstNode): T; virtual; + // 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); - // Default visitor functions implemented analogous to TAstVisitor + // Initial setup of the dispatch table. + // The base implementation maps all kinds to the legacy VisitXYZ virtual methods. + procedure SetupHandlers; virtual; + + // The central dispatch method. Looks up the handler in the registry. + function Visit(const Node: IAstNode): T; virtual; + + // Legacy Virtual Methods (kept for backward compatibility and default mapping) + // Subclasses should eventually move to RegisterHandler, but overriding these still works + // because SetupHandlers maps them by default. function VisitConstant(const Node: IConstantNode): T; virtual; function VisitIdentifier(const Node: IIdentifierNode): T; virtual; function VisitKeyword(const Node: IKeywordNode): T; virtual; @@ -129,8 +147,17 @@ type function VisitPipeSelectorList(const Node: IPipeSelectorList): T; virtual; function VisitPipeInputList(const Node: IPipeInputList): T; virtual; function VisitPipe(const Node: IPipeNode): T; virtual; + + // Bridge to support calling Accept on node directly (double dispatch pattern) + // In the new architecture, we prefer calling Visit(Node) directly. + function Accept(const Node: IAstNode): T; virtual; + + public + constructor Create; end; + // Helper for transformation visitors (returns IAstNode) + // Overrides legacy methods to provide default recursive reconstruction behavior. TAstTransformer = class abstract(TAstVisitor) protected function VisitConstant(const Node: IConstantNode): IAstNode; override; @@ -179,12 +206,91 @@ implementation { TAstVisitor } +constructor TAstVisitor.Create; +begin + inherited Create; + SetupHandlers; +end; + +procedure TAstVisitor.RegisterHandler(Kind: TAstNodeKind; Handler: TNodeHandler); +begin + FHandlers[Kind] := Handler; +end; + +procedure TAstVisitor.SetupHandlers; +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; + + // 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; + + // 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; + + // 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; +end; + +function TAstVisitor.Visit(const Node: IAstNode): T; +var + handler: TNodeHandler; +begin + if not Assigned(Node) then + Exit(Default(T)); + + // O(1) Lookup + handler := FHandlers[Node.Kind]; + + if Assigned(handler) then + Result := handler(Node) + else + raise ENoHandlerException.Create(Node.Kind.ToString); +end; + function TAstVisitor.Accept(const Node: IAstNode): T; begin - if Assigned(Node) then - Result := Node.Accept(Self).AsGeneric - else - Result := Default(T); + Result := Visit(Node); +end; + +// --- Interface Bridge Methods (IAstVisitor -> TDataValue) --- + +function TAstVisitor.VisitBridge(const Node: IAstNode): TDataValue; +begin + Result := TDataValue.FromGeneric(Visit(Node)); end; function TAstVisitor.DoVisitConstant(const Node: IConstantNode): TDataValue; @@ -199,7 +305,6 @@ function TAstVisitor.DoVisitKeyword(const Node: IKeywordNode): TDataValue; begin Result := TDataValue.FromGeneric(VisitKeyword(Node)); end; - function TAstVisitor.DoVisitParameterList(const Node: IParameterList): TDataValue; begin Result := TDataValue.FromGeneric(VisitParameterList(Node)); @@ -220,7 +325,6 @@ function TAstVisitor.DoVisitRecordField(const Node: IRecordFieldNode): TDataV begin Result := TDataValue.FromGeneric(VisitRecordField(Node)); end; - function TAstVisitor.DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue; begin Result := TDataValue.FromGeneric(VisitIfExpression(Node)); @@ -301,7 +405,6 @@ function TAstVisitor.DoVisitNop(const Node: INopNode): TDataValue; begin Result := TDataValue.FromGeneric(VisitNop(Node)); end; - function TAstVisitor.DoVisitPipeInput(const Node: IPipeInputNode): TDataValue; begin Result := TDataValue.FromGeneric(VisitPipeInput(Node)); @@ -319,7 +422,7 @@ begin Result := TDataValue.FromGeneric(VisitPipe(Node)); end; -// --- Default implementations (analogous to TAstVisitor) --- +// --- Default implementations (analogous to original abstract class stubs) --- function TAstVisitor.VisitConstant(const Node: IConstantNode): T; begin @@ -340,43 +443,43 @@ function TAstVisitor.VisitParameterList(const Node: IParameterList): T; begin Result := Default(T); for var Item in Node do - Accept(Item); + Visit(Item); end; function TAstVisitor.VisitArgumentList(const Node: IArgumentList): T; begin Result := Default(T); for var Item in Node do - Accept(Item); + Visit(Item); end; function TAstVisitor.VisitExpressionList(const Node: IExpressionList): T; begin Result := Default(T); for var Item in Node do - Accept(Item); + Visit(Item); end; function TAstVisitor.VisitRecordFieldList(const Node: IRecordFieldList): T; begin Result := Default(T); for var Item in Node do - Accept(Item); + Visit(Item); end; function TAstVisitor.VisitRecordField(const Node: IRecordFieldNode): T; begin Result := Default(T); - Accept(Node.Key); - Accept(Node.Value); + Visit(Node.Key); + Visit(Node.Value); end; function TAstVisitor.VisitIfExpression(const Node: IIfExpressionNode): T; begin Result := Default(T); - Accept(Node.Condition); - Accept(Node.ThenBranch); - Accept(Node.ElseBranch); + Visit(Node.Condition); + Visit(Node.ThenBranch); + Visit(Node.ElseBranch); end; function TAstVisitor.VisitCondExpression(const Node: ICondExpressionNode): T; @@ -386,97 +489,97 @@ begin Result := Default(T); for pair in Node.Pairs do begin - Accept(pair.Condition); - Accept(pair.Branch); + Visit(pair.Condition); + Visit(pair.Branch); end; - Accept(Node.ElseBranch); + Visit(Node.ElseBranch); end; function TAstVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): T; begin Result := Default(T); - Accept(Node.Parameters); - Accept(Node.Body); + Visit(Node.Parameters); + Visit(Node.Body); end; function TAstVisitor.VisitFunctionCall(const Node: IFunctionCallNode): T; begin Result := Default(T); - Accept(Node.Callee); - Accept(Node.Arguments); + Visit(Node.Callee); + Visit(Node.Arguments); end; function TAstVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): T; begin Result := Default(T); - Accept(Node.CallNode); - Accept(Node.ExpandedBody); + Visit(Node.CallNode); + Visit(Node.ExpandedBody); end; function TAstVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): T; begin Result := Default(T); - Accept(Node.Expressions); + Visit(Node.Expressions); end; function TAstVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): T; begin Result := Default(T); - Accept(Node.Target); - Accept(Node.Initializer); + Visit(Node.Target); + Visit(Node.Initializer); end; function TAstVisitor.VisitAssignment(const Node: IAssignmentNode): T; begin Result := Default(T); - Accept(Node.Target); - Accept(Node.Value); + Visit(Node.Target); + Visit(Node.Value); end; function TAstVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): T; begin Result := Default(T); - Accept(Node.Name); - Accept(Node.Parameters); - Accept(Node.Body); + Visit(Node.Name); + Visit(Node.Parameters); + Visit(Node.Body); end; function TAstVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): T; begin Result := Default(T); - Accept(Node.Expression); + Visit(Node.Expression); end; function TAstVisitor.VisitUnquote(const Node: IUnquoteNode): T; begin Result := Default(T); - Accept(Node.Expression); + Visit(Node.Expression); end; function TAstVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T; begin Result := Default(T); - Accept(Node.Expression); + Visit(Node.Expression); end; function TAstVisitor.VisitIndexer(const Node: IIndexerNode): T; begin Result := Default(T); - Accept(Node.Base); - Accept(Node.Index); + Visit(Node.Base); + Visit(Node.Index); end; function TAstVisitor.VisitMemberAccess(const Node: IMemberAccessNode): T; begin Result := Default(T); - Accept(Node.Base); - Accept(Node.Member); + Visit(Node.Base); + Visit(Node.Member); end; function TAstVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): T; begin Result := Default(T); - Accept(Node.Fields); + Visit(Node.Fields); end; function TAstVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): T; @@ -487,21 +590,21 @@ end; function TAstVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): T; begin Result := Default(T); - Accept(Node.Series); - Accept(Node.Value); - Accept(Node.Lookback); + Visit(Node.Series); + Visit(Node.Value); + Visit(Node.Lookback); end; function TAstVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): T; begin Result := Default(T); - Accept(Node.Series); + Visit(Node.Series); end; function TAstVisitor.VisitRecurNode(const Node: IRecurNode): T; begin Result := Default(T); - Accept(Node.Arguments); + Visit(Node.Arguments); end; function TAstVisitor.VisitNop(const Node: INopNode): T; @@ -512,29 +615,29 @@ end; function TAstVisitor.VisitPipeInput(const Node: IPipeInputNode): T; begin Result := Default(T); - Accept(Node.StreamSource); - Accept(Node.Selectors); // Now visits SelectorList + Visit(Node.StreamSource); + Visit(Node.Selectors); end; function TAstVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): T; begin Result := Default(T); for var Item in Node do - Accept(Item); + Visit(Item); end; function TAstVisitor.VisitPipeInputList(const Node: IPipeInputList): T; begin Result := Default(T); for var Item in Node do - Accept(Item); + Visit(Item); end; function TAstVisitor.VisitPipe(const Node: IPipeNode): T; begin Result := Default(T); - Accept(Node.Inputs); - Accept(Node.Transformation); + Visit(Node.Inputs); + Visit(Node.Transformation); end; { TAstTransformer } @@ -580,7 +683,7 @@ begin for i := 0 to Node.Count - 1 do begin item := Node[i]; - newItem := Accept(item).AsIdentifier; + newItem := Visit(item).AsIdentifier; newItems[i] := newItem; if item <> newItem then hasChanged := True; @@ -605,7 +708,7 @@ begin for i := 0 to Node.Count - 1 do begin item := Node[i]; - newItem := Accept(item); + newItem := Visit(item); newItems[i] := newItem; if item <> newItem then hasChanged := True; @@ -629,7 +732,7 @@ begin for i := 0 to Node.Count - 1 do begin item := Node[i]; - newItem := Accept(item); + newItem := Visit(item); newItems[i] := newItem; if item <> newItem then hasChanged := True; @@ -653,8 +756,8 @@ begin for i := 0 to Node.Count - 1 do begin item := Node[i]; - // Ensure we transform the field node itself via Accept to trigger VisitRecordField - newItem := Accept(item).AsRecordField; + // Ensure we transform the field node itself via Visit to trigger VisitRecordField + newItem := Visit(item).AsRecordField; newItems[i] := newItem; if item <> newItem then hasChanged := True; @@ -671,8 +774,8 @@ var newKey: IKeywordNode; newValue: IAstNode; begin - newKey := Accept(Node.Key).AsKeyword; - newValue := Accept(Node.Value); + newKey := Visit(Node.Key).AsKeyword; + newValue := Visit(Node.Value); if (newKey = Node.Key) and (newValue = Node.Value) then Result := Node @@ -686,9 +789,9 @@ function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstN var newCond, newThen, newElse: IAstNode; begin - newCond := Accept(Node.Condition); - newThen := Accept(Node.ThenBranch); - newElse := Accept(Node.ElseBranch); + newCond := Visit(Node.Condition); + newThen := Visit(Node.ThenBranch); + newElse := Visit(Node.ElseBranch); if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then Result := Node @@ -709,15 +812,15 @@ begin for i := 0 to High(Node.Pairs) do begin - newCond := Accept(Node.Pairs[i].Condition); - newBranch := Accept(Node.Pairs[i].Branch); + newCond := Visit(Node.Pairs[i].Condition); + newBranch := Visit(Node.Pairs[i].Branch); newPairs[i] := TCondPair.Create(newCond, newBranch); if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then hasChanged := True; end; - newElse := Accept(Node.ElseBranch); // Accept handles nil + newElse := Visit(Node.ElseBranch); // Visit handles nil if newElse <> Node.ElseBranch then hasChanged := True; @@ -732,8 +835,8 @@ var newParams: IParameterList; newBody: IAstNode; begin - newParams := Accept(Node.Parameters).AsParameterList; - newBody := Accept(Node.Body); + newParams := Visit(Node.Parameters).AsParameterList; + newBody := Visit(Node.Body); if (newParams = Node.Parameters) and (newBody = Node.Body) then Result := Node @@ -761,8 +864,8 @@ var newCallee: IAstNode; newArgs: IArgumentList; begin - newCallee := Accept(Node.Callee); - newArgs := Accept(Node.Arguments).AsArgumentList; + newCallee := Visit(Node.Callee); + newArgs := Visit(Node.Arguments).AsArgumentList; if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then Result := Node @@ -778,7 +881,7 @@ function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode var newBody: IAstNode; begin - newBody := Accept(Node.ExpandedBody); + newBody := Visit(Node.ExpandedBody); if newBody = Node.ExpandedBody then exit(Node); @@ -791,7 +894,7 @@ function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): var newExprs: IExpressionList; begin - newExprs := Accept(Node.Expressions).AsExpressionList; + newExprs := Visit(Node.Expressions).AsExpressionList; if newExprs = Node.Expressions then Result := Node @@ -805,8 +908,8 @@ var newTarget: IAstNode; newInit: IAstNode; begin - newTarget := Accept(Node.Target); - newInit := Accept(Node.Initializer); + newTarget := Visit(Node.Target); + newInit := Visit(Node.Initializer); if (newTarget = Node.Target) and (newInit = Node.Initializer) then Result := Node @@ -821,8 +924,8 @@ var newTarget: IAstNode; newValue: IAstNode; begin - newTarget := Accept(Node.Target); - newValue := Accept(Node.Value); + newTarget := Visit(Node.Target); + newValue := Visit(Node.Value); if (newTarget = Node.Target) and (newValue = Node.Value) then Result := Node @@ -841,7 +944,7 @@ function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; var newExpr: IAstNode; begin - newExpr := Accept(Node.Expression); + newExpr := Visit(Node.Expression); if newExpr = Node.Expression then Result := Node else @@ -852,7 +955,7 @@ function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode; var newExpr: IAstNode; begin - newExpr := Accept(Node.Expression); + newExpr := Visit(Node.Expression); if newExpr = Node.Expression then Result := Node else @@ -863,7 +966,7 @@ function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): var newExpr: IAstNode; begin - newExpr := Accept(Node.Expression); + newExpr := Visit(Node.Expression); if (newExpr = Node.Expression) then Result := Node @@ -875,8 +978,8 @@ function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode; var newBase, newIndex: IAstNode; begin - newBase := Accept(Node.Base); - newIndex := Accept(Node.Index); + newBase := Visit(Node.Base); + newIndex := Visit(Node.Index); if (newBase = Node.Base) and (newIndex = Node.Index) then Result := Node @@ -889,8 +992,8 @@ var newBase: IAstNode; newMember: IKeywordNode; begin - newBase := Accept(Node.Base); - newMember := Accept(Node.Member).AsKeyword; + newBase := Visit(Node.Base); + newMember := Visit(Node.Member).AsKeyword; if (newBase = Node.Base) and (newMember = Node.Member) then Result := Node @@ -902,7 +1005,7 @@ function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAs var newFields: IRecordFieldList; begin - newFields := Accept(Node.Fields).AsRecordFieldList; + newFields := Visit(Node.Fields).AsRecordFieldList; if newFields = Node.Fields then Result := Node @@ -917,9 +1020,9 @@ var newSeries: IIdentifierNode; newValue, newLookback: IAstNode; begin - newSeries := Accept(Node.Series).AsIdentifier; - newValue := Accept(Node.Value); - newLookback := Accept(Node.Lookback); + newSeries := Visit(Node.Series).AsIdentifier; + newValue := Visit(Node.Value); + newLookback := Visit(Node.Lookback); if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then Result := Node @@ -931,7 +1034,7 @@ function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstN var newSeries: IIdentifierNode; begin - newSeries := Accept(Node.Series).AsIdentifier; + newSeries := Visit(Node.Series).AsIdentifier; if newSeries = Node.Series then Result := Node @@ -943,7 +1046,7 @@ function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode; var newArgs: IArgumentList; begin - newArgs := Accept(Node.Arguments).AsArgumentList; + newArgs := Visit(Node.Arguments).AsArgumentList; if newArgs = Node.Arguments then Result := Node @@ -956,8 +1059,8 @@ var newSource: IIdentifierNode; newSels: IPipeSelectorList; begin - newSource := Accept(Node.StreamSource).AsIdentifier; - newSels := Accept(Node.Selectors).AsPipeSelectorList; + newSource := Visit(Node.StreamSource).AsIdentifier; + newSels := Visit(Node.Selectors).AsPipeSelectorList; if (newSource = Node.StreamSource) and (newSels = Node.Selectors) then Result := Node @@ -977,7 +1080,7 @@ begin for i := 0 to Node.Count - 1 do begin item := Node[i]; - newItem := Accept(item).AsKeyword; + newItem := Visit(item).AsKeyword; newItems[i] := newItem; if item <> newItem then hasChanged := True; @@ -1002,7 +1105,7 @@ begin for i := 0 to Node.Count - 1 do begin item := Node[i]; - newItem := Accept(item).AsPipeInput; // Explicit cast via new helper + newItem := Visit(item).AsPipeInput; // Explicit cast via new helper newItems[i] := newItem; if item <> newItem then hasChanged := True; @@ -1021,8 +1124,8 @@ var newInputs: IPipeInputList; newTrans: ILambdaExpressionNode; begin - newInputs := Accept(Node.Inputs).AsPipeInputList; - newTrans := Accept(Node.Transformation).AsLambdaExpression; + newInputs := Visit(Node.Inputs).AsPipeInputList; + newTrans := Visit(Node.Transformation).AsLambdaExpression; if (newInputs = Node.Inputs) and (newTrans = Node.Transformation) then Result := Node