AST Identities
This commit is contained in:
+55
-75
@@ -8,7 +8,8 @@ uses
|
||||
Myc.Data.Value,
|
||||
Myc.Ast,
|
||||
Myc.Ast.Types,
|
||||
Myc.Ast.Nodes;
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Identities; // Needed for identity casting
|
||||
|
||||
type
|
||||
TAstVisitor<T> = class abstract(TInterfacedObject, IAstVisitor)
|
||||
@@ -36,7 +37,7 @@ type
|
||||
function IAstVisitor.VisitAddSeriesItem = DoVisitAddSeriesItem;
|
||||
function IAstVisitor.VisitSeriesLength = DoVisitSeriesLength;
|
||||
function IAstVisitor.VisitRecurNode = DoVisitRecurNode;
|
||||
function IAstVisitor.VisitNop = DoVisitNop; // Added Nop
|
||||
function IAstVisitor.VisitNop = DoVisitNop;
|
||||
|
||||
// Private bridge method implementations
|
||||
function DoVisitConstant(const Node: IConstantNode): TDataValue;
|
||||
@@ -61,7 +62,7 @@ type
|
||||
function DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
function DoVisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
function DoVisitNop(const Node: INopNode): TDataValue; // Added Nop
|
||||
function DoVisitNop(const Node: INopNode): TDataValue;
|
||||
|
||||
protected
|
||||
// Visit a node.
|
||||
@@ -89,7 +90,7 @@ type
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): T; virtual; abstract;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): T; virtual; abstract;
|
||||
function VisitRecurNode(const Node: IRecurNode): T; virtual; abstract;
|
||||
function VisitNop(const Node: INopNode): T; virtual; abstract; // Added Nop
|
||||
function VisitNop(const Node: INopNode): T; virtual; abstract;
|
||||
end;
|
||||
|
||||
TAstTransformer = class abstract(TAstVisitor<IAstNode>)
|
||||
@@ -119,7 +120,7 @@ type
|
||||
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; // Added Nop
|
||||
function VisitNop(const Node: INopNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
TAstVisitor = class abstract(TInterfacedObject, IAstVisitor)
|
||||
@@ -147,7 +148,7 @@ type
|
||||
function IAstVisitor.VisitAddSeriesItem = DoVisitAddSeriesItem;
|
||||
function IAstVisitor.VisitSeriesLength = DoVisitSeriesLength;
|
||||
function IAstVisitor.VisitRecurNode = DoVisitRecurNode;
|
||||
function IAstVisitor.VisitNop = DoVisitNop; // Added Nop
|
||||
function IAstVisitor.VisitNop = DoVisitNop;
|
||||
|
||||
// Private bridge method implementations
|
||||
function DoVisitConstant(const Node: IConstantNode): TDataValue;
|
||||
@@ -172,7 +173,7 @@ type
|
||||
function DoVisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
function DoVisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
function DoVisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
function DoVisitNop(const Node: INopNode): TDataValue; // Added Nop
|
||||
function DoVisitNop(const Node: INopNode): TDataValue;
|
||||
|
||||
protected
|
||||
// Virtual procedures for descendants (Interpreters/Side-effects) to override
|
||||
@@ -198,7 +199,7 @@ type
|
||||
procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); virtual; abstract;
|
||||
procedure VisitSeriesLength(const Node: ISeriesLengthNode); virtual; abstract;
|
||||
procedure VisitRecurNode(const Node: IRecurNode); virtual; abstract;
|
||||
procedure VisitNop(const Node: INopNode); virtual; abstract; // Added Nop
|
||||
procedure VisitNop(const Node: INopNode); virtual; abstract;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -327,7 +328,6 @@ end;
|
||||
|
||||
function TAstVisitor<T>.DoVisitNop(const Node: INopNode): TDataValue;
|
||||
begin
|
||||
// Added Nop implementation
|
||||
Result := TDataValue.FromGeneric<T>(VisitNop(Node));
|
||||
end;
|
||||
|
||||
@@ -337,7 +337,6 @@ var
|
||||
hasChanged: Boolean;
|
||||
newNode: IIdentifierNode;
|
||||
begin
|
||||
// Implement Copy-on-Write for parameter arrays
|
||||
hasChanged := False;
|
||||
SetLength(Result, Length(Nodes));
|
||||
|
||||
@@ -350,7 +349,7 @@ begin
|
||||
end;
|
||||
|
||||
if not hasChanged then
|
||||
Result := Nodes; // Return original array if no changes
|
||||
Result := Nodes;
|
||||
end;
|
||||
|
||||
function TAstTransformer.AcceptNodes(
|
||||
@@ -359,12 +358,10 @@ function TAstTransformer.AcceptNodes(
|
||||
): TArray<IAstNode>;
|
||||
var
|
||||
i: Integer;
|
||||
newNode: IAstNode; // Changed from TDataValue
|
||||
newList: TList<IAstNode>; // Used if nodes are removed (e.g. defmacro)
|
||||
newNode: IAstNode;
|
||||
newList: TList<IAstNode>;
|
||||
hasChanged: Boolean;
|
||||
begin
|
||||
// This implementation already supports CoW and node removal (nil)
|
||||
// We just need to track if the array reference itself needs to change.
|
||||
hasChanged := False;
|
||||
newList := nil;
|
||||
|
||||
@@ -373,25 +370,24 @@ begin
|
||||
if Assigned(AcceptProc) then
|
||||
newNode := AcceptProc(i, Nodes[i])
|
||||
else
|
||||
newNode := Accept(Nodes[i]); // Calls new Accept, returns IAstNode (or nil)
|
||||
newNode := Accept(Nodes[i]);
|
||||
|
||||
if not Assigned(newNode) then // Node was removed
|
||||
if not Assigned(newNode) then
|
||||
begin
|
||||
hasChanged := True;
|
||||
if newList = nil then // First change
|
||||
if newList = nil then
|
||||
begin
|
||||
newList := TList<IAstNode>.Create;
|
||||
for var j := 0 to i - 1 do
|
||||
newList.Add(Nodes[j]);
|
||||
end;
|
||||
// else: just skip adding it
|
||||
end
|
||||
else
|
||||
begin
|
||||
if newNode <> Nodes[i] then // Node was replaced
|
||||
if newNode <> Nodes[i] then
|
||||
begin
|
||||
hasChanged := True;
|
||||
if newList = nil then // First change
|
||||
if newList = nil then
|
||||
begin
|
||||
newList := TList<IAstNode>.Create;
|
||||
for var j := 0 to i - 1 do
|
||||
@@ -401,45 +397,43 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
if newList <> nil then // No change, but we are already copying
|
||||
if newList <> nil then
|
||||
newList.Add(Nodes[i]);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if not hasChanged then
|
||||
Result := Nodes // Return original array
|
||||
Result := Nodes
|
||||
else if newList <> nil then
|
||||
begin
|
||||
Result := newList.ToArray;
|
||||
newList.Free;
|
||||
end
|
||||
else
|
||||
Result := []; // All nodes were removed
|
||||
Result := [];
|
||||
end;
|
||||
|
||||
// --- Base Virtual Implementations (IAstNode-based) ---
|
||||
// --- Now fully implementing Copy-on-Write (CoW) ---
|
||||
// --- Base Virtual Implementations ---
|
||||
|
||||
function TAstTransformer.VisitConstant(const Node: IConstantNode): IAstNode;
|
||||
begin
|
||||
Result := Node; // Leaf node, immutable
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
|
||||
begin
|
||||
Result := Node; // Leaf node, immutable (will be replaced by Binder)
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitKeyword(const Node: IKeywordNode): IAstNode;
|
||||
begin
|
||||
Result := Node; // Leaf node, immutable
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitNop(const Node: INopNode): IAstNode;
|
||||
begin
|
||||
// Added Nop implementation
|
||||
Result := Node; // Leaf node, immutable
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
|
||||
@@ -448,13 +442,12 @@ var
|
||||
begin
|
||||
newCond := Accept(Node.Condition);
|
||||
newThen := Accept(Node.ThenBranch);
|
||||
newElse := Accept(Node.ElseBranch); // Accept handles nil
|
||||
newElse := Accept(Node.ElseBranch);
|
||||
|
||||
if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.IfExpr(newCond, newThen, newElse, Node.StaticType);
|
||||
Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
|
||||
@@ -468,8 +461,7 @@ begin
|
||||
if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.TernaryExpr(newCond, newThen, newElse, Node.StaticType);
|
||||
Result := TAst.TernaryExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
@@ -477,7 +469,6 @@ var
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
begin
|
||||
// No longer cast to concrete class, use interface
|
||||
newParams := AcceptParameters(Node.Parameters);
|
||||
newBody := Accept(Node.Body);
|
||||
|
||||
@@ -485,9 +476,9 @@ begin
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
// Use TAst factory and copy properties via interface getters
|
||||
Result :=
|
||||
TAst.LambdaExpr(
|
||||
Node.Identity,
|
||||
newParams,
|
||||
newBody,
|
||||
Node.Layout,
|
||||
@@ -512,8 +503,8 @@ begin
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
// Use TAst factory and copy properties via interface getters
|
||||
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, Node.IsTailCall, Node.StaticTarget);
|
||||
Result :=
|
||||
TAst.FunctionCall(Node.Identity, newCallee, newArgs, Node.StaticType, Node.IsTailCall, Node.StaticTarget, Node.IsTargetPure);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -521,13 +512,12 @@ function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode
|
||||
var
|
||||
newBody: IAstNode;
|
||||
begin
|
||||
// Visit the body and check if it changed
|
||||
newBody := Accept(Node.ExpandedBody);
|
||||
if newBody = Node.ExpandedBody then
|
||||
exit(Node);
|
||||
|
||||
// The body changed. Create a NEW wrapper node.
|
||||
Result := TAst.MacroExpansionNode(Node.CallNode, newBody);
|
||||
// Macro Expansion Nodes are structural, reuse identity
|
||||
Result := TAst.MacroExpansionNode(Node.Identity, Node.CallNode, newBody);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
||||
@@ -539,8 +529,7 @@ begin
|
||||
if newExprs = Node.Expressions then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.Block(newExprs, Node.StaticType);
|
||||
Result := TAst.Block(Node.Identity, newExprs, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
||||
@@ -555,7 +544,7 @@ begin
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
Result := TAst.VarDecl(newTarget, newInit, Node.StaticType, Node.IsBoxed);
|
||||
Result := TAst.VarDecl(Node.Identity, newTarget, newInit, Node.StaticType, Node.IsBoxed);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -570,17 +559,13 @@ begin
|
||||
if (newTarget = Node.Target) and (newValue = Node.Value) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.Assign(newTarget, newValue, Node.StaticType);
|
||||
Result := TAst.Assign(Node.Identity, newTarget, newValue, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
|
||||
begin
|
||||
// A macro definition is a compile-time construct.
|
||||
// Transformers (Binder, TypeChecker, Lowerer) should not traverse its
|
||||
// children (Name, Parameters, Body) as they are not part of the
|
||||
// standard execution AST.
|
||||
// Serializers (TAstDumper/TJsonAstConverter), which need to traverse,
|
||||
// provide their own override.
|
||||
// Macro definitions are usually stripped/ignored in transformers,
|
||||
// but we return them as-is to support partial pipelines.
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
@@ -588,37 +573,38 @@ function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
|
||||
var
|
||||
newExpr: IAstNode;
|
||||
begin
|
||||
// Rebuild instead of mutate (Copy-on-Write)
|
||||
newExpr := Accept(Node.Expression);
|
||||
if newExpr = Node.Expression then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.Quasiquote(newExpr);
|
||||
Result := TAst.Quasiquote(Node.Identity, newExpr);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode;
|
||||
var
|
||||
newExpr: IAstNode;
|
||||
begin
|
||||
// Rebuild instead of mutate (Copy-on-Write)
|
||||
newExpr := Accept(Node.Expression);
|
||||
if newExpr = Node.Expression then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.Unquote(newExpr);
|
||||
Result := TAst.Unquote(Node.Identity, newExpr);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
|
||||
var
|
||||
newExpr: IAstNode;
|
||||
begin
|
||||
// Rebuild instead of mutate (Copy-on-Write)
|
||||
// Note: Accept expects IAstNode, IQuasiquoteNode inherits from it.
|
||||
newExpr := Accept(Node.Expression);
|
||||
|
||||
if (newExpr = Node.Expression) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.UnquoteSplicing(newExpr.AsQuasiquote);
|
||||
// We need to ensure the transformed expression is still a Quasiquote,
|
||||
// or we have to assume the transformer knows what it's doing.
|
||||
// For safety, we cast back. If transformation changed type, this will fail fast.
|
||||
Result := TAst.UnquoteSplicing(Node.Identity, newExpr.AsQuasiquote);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode;
|
||||
@@ -631,8 +617,7 @@ begin
|
||||
if (newBase = Node.Base) and (newIndex = Node.Index) then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.Indexer(newBase, newIndex, Node.StaticType);
|
||||
Result := TAst.Indexer(Node.Identity, newBase, newIndex, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
|
||||
@@ -641,13 +626,12 @@ var
|
||||
newMember: IKeywordNode;
|
||||
begin
|
||||
newBase := Accept(Node.Base);
|
||||
newMember := Accept(Node.Member).AsKeyword; // Keyword ist Blattknoten
|
||||
newMember := Accept(Node.Member).AsKeyword;
|
||||
|
||||
if (newBase = Node.Base) and (newMember = Node.Member) then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.MemberAccess(newBase, newMember, Node.StaticType);
|
||||
Result := TAst.MemberAccess(Node.Identity, newBase, newMember, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
|
||||
@@ -661,6 +645,7 @@ begin
|
||||
|
||||
for i := 0 to High(Node.Fields) do
|
||||
begin
|
||||
// Keys are typically static, but we visit them anyway
|
||||
newFields[i].Key := Accept(Node.Fields[i].Key).AsKeyword;
|
||||
newFields[i].Value := Accept(Node.Fields[i].Value);
|
||||
if (newFields[i].Key <> Node.Fields[i].Key) or (newFields[i].Value <> Node.Fields[i].Value) then
|
||||
@@ -671,15 +656,13 @@ begin
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
// Rebuild the node, preserving its specific type and definitions
|
||||
// Use TAst factory
|
||||
Result := TAst.RecordLiteral(newFields, Node.ScalarDefinition, Node.GenericDefinition, Node.StaticType);
|
||||
Result := TAst.RecordLiteral(Node.Identity, newFields, Node.ScalarDefinition, Node.GenericDefinition, Node.StaticType);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
|
||||
begin
|
||||
Result := Node; // Leaf node, immutable
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
|
||||
@@ -689,13 +672,12 @@ var
|
||||
begin
|
||||
newSeries := Accept(Node.Series).AsIdentifier;
|
||||
newValue := Accept(Node.Value);
|
||||
newLookback := Accept(Node.Lookback); // Accept handles nil
|
||||
newLookback := Accept(Node.Lookback);
|
||||
|
||||
if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.AddSeriesItem(newSeries, newValue, newLookback, Node.StaticType);
|
||||
Result := TAst.AddSeriesItem(Node.Identity, newSeries, newValue, newLookback, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
|
||||
@@ -707,8 +689,7 @@ begin
|
||||
if newSeries = Node.Series then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.SeriesLength(newSeries, Node.StaticType);
|
||||
Result := TAst.SeriesLength(Node.Identity, newSeries, Node.StaticType);
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode;
|
||||
@@ -720,8 +701,7 @@ begin
|
||||
if newArgs = Node.Arguments then
|
||||
Result := Node
|
||||
else
|
||||
// Use TAst factory
|
||||
Result := TAst.Recur(newArgs, Node.StaticType);
|
||||
Result := TAst.Recur(Node.Identity, newArgs, Node.StaticType);
|
||||
end;
|
||||
|
||||
{ TAstVisitor }
|
||||
|
||||
Reference in New Issue
Block a user