842 lines
24 KiB
ObjectPascal
842 lines
24 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;
|
|
|
|
TNodeHandler<T> = reference to function(const Node: IAstNode): T;
|
|
|
|
TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor)
|
|
strict private
|
|
FHandlers: array[TAstNodeKind] of TNodeHandler<T>;
|
|
|
|
// IAstVisitor bridge
|
|
function IAstVisitor.Visit = VisitBridge;
|
|
function VisitBridge(const Node: IAstNode): TDataValue;
|
|
|
|
// Sentinel to fill gaps (Fail Fast protection)
|
|
function DefaultSentinel(const Node: IAstNode): T;
|
|
|
|
protected
|
|
// --- Registration ---
|
|
|
|
// Register a handler.
|
|
procedure Register(Kind: TAstNodeKind; const Handler: TNodeHandler<T>); virtual;
|
|
|
|
// Subclasses implement this to call Register()
|
|
procedure SetupHandlers; virtual;
|
|
|
|
// --- Dispatch ---
|
|
|
|
function Visit(const Node: IAstNode): T; virtual;
|
|
function Accept(const Node: IAstNode): T; virtual;
|
|
|
|
public
|
|
procedure AfterConstruction; override;
|
|
procedure BeforeDestruction; override;
|
|
end;
|
|
|
|
// Helper for transformation visitors (returns IAstNode)
|
|
// Implements recursive reconstruction: If a child changes, a new node is returned, but node.Identity stays the same.
|
|
TAstTransformer = class abstract(TAstVisitor<IAstNode>)
|
|
protected
|
|
// Leaves
|
|
function VisitLeaf(const N: IAstNode): IAstNode;
|
|
|
|
// Elements
|
|
function VisitRecordField(const N: IAstNode): IAstNode;
|
|
|
|
// Structures
|
|
function VisitIfExpression(const N: IAstNode): IAstNode;
|
|
function VisitCondExpression(const N: IAstNode): IAstNode;
|
|
function VisitLambdaExpression(const N: IAstNode): IAstNode;
|
|
function VisitFunctionCall(const N: IAstNode): IAstNode;
|
|
function VisitMacroExpansionNode(const N: IAstNode): IAstNode;
|
|
function VisitBlockExpression(const N: IAstNode): IAstNode;
|
|
function VisitVariableDeclaration(const N: IAstNode): IAstNode;
|
|
function VisitAssignment(const N: IAstNode): IAstNode;
|
|
function VisitMacroDefinition(const N: IAstNode): IAstNode;
|
|
|
|
// Wrappers
|
|
function VisitQuasiquote(const N: IAstNode): IAstNode;
|
|
function VisitUnquote(const N: IAstNode): IAstNode;
|
|
function VisitUnquoteSplicing(const N: IAstNode): IAstNode;
|
|
|
|
function VisitIndexer(const N: IAstNode): IAstNode;
|
|
function VisitMemberAccess(const N: IAstNode): IAstNode;
|
|
function VisitRecordLiteral(const N: IAstNode): IAstNode;
|
|
function VisitAddSeriesItem(const N: IAstNode): IAstNode;
|
|
function VisitSeriesLength(const N: IAstNode): IAstNode;
|
|
function VisitRecurNode(const N: IAstNode): IAstNode;
|
|
|
|
// Unified List Type
|
|
function VisitTuple(const N: IAstNode): IAstNode;
|
|
|
|
// Pipes
|
|
function VisitPipeInput(const N: IAstNode): IAstNode;
|
|
function VisitPipeSelectorList(const N: IAstNode): IAstNode;
|
|
function VisitPipeInputList(const N: IAstNode): IAstNode;
|
|
function VisitPipe(const N: IAstNode): IAstNode;
|
|
|
|
procedure SetupHandlers; override;
|
|
end;
|
|
|
|
TVoid = record
|
|
end;
|
|
|
|
// A concrete "Walker" that traverses all children by default.
|
|
// Useful for Analyzers, Linters, or Searchers.
|
|
TAstVisitor = class(TAstVisitor<TVoid>)
|
|
protected
|
|
// Walker implementations
|
|
function WalkLeaf(const N: IAstNode): TVoid;
|
|
|
|
function WalkRecordField(const N: IAstNode): TVoid;
|
|
|
|
function WalkIfExpression(const N: IAstNode): TVoid;
|
|
function WalkCondExpression(const N: IAstNode): TVoid;
|
|
function WalkLambdaExpression(const N: IAstNode): TVoid;
|
|
function WalkFunctionCall(const N: IAstNode): TVoid;
|
|
function WalkMacroExpansionNode(const N: IAstNode): TVoid;
|
|
function WalkBlockExpression(const N: IAstNode): TVoid;
|
|
function WalkVariableDeclaration(const N: IAstNode): TVoid;
|
|
function WalkAssignment(const N: IAstNode): TVoid;
|
|
function WalkMacroDefinition(const N: IAstNode): TVoid;
|
|
function WalkWrapper(const N: IAstNode): TVoid; // Quasiquote, Unquote, etc.
|
|
function WalkIndexer(const N: IAstNode): TVoid;
|
|
function WalkMemberAccess(const N: IAstNode): TVoid;
|
|
function WalkRecordLiteral(const N: IAstNode): TVoid;
|
|
function WalkAddSeriesItem(const N: IAstNode): TVoid;
|
|
function WalkSeriesLength(const N: IAstNode): TVoid;
|
|
function WalkRecurNode(const N: IAstNode): TVoid;
|
|
|
|
function WalkTuple(const N: IAstNode): TVoid;
|
|
|
|
function WalkPipeInput(const N: IAstNode): TVoid;
|
|
function WalkPipeSelectorList(const N: IAstNode): TVoid;
|
|
function WalkPipeInputList(const N: IAstNode): TVoid;
|
|
function WalkPipe(const N: IAstNode): TVoid;
|
|
|
|
procedure SetupHandlers; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TAstVisitor<T> }
|
|
|
|
procedure TAstVisitor<T>.AfterConstruction;
|
|
var
|
|
k: TAstNodeKind;
|
|
begin
|
|
inherited;
|
|
|
|
// 1. Initialize with Sentinel (Fail Fast / Safety)
|
|
for k := Low(TAstNodeKind) to High(TAstNodeKind) do
|
|
FHandlers[k] := DefaultSentinel;
|
|
|
|
// 2. Allow subclass to overwrite with real handlers
|
|
SetupHandlers;
|
|
end;
|
|
|
|
procedure TAstVisitor<T>.BeforeDestruction;
|
|
begin
|
|
for var i := Low(TAstNodeKind) to High(TAstNodeKind) do
|
|
FHandlers[i] := nil;
|
|
inherited;
|
|
end;
|
|
|
|
function TAstVisitor<T>.DefaultSentinel(const Node: IAstNode): T;
|
|
begin
|
|
Assert(false, 'No visitor handler registered for AST node kind: ' + Node.Kind.ToString + ' in class ' + ClassName);
|
|
exit(Default(T));
|
|
end;
|
|
|
|
procedure TAstVisitor<T>.SetupHandlers;
|
|
begin
|
|
// Base implementation does nothing.
|
|
// Subclasses must call Register() here.
|
|
end;
|
|
|
|
procedure TAstVisitor<T>.Register(Kind: TAstNodeKind; const Handler: TNodeHandler<T>);
|
|
begin
|
|
if Assigned(Handler) then
|
|
FHandlers[Kind] := Handler
|
|
else
|
|
FHandlers[Kind] := DefaultSentinel;
|
|
end;
|
|
|
|
function TAstVisitor<T>.Visit(const Node: IAstNode): T;
|
|
begin
|
|
if Assigned(Node) then
|
|
Result := FHandlers[Node.Kind](Node)
|
|
else
|
|
Result := Default(T);
|
|
end;
|
|
|
|
function TAstVisitor<T>.Accept(const Node: IAstNode): T;
|
|
begin
|
|
Result := Visit(Node);
|
|
end;
|
|
|
|
function TAstVisitor<T>.VisitBridge(const Node: IAstNode): TDataValue;
|
|
begin
|
|
Result := TDataValue.FromGeneric<T>(Visit(Node));
|
|
end;
|
|
|
|
{ TAstTransformer }
|
|
|
|
procedure TAstTransformer.SetupHandlers;
|
|
begin
|
|
// Leaves (Identity)
|
|
Register(akConstant, VisitLeaf);
|
|
Register(akIdentifier, VisitLeaf);
|
|
Register(akKeyword, VisitLeaf);
|
|
Register(akNop, VisitLeaf);
|
|
Register(akCreateSeries, VisitLeaf);
|
|
|
|
// Elements
|
|
Register(akRecordField, VisitRecordField);
|
|
|
|
// Structures
|
|
Register(akIfExpression, VisitIfExpression);
|
|
Register(akCondExpression, VisitCondExpression);
|
|
Register(akLambdaExpression, VisitLambdaExpression);
|
|
Register(akFunctionCall, VisitFunctionCall);
|
|
Register(akMacroExpansion, VisitMacroExpansionNode);
|
|
Register(akBlockExpression, VisitBlockExpression);
|
|
Register(akVariableDeclaration, VisitVariableDeclaration);
|
|
Register(akAssignment, VisitAssignment);
|
|
Register(akMacroDefinition, VisitMacroDefinition);
|
|
|
|
Register(akQuasiquote, VisitQuasiquote);
|
|
Register(akUnquote, VisitUnquote);
|
|
Register(akUnquoteSplicing, VisitUnquoteSplicing);
|
|
|
|
Register(akIndexer, VisitIndexer);
|
|
Register(akMemberAccess, VisitMemberAccess);
|
|
Register(akRecordLiteral, VisitRecordLiteral);
|
|
Register(akAddSeriesItem, VisitAddSeriesItem);
|
|
Register(akSeriesLength, VisitSeriesLength);
|
|
Register(akRecur, VisitRecurNode);
|
|
|
|
// Unified List Type
|
|
Register(akTuple, VisitTuple);
|
|
|
|
// Pipes
|
|
Register(akPipeInput, VisitPipeInput);
|
|
Register(akPipeSelectorList, VisitPipeSelectorList);
|
|
Register(akPipeInputList, VisitPipeInputList);
|
|
Register(akPipe, VisitPipe);
|
|
end;
|
|
|
|
function TAstTransformer.VisitLeaf(const N: IAstNode): IAstNode;
|
|
begin
|
|
Result := N;
|
|
end;
|
|
|
|
function TAstTransformer.VisitRecordField(const N: IAstNode): IAstNode;
|
|
var
|
|
F: IRecordFieldNode;
|
|
newKey: IKeywordNode;
|
|
newValue: IAstNode;
|
|
begin
|
|
F := N.AsRecordField;
|
|
newKey := Visit(F.Key).AsKeyword;
|
|
newValue := Visit(F.Value);
|
|
|
|
if (newKey = F.Key) and (newValue = F.Value) then
|
|
Result := N
|
|
else
|
|
Result := TAst.RecordField(N.Identity, newKey, newValue);
|
|
end;
|
|
|
|
// --- Structures ---
|
|
|
|
function TAstTransformer.VisitIfExpression(const N: IAstNode): IAstNode;
|
|
var
|
|
E: IIfExpressionNode;
|
|
c, t, eBr: IAstNode;
|
|
begin
|
|
E := N.AsIfExpression;
|
|
c := Visit(E.Condition);
|
|
t := Visit(E.ThenBranch);
|
|
eBr := Visit(E.ElseBranch);
|
|
|
|
if (c = E.Condition) and (t = E.ThenBranch) and (eBr = E.ElseBranch) then
|
|
Result := N
|
|
else
|
|
Result := TAst.IfExpr(N.Identity, c, t, eBr, E.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitCondExpression(const N: IAstNode): IAstNode;
|
|
var
|
|
E: ICondExpressionNode;
|
|
hasChanged: Boolean;
|
|
newPairs: TArray<TCondPair>;
|
|
newElse, c, b: IAstNode;
|
|
i: Integer;
|
|
begin
|
|
E := N.AsCondExpression;
|
|
hasChanged := False;
|
|
SetLength(newPairs, Length(E.Pairs));
|
|
for i := 0 to High(E.Pairs) do
|
|
begin
|
|
c := Visit(E.Pairs[i].Condition);
|
|
b := Visit(E.Pairs[i].Branch);
|
|
newPairs[i] := TCondPair.Create(c, b);
|
|
if (c <> E.Pairs[i].Condition) or (b <> E.Pairs[i].Branch) then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
newElse := Visit(E.ElseBranch);
|
|
if newElse <> E.ElseBranch then
|
|
hasChanged := True;
|
|
|
|
if not hasChanged then
|
|
Result := N
|
|
else
|
|
Result := TAst.CondExpr(N.Identity, newPairs, newElse, E.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitLambdaExpression(const N: IAstNode): IAstNode;
|
|
var
|
|
E: ILambdaExpressionNode;
|
|
p: ITupleNode;
|
|
b: IAstNode;
|
|
begin
|
|
E := N.AsLambdaExpression;
|
|
p := Visit(E.Parameters).AsTuple;
|
|
b := Visit(E.Body);
|
|
|
|
if (p = E.Parameters) and (b = E.Body) then
|
|
Result := N
|
|
else
|
|
Result :=
|
|
TLambdaExpressionNode.Create(p, b, E.StaticType, E.Layout, E.Descriptor, E.Upvalues, E.HasNestedLambdas, E.IsPure, N.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitFunctionCall(const N: IAstNode): IAstNode;
|
|
var
|
|
C: IFunctionCallNode;
|
|
callee: IAstNode;
|
|
args: ITupleNode;
|
|
begin
|
|
C := N.AsFunctionCall;
|
|
callee := Visit(C.Callee);
|
|
args := Visit(C.Arguments).AsTuple;
|
|
|
|
if (callee = C.Callee) and (args = C.Arguments) then
|
|
Result := N
|
|
else
|
|
Result := TAst.FunctionCall(N.Identity, callee, args, C.StaticType, C.IsTailCall, C.StaticTarget, C.IsTargetPure);
|
|
end;
|
|
|
|
function TAstTransformer.VisitMacroExpansionNode(const N: IAstNode): IAstNode;
|
|
var
|
|
M: IMacroExpansionNode;
|
|
exp: IAstNode;
|
|
begin
|
|
M := N.AsMacroExpansion;
|
|
exp := Visit(M.ExpandedBody);
|
|
if exp = M.ExpandedBody then
|
|
Result := N
|
|
else
|
|
Result := TAst.MacroExpansionNode(N.Identity, M.CallNode, exp);
|
|
end;
|
|
|
|
function TAstTransformer.VisitBlockExpression(const N: IAstNode): IAstNode;
|
|
var
|
|
B: IBlockExpressionNode;
|
|
e: ITupleNode;
|
|
begin
|
|
B := N.AsBlockExpression;
|
|
e := Visit(B.Expressions).AsTuple;
|
|
if e = B.Expressions then
|
|
Result := N
|
|
else
|
|
Result := TAst.Block(N.Identity, e, B.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitVariableDeclaration(const N: IAstNode): IAstNode;
|
|
var
|
|
V: IVariableDeclarationNode;
|
|
t, i: IAstNode;
|
|
begin
|
|
V := N.AsVariableDeclaration;
|
|
t := Visit(V.Target);
|
|
i := Visit(V.Initializer);
|
|
if (t = V.Target) and (i = V.Initializer) then
|
|
Result := N
|
|
else
|
|
Result := TAst.VarDecl(N.Identity, t, i, V.StaticType, V.IsBoxed);
|
|
end;
|
|
|
|
function TAstTransformer.VisitAssignment(const N: IAstNode): IAstNode;
|
|
var
|
|
A: IAssignmentNode;
|
|
t, v: IAstNode;
|
|
begin
|
|
A := N.AsAssignment;
|
|
t := Visit(A.Target);
|
|
v := Visit(A.Value);
|
|
if (t = A.Target) and (v = A.Value) then
|
|
Result := N
|
|
else
|
|
Result := TAst.Assign(N.Identity, t, v, A.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitMacroDefinition(const N: IAstNode): IAstNode;
|
|
begin
|
|
// Macro definitions are typically constant during runtime transformation, return as is.
|
|
Result := N;
|
|
end;
|
|
|
|
function TAstTransformer.VisitQuasiquote(const N: IAstNode): IAstNode;
|
|
var
|
|
Q: IQuasiquoteNode;
|
|
e: IAstNode;
|
|
begin
|
|
Q := N.AsQuasiquote;
|
|
e := Visit(Q.Expression);
|
|
if e = Q.Expression then
|
|
Result := N
|
|
else
|
|
Result := TAst.Quasiquote(N.Identity, e);
|
|
end;
|
|
|
|
function TAstTransformer.VisitUnquote(const N: IAstNode): IAstNode;
|
|
var
|
|
U: IUnquoteNode;
|
|
e: IAstNode;
|
|
begin
|
|
U := N.AsUnquote;
|
|
e := Visit(U.Expression);
|
|
if e = U.Expression then
|
|
Result := N
|
|
else
|
|
Result := TAst.Unquote(N.Identity, e);
|
|
end;
|
|
|
|
function TAstTransformer.VisitUnquoteSplicing(const N: IAstNode): IAstNode;
|
|
var
|
|
U: IUnquoteSplicingNode;
|
|
e: IAstNode;
|
|
begin
|
|
U := N.AsUnquoteSplicing;
|
|
e := Visit(U.Expression);
|
|
if e = U.Expression then
|
|
Result := N
|
|
else
|
|
Result := TAst.UnquoteSplicing(N.Identity, e.AsQuasiquote);
|
|
end;
|
|
|
|
function TAstTransformer.VisitIndexer(const N: IAstNode): IAstNode;
|
|
var
|
|
I: IIndexerNode;
|
|
b, idx: IAstNode;
|
|
begin
|
|
I := N.AsIndexer;
|
|
b := Visit(I.Base);
|
|
idx := Visit(I.Index);
|
|
if (b = I.Base) and (idx = I.Index) then
|
|
Result := N
|
|
else
|
|
Result := TAst.Indexer(N.Identity, b, idx, I.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitMemberAccess(const N: IAstNode): IAstNode;
|
|
var
|
|
M: IMemberAccessNode;
|
|
b: IAstNode;
|
|
begin
|
|
M := N.AsMemberAccess;
|
|
b := Visit(M.Base);
|
|
if b = M.Base then
|
|
Result := N
|
|
else
|
|
Result := TAst.MemberAccess(N.Identity, b, M.Member, M.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitRecordLiteral(const N: IAstNode): IAstNode;
|
|
var
|
|
R: IRecordLiteralNode;
|
|
f: ITupleNode;
|
|
begin
|
|
R := N.AsRecordLiteral;
|
|
f := Visit(R.Fields).AsTuple;
|
|
if f = R.Fields then
|
|
Result := N
|
|
else
|
|
Result := TAst.RecordLiteral(N.Identity, f, R.ScalarDefinition, R.GenericDefinition, R.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitAddSeriesItem(const N: IAstNode): IAstNode;
|
|
var
|
|
A: IAddSeriesItemNode;
|
|
s, v, l: IAstNode;
|
|
begin
|
|
A := N.AsAddSeriesItem;
|
|
s := Visit(A.Series);
|
|
v := Visit(A.Value);
|
|
l := Visit(A.Lookback);
|
|
|
|
if (s = A.Series) and (v = A.Value) and (l = A.Lookback) then
|
|
Result := N
|
|
else
|
|
Result := TAst.AddSeriesItem(N.Identity, s.AsIdentifier, v, l, A.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitSeriesLength(const N: IAstNode): IAstNode;
|
|
var
|
|
S: ISeriesLengthNode;
|
|
series: IAstNode;
|
|
begin
|
|
S := N.AsSeriesLength;
|
|
series := Visit(S.Series);
|
|
if series = S.Series then
|
|
Result := N
|
|
else
|
|
Result := TAst.SeriesLength(N.Identity, series.AsIdentifier, S.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitRecurNode(const N: IAstNode): IAstNode;
|
|
var
|
|
R: IRecurNode;
|
|
a: ITupleNode;
|
|
begin
|
|
R := N.AsRecur;
|
|
a := Visit(R.Arguments).AsTuple;
|
|
if a = R.Arguments then
|
|
Result := N
|
|
else
|
|
Result := TAst.Recur(N.Identity, a, R.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipeInput(const N: IAstNode): IAstNode;
|
|
var
|
|
P: IPipeInputNode;
|
|
s: IAstNode;
|
|
sel: IPipeSelectorList;
|
|
begin
|
|
P := N.AsPipeInput;
|
|
s := Visit(P.StreamSource);
|
|
sel := Visit(P.Selectors).AsPipeSelectorList;
|
|
|
|
if (s = P.StreamSource) and (sel = P.Selectors) then
|
|
Result := N
|
|
else
|
|
Result := TAst.PipeInput(s.AsIdentifier, sel, N.Identity.Location);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipeSelectorList(const N: IAstNode): IAstNode;
|
|
var
|
|
L: IPipeSelectorList;
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IKeywordNode>;
|
|
item, newItem: IKeywordNode;
|
|
i: Integer;
|
|
begin
|
|
L := N.AsPipeSelectorList;
|
|
hasChanged := False;
|
|
SetLength(newItems, L.Count);
|
|
for i := 0 to L.Count - 1 do
|
|
begin
|
|
item := L[i];
|
|
newItem := Visit(item).AsKeyword;
|
|
newItems[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := N
|
|
else
|
|
Result := TPipeSelectorList.Create(newItems, N.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipeInputList(const N: IAstNode): IAstNode;
|
|
var
|
|
L: IPipeInputList;
|
|
hasChanged: Boolean;
|
|
newItems: TArray<IPipeInputNode>;
|
|
item, newItem: IPipeInputNode;
|
|
i: Integer;
|
|
begin
|
|
L := N.AsPipeInputList;
|
|
hasChanged := False;
|
|
SetLength(newItems, L.Count);
|
|
for i := 0 to L.Count - 1 do
|
|
begin
|
|
item := L[i];
|
|
newItem := Visit(item).AsPipeInput;
|
|
newItems[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := N
|
|
else
|
|
Result := TPipeInputList.Create(newItems, N.Identity);
|
|
end;
|
|
|
|
function TAstTransformer.VisitPipe(const N: IAstNode): IAstNode;
|
|
var
|
|
P: IPipeNode;
|
|
inp: IPipeInputList;
|
|
trans: ILambdaExpressionNode;
|
|
begin
|
|
P := N.AsPipe;
|
|
inp := Visit(P.Inputs).AsPipeInputList;
|
|
trans := Visit(P.Transformation).AsLambdaExpression;
|
|
|
|
if (inp = P.Inputs) and (trans = P.Transformation) then
|
|
Result := N
|
|
else
|
|
Result := TAst.Pipe(N.Identity, inp, trans, P.StaticType);
|
|
end;
|
|
|
|
function TAstTransformer.VisitTuple(const N: IAstNode): IAstNode;
|
|
var
|
|
T: ITupleNode;
|
|
hasChanged: Boolean;
|
|
newElements: TArray<IAstNode>;
|
|
item, newItem: IAstNode;
|
|
i: Integer;
|
|
begin
|
|
T := N.AsTuple;
|
|
hasChanged := False;
|
|
SetLength(newElements, T.Count);
|
|
|
|
for i := 0 to T.Count - 1 do
|
|
begin
|
|
item := T.Items[i];
|
|
newItem := Visit(item);
|
|
newElements[i] := newItem;
|
|
if item <> newItem then
|
|
hasChanged := True;
|
|
end;
|
|
|
|
if not hasChanged then
|
|
Result := N
|
|
else
|
|
Result := TAst.Tuple(N.Identity, newElements, N.AsTypedNode.StaticType);
|
|
end;
|
|
|
|
{ TAstVisitor (Walker) }
|
|
|
|
procedure TAstVisitor.SetupHandlers;
|
|
begin
|
|
// Leaves (Do nothing)
|
|
Register(akConstant, WalkLeaf);
|
|
Register(akIdentifier, WalkLeaf);
|
|
Register(akKeyword, WalkLeaf);
|
|
Register(akNop, WalkLeaf);
|
|
Register(akCreateSeries, WalkLeaf);
|
|
|
|
// Elements
|
|
Register(akRecordField, WalkRecordField);
|
|
|
|
// Structures
|
|
Register(akIfExpression, WalkIfExpression);
|
|
Register(akCondExpression, WalkCondExpression);
|
|
Register(akLambdaExpression, WalkLambdaExpression);
|
|
Register(akFunctionCall, WalkFunctionCall);
|
|
Register(akMacroExpansion, WalkMacroExpansionNode);
|
|
Register(akBlockExpression, WalkBlockExpression);
|
|
Register(akVariableDeclaration, WalkVariableDeclaration);
|
|
Register(akAssignment, WalkAssignment);
|
|
Register(akMacroDefinition, WalkMacroDefinition);
|
|
|
|
// Wrappers (Generic Node Handler)
|
|
Register(akQuasiquote, WalkWrapper);
|
|
Register(akUnquote, WalkWrapper);
|
|
Register(akUnquoteSplicing, WalkWrapper);
|
|
|
|
Register(akIndexer, WalkIndexer);
|
|
Register(akMemberAccess, WalkMemberAccess);
|
|
Register(akRecordLiteral, WalkRecordLiteral);
|
|
Register(akAddSeriesItem, WalkAddSeriesItem);
|
|
Register(akSeriesLength, WalkSeriesLength);
|
|
Register(akRecur, WalkRecurNode);
|
|
|
|
Register(akTuple, WalkTuple);
|
|
|
|
// Pipes
|
|
Register(akPipeInput, WalkPipeInput);
|
|
Register(akPipeSelectorList, WalkPipeSelectorList);
|
|
Register(akPipeInputList, WalkPipeInputList);
|
|
Register(akPipe, WalkPipe);
|
|
end;
|
|
|
|
function TAstVisitor.WalkLeaf(const N: IAstNode): TVoid;
|
|
begin
|
|
// Do nothing
|
|
end;
|
|
|
|
function TAstVisitor.WalkWrapper(const N: IAstNode): TVoid;
|
|
begin
|
|
if N.Kind = akQuasiquote then
|
|
Visit(N.AsQuasiquote.Expression)
|
|
else if N.Kind = akUnquote then
|
|
Visit(N.AsUnquote.Expression)
|
|
else if N.Kind = akUnquoteSplicing then
|
|
Visit(N.AsUnquoteSplicing.Expression);
|
|
end;
|
|
|
|
function TAstVisitor.WalkRecordField(const N: IAstNode): TVoid;
|
|
begin
|
|
var F := N.AsRecordField;
|
|
Visit(F.Key);
|
|
Visit(F.Value);
|
|
end;
|
|
|
|
function TAstVisitor.WalkIfExpression(const N: IAstNode): TVoid;
|
|
begin
|
|
var E := N.AsIfExpression;
|
|
Visit(E.Condition);
|
|
Visit(E.ThenBranch);
|
|
Visit(E.ElseBranch);
|
|
end;
|
|
|
|
function TAstVisitor.WalkCondExpression(const N: IAstNode): TVoid;
|
|
begin
|
|
var E := N.AsCondExpression;
|
|
for var pair in E.Pairs do
|
|
begin
|
|
Visit(pair.Condition);
|
|
Visit(pair.Branch);
|
|
end;
|
|
Visit(E.ElseBranch);
|
|
end;
|
|
|
|
function TAstVisitor.WalkLambdaExpression(const N: IAstNode): TVoid;
|
|
begin
|
|
var E := N.AsLambdaExpression;
|
|
Visit(E.Parameters);
|
|
Visit(E.Body);
|
|
end;
|
|
|
|
function TAstVisitor.WalkFunctionCall(const N: IAstNode): TVoid;
|
|
begin
|
|
var C := N.AsFunctionCall;
|
|
Visit(C.Callee);
|
|
Visit(C.Arguments);
|
|
end;
|
|
|
|
function TAstVisitor.WalkMacroExpansionNode(const N: IAstNode): TVoid;
|
|
begin
|
|
Visit(N.AsMacroExpansion.ExpandedBody);
|
|
end;
|
|
|
|
function TAstVisitor.WalkBlockExpression(const N: IAstNode): TVoid;
|
|
begin
|
|
Visit(N.AsBlockExpression.Expressions);
|
|
end;
|
|
|
|
function TAstVisitor.WalkVariableDeclaration(const N: IAstNode): TVoid;
|
|
begin
|
|
var V := N.AsVariableDeclaration;
|
|
Visit(V.Target);
|
|
Visit(V.Initializer);
|
|
end;
|
|
|
|
function TAstVisitor.WalkAssignment(const N: IAstNode): TVoid;
|
|
begin
|
|
var A := N.AsAssignment;
|
|
Visit(A.Target);
|
|
Visit(A.Value);
|
|
end;
|
|
|
|
function TAstVisitor.WalkMacroDefinition(const N: IAstNode): TVoid;
|
|
begin
|
|
var M := N.AsMacroDefinition;
|
|
Visit(M.Name);
|
|
Visit(M.Parameters);
|
|
Visit(M.Body);
|
|
end;
|
|
|
|
function TAstVisitor.WalkIndexer(const N: IAstNode): TVoid;
|
|
begin
|
|
var I := N.AsIndexer;
|
|
Visit(I.Base);
|
|
Visit(I.Index);
|
|
end;
|
|
|
|
function TAstVisitor.WalkMemberAccess(const N: IAstNode): TVoid;
|
|
begin
|
|
var M := N.AsMemberAccess;
|
|
Visit(M.Base);
|
|
Visit(M.Member);
|
|
end;
|
|
|
|
function TAstVisitor.WalkRecordLiteral(const N: IAstNode): TVoid;
|
|
begin
|
|
Visit(N.AsRecordLiteral.Fields);
|
|
end;
|
|
|
|
function TAstVisitor.WalkAddSeriesItem(const N: IAstNode): TVoid;
|
|
begin
|
|
var A := N.AsAddSeriesItem;
|
|
Visit(A.Series);
|
|
Visit(A.Value);
|
|
Visit(A.Lookback);
|
|
end;
|
|
|
|
function TAstVisitor.WalkSeriesLength(const N: IAstNode): TVoid;
|
|
begin
|
|
Visit(N.AsSeriesLength.Series);
|
|
end;
|
|
|
|
function TAstVisitor.WalkRecurNode(const N: IAstNode): TVoid;
|
|
begin
|
|
Visit(N.AsRecur.Arguments);
|
|
end;
|
|
|
|
function TAstVisitor.WalkPipeInput(const N: IAstNode): TVoid;
|
|
begin
|
|
var P := N.AsPipeInput;
|
|
Visit(P.StreamSource);
|
|
Visit(P.Selectors);
|
|
end;
|
|
|
|
function TAstVisitor.WalkPipeSelectorList(const N: IAstNode): TVoid;
|
|
begin
|
|
for var item in N.AsPipeSelectorList do
|
|
Visit(item);
|
|
end;
|
|
|
|
function TAstVisitor.WalkPipeInputList(const N: IAstNode): TVoid;
|
|
begin
|
|
for var item in N.AsPipeInputList do
|
|
Visit(item);
|
|
end;
|
|
|
|
function TAstVisitor.WalkPipe(const N: IAstNode): TVoid;
|
|
begin
|
|
var P := N.AsPipe;
|
|
Visit(P.Inputs);
|
|
Visit(P.Transformation);
|
|
end;
|
|
|
|
function TAstVisitor.WalkTuple(const N: IAstNode): TVoid;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
var t := N.AsTuple;
|
|
for i := 0 to t.Count - 1 do
|
|
Visit(t.Items[i]);
|
|
end;
|
|
|
|
end.
|