Imutability for macro nodes

This commit is contained in:
Michael Schimmel
2025-11-05 12:19:34 +01:00
parent 60358365cd
commit 9bd2d6f7ab
10 changed files with 385 additions and 151 deletions
+1 -1
View File
@@ -1809,7 +1809,7 @@ end;
function TMacroExpansionNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; function TMacroExpansionNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode;
begin begin
Result := TAst.MacroExpansionNode(FNode, FExpandedBodyNode.CreateAst); Result := TAst.MacroExpansionNode(FNode.CallNode, FExpandedBodyNode.CreateAst);
end; end;
{ TRecurNodeHandler } { TRecurNodeHandler }
+1 -1
View File
@@ -117,7 +117,7 @@ begin
// To represent a macro expansion as a single line of text, the most // To represent a macro expansion as a single line of text, the most
// informative representation is the original call itself. We can simply // informative representation is the original call itself. We can simply
// delegate to the VisitFunctionCall implementation to format it. // delegate to the VisitFunctionCall implementation to format it.
Result := VisitFunctionCall(Node); Result := VisitFunctionCall(Node.CallNode);
end; end;
function TAstToTextVisitor.VisitRecurNode(const Node: IRecurNode): string; function TAstToTextVisitor.VisitRecurNode(const Node: IRecurNode): string;
+76 -31
View File
@@ -116,79 +116,109 @@ end;
function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
var var
i: Integer;
isContextTail: Boolean; isContextTail: Boolean;
N: TBlockExpressionNode; newExprs: TArray<IAstNode>; // For CoW
begin begin
N := (Node as TBlockExpressionNode);
isContextTail := FIsTailStack.Peek; isContextTail := FIsTailStack.Peek;
// We must manually iterate here to set FNextIsTail for each child // Call the base implementation which handles CoW
for i := 0 to High(N.Expressions) do var nTail := High(Node.Expressions);
begin newExprs :=
// Only the last expression in a block is in tail position AcceptNodes(
FNextIsTail := isContextTail and (i = High(N.Expressions)); Node.Expressions,
N.Expressions[i] := Accept(N.Expressions[i]); function(idx: Integer; Node: IAstNode): IAstNode
end; begin
Result := N; FNextIsTail := isContextTail and (idx = nTail);
Result := Accept(Node);
end
);
if newExprs = Node.Expressions then
Result := Node
else
Result := TBlockExpressionNode.Create(newExprs, Node.StaticType);
end; end;
function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): IAstNode; function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
var var
isContextTail: Boolean; isContextTail: Boolean;
N: TIfExpressionNode; N: TIfExpressionNode;
newCond, newThen, newElse: IAstNode;
begin begin
N := (Node as TIfExpressionNode); N := (Node as TIfExpressionNode);
isContextTail := FIsTailStack.Peek; isContextTail := FIsTailStack.Peek;
// Condition is never in tail position // Condition is never in tail position
FNextIsTail := False; FNextIsTail := False;
N.Condition := Accept(N.Condition); newCond := Accept(N.Condition);
// Then/Else branches ARE in tail position if the IfExpr is // Then/Else branches ARE in tail position if the IfExpr is
FNextIsTail := isContextTail; FNextIsTail := isContextTail;
N.ThenBranch := Accept(N.ThenBranch); newThen := Accept(N.ThenBranch);
N.ElseBranch := Accept(N.ElseBranch); newElse := Accept(N.ElseBranch);
Result := N; if (newCond = N.Condition) and (newThen = N.ThenBranch) and (newElse = N.ElseBranch) then
Result := Node
else
Result := TIfExpressionNode.Create(newCond, newThen, newElse, N.StaticType);
end; end;
function TAstTCO.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode; function TAstTCO.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
var var
isContextTail: Boolean; isContextTail: Boolean;
N: TTernaryExpressionNode; N: TTernaryExpressionNode;
newCond, newThen, newElse: IAstNode;
begin begin
N := (Node as TTernaryExpressionNode); N := (Node as TTernaryExpressionNode);
isContextTail := FIsTailStack.Peek; isContextTail := FIsTailStack.Peek;
// Condition is never in tail position // Condition is never in tail position
FNextIsTail := False; FNextIsTail := False;
N.Condition := Accept(N.Condition); newCond := Accept(N.Condition);
// Then/Else branches ARE in tail position if the TernaryExpr is // Then/Else branches ARE in tail position if the TernaryExpr is
FNextIsTail := isContextTail; FNextIsTail := isContextTail;
N.ThenBranch := Accept(N.ThenBranch); newThen := Accept(N.ThenBranch);
N.ElseBranch := Accept(N.ElseBranch); newElse := Accept(N.ElseBranch);
Result := N; if (newCond = N.Condition) and (newThen = N.ThenBranch) and (newElse = N.ElseBranch) then
Result := Node
else
Result := TTernaryExpressionNode.Create(newCond, newThen, newElse, N.StaticType);
end; end;
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var var
N: TLambdaExpressionNode; N: TLambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
begin begin
N := (Node as TLambdaExpressionNode); N := (Node as TLambdaExpressionNode);
// Parameters are not in tail position (handled by AcceptParameters)
FNextIsTail := False;
newParams := AcceptParameters(N.Parameters);
// The body of a lambda is *always* a tail position (relative to the lambda) // The body of a lambda is *always* a tail position (relative to the lambda)
FNextIsTail := True; FNextIsTail := True;
N.Body := Accept(N.Body); newBody := Accept(N.Body);
Result := N; if (newParams = N.Parameters) and (newBody = N.Body) then
Result := Node
else
begin
Result := TLambdaExpressionNode.Create(newParams, newBody, N.StaticType);
// Copy runtime properties
(Result as TLambdaExpressionNode).ScopeDescriptor := N.ScopeDescriptor;
(Result as TLambdaExpressionNode).Upvalues := N.Upvalues;
(Result as TLambdaExpressionNode).HasNestedLambdas := N.HasNestedLambdas;
end;
end; end;
function TAstTCO.VisitRecurNode(const Node: IRecurNode): IAstNode; function TAstTCO.VisitRecurNode(const Node: IRecurNode): IAstNode;
var var
N: TRecurNode; N: TRecurNode;
newArgs: TArray<IAstNode>;
begin begin
if not FIsTailStack.Peek then if not FIsTailStack.Peek then
raise Exception.Create('''recur'' can only be used in a tail position.'); raise Exception.Create('''recur'' can only be used in a tail position.');
@@ -197,41 +227,56 @@ begin
// Arguments are not in tail position // Arguments are not in tail position
FNextIsTail := False; FNextIsTail := False;
N.Arguments := AcceptNodes(N.Arguments); newArgs := AcceptNodes(N.Arguments);
Result := N; if newArgs = N.Arguments then
Result := Node
else
Result := TRecurNode.Create(newArgs, N.StaticType);
end; end;
function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
var var
N: TMacroExpansionNode; N: TMacroExpansionNode;
newBody: IAstNode;
begin begin
N := (Node as TMacroExpansionNode); N := (Node as TMacroExpansionNode);
// Propagate tail call status to the expanded body // Propagate tail call status to the expanded body
FNextIsTail := FIsTailStack.Peek; FNextIsTail := FIsTailStack.Peek;
N.ExpandedBody := Accept(N.ExpandedBody); newBody := Accept(N.ExpandedBody);
Result := N; if newBody = N.ExpandedBody then
Result := Node
else
// Rebuild, preserving the original CallNode metadata
Result := TMacroExpansionNode.Create(N.CallNode, newBody, newBody.AsTypedNode.StaticType);
end; end;
function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var var
isTailCall: Boolean; isTailCall: Boolean;
N: TFunctionCallNode; N: TFunctionCallNode;
newCallee: IAstNode;
newArgs: TArray<IAstNode>;
begin begin
isTailCall := FIsTailStack.Peek; isTailCall := FIsTailStack.Peek;
N := (Node as TFunctionCallNode); N := (Node as TFunctionCallNode);
// Arguments are not in tail position // Arguments are not in tail position
FNextIsTail := False; FNextIsTail := False;
N.Callee := Accept(N.Callee); newCallee := Accept(N.Callee);
N.Arguments := AcceptNodes(N.Arguments); newArgs := AcceptNodes(N.Arguments);
// Mutate the node with the TCO status // CoW check: Create a new node only if children changed OR IsTailCall needs update
N.IsTailCall := isTailCall; if (newCallee = N.Callee) and (newArgs = N.Arguments) and (isTailCall = N.IsTailCall) then
Result := Node
Result := N; else
begin
Result := TFunctionCallNode.Create(newCallee, newArgs, N.StaticType);
// Mutate the *new* node with the TCO status
(Result as TFunctionCallNode).IsTailCall := isTailCall;
end;
end; end;
end. end.
+3 -3
View File
@@ -288,10 +288,10 @@ begin
Log('Original Call:'); Log('Original Call:');
Indent; Indent;
Log('Callee:'); Log('Callee:');
Node.Callee.Accept(Self); Node.CallNode.Callee.Accept(Self);
LogFmt('Arguments (%d):', [Length(Node.Arguments)]); LogFmt('Arguments (%d):', [Length(Node.CallNode.Arguments)]);
Indent; Indent;
for arg in Node.Arguments do for arg in Node.CallNode.Arguments do
arg.Accept(Self); arg.Accept(Self);
Unindent; Unindent;
Unindent; Unindent;
+2 -2
View File
@@ -303,11 +303,11 @@ var
argsArray: TJSONArray; argsArray: TJSONArray;
arg: IAstNode; arg: IAstNode;
begin begin
calleeObj := Accept(Node.Callee); calleeObj := Accept(Node.CallNode.Callee);
bodyObj := Accept(Node.ExpandedBody); bodyObj := Accept(Node.ExpandedBody);
argsArray := TJSONArray.Create; argsArray := TJSONArray.Create;
for arg in Node.Arguments do for arg in Node.CallNode.Arguments do
argsArray.Add(Accept(arg)); argsArray.Add(Accept(arg));
Result := TJSONObject.Create; Result := TJSONObject.Create;
+18
View File
@@ -57,6 +57,9 @@ type
function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
// Finds and expands macro calls // Finds and expands macro calls
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override;
function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
public public
constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory); constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory);
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
@@ -327,4 +330,19 @@ begin
Result := Self.Accept(macroNode); // Calls the IAstNode helper, returns IAstNode Result := Self.Accept(macroNode); // Calls the IAstNode helper, returns IAstNode
end; end;
function TMacroExpander.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
begin
Result := Accept(Node.Expression);
end;
function TMacroExpander.VisitUnquote(const Node: IUnquoteNode): IAstNode;
begin
Result := Accept(Node.Expression);
end;
function TMacroExpander.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
begin
Result := Accept(Node.Expression);
end;
end. end.
+22 -20
View File
@@ -179,7 +179,7 @@ type
// This node is illegal during compilation. // This node is illegal during compilation.
end; end;
IConstantNode = interface(IAstNode) IConstantNode = interface(IAstTypedNode)
// Represents a constant value in the AST (Scalar, Text, or Void). // Represents a constant value in the AST (Scalar, Text, or Void).
{$region 'private'} {$region 'private'}
function GetValue: TDataValue; function GetValue: TDataValue;
@@ -187,7 +187,7 @@ type
property Value: TDataValue read GetValue; property Value: TDataValue read GetValue;
end; end;
IIdentifierNode = interface(IAstNode) IIdentifierNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetName: string; function GetName: string;
{$endregion} {$endregion}
@@ -202,7 +202,7 @@ type
end; end;
// Represents a keyword literal in the AST (e.g., :foo) // Represents a keyword literal in the AST (e.g., :foo)
IKeywordNode = interface(IAstNode) IKeywordNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetValue: IKeyword; function GetValue: IKeyword;
{$endregion} {$endregion}
@@ -210,7 +210,7 @@ type
property Value: IKeyword read GetValue; property Value: IKeyword read GetValue;
end; end;
IBinaryExpressionNode = interface(IAstNode) IBinaryExpressionNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetLeft: IAstNode; function GetLeft: IAstNode;
function GetOperator: TScalar.TBinaryOp; function GetOperator: TScalar.TBinaryOp;
@@ -221,7 +221,7 @@ type
property Right: IAstNode read GetRight; property Right: IAstNode read GetRight;
end; end;
IUnaryExpressionNode = interface(IAstNode) IUnaryExpressionNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetOperator: TScalar.TUnaryOp; function GetOperator: TScalar.TUnaryOp;
function GetRight: IAstNode; function GetRight: IAstNode;
@@ -230,7 +230,7 @@ type
property Right: IAstNode read GetRight; property Right: IAstNode read GetRight;
end; end;
IIfExpressionNode = interface(IAstNode) IIfExpressionNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetCondition: IAstNode; function GetCondition: IAstNode;
function GetThenBranch: IAstNode; function GetThenBranch: IAstNode;
@@ -241,7 +241,7 @@ type
property ElseBranch: IAstNode read GetElseBranch; property ElseBranch: IAstNode read GetElseBranch;
end; end;
ITernaryExpressionNode = interface(IAstNode) ITernaryExpressionNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetCondition: IAstNode; function GetCondition: IAstNode;
function GetThenBranch: IAstNode; function GetThenBranch: IAstNode;
@@ -252,7 +252,7 @@ type
property ElseBranch: IAstNode read GetElseBranch; property ElseBranch: IAstNode read GetElseBranch;
end; end;
ILambdaExpressionNode = interface(IAstNode) ILambdaExpressionNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetParameters: TArray<IIdentifierNode>; function GetParameters: TArray<IIdentifierNode>;
function GetBody: IAstNode; function GetBody: IAstNode;
@@ -261,7 +261,7 @@ type
property Body: IAstNode read GetBody; property Body: IAstNode read GetBody;
end; end;
IFunctionCallNode = interface(IAstNode) IFunctionCallNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetCallee: IAstNode; function GetCallee: IAstNode;
function GetArguments: TArray<IAstNode>; function GetArguments: TArray<IAstNode>;
@@ -271,30 +271,32 @@ type
end; end;
// A node representing a macro call. // A node representing a macro call.
IMacroExpansionNode = interface(IFunctionCallNode) IMacroExpansionNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetCallNode: IFunctionCallNode;
function GetExpandedBody: IAstNode; function GetExpandedBody: IAstNode;
{$endregion} {$endregion}
// The AST fragment that resulted from the macro expansion. // The AST fragment that resulted from the macro expansion.
property CallNode: IFunctionCallNode read GetCallNode;
property ExpandedBody: IAstNode read GetExpandedBody; property ExpandedBody: IAstNode read GetExpandedBody;
end; end;
// A node representing a tail-recursive call. // A node representing a tail-recursive call.
IRecurNode = interface(IAstNode) IRecurNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetArguments: TArray<IAstNode>; function GetArguments: TArray<IAstNode>;
{$endregion} {$endregion}
property Arguments: TArray<IAstNode> read GetArguments; property Arguments: TArray<IAstNode> read GetArguments;
end; end;
IBlockExpressionNode = interface(IAstNode) IBlockExpressionNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetExpressions: TArray<IAstNode>; function GetExpressions: TArray<IAstNode>;
{$endregion} {$endregion}
property Expressions: TArray<IAstNode> read GetExpressions; property Expressions: TArray<IAstNode> read GetExpressions;
end; end;
IVariableDeclarationNode = interface(IAstNode) IVariableDeclarationNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetIdentifier: IIdentifierNode; function GetIdentifier: IIdentifierNode;
function GetInitializer: IAstNode; function GetInitializer: IAstNode;
@@ -303,7 +305,7 @@ type
property Initializer: IAstNode read GetInitializer; property Initializer: IAstNode read GetInitializer;
end; end;
IAssignmentNode = interface(IAstNode) IAssignmentNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetIdentifier: IIdentifierNode; function GetIdentifier: IIdentifierNode;
function GetValue: IAstNode; function GetValue: IAstNode;
@@ -347,7 +349,7 @@ type
property Expression: IQuasiquoteNode read GetExpression; property Expression: IQuasiquoteNode read GetExpression;
end; end;
IIndexerNode = interface(IAstNode) IIndexerNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetBase: IAstNode; function GetBase: IAstNode;
function GetIndex: IAstNode; function GetIndex: IAstNode;
@@ -356,7 +358,7 @@ type
property Index: IAstNode read GetIndex; property Index: IAstNode read GetIndex;
end; end;
IMemberAccessNode = interface(IAstNode) IMemberAccessNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetBase: IAstNode; function GetBase: IAstNode;
function GetMember: IKeywordNode; function GetMember: IKeywordNode;
@@ -366,21 +368,21 @@ type
end; end;
// Represents a record literal, e.g., {:a 1 :b 2} // Represents a record literal, e.g., {:a 1 :b 2}
IRecordLiteralNode = interface(IAstNode) IRecordLiteralNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetFields: TArray<TRecordFieldLiteral>; function GetFields: TArray<TRecordFieldLiteral>;
{$endregion} {$endregion}
property Fields: TArray<TRecordFieldLiteral> read GetFields; property Fields: TArray<TRecordFieldLiteral> read GetFields;
end; end;
ICreateSeriesNode = interface(IAstNode) ICreateSeriesNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetDefinition: String; function GetDefinition: String;
{$endregion} {$endregion}
property Definition: String read GetDefinition; property Definition: String read GetDefinition;
end; end;
IAddSeriesItemNode = interface(IAstNode) IAddSeriesItemNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetSeries: IIdentifierNode; function GetSeries: IIdentifierNode;
function GetValue: IAstNode; function GetValue: IAstNode;
@@ -391,7 +393,7 @@ type
property Lookback: IAstNode read GetLookback; property Lookback: IAstNode read GetLookback;
end; end;
ISeriesLengthNode = interface(IAstNode) ISeriesLengthNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetSeries: IIdentifierNode; function GetSeries: IIdentifierNode;
{$endregion} {$endregion}
+1 -1
View File
@@ -970,7 +970,7 @@ procedure TPrettyPrintVisitor.VisitMacroExpansionNode(const Node: IMacroExpansio
begin begin
// For pretty-printing, show the original macro call, not the expanded body. // For pretty-printing, show the original macro call, not the expanded body.
// Delegate to the regular VisitFunctionCall to print it as such. // Delegate to the regular VisitFunctionCall to print it as such.
VisitFunctionCall(Node); VisitFunctionCall(Node.CallNode);
end; end;
procedure TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode); procedure TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode);
+241 -77
View File
@@ -7,6 +7,7 @@ uses
System.Generics.Collections, System.Generics.Collections,
Myc.Data.Value, Myc.Data.Value,
Myc.Ast, Myc.Ast,
Myc.Ast.Types,
Myc.Ast.Nodes; Myc.Ast.Nodes;
type type
@@ -100,7 +101,7 @@ type
TAstTransformer = class abstract(TAstVisitor<IAstNode>) TAstTransformer = class abstract(TAstVisitor<IAstNode>)
protected protected
function AcceptParameters(const Nodes: TArray<IIdentifierNode>): TArray<IIdentifierNode>; function AcceptParameters(const Nodes: TArray<IIdentifierNode>): TArray<IIdentifierNode>;
function AcceptNodes(const Nodes: TArray<IAstNode>): TArray<IAstNode>; function AcceptNodes(const Nodes: TArray<IAstNode>; const AcceptProc: TFunc<Integer, IAstNode, IAstNode> = nil): TArray<IAstNode>;
function VisitConstant(const Node: IConstantNode): IAstNode; override; function VisitConstant(const Node: IConstantNode): IAstNode; override;
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override; function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
@@ -357,27 +358,50 @@ end;
function TAstTransformer.AcceptParameters(const Nodes: TArray<IIdentifierNode>): TArray<IIdentifierNode>; function TAstTransformer.AcceptParameters(const Nodes: TArray<IIdentifierNode>): TArray<IIdentifierNode>;
var var
i: Integer; i: Integer;
hasChanged: Boolean;
newNode: IIdentifierNode;
begin begin
// Implement Copy-on-Write for parameter arrays
hasChanged := False;
SetLength(Result, Length(Nodes)); SetLength(Result, Length(Nodes));
for i := 0 to High(Nodes) do for i := 0 to High(Nodes) do
Result[i] := Accept(Nodes[i]).AsIdentifier; begin
newNode := Accept(Nodes[i]).AsIdentifier;
Result[i] := newNode;
if newNode <> Nodes[i] then
hasChanged := True;
end;
if not hasChanged then
Result := Nodes; // Return original array if no changes
end; end;
function TAstTransformer.AcceptNodes(const Nodes: TArray<IAstNode>): TArray<IAstNode>; function TAstTransformer.AcceptNodes(
const Nodes: TArray<IAstNode>;
const AcceptProc: TFunc<Integer, IAstNode, IAstNode> = nil
): TArray<IAstNode>;
var var
i: Integer; i: Integer;
newNode: IAstNode; // Changed from TDataValue newNode: IAstNode; // Changed from TDataValue
newList: TList<IAstNode>; // Used if nodes are removed (e.g. defmacro) newList: TList<IAstNode>; // Used if nodes are removed (e.g. defmacro)
hasChanged: Boolean;
begin begin
Result := Nodes; // Assume no changes // 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; newList := nil;
for i := 0 to High(Nodes) do for i := 0 to High(Nodes) do
begin begin
newNode := Accept(Nodes[i]); // Calls new Accept, returns IAstNode (or nil) if Assigned(AcceptProc) then
newNode := AcceptProc(i, Nodes[i])
else
newNode := Accept(Nodes[i]); // Calls new Accept, returns IAstNode (or nil)
if not Assigned(newNode) then // Node was removed (e.g. defmacro) if not Assigned(newNode) then // Node was removed
begin begin
hasChanged := True;
if newList = nil then // First change if newList = nil then // First change
begin begin
newList := TList<IAstNode>.Create; newList := TList<IAstNode>.Create;
@@ -390,6 +414,7 @@ begin
begin begin
if newNode <> Nodes[i] then // Node was replaced if newNode <> Nodes[i] then // Node was replaced
begin begin
hasChanged := True;
if newList = nil then // First change if newList = nil then // First change
begin begin
newList := TList<IAstNode>.Create; newList := TList<IAstNode>.Create;
@@ -406,193 +431,297 @@ begin
end; end;
end; end;
if newList <> nil then if not hasChanged then
Result := Nodes // Return original array
else if newList <> nil then
begin begin
Result := newList.ToArray; Result := newList.ToArray;
newList.Free; newList.Free;
end; end
else
Result := []; // All nodes were removed
end; end;
// --- Base Virtual Implementations (IAstNode-based) --- // --- Base Virtual Implementations (IAstNode-based) ---
// --- Implemented with traversal logic from old TAstTransformer --- // --- Now fully implementing Copy-on-Write (CoW) ---
function TAstTransformer.VisitConstant(const Node: IConstantNode): IAstNode; function TAstTransformer.VisitConstant(const Node: IConstantNode): IAstNode;
begin begin
Result := Node; // Leaf node Result := Node; // Leaf node, immutable
end; end;
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): IAstNode; function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
begin begin
Result := Node; // Leaf node Result := Node; // Leaf node, immutable (will be replaced by Binder)
end; end;
function TAstTransformer.VisitKeyword(const Node: IKeywordNode): IAstNode; function TAstTransformer.VisitKeyword(const Node: IKeywordNode): IAstNode;
begin begin
Result := Node; // Leaf node Result := Node; // Leaf node, immutable
end; end;
function TAstTransformer.VisitNop(const Node: INopNode): IAstNode; function TAstTransformer.VisitNop(const Node: INopNode): IAstNode;
begin begin
// Added Nop implementation // Added Nop implementation
Result := Node; // Leaf node Result := Node; // Leaf node, immutable
end; end;
function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode; function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode;
var var
newLeft, newRight: IAstNode;
N: TBinaryExpressionNode; N: TBinaryExpressionNode;
begin begin
N := (Node as TBinaryExpressionNode); N := (Node as TBinaryExpressionNode);
N.Left := Accept(N.Left); newLeft := Accept(N.Left);
N.Right := Accept(N.Right); newRight := Accept(N.Right);
Result := Node;
if (newLeft = N.Left) and (newRight = N.Right) then
Result := Node
else
Result := TBinaryExpressionNode.Create(newLeft, N.Operator, newRight, N.StaticType);
end; end;
function TAstTransformer.VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode; function TAstTransformer.VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode;
var var
newRight: IAstNode;
N: TUnaryExpressionNode; N: TUnaryExpressionNode;
begin begin
N := (Node as TUnaryExpressionNode); N := (Node as TUnaryExpressionNode);
N.Right := Accept(N.Right); newRight := Accept(N.Right);
Result := Node;
if newRight = N.Right then
Result := Node
else
Result := TUnaryExpressionNode.Create(N.Operator, newRight, N.StaticType);
end; end;
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstNode; function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
var var
newCond, newThen, newElse: IAstNode;
N: TIfExpressionNode; N: TIfExpressionNode;
begin begin
N := (Node as TIfExpressionNode); N := (Node as TIfExpressionNode);
N.Condition := Accept(N.Condition); newCond := Accept(N.Condition);
N.ThenBranch := Accept(N.ThenBranch); newThen := Accept(N.ThenBranch);
N.ElseBranch := Accept(N.ElseBranch); // Accept handles nil newElse := Accept(N.ElseBranch); // Accept handles nil
Result := Node;
if (newCond = N.Condition) and (newThen = N.ThenBranch) and (newElse = N.ElseBranch) then
Result := Node
else
Result := TIfExpressionNode.Create(newCond, newThen, newElse, N.StaticType);
end; end;
function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode; function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
var var
newCond, newThen, newElse: IAstNode;
N: TTernaryExpressionNode; N: TTernaryExpressionNode;
begin begin
N := (Node as TTernaryExpressionNode); N := (Node as TTernaryExpressionNode);
N.Condition := Accept(N.Condition); newCond := Accept(N.Condition);
N.ThenBranch := Accept(N.ThenBranch); newThen := Accept(N.ThenBranch);
N.ElseBranch := Accept(N.ElseBranch); newElse := Accept(N.ElseBranch);
Result := Node;
if (newCond = N.Condition) and (newThen = N.ThenBranch) and (newElse = N.ElseBranch) then
Result := Node
else
Result := TTernaryExpressionNode.Create(newCond, newThen, newElse, N.StaticType);
end; end;
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var var
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
N: TLambdaExpressionNode; N: TLambdaExpressionNode;
begin begin
N := (Node as TLambdaExpressionNode); N := (Node as TLambdaExpressionNode);
N.Parameters := AcceptParameters(N.Parameters); newParams := AcceptParameters(N.Parameters);
N.Body := Accept(N.Body); newBody := Accept(N.Body);
Result := Node;
if (newParams = N.Parameters) and (newBody = N.Body) then
Result := Node
else
begin
Result := TLambdaExpressionNode.Create(newParams, newBody, N.StaticType);
// Copy runtime properties
(Result as TLambdaExpressionNode).ScopeDescriptor := N.ScopeDescriptor;
(Result as TLambdaExpressionNode).Upvalues := N.Upvalues;
(Result as TLambdaExpressionNode).HasNestedLambdas := N.HasNestedLambdas;
end;
end; end;
function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var var
newCallee: IAstNode;
newArgs: TArray<IAstNode>;
N: TFunctionCallNode; N: TFunctionCallNode;
begin begin
N := (Node as TFunctionCallNode); N := (Node as TFunctionCallNode);
N.Callee := Accept(N.Callee); newCallee := Accept(N.Callee);
N.Arguments := AcceptNodes(N.Arguments); newArgs := AcceptNodes(N.Arguments);
Result := Node;
if (newCallee = N.Callee) and (newArgs = N.Arguments) then
Result := Node
else
begin
Result := TFunctionCallNode.Create(newCallee, newArgs, N.StaticType);
// Copy runtime properties
(Result as TFunctionCallNode).IsTailCall := N.IsTailCall;
end;
end; end;
function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
var var
newBody: IAstNode;
N: TMacroExpansionNode; N: TMacroExpansionNode;
newType: IStaticType;
begin begin
N := (Node as TMacroExpansionNode); N := (Node as TMacroExpansionNode);
// ONLY visit the expanded body, as this is the "real" AST node. // 1. Visit the body
N.ExpandedBody := Accept(N.ExpandedBody); newBody := Accept(N.ExpandedBody);
// Callee and Arguments are metadata for the visualizer and must NOT be visited // 2. Check if the body changed (Copy-on-Write)
// by subsequent phases (Binder, TypeChecker, etc.). if newBody = N.ExpandedBody then
begin
Result := Node; // Return the (mutated) wrapper // No change, return the original immutable node
Result := Node;
end
else
begin
// The body changed. Create a NEW wrapper node.
// The type of the wrapper is the type of its body.
newType := newBody.AsTypedNode.StaticType;
Result := TMacroExpansionNode.Create(N.CallNode, newBody, newType);
end;
end; end;
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
var var
newExprs: TArray<IAstNode>;
N: TBlockExpressionNode; N: TBlockExpressionNode;
begin begin
N := (Node as TBlockExpressionNode); N := (Node as TBlockExpressionNode);
N.Expressions := AcceptNodes(N.Expressions); newExprs := AcceptNodes(N.Expressions);
Result := Node;
if newExprs = N.Expressions then
Result := Node
else
Result := TBlockExpressionNode.Create(newExprs, N.StaticType);
end; end;
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
var var
newIdent: IIdentifierNode;
newInit: IAstNode;
N: TVariableDeclarationNode; N: TVariableDeclarationNode;
begin begin
N := (Node as TVariableDeclarationNode); N := (Node as TVariableDeclarationNode);
N.Identifier := Accept(N.Identifier).AsIdentifier; newIdent := Accept(N.Identifier).AsIdentifier;
N.Initializer := Accept(N.Initializer); // Accept handles nil newInit := Accept(N.Initializer); // Accept handles nil
Result := Node;
if (newIdent = N.Identifier) and (newInit = N.Initializer) then
Result := Node
else
begin
Result := TVariableDeclarationNode.Create(newIdent, newInit, N.StaticType);
// Copy runtime properties
(Result as TVariableDeclarationNode).IsBoxed := N.IsBoxed;
end;
end; end;
function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): IAstNode; function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): IAstNode;
var var
newIdent: IIdentifierNode;
newValue: IAstNode;
N: TAssignmentNode; N: TAssignmentNode;
begin begin
N := (Node as TAssignmentNode); N := (Node as TAssignmentNode);
N.Value := Accept(N.Value); newValue := Accept(N.Value);
N.Identifier := Accept(N.Identifier).AsIdentifier; newIdent := Accept(N.Identifier).AsIdentifier;
Result := Node;
if (newValue = N.Value) and (newIdent = N.Identifier) then
Result := Node
else
Result := TAssignmentNode.Create(newIdent, newValue, N.StaticType);
end; end;
function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
begin begin
// Do nothing, just pass the node through // 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.
Result := Node; Result := Node;
end; end;
function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
var var
N: TQuasiquoteNode; newExpr: IAstNode;
begin begin
N := (Node as TQuasiquoteNode); // Rebuild instead of mutate (Copy-on-Write)
N.Expression := Accept(N.Expression); newExpr := Accept(Node.Expression);
Result := Node; if newExpr = Node.Expression then
Result := Node
else
Result := TAst.Quasiquote(newExpr);
end; end;
function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode; function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode;
var var
N: TUnquoteNode; newExpr: IAstNode;
begin begin
N := (Node as TUnquoteNode); // Rebuild instead of mutate (Copy-on-Write)
N.Expression := Accept(N.Expression); newExpr := Accept(Node.Expression);
Result := Node; if newExpr = Node.Expression then
Result := Node
else
Result := TAst.Unquote(newExpr);
end; end;
function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
var var
N: TUnquoteSplicingNode; newExpr: IAstNode;
begin begin
N := (Node as TUnquoteSplicingNode); // Rebuild instead of mutate (Copy-on-Write)
N.Expression := Accept(N.Expression).AsQuasiquote; newExpr := Accept(Node.Expression);
Result := Node;
if (newExpr = Node.Expression) then
Result := Node
else
Result := TAst.UnquoteSplicing(newExpr.AsQuasiquote);
end; end;
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode; function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode;
var var
newBase, newIndex: IAstNode;
N: TIndexerNode; N: TIndexerNode;
begin begin
N := (Node as TIndexerNode); N := (Node as TIndexerNode);
N.Base := Accept(N.Base); newBase := Accept(N.Base);
N.Index := Accept(N.Index); newIndex := Accept(N.Index);
Result := Node;
if (newBase = N.Base) and (newIndex = N.Index) then
Result := Node
else
Result := TIndexerNode.Create(newBase, newIndex, N.StaticType);
end; end;
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
var var
newBase: IAstNode;
newMember: IKeywordNode;
N: TMemberAccessNode; N: TMemberAccessNode;
begin begin
N := (Node as TMemberAccessNode); N := (Node as TMemberAccessNode);
N.Base := Accept(N.Base); newBase := Accept(N.Base);
N.Member := Accept(N.Member).AsKeyword; newMember := Accept(N.Member).AsKeyword; // Keyword ist Blattknoten
Result := Node;
if (newBase = N.Base) and (newMember = N.Member) then
Result := Node
else
Result := TMemberAccessNode.Create(newBase, newMember, N.StaticType);
end; end;
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
@@ -600,51 +729,86 @@ var
N: TRecordLiteralNode; N: TRecordLiteralNode;
i: Integer; i: Integer;
newFields: TArray<TRecordFieldLiteral>; newFields: TArray<TRecordFieldLiteral>;
hasChanged: Boolean;
begin begin
N := (Node as TRecordLiteralNode); N := (Node as TRecordLiteralNode);
// TRecordFieldLiteral ist ein Record, kein IAstNode, wir müssen manuell traversieren
SetLength(newFields, Length(N.Fields)); SetLength(newFields, Length(N.Fields));
hasChanged := False;
for i := 0 to High(N.Fields) do for i := 0 to High(N.Fields) do
begin begin
newFields[i].Key := Accept(N.Fields[i].Key).AsKeyword; newFields[i].Key := Accept(N.Fields[i].Key).AsKeyword;
newFields[i].Value := Accept(N.Fields[i].Value); newFields[i].Value := Accept(N.Fields[i].Value);
if (newFields[i].Key <> N.Fields[i].Key) or (newFields[i].Value <> N.Fields[i].Value) then
hasChanged := True;
end;
if not hasChanged then
Result := Node
else
begin
// Rebuild the node, preserving its specific type and definitions
if N is TGenericRecordLiteralNode then
begin
Result := TGenericRecordLiteralNode.Create(newFields, N.StaticType);
(Result as TGenericRecordLiteralNode).GenericDefinition := (N as TGenericRecordLiteralNode).GenericDefinition;
end
else
begin
Result := TRecordLiteralNode.Create(newFields, N.StaticType);
(Result as TRecordLiteralNode).Definition := N.Definition;
end;
end; end;
N.Fields := newFields;
Result := Node;
end; end;
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
begin begin
Result := Node; // Leaf node Result := Node; // Leaf node, immutable
end; end;
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
var var
newSeries: IIdentifierNode;
newValue, newLookback: IAstNode;
N: TAddSeriesItemNode; N: TAddSeriesItemNode;
begin begin
N := (Node as TAddSeriesItemNode); N := (Node as TAddSeriesItemNode);
N.Series := Accept(N.Series).AsIdentifier; newSeries := Accept(N.Series).AsIdentifier;
N.Value := Accept(N.Value); newValue := Accept(N.Value);
N.Lookback := Accept(N.Lookback); // Accept handles nil newLookback := Accept(N.Lookback); // Accept handles nil
Result := Node;
if (newSeries = N.Series) and (newValue = N.Value) and (newLookback = N.Lookback) then
Result := Node
else
Result := TAddSeriesItemNode.Create(newSeries, newValue, newLookback, N.StaticType);
end; end;
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
var var
newSeries: IIdentifierNode;
N: TSeriesLengthNode; N: TSeriesLengthNode;
begin begin
N := (Node as TSeriesLengthNode); N := (Node as TSeriesLengthNode);
N.Series := Accept(N.Series).AsIdentifier; newSeries := Accept(N.Series).AsIdentifier;
Result := Node;
if newSeries = N.Series then
Result := Node
else
Result := TSeriesLengthNode.Create(newSeries, N.StaticType);
end; end;
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode; function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode;
var var
newArgs: TArray<IAstNode>;
N: TRecurNode; N: TRecurNode;
begin begin
N := (Node as TRecurNode); N := (Node as TRecurNode);
N.Arguments := AcceptNodes(N.Arguments); newArgs := AcceptNodes(N.Arguments);
Result := Node;
if newArgs = N.Arguments then
Result := Node
else
Result := TRecurNode.Create(newArgs, N.StaticType);
end; end;
{ TAstVisitor } { TAstVisitor }
+20 -15
View File
@@ -276,9 +276,9 @@ type
constructor Create(const AName: IIdentifierNode; const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode); constructor Create(const AName: IIdentifierNode; const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode);
function Accept(const Visitor: IAstVisitor): TDataValue; override; function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsMacroDefinition: IMacroDefinitionNode; override; function AsMacroDefinition: IMacroDefinitionNode; override;
property Name: IIdentifierNode read GetName write FName; // Writeable property Name: IIdentifierNode read GetName;
property Parameters: TArray<IIdentifierNode> read FParameters write FParameters; property Parameters: TArray<IIdentifierNode> read FParameters;
property Body: IAstNode read FBody write FBody; // Writeable property Body: IAstNode read FBody;
end; end;
TQuasiquoteNode = class(TAstNode, IQuasiquoteNode) TQuasiquoteNode = class(TAstNode, IQuasiquoteNode)
@@ -290,7 +290,7 @@ type
constructor Create(const AExpression: IAstNode); constructor Create(const AExpression: IAstNode);
function Accept(const Visitor: IAstVisitor): TDataValue; override; function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsQuasiquote: IQuasiquoteNode; override; function AsQuasiquote: IQuasiquoteNode; override;
property Expression: IAstNode read GetExpression write FExpression; // Writeable property Expression: IAstNode read GetExpression;
end; end;
TUnquoteNode = class(TAstNode, IUnquoteNode) TUnquoteNode = class(TAstNode, IUnquoteNode)
@@ -302,7 +302,7 @@ type
constructor Create(const AExpression: IAstNode); constructor Create(const AExpression: IAstNode);
function Accept(const Visitor: IAstVisitor): TDataValue; override; function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsUnquote: IUnquoteNode; override; function AsUnquote: IUnquoteNode; override;
property Expression: IAstNode read GetExpression write FExpression; // Writeable property Expression: IAstNode read GetExpression;
end; end;
TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode) TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode)
@@ -314,7 +314,7 @@ type
constructor Create(const AExpression: IQuasiquoteNode); constructor Create(const AExpression: IQuasiquoteNode);
function Accept(const Visitor: IAstVisitor): TDataValue; override; function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsUnquoteSplicing: IUnquoteSplicingNode; override; function AsUnquoteSplicing: IUnquoteSplicingNode; override;
property Expression: IQuasiquoteNode read GetExpression write FExpression; property Expression: IQuasiquoteNode read GetExpression;
end; end;
TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode) TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode)
@@ -346,16 +346,19 @@ type
property Arguments: TArray<IAstNode> read FArguments write FArguments; // Writeable property Arguments: TArray<IAstNode> read FArguments write FArguments; // Writeable
end; end;
TMacroExpansionNode = class(TFunctionCallNode, IMacroExpansionNode) TMacroExpansionNode = class(TAstTypedNode, IMacroExpansionNode)
private private
FCallNode: IFunctionCallNode;
FExpandedBody: IAstNode; FExpandedBody: IAstNode;
function GetCallNode: IFunctionCallNode;
function GetExpandedBody: IAstNode; function GetExpandedBody: IAstNode;
function GetKind: TAstNodeKind; override; function GetKind: TAstNodeKind; override;
public public
constructor Create(const AOriginalCallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AStaticType: IStaticType); constructor Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AStaticType: IStaticType);
function Accept(const Visitor: IAstVisitor): TDataValue; override; function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsMacroExpansion: IMacroExpansionNode; override; function AsMacroExpansion: IMacroExpansionNode; override;
property ExpandedBody: IAstNode read FExpandedBody write FExpandedBody; // Writeable property CallNode: IFunctionCallNode read GetCallNode;
property ExpandedBody: IAstNode read FExpandedBody;
end; end;
TBlockExpressionNode = class(TAstTypedNode, IBlockExpressionNode) TBlockExpressionNode = class(TAstTypedNode, IBlockExpressionNode)
@@ -1331,15 +1334,12 @@ end;
{ TMacroExpansionNode } { TMacroExpansionNode }
constructor TMacroExpansionNode.Create( constructor TMacroExpansionNode.Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AStaticType: IStaticType);
const AOriginalCallNode: IFunctionCallNode;
const AExpandedBody: IAstNode;
const AStaticType: IStaticType
);
begin begin
// Copy properties from the original call node // Copy properties from the original call node
inherited Create(AOriginalCallNode.Callee, AOriginalCallNode.Arguments, AStaticType); inherited Create(AStaticType);
FExpandedBody := AExpandedBody; FExpandedBody := AExpandedBody;
FCallNode := ACallNode;
end; end;
function TMacroExpansionNode.Accept(const Visitor: IAstVisitor): TDataValue; function TMacroExpansionNode.Accept(const Visitor: IAstVisitor): TDataValue;
@@ -1352,6 +1352,11 @@ begin
Result := Self; Result := Self;
end; end;
function TMacroExpansionNode.GetCallNode: IFunctionCallNode;
begin
Result := FCallNode;
end;
function TMacroExpansionNode.GetExpandedBody: IAstNode; function TMacroExpansionNode.GetExpandedBody: IAstNode;
begin begin
Result := FExpandedBody; Result := FExpandedBody;