1137 lines
42 KiB
ObjectPascal
1137 lines
42 KiB
ObjectPascal
unit Myc.Ast.Visitor;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Generics.Collections,
|
|
Myc.Data.Value,
|
|
Myc.Ast,
|
|
Myc.Ast.Types,
|
|
Myc.Ast.Nodes,
|
|
Myc.Ast.Identities;
|
|
|
|
type
|
|
TAstVisitor<T> = class;
|
|
|
|
// Delegate for handling a specific node kind within the visitor context
|
|
TNodeHandler<T> = reference to function(const Node: IAstNode): T;
|
|
|
|
TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor)
|
|
strict private
|
|
// The registry for O(1) dispatch based on TAstNodeKind
|
|
FHandlers: array[TAstNodeKind] of TNodeHandler<T>;
|
|
|
|
// 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;
|
|
|
|
function IAstVisitor.VisitParameterList = DoVisitParameterList;
|
|
function IAstVisitor.VisitArgumentList = DoVisitArgumentList;
|
|
function IAstVisitor.VisitExpressionList = DoVisitExpressionList;
|
|
function IAstVisitor.VisitRecordFieldList = DoVisitRecordFieldList;
|
|
function IAstVisitor.VisitRecordField = DoVisitRecordField;
|
|
|
|
function IAstVisitor.VisitIfExpression = DoVisitIfExpression;
|
|
function IAstVisitor.VisitCondExpression = DoVisitCondExpression;
|
|
function IAstVisitor.VisitLambdaExpression = DoVisitLambdaExpression;
|
|
function IAstVisitor.VisitFunctionCall = DoVisitFunctionCall;
|
|
function IAstVisitor.VisitMacroExpansionNode = DoVisitMacroExpansionNode;
|
|
function IAstVisitor.VisitBlockExpression = DoVisitBlockExpression;
|
|
function IAstVisitor.VisitVariableDeclaration = DoVisitVariableDeclaration;
|
|
function IAstVisitor.VisitAssignment = DoVisitAssignment;
|
|
function IAstVisitor.VisitMacroDefinition = DoVisitMacroDefinition;
|
|
function IAstVisitor.VisitQuasiquote = DoVisitQuasiquote;
|
|
function IAstVisitor.VisitUnquote = DoVisitUnquote;
|
|
function IAstVisitor.VisitUnquoteSplicing = DoVisitUnquoteSplicing;
|
|
function IAstVisitor.VisitIndexer = DoVisitIndexer;
|
|
function IAstVisitor.VisitMemberAccess = DoVisitMemberAccess;
|
|
function IAstVisitor.VisitRecordLiteral = DoVisitRecordLiteral;
|
|
function IAstVisitor.VisitCreateSeries = DoVisitCreateSeries;
|
|
function IAstVisitor.VisitAddSeriesItem = DoVisitAddSeriesItem;
|
|
function IAstVisitor.VisitSeriesLength = DoVisitSeriesLength;
|
|
function IAstVisitor.VisitRecurNode = DoVisitRecurNode;
|
|
function IAstVisitor.VisitNop = DoVisitNop;
|
|
|
|
function IAstVisitor.VisitPipeInput = DoVisitPipeInput;
|
|
function IAstVisitor.VisitPipeSelectorList = DoVisitPipeSelectorList;
|
|
function IAstVisitor.VisitPipeInputList = DoVisitPipeInputList;
|
|
function IAstVisitor.VisitPipe = DoVisitPipe;
|
|
|
|
// 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;
|
|
function DoVisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
|
function DoVisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
|
function DoVisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
|
function DoVisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
|
function DoVisitAssignment(const Node: IAssignmentNode): TDataValue;
|
|
function DoVisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
|
|
function DoVisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
|
|
function DoVisitUnquote(const Node: IUnquoteNode): TDataValue;
|
|
function DoVisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
|
|
function DoVisitIndexer(const Node: IIndexerNode): TDataValue;
|
|
function DoVisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
|
function DoVisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
|
|
function DoVisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
|
function DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
|
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
|
|
// 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<T>);
|
|
|
|
// 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;
|
|
|
|
function VisitParameterList(const Node: IParameterList): T; virtual;
|
|
function VisitArgumentList(const Node: IArgumentList): T; virtual;
|
|
function VisitExpressionList(const Node: IExpressionList): T; virtual;
|
|
function VisitRecordFieldList(const Node: IRecordFieldList): T; virtual;
|
|
function VisitRecordField(const Node: IRecordFieldNode): T; virtual;
|
|
|
|
function VisitIfExpression(const Node: IIfExpressionNode): T; virtual;
|
|
function VisitCondExpression(const Node: ICondExpressionNode): T; virtual;
|
|
function VisitLambdaExpression(const Node: ILambdaExpressionNode): T; virtual;
|
|
function VisitFunctionCall(const Node: IFunctionCallNode): T; virtual;
|
|
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): T; virtual;
|
|
function VisitBlockExpression(const Node: IBlockExpressionNode): T; virtual;
|
|
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): T; virtual;
|
|
function VisitAssignment(const Node: IAssignmentNode): T; virtual;
|
|
function VisitMacroDefinition(const Node: IMacroDefinitionNode): T; virtual;
|
|
function VisitQuasiquote(const Node: IQuasiquoteNode): T; virtual;
|
|
function VisitUnquote(const Node: IUnquoteNode): T; virtual;
|
|
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T; virtual;
|
|
function VisitIndexer(const Node: IIndexerNode): T; virtual;
|
|
function VisitMemberAccess(const Node: IMemberAccessNode): T; virtual;
|
|
function VisitRecordLiteral(const Node: IRecordLiteralNode): T; virtual;
|
|
function VisitCreateSeries(const Node: ICreateSeriesNode): T; virtual;
|
|
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): T; virtual;
|
|
function VisitSeriesLength(const Node: ISeriesLengthNode): T; virtual;
|
|
function VisitRecurNode(const Node: IRecurNode): T; virtual;
|
|
function VisitNop(const Node: INopNode): T; virtual;
|
|
|
|
function VisitPipeInput(const Node: IPipeInputNode): T; virtual;
|
|
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<IAstNode>)
|
|
protected
|
|
function VisitConstant(const Node: IConstantNode): IAstNode; override;
|
|
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
|
|
function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
|
|
|
|
function VisitParameterList(const Node: IParameterList): IAstNode; override;
|
|
function VisitArgumentList(const Node: IArgumentList): IAstNode; override;
|
|
function VisitExpressionList(const Node: IExpressionList): IAstNode; override;
|
|
function VisitRecordFieldList(const Node: IRecordFieldList): IAstNode; override;
|
|
function VisitRecordField(const Node: IRecordFieldNode): IAstNode; override;
|
|
|
|
function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
|
|
function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override;
|
|
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
|
|
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
|
|
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
|
|
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
|
|
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
|
|
function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
|
|
function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
|
|
function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override;
|
|
function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
|
|
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
|
|
function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
|
|
function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
|
|
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
|
|
function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
|
|
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
|
|
function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
|
|
function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
|
|
function VisitNop(const Node: INopNode): IAstNode; override;
|
|
|
|
function VisitPipeInput(const Node: IPipeInputNode): IAstNode; override;
|
|
function VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode; override;
|
|
function VisitPipeInputList(const Node: IPipeInputList): IAstNode; override;
|
|
function VisitPipe(const Node: IPipeNode): IAstNode; override;
|
|
end;
|
|
|
|
TVoid = record
|
|
end;
|
|
|
|
TAstVisitor = class(TAstVisitor<TVoid>);
|
|
|
|
implementation
|
|
|
|
{ TAstVisitor<T> }
|
|
|
|
constructor TAstVisitor<T>.Create;
|
|
begin
|
|
inherited Create;
|
|
SetupHandlers;
|
|
end;
|
|
|
|
procedure TAstVisitor<T>.RegisterHandler(Kind: TAstNodeKind; Handler: TNodeHandler<T>);
|
|
begin
|
|
FHandlers[Kind] := Handler;
|
|
end;
|
|
|
|
procedure TAstVisitor<T>.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<T>.Visit(const Node: IAstNode): T;
|
|
var
|
|
handler: TNodeHandler<T>;
|
|
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<T>.Accept(const Node: IAstNode): T;
|
|
begin
|
|
Result := Visit(Node);
|
|
end;
|
|
|
|
// --- Interface Bridge Methods (IAstVisitor -> TDataValue) ---
|
|
|
|
function TAstVisitor<T>.VisitBridge(const Node: IAstNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(Visit(Node));
|
|
end;
|
|
|
|
function TAstVisitor<T>.DoVisitConstant(const Node: IConstantNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitConstant(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitIdentifier(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitKeyword(const Node: IKeywordNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitKeyword(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitParameterList(const Node: IParameterList): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitParameterList(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitArgumentList(const Node: IArgumentList): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitArgumentList(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitExpressionList(const Node: IExpressionList): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitExpressionList(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitRecordFieldList(const Node: IRecordFieldList): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitRecordFieldList(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitRecordField(const Node: IRecordFieldNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitRecordField(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitIfExpression(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitCondExpression(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitLambdaExpression(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitFunctionCall(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitMacroExpansionNode(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitBlockExpression(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitVariableDeclaration(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitAssignment(const Node: IAssignmentNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitAssignment(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitMacroDefinition(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitQuasiquote(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitUnquote(const Node: IUnquoteNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitUnquote(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitUnquoteSplicing(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitIndexer(const Node: IIndexerNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitIndexer(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitMemberAccess(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitRecordLiteral(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitCreateSeries(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitAddSeriesItem(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitSeriesLength(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitRecurNode(const Node: IRecurNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitRecurNode(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitNop(const Node: INopNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitNop(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitPipeInput(const Node: IPipeInputNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitPipeInput(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitPipeSelectorList(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitPipeInputList(const Node: IPipeInputList): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitPipeInputList(Node));
|
|
end;
|
|
function TAstVisitor<T>.DoVisitPipe(const Node: IPipeNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(VisitPipe(Node));
|
|
end;
|
|
|
|
// --- Default implementations (analogous to original abstract class stubs) ---
|
|
|
|
function TAstVisitor<T>.VisitConstant(const Node: IConstantNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitIdentifier(const Node: IIdentifierNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitKeyword(const Node: IKeywordNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitParameterList(const Node: IParameterList): T;
|
|
begin
|
|
Result := Default(T);
|
|
for var Item in Node do
|
|
Visit(Item);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitArgumentList(const Node: IArgumentList): T;
|
|
begin
|
|
Result := Default(T);
|
|
for var Item in Node do
|
|
Visit(Item);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitExpressionList(const Node: IExpressionList): T;
|
|
begin
|
|
Result := Default(T);
|
|
for var Item in Node do
|
|
Visit(Item);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitRecordFieldList(const Node: IRecordFieldList): T;
|
|
begin
|
|
Result := Default(T);
|
|
for var Item in Node do
|
|
Visit(Item);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitRecordField(const Node: IRecordFieldNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Key);
|
|
Visit(Node.Value);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitIfExpression(const Node: IIfExpressionNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Condition);
|
|
Visit(Node.ThenBranch);
|
|
Visit(Node.ElseBranch);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitCondExpression(const Node: ICondExpressionNode): T;
|
|
var
|
|
pair: TCondPair;
|
|
begin
|
|
Result := Default(T);
|
|
for pair in Node.Pairs do
|
|
begin
|
|
Visit(pair.Condition);
|
|
Visit(pair.Branch);
|
|
end;
|
|
Visit(Node.ElseBranch);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitLambdaExpression(const Node: ILambdaExpressionNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Parameters);
|
|
Visit(Node.Body);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitFunctionCall(const Node: IFunctionCallNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Callee);
|
|
Visit(Node.Arguments);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitMacroExpansionNode(const Node: IMacroExpansionNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.CallNode);
|
|
Visit(Node.ExpandedBody);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitBlockExpression(const Node: IBlockExpressionNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Expressions);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitVariableDeclaration(const Node: IVariableDeclarationNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Target);
|
|
Visit(Node.Initializer);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitAssignment(const Node: IAssignmentNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Target);
|
|
Visit(Node.Value);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitMacroDefinition(const Node: IMacroDefinitionNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Name);
|
|
Visit(Node.Parameters);
|
|
Visit(Node.Body);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitQuasiquote(const Node: IQuasiquoteNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Expression);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitUnquote(const Node: IUnquoteNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Expression);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Expression);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitIndexer(const Node: IIndexerNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Base);
|
|
Visit(Node.Index);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitMemberAccess(const Node: IMemberAccessNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Base);
|
|
Visit(Node.Member);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitRecordLiteral(const Node: IRecordLiteralNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Fields);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitCreateSeries(const Node: ICreateSeriesNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitAddSeriesItem(const Node: IAddSeriesItemNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Series);
|
|
Visit(Node.Value);
|
|
Visit(Node.Lookback);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitSeriesLength(const Node: ISeriesLengthNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Series);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitRecurNode(const Node: IRecurNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Arguments);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitNop(const Node: INopNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitPipeInput(const Node: IPipeInputNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.StreamSource);
|
|
Visit(Node.Selectors);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitPipeSelectorList(const Node: IPipeSelectorList): T;
|
|
begin
|
|
Result := Default(T);
|
|
for var Item in Node do
|
|
Visit(Item);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitPipeInputList(const Node: IPipeInputList): T;
|
|
begin
|
|
Result := Default(T);
|
|
for var Item in Node do
|
|
Visit(Item);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitPipe(const Node: IPipeNode): T;
|
|
begin
|
|
Result := Default(T);
|
|
Visit(Node.Inputs);
|
|
Visit(Node.Transformation);
|
|
end;
|
|
|
|
{ TAstTransformer }
|
|
|
|
// --- Base Virtual Implementations ---
|
|
|
|
function TAstTransformer.VisitConstant(const Node: IConstantNode): IAstNode;
|
|
begin
|
|
Result := Node;
|
|
end;
|
|
|
|
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
|
|
begin
|
|
Result := Node;
|
|
end;
|
|
|
|
function TAstTransformer.VisitKeyword(const Node: IKeywordNode): IAstNode;
|
|
begin
|
|
Result := Node;
|
|
end;
|
|
|
|
function TAstTransformer.VisitNop(const Node: INopNode): IAstNode;
|
|
begin
|
|
Result := Node;
|
|
end;
|
|
|
|
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
|
|
begin
|
|
Result := Node;
|
|
end;
|
|
|
|
// --- List Transformations ---
|
|
|
|
function TAstTransformer.VisitParameterList(const Node: IParameterList): IAstNode;
|
|
var
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IIdentifierNode>;
|
|
item, newItem: IIdentifierNode;
|
|
i: Integer;
|
|
begin
|
|
hasChanged := False;
|
|
SetLength(newItems, Node.Count);
|
|
for i := 0 to Node.Count - 1 do
|
|
begin
|
|
item := Node[i];
|
|
newItem := Visit(item).AsIdentifier;
|
|
newItems[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := Node
|
|
else
|
|
// Manually reconstruct using concrete class to verify transformation
|
|
Result := TParameterList.Create(newItems, Node.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitArgumentList(const Node: IArgumentList): IAstNode;
|
|
var
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IAstNode>;
|
|
item, newItem: IAstNode;
|
|
i: Integer;
|
|
begin
|
|
hasChanged := False;
|
|
SetLength(newItems, Node.Count);
|
|
for i := 0 to Node.Count - 1 do
|
|
begin
|
|
item := Node[i];
|
|
newItem := Visit(item);
|
|
newItems[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := Node
|
|
else
|
|
Result := TArgumentList.Create(newItems, Node.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitExpressionList(const Node: IExpressionList): IAstNode;
|
|
var
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IAstNode>;
|
|
item, newItem: IAstNode;
|
|
i: Integer;
|
|
begin
|
|
hasChanged := False;
|
|
SetLength(newItems, Node.Count);
|
|
for i := 0 to Node.Count - 1 do
|
|
begin
|
|
item := Node[i];
|
|
newItem := Visit(item);
|
|
newItems[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := Node
|
|
else
|
|
Result := TExpressionList.Create(newItems, Node.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitRecordFieldList(const Node: IRecordFieldList): IAstNode;
|
|
var
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IRecordFieldNode>;
|
|
item, newItem: IRecordFieldNode;
|
|
i: Integer;
|
|
begin
|
|
hasChanged := False;
|
|
SetLength(newItems, Node.Count);
|
|
for i := 0 to Node.Count - 1 do
|
|
begin
|
|
item := Node[i];
|
|
// 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;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := Node
|
|
else
|
|
Result := TRecordFieldList.Create(newItems, Node.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitRecordField(const Node: IRecordFieldNode): IAstNode;
|
|
var
|
|
newKey: IKeywordNode;
|
|
newValue: IAstNode;
|
|
begin
|
|
newKey := Visit(Node.Key).AsKeyword;
|
|
newValue := Visit(Node.Value);
|
|
|
|
if (newKey = Node.Key) and (newValue = Node.Value) then
|
|
Result := Node
|
|
else
|
|
Result := TAst.RecordField(newKey, newValue, Node.Identity.Location);
|
|
end;
|
|
|
|
// --- Node Transformations ---
|
|
|
|
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
|
|
var
|
|
newCond, newThen, newElse: IAstNode;
|
|
begin
|
|
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
|
|
else
|
|
Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitCondExpression(const Node: ICondExpressionNode): IAstNode;
|
|
var
|
|
newPairs: TArray<TCondPair>;
|
|
newElse: IAstNode;
|
|
hasChanged: Boolean;
|
|
i: Integer;
|
|
newCond, newBranch: IAstNode;
|
|
begin
|
|
hasChanged := False;
|
|
SetLength(newPairs, Length(Node.Pairs));
|
|
|
|
for i := 0 to High(Node.Pairs) do
|
|
begin
|
|
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 := Visit(Node.ElseBranch); // Visit handles nil
|
|
if newElse <> Node.ElseBranch then
|
|
hasChanged := True;
|
|
|
|
if not hasChanged then
|
|
Result := Node
|
|
else
|
|
Result := TAst.CondExpr(Node.Identity, newPairs, newElse, Node.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
|
var
|
|
newParams: IParameterList;
|
|
newBody: IAstNode;
|
|
begin
|
|
newParams := Visit(Node.Parameters).AsParameterList;
|
|
newBody := Visit(Node.Body);
|
|
|
|
if (newParams = Node.Parameters) and (newBody = Node.Body) then
|
|
Result := Node
|
|
else
|
|
begin
|
|
// Rebuild Lambda via concrete constructor to keep Layout/Descriptor info
|
|
// TAst factory would reset layout info.
|
|
Result :=
|
|
TLambdaExpressionNode.Create(
|
|
newParams,
|
|
newBody,
|
|
Node.StaticType,
|
|
Node.Layout,
|
|
Node.Descriptor,
|
|
Node.Upvalues,
|
|
Node.HasNestedLambdas,
|
|
Node.IsPure,
|
|
Node.Identity
|
|
);
|
|
end;
|
|
end;
|
|
|
|
function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
|
var
|
|
newCallee: IAstNode;
|
|
newArgs: IArgumentList;
|
|
begin
|
|
newCallee := Visit(Node.Callee);
|
|
newArgs := Visit(Node.Arguments).AsArgumentList;
|
|
|
|
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
|
|
Result := Node
|
|
else
|
|
begin
|
|
Result :=
|
|
TFunctionCallNode
|
|
.Create(newCallee, newArgs, Node.StaticType, Node.IsTailCall, Node.StaticTarget, Node.IsTargetPure, Node.Identity);
|
|
end;
|
|
end;
|
|
|
|
function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
|
|
var
|
|
newBody: IAstNode;
|
|
begin
|
|
newBody := Visit(Node.ExpandedBody);
|
|
if newBody = Node.ExpandedBody then
|
|
exit(Node);
|
|
|
|
// Macro Expansion Nodes are structural, reuse identity.
|
|
// Note: We don't visit the CallNode as it is the "source" which doesn't change during this transformation typically
|
|
Result := TAst.MacroExpansionNode(Node.Identity, Node.CallNode, newBody);
|
|
end;
|
|
|
|
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
|
var
|
|
newExprs: IExpressionList;
|
|
begin
|
|
newExprs := Visit(Node.Expressions).AsExpressionList;
|
|
|
|
if newExprs = Node.Expressions then
|
|
Result := Node
|
|
else
|
|
// Rebuild manually to preserve type
|
|
Result := TBlockExpressionNode.Create(newExprs, Node.StaticType, Node.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
|
var
|
|
newTarget: IAstNode;
|
|
newInit: IAstNode;
|
|
begin
|
|
newTarget := Visit(Node.Target);
|
|
newInit := Visit(Node.Initializer);
|
|
|
|
if (newTarget = Node.Target) and (newInit = Node.Initializer) then
|
|
Result := Node
|
|
else
|
|
begin
|
|
Result := TAst.VarDecl(Node.Identity, newTarget, newInit, Node.StaticType, Node.IsBoxed);
|
|
end;
|
|
end;
|
|
|
|
function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): IAstNode;
|
|
var
|
|
newTarget: IAstNode;
|
|
newValue: IAstNode;
|
|
begin
|
|
newTarget := Visit(Node.Target);
|
|
newValue := Visit(Node.Value);
|
|
|
|
if (newTarget = Node.Target) and (newValue = Node.Value) then
|
|
Result := Node
|
|
else
|
|
Result := TAst.Assign(Node.Identity, newTarget, newValue, Node.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
|
|
begin
|
|
// Macro definitions are usually stripped/ignored in transformers,
|
|
// but we return them as-is to support partial pipelines.
|
|
Result := Node;
|
|
end;
|
|
|
|
function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
|
|
var
|
|
newExpr: IAstNode;
|
|
begin
|
|
newExpr := Visit(Node.Expression);
|
|
if newExpr = Node.Expression then
|
|
Result := Node
|
|
else
|
|
Result := TAst.Quasiquote(Node.Identity, newExpr);
|
|
end;
|
|
|
|
function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode;
|
|
var
|
|
newExpr: IAstNode;
|
|
begin
|
|
newExpr := Visit(Node.Expression);
|
|
if newExpr = Node.Expression then
|
|
Result := Node
|
|
else
|
|
Result := TAst.Unquote(Node.Identity, newExpr);
|
|
end;
|
|
|
|
function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
|
|
var
|
|
newExpr: IAstNode;
|
|
begin
|
|
newExpr := Visit(Node.Expression);
|
|
|
|
if (newExpr = Node.Expression) then
|
|
Result := Node
|
|
else
|
|
Result := TAst.UnquoteSplicing(Node.Identity, newExpr.AsQuasiquote);
|
|
end;
|
|
|
|
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode;
|
|
var
|
|
newBase, newIndex: IAstNode;
|
|
begin
|
|
newBase := Visit(Node.Base);
|
|
newIndex := Visit(Node.Index);
|
|
|
|
if (newBase = Node.Base) and (newIndex = Node.Index) then
|
|
Result := Node
|
|
else
|
|
Result := TAst.Indexer(Node.Identity, newBase, newIndex, Node.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
|
|
var
|
|
newBase: IAstNode;
|
|
newMember: IKeywordNode;
|
|
begin
|
|
newBase := Visit(Node.Base);
|
|
newMember := Visit(Node.Member).AsKeyword;
|
|
|
|
if (newBase = Node.Base) and (newMember = Node.Member) then
|
|
Result := Node
|
|
else
|
|
Result := TAst.MemberAccess(Node.Identity, newBase, newMember, Node.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
|
|
var
|
|
newFields: IRecordFieldList;
|
|
begin
|
|
newFields := Visit(Node.Fields).AsRecordFieldList;
|
|
|
|
if newFields = Node.Fields then
|
|
Result := Node
|
|
else
|
|
begin
|
|
Result := TRecordLiteralNode.Create(newFields, Node.ScalarDefinition, Node.GenericDefinition, Node.StaticType, Node.Identity);
|
|
end;
|
|
end;
|
|
|
|
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
|
|
var
|
|
newSeries: IIdentifierNode;
|
|
newValue, newLookback: IAstNode;
|
|
begin
|
|
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
|
|
else
|
|
Result := TAst.AddSeriesItem(Node.Identity, newSeries, newValue, newLookback, Node.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
|
|
var
|
|
newSeries: IIdentifierNode;
|
|
begin
|
|
newSeries := Visit(Node.Series).AsIdentifier;
|
|
|
|
if newSeries = Node.Series then
|
|
Result := Node
|
|
else
|
|
Result := TAst.SeriesLength(Node.Identity, newSeries, Node.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode;
|
|
var
|
|
newArgs: IArgumentList;
|
|
begin
|
|
newArgs := Visit(Node.Arguments).AsArgumentList;
|
|
|
|
if newArgs = Node.Arguments then
|
|
Result := Node
|
|
else
|
|
Result := TRecurNode.Create(newArgs, Node.StaticType, Node.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipeInput(const Node: IPipeInputNode): IAstNode;
|
|
var
|
|
newSource: IIdentifierNode;
|
|
newSels: IPipeSelectorList;
|
|
begin
|
|
newSource := Visit(Node.StreamSource).AsIdentifier;
|
|
newSels := Visit(Node.Selectors).AsPipeSelectorList;
|
|
|
|
if (newSource = Node.StreamSource) and (newSels = Node.Selectors) then
|
|
Result := Node
|
|
else
|
|
Result := TAst.PipeInput(newSource, newSels, Node.Identity.Location);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode;
|
|
var
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IKeywordNode>;
|
|
item, newItem: IKeywordNode;
|
|
i: Integer;
|
|
begin
|
|
hasChanged := False;
|
|
SetLength(newItems, Node.Count);
|
|
for i := 0 to Node.Count - 1 do
|
|
begin
|
|
item := Node[i];
|
|
newItem := Visit(item).AsKeyword;
|
|
newItems[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := Node
|
|
else
|
|
// Manually reconstruct using helper
|
|
Result := TAst.PipeSelectorList(newItems, Node.Identity.Location);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipeInputList(const Node: IPipeInputList): IAstNode;
|
|
var
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IPipeInputNode>;
|
|
item, newItem: IPipeInputNode;
|
|
i: Integer;
|
|
begin
|
|
hasChanged := False;
|
|
SetLength(newItems, Node.Count);
|
|
for i := 0 to Node.Count - 1 do
|
|
begin
|
|
item := Node[i];
|
|
newItem := Visit(item).AsPipeInput; // Explicit cast via new helper
|
|
newItems[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := Node
|
|
else
|
|
// Using TList implementation directly implies we are creating the list structure
|
|
// Since IPipeInputList is just INodeList<IPipeInputNode>, we reuse TPipeInputList
|
|
Result := TPipeInputList.Create(newItems, Node.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipe(const Node: IPipeNode): IAstNode;
|
|
var
|
|
newInputs: IPipeInputList;
|
|
newTrans: ILambdaExpressionNode;
|
|
begin
|
|
newInputs := Visit(Node.Inputs).AsPipeInputList;
|
|
newTrans := Visit(Node.Transformation).AsLambdaExpression;
|
|
|
|
if (newInputs = Node.Inputs) and (newTrans = Node.Transformation) then
|
|
Result := Node
|
|
else
|
|
Result := TAst.Pipe(Node.Identity, newInputs, newTrans, Node.StaticType);
|
|
end;
|
|
|
|
end.
|