Imutability for macro nodes
This commit is contained in:
@@ -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 }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user