1st full macro expander

This commit is contained in:
Michael Schimmel
2025-10-03 19:46:30 +02:00
parent bb0ecda6be
commit d9219474e0
16 changed files with 700 additions and 323 deletions
+2 -1
View File
@@ -19,7 +19,8 @@ uses
Myc.Fmx.AstEditor.Workspace in 'Myc.Fmx.AstEditor.Workspace.pas',
Myc.Fmx.AstEditor.Text in 'Myc.Fmx.AstEditor.Text.pas',
Myc.Utils in '..\Src\Myc.Utils.pas',
Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas';
Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas',
Myc.Ast.MacroExpander in '..\Src\AST\Myc.Ast.MacroExpander.pas';
{$R *.res}
+2
View File
@@ -105,6 +105,7 @@
<DCC_RemoteDebug>true</DCC_RemoteDebug>
<DCC_IntegerOverflowCheck>true</DCC_IntegerOverflowCheck>
<DCC_RangeChecking>true</DCC_RangeChecking>
<DCC_Inlining>off</DCC_Inlining>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<DCC_RemoteDebug>false</DCC_RemoteDebug>
@@ -151,6 +152,7 @@
<DCCReference Include="Myc.Fmx.AstEditor.Text.pas"/>
<DCCReference Include="..\Src\Myc.Utils.pas"/>
<DCCReference Include="..\Src\AST\Myc.Ast.Script.pas"/>
<DCCReference Include="..\Src\AST\Myc.Ast.MacroExpander.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
+18 -1
View File
@@ -33,6 +33,7 @@ uses
Myc.Ast.Binding,
Myc.Ast.RTL,
Myc.Ast.Script,
Myc.Ast.MacroExpander,
FMX.Layouts,
FMX.Objects,
Myc.Ast.Debugger,
@@ -214,8 +215,24 @@ var
scriptScope: IExecutionScope;
visitor: IEvaluatorVisitor;
begin
var macroExpander :=
TMacroExpander.Create(
function(const ANode: IAstNode): TDataValue
begin
var binder := TAstBinder.Create(AParentScope) as IAstBinder;
var descriptor: IScopeDescriptor;
var boundAst := binder.Execute(ANode, descriptor);
var evalScope := descriptor.CreateScope(AParentScope);
var evaluator := CreateEvaluator(evalScope);
Result := evaluator.Execute(boundAst);
end)
as IMacroExpander;
binder := TAstBinder.Create(AParentScope);
FCurrAst := binder.Execute(ANode, FCurrDesc);
FCurrAst := binder.Execute(macroExpander.Execute(ANode), FCurrDesc);
scriptScope := FCurrDesc.CreateScope(AParentScope);
visitor := CreateEvaluator(scriptScope);
+18
View File
@@ -27,6 +27,9 @@ type
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
@@ -200,6 +203,21 @@ begin
Result := Format('%s.%s', [baseStr, Node.Member.Name]);
end;
function TAstToTextVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
Result := '`' + Node.Expression.Accept(Self).AsText;
end;
function TAstToTextVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
Result := '~' + Node.Expression.Accept(Self).AsText;
end;
function TAstToTextVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
Result := '~@' + Node.Expression.Accept(Self).AsText;
end;
function TAstToTextVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
var
seriesStr: string;
+72
View File
@@ -115,6 +115,9 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
@@ -1245,6 +1248,75 @@ begin
Result := TDataValue.Void;
end;
function TAstToAuraNodeVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
VisitOperatorNode(
[Node.Expression],
function(const InputResults: TAuraNodeResultList): TAuraNodeResult
var
qqNode: TAuraNode;
inputPin, outPin: TControl;
begin
qqNode := BuildNodeControl('`', '');
qqNode.TitleFont.Size := 20;
inputPin := CreateInput(qqNode, '');
outPin := CreateOutput(qqNode);
if Assigned(InputResults[0].OutputPin) then
FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, inputPin));
FinalizeNodeLayout(qqNode);
Result.LayoutNode := qqNode;
Result.OutputPin := outPin;
end
);
Result := TDataValue.Void;
end;
function TAstToAuraNodeVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
VisitOperatorNode(
[Node.Expression],
function(const InputResults: TAuraNodeResultList): TAuraNodeResult
var
uqNode: TAuraNode;
inputPin, outPin: TControl;
begin
uqNode := BuildNodeControl('~', '');
uqNode.TitleFont.Size := 20;
inputPin := CreateInput(uqNode, '');
outPin := CreateOutput(uqNode);
if Assigned(InputResults[0].OutputPin) then
FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, inputPin));
FinalizeNodeLayout(uqNode);
Result.LayoutNode := uqNode;
Result.OutputPin := outPin;
end
);
Result := TDataValue.Void;
end;
function TAstToAuraNodeVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
VisitOperatorNode(
[Node.Expression],
function(const InputResults: TAuraNodeResultList): TAuraNodeResult
var
uqsNode: TAuraNode;
inputPin, outPin: TControl;
begin
uqsNode := BuildNodeControl('~@', '');
uqsNode.TitleFont.Size := 20;
inputPin := CreateInput(uqsNode, '');
outPin := CreateOutput(uqsNode);
if Assigned(InputResults[0].OutputPin) then
FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, inputPin));
FinalizeNodeLayout(uqsNode);
Result.LayoutNode := uqsNode;
Result.OutputPin := outPin;
end
);
Result := TDataValue.Void;
end;
function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
details: string;
+29 -20
View File
@@ -8,21 +8,23 @@ uses
System.Generics.Collections,
Myc.Ast.Nodes,
Myc.Ast.Visitor,
Myc.Ast.Scope;
Myc.Ast.Scope,
Myc.Data.Value;
type
// This visitor analyzes the AST to find all variables that need to be "lifted" or "boxed"
// because they are captured by a nested lambda.
TUpvalueAnalyzer = class(TAstTraverser)
TUpvalueAnalyzer = class(TAstTransformer)
private
FBoxedDeclarations: THashSet<IVariableDeclarationNode>;
FCurrentScope: IScopeDescriptor;
FDeclarationMap: TDictionary<IScopeDescriptor, TDictionary<string, IVariableDeclarationNode>>;
procedure MarkDeclarationForBoxing(const AName: string);
protected
function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; override;
function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; override;
function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; override;
// Overridden Visit methods to perform analysis during traversal.
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
public
constructor Create(const AParent: IScopeDescriptor);
destructor Destroy; override;
@@ -96,24 +98,26 @@ begin
end;
end;
function TUpvalueAnalyzer.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode;
function TUpvalueAnalyzer.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
var
address: TResolvedAddress;
begin
Result := Node;
if not Assigned(FCurrentScope) then
exit;
address := FCurrentScope.FindSymbol(Node.Name);
if (address.Kind = akLocalOrParent) and (address.ScopeDepth > 0) then
if Assigned(FCurrentScope) then
begin
// This is an upvalue. Mark its original declaration for boxing.
MarkDeclarationForBoxing(Node.Name);
address := FCurrentScope.FindSymbol(Node.Name);
if (address.Kind = akLocalOrParent) and (address.ScopeDepth > 0) then
begin
// This is an upvalue. Mark its original declaration for boxing.
MarkDeclarationForBoxing(Node.Name);
end;
end;
// As a traverser, return the original node wrapped in a TDataValue.
Result := TDataValue.FromIntf<IIdentifierNode>(Node);
end;
function TUpvalueAnalyzer.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode;
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
begin
// A lambda creates a new lexical scope, inheriting from the current one.
FCurrentScope := TScope.CreateDescriptor(FCurrentScope);
@@ -124,19 +128,21 @@ begin
// Traverse the lambda body within the new scope context.
Node.Body.Accept(Self);
Result := Node; // We do not transform, just analyze.
// We do not transform, just analyze. Return the original node wrapped.
Result := TDataValue.FromIntf<ILambdaExpressionNode>(Node);
finally
// Restore the parent scope after leaving the lambda.
FCurrentScope := FCurrentScope.Parent;
end;
end;
function TUpvalueAnalyzer.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode;
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
scopeDeclarations: TDictionary<string, IVariableDeclarationNode>;
begin
Result := Node;
// Traverse the initializer first. It's evaluated in the current scope
// before the new variable is defined.
if Assigned(Node.Initializer) then
Node.Initializer.Accept(Self);
@@ -150,6 +156,9 @@ begin
FDeclarationMap.Add(FCurrentScope, scopeDeclarations);
end;
scopeDeclarations.Add(Node.Identifier.Name, Node);
// As a traverser, return the original node wrapped in a TDataValue.
Result := TDataValue.FromIntf<IVariableDeclarationNode>(Node);
end;
end.
+50 -43
View File
@@ -49,25 +49,24 @@ type
// Stack management for tail-call state is centralized here.
function Accept(const Node: IAstNode): TDataValue; override;
// The binder overrides specific visit methods to enrich the AST.
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
public
constructor Create(const AInitialScope: IExecutionScope);
destructor Destroy; override;
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
// The binder overrides specific transform methods to enrich the AST.
function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; override;
function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; override;
function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; override;
function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; override;
function TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode; override;
function TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; override;
function TransformRecur(const Node: IRecurNode): IRecurNode; override;
function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; override;
function TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode; override;
function TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; override;
function TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode; override;
function TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode; override;
end;
TBoundIdentifierNode = class(TIdentifierNode)
@@ -285,9 +284,10 @@ begin
Result := True;
end;
function TAstBinder.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode;
function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
var
adr: TResolvedAddress;
boundNode: IIdentifierNode;
begin
adr := FCurrentDescriptor.FindSymbol(Node.Name);
if adr.Kind = akLocalOrParent then
@@ -306,22 +306,24 @@ begin
upvalue.Map.Add(adr, upvalueIndex);
end;
Result := TBoundIdentifierNode.Create(Node, TResolvedAddress.Create(akUpvalue, 0, upvalueIndex));
boundNode := TBoundIdentifierNode.Create(Node, TResolvedAddress.Create(akUpvalue, 0, upvalueIndex));
end
else
Result := TBoundIdentifierNode.Create(Node, adr);
boundNode := TBoundIdentifierNode.Create(Node, adr);
Result := TDataValue.FromIntf<IIdentifierNode>(boundNode);
end
else
raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
end;
function TAstBinder.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode;
function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
initializer: IAstNode;
slotIndex: Integer;
address: TResolvedAddress;
boundIdentifier: IIdentifierNode;
isBoxed: Boolean;
boundDecl: IVariableDeclarationNode;
begin
if not IsValidIdentifier(Node.Identifier.Name) then
raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
@@ -340,28 +342,29 @@ begin
isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
// Always create a bound declaration node, passing the IsBoxed flag.
Result := TBoundVariableDeclarationNode.Create(boundIdentifier, initializer, isBoxed);
boundDecl := TBoundVariableDeclarationNode.Create(boundIdentifier, initializer, isBoxed);
Result := TDataValue.FromIntf<IVariableDeclarationNode>(boundDecl);
end;
function TAstBinder.TransformAssignment(const Node: IAssignmentNode): IAssignmentNode;
function TAstBinder.VisitAssignment(const Node: IAssignmentNode): TDataValue;
begin
FNextIsTail := False;
Result := inherited TransformAssignment(Node);
Result := inherited VisitAssignment(Node);
end;
function TAstBinder.TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode;
function TAstBinder.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
begin
FNextIsTail := False;
Result := inherited TransformBinaryExpression(Node);
Result := inherited VisitBinaryExpression(Node);
end;
function TAstBinder.TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode;
function TAstBinder.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
begin
FNextIsTail := False;
Result := inherited TransformUnaryExpression(Node);
Result := inherited VisitUnaryExpression(Node);
end;
function TAstBinder.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode;
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
var
i: integer;
boundParams: TArray<IIdentifierNode>;
@@ -370,6 +373,7 @@ var
upvalues: TArray<TResolvedAddress>;
hasNestedLambdas: Boolean;
lastNestedLambdaCount: Integer;
boundLambda: ILambdaExpressionNode;
begin
FUpvalueStack.Push(TUpvalueMapping.Create);
try
@@ -415,14 +419,16 @@ begin
end;
inc(FNestedLambdaCount);
Result := TBoundLambdaExpressionNode.Create(Node, boundBody, boundParams, lambdaScope, upvalues, hasNestedLambdas);
boundLambda := TBoundLambdaExpressionNode.Create(Node, boundBody, boundParams, lambdaScope, upvalues, hasNestedLambdas);
Result := TDataValue.FromIntf<ILambdaExpressionNode>(boundLambda);
end;
function TAstBinder.TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode;
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
isTailCall: Boolean;
callee: IAstNode;
args: TArray<IAstNode>;
boundCall: IFunctionCallNode;
begin
isTailCall := FIsTailStack.Peek;
@@ -430,19 +436,20 @@ begin
callee := Accept(Node.Callee).AsIntf<IAstNode>;
args := TransformNodes<IAstNode>(Node.Arguments);
Result := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall);
boundCall := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall);
Result := TDataValue.FromIntf<IFunctionCallNode>(boundCall);
end;
function TAstBinder.TransformRecur(const Node: IRecurNode): IRecurNode;
function TAstBinder.VisitRecurNode(const Node: IRecurNode): TDataValue;
begin
if not FIsTailStack.Peek then
raise Exception.Create('''recur'' can only be used in a tail position.');
FNextIsTail := False;
Result := inherited TransformRecur(Node);
Result := inherited VisitRecurNode(Node);
end;
function TAstBinder.TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode;
function TAstBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
var
exprs: TArray<IAstNode>;
i: Integer;
@@ -450,16 +457,16 @@ var
begin
isContextTail := FIsTailStack.Peek;
SetLength(exprs, Node.Expressions.Count);
for i := 0 to Node.Expressions.Count - 1 do
SetLength(exprs, Length(Node.Expressions));
for i := 0 to High(exprs) do
begin
FNextIsTail := isContextTail and (i = Node.Expressions.Count - 1);
FNextIsTail := isContextTail and (i = High(exprs));
exprs[i] := Accept(Node.Expressions[i]).AsIntf<IAstNode>;
end;
Result := TAst.Block(exprs);
Result := TDataValue.FromIntf<IBlockExpressionNode>(TAst.Block(exprs));
end;
function TAstBinder.TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode;
function TAstBinder.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
var
isContextTail: Boolean;
condition, thenBranch, elseBranch: IAstNode;
@@ -474,18 +481,18 @@ begin
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then
Result := TAst.IfExpr(condition, thenBranch, elseBranch)
Result := TDataValue.FromIntf<IIfExpressionNode>(TAst.IfExpr(condition, thenBranch, elseBranch))
else
Result := Node;
Result := TDataValue.FromIntf<IIfExpressionNode>(Node);
end;
function TAstBinder.TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode;
function TAstBinder.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin
// The binder runs after the macro expander. It should never see a macro definition.
raise Exception.Create('IMacroDefinitionNode found in AST after macro expansion phase.');
end;
function TAstBinder.TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode;
function TAstBinder.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
var
isContextTail: Boolean;
condition, thenBranch, elseBranch: IAstNode;
@@ -500,9 +507,9 @@ begin
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then
Result := TAst.TernaryExpr(condition, thenBranch, elseBranch)
Result := TDataValue.FromIntf<ITernaryExpressionNode>(TAst.TernaryExpr(condition, thenBranch, elseBranch))
else
Result := Node;
Result := TDataValue.FromIntf<ITernaryExpressionNode>(Node);
end;
end.
+4 -4
View File
@@ -26,7 +26,7 @@ type
protected
// Overridden to provide the debug-specific visitor factory.
function CreateVisitorFactory: TVisitorFactory; override;
function CreateVisitorFactory: TEvaluatorFactory; override;
public
constructor Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
@@ -66,13 +66,13 @@ begin
ShowScope;
end;
function TDebugEvaluatorVisitor.CreateVisitorFactory: TVisitorFactory;
function TDebugEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
begin
// Return a closure that creates a new Debug visitor, using the current context
var callingVisitor := Self as IAstVisitor;
var callingVisitor := Self as IEvaluatorVisitor;
Result :=
function(const AScope: IExecutionScope): IAstVisitor
function(const AScope: IExecutionScope): IEvaluatorVisitor
begin
var visitor := callingVisitor as TDebugEvaluatorVisitor;
Result := TDebugEvaluatorVisitor.Create(AScope, visitor.FLog, visitor.FShowScope, visitor.FIndentLevel);
+30
View File
@@ -42,6 +42,9 @@ type
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
@@ -350,6 +353,33 @@ begin
Result := TDataValue.Void;
end;
function TAstDumper.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
Log('Quasiquote');
Indent;
Node.Expression.Accept(Self);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
Log('Unquote');
Indent;
Node.Expression.Accept(Self);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
Log('UnquoteSplicing');
Indent;
Node.Expression.Accept(Self);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitIndexer(const Node: IIndexerNode): TDataValue;
begin
Log('Indexer');
+25 -7
View File
@@ -14,13 +14,13 @@ uses
Myc.Ast.Scope;
type
// A factory for creating visitors, primarily used by the debugger subsystem.
TVisitorFactory = reference to function(const Scope: IExecutionScope): IAstVisitor;
IEvaluatorVisitor = interface(IAstVisitor)
function Execute(const RootNode: IAstNode): TDataValue;
end;
// A factory for creating visitors, primarily used by the debugger subsystem.
TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;
// The standard AST evaluator for production use.
TEvaluatorVisitor = class(TAstVisitor, IEvaluatorVisitor)
private
@@ -36,6 +36,9 @@ type
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
@@ -50,7 +53,7 @@ type
function IsTruthy(const AValue: TDataValue): Boolean; inline;
// Returns a closure that can create the correct type of visitor for a lambda's body.
function CreateVisitorFactory: TVisitorFactory; virtual;
function CreateVisitorFactory: TEvaluatorFactory; virtual;
property Scope: IExecutionScope read FScope;
public
@@ -132,10 +135,10 @@ begin
HandleTCO(Result);
end;
function TEvaluatorVisitor.CreateVisitorFactory: TVisitorFactory;
function TEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
begin
// The production visitor returns a factory that creates another production visitor.
Result := function(const AScope: IExecutionScope): IAstVisitor begin Result := TEvaluatorVisitor.Create(AScope); end;
Result := function(const AScope: IExecutionScope): IEvaluatorVisitor begin Result := TEvaluatorVisitor.Create(AScope); end;
end;
procedure TEvaluatorVisitor.HandleTCO(var ResultValue: TDataValue);
@@ -173,7 +176,7 @@ var
i: Integer;
sourceAddresses: TArray<TResolvedAddress>;
closureScope: IExecutionScope;
visitorFactory: TVisitorFactory;
visitorFactory: TEvaluatorFactory;
begin
// Cast to the bound node to access binder-specific information.
boundNode := Node as TBoundLambdaExpressionNode;
@@ -249,6 +252,21 @@ begin
raise Exception.Create('Macro definitions cannot be evaluated at runtime.');
end;
function TEvaluatorVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
raise Exception.Create('Quasiquote nodes are a compile-time construct and cannot be evaluated at runtime.');
end;
function TEvaluatorVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
raise Exception.Create('Unquote nodes are a compile-time construct and cannot be evaluated at runtime.');
end;
function TEvaluatorVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
raise Exception.Create('Unquote-splicing nodes are a compile-time construct and cannot be evaluated at runtime.');
end;
function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
calleeValue: TDataValue;
+70 -1
View File
@@ -35,6 +35,9 @@ type
function JsonToTernaryExprNode(const AObj: TJSONObject): ITernaryExpressionNode;
function JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode;
function JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode;
function JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
function JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode;
function JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
function JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
function JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
function JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
@@ -55,6 +58,9 @@ type
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
@@ -295,6 +301,21 @@ begin
Result := TDataValue.Void;
end;
function TJsonAstConverter.JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
begin
Result := TAst.Quasiquote(JsonToNode(AObj.GetValue('Expression')));
end;
function TJsonAstConverter.JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode;
begin
Result := TAst.Unquote(JsonToNode(AObj.GetValue('Expression')));
end;
function TJsonAstConverter.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
begin
Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression')));
end;
function TJsonAstConverter.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
obj, calleeObj: TJSONObject;
@@ -362,7 +383,7 @@ begin
for expr in Node.Expressions do
expr.Accept(Self);
SetLength(tempExprs, Node.Expressions.Count);
SetLength(tempExprs, Length(Node.Expressions));
for i := High(tempExprs) downto 0 do
tempExprs[i] := FJsonObjectStack.Pop;
@@ -646,6 +667,48 @@ begin
Result := TAst.MacroDef(name, params, body);
end;
function TJsonAstConverter.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
var
obj, exprObj: TJSONObject;
begin
Node.Expression.Accept(Self);
exprObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Quasiquote'));
obj.AddPair('Expression', exprObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end;
function TJsonAstConverter.VisitUnquote(const Node: IUnquoteNode): TDataValue;
var
obj, exprObj: TJSONObject;
begin
Node.Expression.Accept(Self);
exprObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Unquote'));
obj.AddPair('Expression', exprObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end;
function TJsonAstConverter.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
var
obj, exprObj: TJSONObject;
begin
Node.Expression.Accept(Self);
exprObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('UnquoteSplicing'));
obj.AddPair('Expression', exprObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end;
function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
var
callee: IAstNode;
@@ -774,6 +837,12 @@ begin
Result := JsonToLambdaExprNode(obj)
else if nodeType = 'MacroDef' then
Result := JsonToMacroDefNode(obj)
else if nodeType = 'Quasiquote' then
Result := JsonToQuasiquoteNode(obj)
else if nodeType = 'Unquote' then
Result := JsonToUnquoteNode(obj)
else if nodeType = 'UnquoteSplicing' then
Result := JsonToUnquoteSplicingNode(obj)
else if nodeType = 'FunctionCall' then
Result := JsonToFunctionCallNode(obj)
else if nodeType = 'Recur' then
+269
View File
@@ -0,0 +1,269 @@
unit Myc.Ast.MacroExpander;
interface
uses
System.SysUtils,
System.Classes,
System.Generics.Collections,
Myc.Data.Value,
Myc.Ast.Nodes,
Myc.Ast.Scope,
Myc.Ast.Visitor;
type
// Defines the strategy for compiling and evaluating an AST node at compile time.
TCompileTimeExecutor = reference to function(const ANode: IAstNode): TDataValue;
IMacroExpander = interface(IAstVisitor)
function Execute(const RootNode: IAstNode): IAstNode;
end;
// The macro expander is now a pure AST transformation engine.
TMacroExpander = class(TAstTransformer, IMacroExpander)
private
FMacros: TDictionary<string, IMacroDefinitionNode>;
FExecutor: TCompileTimeExecutor;
function ExpandNode(const ANode: IAstNode): IAstNode;
protected
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
public
constructor Create(const AExecutor: TCompileTimeExecutor);
destructor Destroy; override;
function Execute(const RootNode: IAstNode): IAstNode;
end;
implementation
uses
Myc.Ast;
type
TExpansionHelper = class(TAstTransformer)
private
FExpansionScope: IExecutionScope;
FExpander: TMacroExpander;
FExecutor: TCompileTimeExecutor;
protected
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
public
constructor Create(AExpansionScope: IExecutionScope; AExpander: TMacroExpander; AExecutor: TCompileTimeExecutor);
class function Expand(
Node: IAstNode;
ExpansionScope: IExecutionScope;
Expander: TMacroExpander;
Executor: TCompileTimeExecutor
): IAstNode; static;
end;
TSubstitutor = class(TAstTransformer)
private
FParamScope: IExecutionScope;
protected
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
public
constructor Create(AParamScope: IExecutionScope);
end;
{ TMacroExpander }
constructor TMacroExpander.Create(const AExecutor: TCompileTimeExecutor);
begin
inherited Create;
Assert(Assigned(AExecutor), 'A TCompileTimeExecutor must be provided.');
FMacros := TDictionary<string, IMacroDefinitionNode>.Create;
FExecutor := AExecutor;
end;
destructor TMacroExpander.Destroy;
begin
FMacros.Free;
inherited;
end;
function TMacroExpander.ExpandNode(const ANode: IAstNode): IAstNode;
var
lastNode: IAstNode;
begin
Result := ANode;
if not Assigned(Result) then
exit;
repeat
lastNode := Result;
Result := Self.Accept(lastNode).AsIntf<IAstNode>;
until Result = lastNode;
end;
function TMacroExpander.Execute(const RootNode: IAstNode): IAstNode;
begin
FMacros.Clear;
Result := ExpandNode(RootNode);
end;
function TMacroExpander.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
macroDef: IMacroDefinitionNode;
expandedNode: IAstNode;
i: Integer;
begin
if not (Node.Callee is TIdentifierNode) then
begin
Result := inherited VisitFunctionCall(Node);
exit;
end;
var calleeIdentifier := Node.Callee as TIdentifierNode;
if FMacros.TryGetValue(calleeIdentifier.Name, macroDef) then
begin
var expansionScope := TAst.CreateScope(nil); // Temporary scope for parameters, no parent needed.
if Length(Node.Arguments) <> Length(macroDef.Parameters) then
raise Exception.CreateFmt(
'Macro %s expects %d arguments, but got %d',
[macroDef.Name.Name, Length(macroDef.Parameters), Length(Node.Arguments)]);
for i := 0 to High(macroDef.Parameters) do
expansionScope.Define(macroDef.Parameters[i].Name, TDataValue.FromIntf<IAstNode>(Node.Arguments[i]));
if not (macroDef.Body is TQuasiquoteNode) then
raise Exception.CreateFmt('Macro body for "%s" must be a quasiquoted expression.', [macroDef.Name.Name]);
expandedNode := TExpansionHelper.Expand(IQuasiquoteNode(macroDef.Body).Expression, expansionScope, Self, FExecutor);
Result := Self.Accept(expandedNode);
end
else
begin
Result := inherited VisitFunctionCall(Node);
end;
end;
function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin
FMacros.AddOrSetValue(Node.Name.Name, Node);
Result := TDataValue.FromIntf<IBlockExpressionNode>(TBlockExpressionNode.Create([]));
end;
{ TExpansionHelper }
constructor TExpansionHelper.Create(AExpansionScope: IExecutionScope; AExpander: TMacroExpander; AExecutor: TCompileTimeExecutor);
begin
inherited Create;
FExpansionScope := AExpansionScope;
FExpander := AExpander;
FExecutor := AExecutor;
end;
class function TExpansionHelper.Expand(
Node: IAstNode;
ExpansionScope: IExecutionScope;
Expander: TMacroExpander;
Executor: TCompileTimeExecutor
): IAstNode;
var
helper: TExpansionHelper;
begin
helper := TExpansionHelper.Create(ExpansionScope, Expander, Executor);
try
Result := helper.Accept(Node).AsIntf<IAstNode>;
finally
helper.Free;
end;
end;
function TExpansionHelper.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
newArgs: TList<IAstNode>;
transformedArgValue: TDataValue;
transformedArgNode: IAstNode;
transformedCallee: IAstNode;
begin
newArgs := TList<IAstNode>.Create;
try
for var arg in Node.Arguments do
begin
transformedArgValue := Self.Accept(arg);
if transformedArgValue.IsVoid then
continue;
transformedArgNode := transformedArgValue.AsIntf<IAstNode>;
if (transformedArgNode is TBlockExpressionNode) then
newArgs.AddRange((transformedArgNode as TBlockExpressionNode).Expressions)
else
newArgs.Add(transformedArgNode);
end;
transformedCallee := Self.Accept(Node.Callee).AsIntf<IAstNode>;
Result := TDataValue.FromIntf<IAstNode>(TAst.FunctionCall(transformedCallee, newArgs.ToArray));
finally
newArgs.Free;
end;
end;
function TExpansionHelper.VisitUnquote(const Node: IUnquoteNode): TDataValue;
var
substitutor: TSubstitutor;
substitutedAst, expandedAst: IAstNode;
value: TDataValue;
begin
// Step 1: Substitute macro parameters with their AST node values.
substitutor := TSubstitutor.Create(FExpansionScope);
try
substitutedAst := substitutor.Execute(Node.Expression);
finally
substitutor.Free;
end;
// Step 2: Recursively expand any macros within the substituted expression.
expandedAst := FExpander.ExpandNode(substitutedAst);
// Step 3: Call the injected executor strategy to bind and evaluate the AST.
value := FExecutor(expandedAst);
// Step 4: Wrap the resulting value for insertion into the template.
if value.Kind in [vkScalar, vkText] then
Result := TDataValue.FromIntf<IAstNode>(TAst.Constant(value))
else
Result := value;
end;
function TExpansionHelper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
var
unquotedValue: TDataValue;
unquotedNode: IAstNode;
begin
unquotedValue := Self.VisitUnquote(TAst.Unquote(Node.Expression));
if unquotedValue.Kind = vkInterface then
unquotedNode := unquotedValue.AsIntf<IAstNode>
else
unquotedNode := nil;
if not (unquotedNode is TBlockExpressionNode) then
raise Exception.Create('Expression inside unquote-splicing (`~@`) must evaluate to a list of nodes (a block).');
Result := unquotedValue;
end;
{ TSubstitutor }
constructor TSubstitutor.Create(AParamScope: IExecutionScope);
begin
inherited Create;
FParamScope := AParamScope;
end;
function TSubstitutor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
var
addr: TResolvedAddress;
nodeValue: TDataValue;
begin
addr := FParamScope.CreateDescriptor.FindSymbol(Node.Name);
if (addr.Kind = akLocalOrParent) and (addr.ScopeDepth = 0) then
begin
nodeValue := FParamScope.Values[addr];
Result := nodeValue;
exit;
end;
Result := inherited VisitIdentifier(Node);
end;
end.
+2 -2
View File
@@ -162,9 +162,9 @@ type
IBlockExpressionNode = interface(IAstNode)
{$region 'private'}
function GetExpressions: TList<IAstNode>;
function GetExpressions: TArray<IAstNode>;
{$endregion}
property Expressions: TList<IAstNode> read GetExpressions;
property Expressions: TArray<IAstNode> read GetExpressions;
end;
IVariableDeclarationNode = interface(IAstNode)
+3 -3
View File
@@ -386,11 +386,11 @@ begin
// (defmacro name [params] body)
if (Length(tailNodes) <> 3) or (tailTokens[0].Kind <> tkIdentifier) then
raise Exception.Create('Syntax Error: ''defmacro'' requires a name, a parameter list, and a body.');
if elements[1].Node <> nil then
if elements[2].Params = nil then
raise Exception.Create('Syntax Error: Expected a parameter list [...] after macro name.');
var macroName := IIdentifierNode(tailNodes[0]);
var macroParams := elements[1].Params;
var macroParams := elements[2].Params;
var macroBody := tailNodes[2];
Result := TAst.MacroDef(macroName, macroParams, macroBody);
@@ -405,7 +405,7 @@ begin
begin
if Length(tailNodes) <> 2 then
raise Exception.Create('Syntax Error: ''fn'' requires a parameter list and a body.');
if elements[1].Node <> nil then
if elements[1].Params = nil then
raise Exception.Create('Syntax Error: Expected a parameter list [...] after ''fn''.');
Result := TAst.LambdaExpr(elements[1].Params, tailNodes[1]);
end
+100 -225
View File
@@ -41,59 +41,34 @@ type
end;
// A base visitor that walks the AST and rebuilds it node by node.
// This class uses the Template Method pattern:
// - The Visit... methods are the template and should not be overridden.
// - The virtual Transform... methods are the hooks for derived classes to customize behavior.
// Derived classes can override the virtual Visit... methods to customize behavior.
TAstTransformer = class abstract(TAstVisitor, IAstTransformer)
private
FDone: Boolean;
protected
// Final Visit methods - these should not be overridden. They call the virtual Transform methods.
function VisitConstant(const Node: IConstantNode): TDataValue; override; final;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; final;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; final;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; final;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; final;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; final;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; final;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; final;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; final;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; final;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; final;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; final;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override; final;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; final;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; final;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; final;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; final;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; final;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; final;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; final;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; final;
// Dispatch methods for each node type. Derived classes override these to change the transformation.
function TransformConstant(const Node: IConstantNode): IConstantNode; virtual;
function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; virtual;
function TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode; virtual;
function TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode; virtual;
function TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode; virtual;
function TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; virtual;
function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; virtual;
function TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; virtual;
function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; virtual;
function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; virtual;
function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; virtual;
function TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode; virtual;
function TransformQuasiquote(const Node: IQuasiquoteNode): IQuasiquoteNode; virtual;
function TransformUnquote(const Node: IUnquoteNode): IUnquoteNode; virtual;
function TransformUnquoteSplicing(const Node: IUnquoteSplicingNode): IUnquoteSplicingNode; virtual;
function TransformIndexer(const Node: IIndexerNode): IIndexerNode; virtual;
function TransformMemberAccess(const Node: IMemberAccessNode): IMemberAccessNode; virtual;
function TransformCreateSeries(const Node: ICreateSeriesNode): ICreateSeriesNode; virtual;
function TransformAddSeriesItem(const Node: IAddSeriesItemNode): IAddSeriesItemNode; virtual;
function TransformSeriesLength(const Node: ISeriesLengthNode): ISeriesLengthNode; virtual;
function TransformRecur(const Node: IRecurNode): IRecurNode; virtual;
// These virtual methods implement the default identity-transformation (cloning) behavior.
function VisitConstant(const Node: IConstantNode): TDataValue; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
// Helper to transform an array of nodes.
function TransformNodes<T: IAstNode>(const Nodes: TArray<T>): TArray<T>;
@@ -106,11 +81,8 @@ type
function Execute(const RootNode: IAstNode): IAstNode;
end;
// TAstTraverser simply inherits the identity-transform (cloning) behavior from TAstTransformer.
TAstTraverser = class(TAstTransformer);
// Generic traverser for managing state during AST walks.
TAstTraverser<T> = class(TAstTraverser)
TAstTraverser<T> = class(TAstTransformer)
private
FData: TStack<T>;
protected
@@ -141,202 +113,210 @@ begin
Result := Accept(RootNode).AsIntf<IAstNode>;
end;
// --- Default implementations for Transform... methods (Identity Transformation / Cloning) ---
function TAstTransformer.TransformConstant(const Node: IConstantNode): IConstantNode;
function TAstTransformer.VisitConstant(const Node: IConstantNode): TDataValue;
begin
// The node itself is immutable, return it directly.
Result := Node;
Result := TDataValue.FromIntf<IConstantNode>(Node);
end;
function TAstTransformer.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode;
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
Result := Node;
Result := TDataValue.FromIntf<IIdentifierNode>(Node);
end;
function TAstTransformer.TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode;
function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
begin
var left := Accept(Node.Left).AsIntf<IAstNode>;
var right := Accept(Node.Right).AsIntf<IAstNode>;
if (left = Node.Left) and (right = Node.Right) then
Result := Node
Result := TDataValue.FromIntf<IBinaryExpressionNode>(Node)
else
Result := TAst.BinaryExpr(left, Node.Operator, right);
Result := TDataValue.FromIntf<IBinaryExpressionNode>(TAst.BinaryExpr(left, Node.Operator, right));
end;
function TAstTransformer.TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode;
function TAstTransformer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
begin
var right := Accept(Node.Right).AsIntf<IAstNode>;
if right = Node.Right then
Result := Node
Result := TDataValue.FromIntf<IUnaryExpressionNode>(Node)
else
Result := TAst.UnaryExpr(Node.Operator, right);
Result := TDataValue.FromIntf<IUnaryExpressionNode>(TAst.UnaryExpr(Node.Operator, right));
end;
function TAstTransformer.TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode;
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin
var condition := Accept(Node.Condition).AsIntf<IAstNode>;
var thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
var elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then
Result := Node
Result := TDataValue.FromIntf<IIfExpressionNode>(Node)
else
Result := TAst.IfExpr(condition, thenBranch, elseBranch);
Result := TDataValue.FromIntf<IIfExpressionNode>(TAst.IfExpr(condition, thenBranch, elseBranch));
end;
function TAstTransformer.TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode;
function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
begin
var condition := Accept(Node.Condition).AsIntf<IAstNode>;
var thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
var elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then
Result := Node
Result := TDataValue.FromIntf<ITernaryExpressionNode>(Node)
else
Result := TAst.TernaryExpr(condition, thenBranch, elseBranch);
Result := TDataValue.FromIntf<ITernaryExpressionNode>(TAst.TernaryExpr(condition, thenBranch, elseBranch));
end;
function TAstTransformer.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode;
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
begin
var parameters := TransformNodes<IIdentifierNode>(Node.Parameters);
var body := Accept(Node.Body).AsIntf<IAstNode>;
if (parameters = Node.Parameters) and (body = Node.Body) then
Result := Node
Result := TDataValue.FromIntf<ILambdaExpressionNode>(Node)
else
Result := TAst.LambdaExpr(parameters, body);
Result := TDataValue.FromIntf<ILambdaExpressionNode>(TAst.LambdaExpr(parameters, body));
end;
function TAstTransformer.TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode;
function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
begin
var callee := Accept(Node.Callee).AsIntf<IAstNode>;
var args := TransformNodes<IAstNode>(Node.Arguments);
if (callee = Node.Callee) and (args = Node.Arguments) then
Result := Node
Result := TDataValue.FromIntf<IFunctionCallNode>(Node)
else
Result := TAst.FunctionCall(callee, args);
Result := TDataValue.FromIntf<IFunctionCallNode>(TAst.FunctionCall(callee, args));
end;
function TAstTransformer.TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode;
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
var
expressions: TArray<IAstNode>;
i: Integer;
hasChanged: Boolean;
begin
// TList is mutable, so we always transform.
var exprs: TArray<IAstNode>;
var i: Integer;
SetLength(exprs, Node.Expressions.Count);
for i := 0 to Node.Expressions.Count - 1 do
exprs[i] := Accept(Node.Expressions[i]).AsIntf<IAstNode>;
Result := TAst.Block(exprs);
// Check if any child expression has changed before creating a new node.
hasChanged := false;
SetLength(expressions, Length(Node.Expressions));
for i := 0 to High(Node.Expressions) do
begin
expressions[i] := Accept(Node.Expressions[i]).AsIntf<IAstNode>;
if expressions[i] <> Node.Expressions[i] then
hasChanged := true;
end;
if hasChanged then
Result := TDataValue.FromIntf<IBlockExpressionNode>(TAst.Block(expressions))
else
Result := TDataValue.FromIntf<IBlockExpressionNode>(Node);
end;
function TAstTransformer.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode;
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
begin
var identifier := Accept(Node.Identifier).AsIntf<IIdentifierNode>;
var initializer := Accept(Node.Initializer).AsIntf<IAstNode>;
if (identifier = Node.Identifier) and (initializer = Node.Initializer) then
Result := Node
Result := TDataValue.FromIntf<IVariableDeclarationNode>(Node)
else
Result := TAst.VarDecl(identifier, initializer);
Result := TDataValue.FromIntf<IVariableDeclarationNode>(TAst.VarDecl(identifier, initializer));
end;
function TAstTransformer.TransformAssignment(const Node: IAssignmentNode): IAssignmentNode;
function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): TDataValue;
begin
var identifier := Accept(Node.Identifier).AsIntf<IIdentifierNode>;
var value := Accept(Node.Value).AsIntf<IAstNode>;
if (identifier = Node.Identifier) and (value = Node.Value) then
Result := Node
Result := TDataValue.FromIntf<IAssignmentNode>(Node)
else
Result := TAst.Assign(identifier, value);
Result := TDataValue.FromIntf<IAssignmentNode>(TAst.Assign(identifier, value));
end;
function TAstTransformer.TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode;
function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin
// Added identity transform for macro definitions.
var name := Accept(Node.Name).AsIntf<IIdentifierNode>;
var parameters := TransformNodes<IIdentifierNode>(Node.Parameters);
var body := Accept(Node.Body).AsIntf<IAstNode>;
if (name = Node.Name) and (parameters = Node.Parameters) and (body = Node.Body) then
Result := Node
Result := TDataValue.FromIntf<IMacroDefinitionNode>(Node)
else
Result := TAst.MacroDef(name, parameters, body);
Result := TDataValue.FromIntf<IMacroDefinitionNode>(TAst.MacroDef(name, parameters, body));
end;
function TAstTransformer.TransformQuasiquote(const Node: IQuasiquoteNode): IQuasiquoteNode;
function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
var expr := Accept(Node.Expression).AsIntf<IAstNode>;
if expr = Node.Expression then
Result := Node
Result := TDataValue.FromIntf<IQuasiquoteNode>(Node)
else
Result := TAst.Quasiquote(expr);
Result := TDataValue.FromIntf<IQuasiquoteNode>(TAst.Quasiquote(expr));
end;
function TAstTransformer.TransformUnquote(const Node: IUnquoteNode): IUnquoteNode;
function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
var expr := Accept(Node.Expression).AsIntf<IAstNode>;
if expr = Node.Expression then
Result := Node
Result := TDataValue.FromIntf<IUnquoteNode>(Node)
else
Result := TAst.Unquote(expr);
Result := TDataValue.FromIntf<IUnquoteNode>(TAst.Unquote(expr));
end;
function TAstTransformer.TransformUnquoteSplicing(const Node: IUnquoteSplicingNode): IUnquoteSplicingNode;
function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
var expr := Accept(Node.Expression).AsIntf<IAstNode>;
if expr = Node.Expression then
Result := Node
Result := TDataValue.FromIntf<IUnquoteSplicingNode>(Node)
else
Result := TAst.UnquoteSplicing(expr);
Result := TDataValue.FromIntf<IUnquoteSplicingNode>(TAst.UnquoteSplicing(expr));
end;
function TAstTransformer.TransformIndexer(const Node: IIndexerNode): IIndexerNode;
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): TDataValue;
begin
var base := Accept(Node.Base).AsIntf<IAstNode>;
var index := Accept(Node.Index).AsIntf<IAstNode>;
if (base = Node.Base) and (index = Node.Index) then
Result := Node
Result := TDataValue.FromIntf<IIndexerNode>(Node)
else
Result := TAst.Indexer(base, index);
Result := TDataValue.FromIntf<IIndexerNode>(TAst.Indexer(base, index));
end;
function TAstTransformer.TransformMemberAccess(const Node: IMemberAccessNode): IMemberAccessNode;
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
begin
var base := Accept(Node.Base).AsIntf<IAstNode>;
var member := Node.Member;
if (base = Node.Base) and (member = Node.Member) then
Result := Node
Result := TDataValue.FromIntf<IMemberAccessNode>(Node)
else
Result := TAst.MemberAccess(base, member);
Result := TDataValue.FromIntf<IMemberAccessNode>(TAst.MemberAccess(base, member));
end;
function TAstTransformer.TransformCreateSeries(const Node: ICreateSeriesNode): ICreateSeriesNode;
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
begin
Result := Node;
Result := TDataValue.FromIntf<ICreateSeriesNode>(Node);
end;
function TAstTransformer.TransformAddSeriesItem(const Node: IAddSeriesItemNode): IAddSeriesItemNode;
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
begin
var series := Accept(Node.Series).AsIntf<IIdentifierNode>;
var value := Accept(Node.Value).AsIntf<IAstNode>;
var lookback := Accept(Node.Lookback).AsIntf<IAstNode>;
if (series = Node.Series) and (value = Node.Value) and (lookback = Node.Lookback) then
Result := Node
Result := TDataValue.FromIntf<IAddSeriesItemNode>(Node)
else
Result := TAst.AddSeriesItem(series, value, lookback);
Result := TDataValue.FromIntf<IAddSeriesItemNode>(TAst.AddSeriesItem(series, value, lookback));
end;
function TAstTransformer.TransformSeriesLength(const Node: ISeriesLengthNode): ISeriesLengthNode;
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
begin
var series := Accept(Node.Series).AsIntf<IIdentifierNode>;
if series = Node.Series then
Result := Node
Result := TDataValue.FromIntf<ISeriesLengthNode>(Node)
else
Result := TAst.SeriesLength(series);
Result := TDataValue.FromIntf<ISeriesLengthNode>(TAst.SeriesLength(series));
end;
function TAstTransformer.TransformRecur(const Node: IRecurNode): IRecurNode;
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): TDataValue;
begin
var args := TransformNodes<IAstNode>(Node.Arguments);
if args = Node.Arguments then
Result := Node
Result := TDataValue.FromIntf<IRecurNode>(Node)
else
Result := TAst.Recur(args);
Result := TDataValue.FromIntf<IRecurNode>(TAst.Recur(args));
end;
function TAstTransformer.TransformNodes<T>(const Nodes: TArray<T>): TArray<T>;
@@ -356,111 +336,6 @@ begin
Result := Nodes;
end;
function TAstTransformer.VisitConstant(const Node: IConstantNode): TDataValue;
begin
Result := TDataValue.FromIntf<IConstantNode>(TransformConstant(Node));
end;
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
Result := TDataValue.FromIntf<IIdentifierNode>(TransformIdentifier(Node));
end;
function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
begin
Result := TDataValue.FromIntf<IBinaryExpressionNode>(TransformBinaryExpression(Node));
end;
function TAstTransformer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
begin
Result := TDataValue.FromIntf<IUnaryExpressionNode>(TransformUnaryExpression(Node));
end;
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin
Result := TDataValue.FromIntf<IIfExpressionNode>(TransformIfExpression(Node));
end;
function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
begin
Result := TDataValue.FromIntf<ITernaryExpressionNode>(TransformTernaryExpression(Node));
end;
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
begin
Result := TDataValue.FromIntf<ILambdaExpressionNode>(TransformLambdaExpression(Node));
end;
function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
begin
Result := TDataValue.FromIntf<IFunctionCallNode>(TransformFunctionCall(Node));
end;
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
begin
Result := TDataValue.FromIntf<IBlockExpressionNode>(TransformBlockExpression(Node));
end;
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
begin
Result := TDataValue.FromIntf<IVariableDeclarationNode>(TransformVariableDeclaration(Node));
end;
function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): TDataValue;
begin
Result := TDataValue.FromIntf<IAssignmentNode>(TransformAssignment(Node));
end;
function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin
Result := TDataValue.FromIntf<IMacroDefinitionNode>(TransformMacroDefinition(Node));
end;
function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
Result := TDataValue.FromIntf<IQuasiquoteNode>(TransformQuasiquote(Node));
end;
function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
Result := TDataValue.FromIntf<IUnquoteNode>(TransformUnquote(Node));
end;
function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
Result := TDataValue.FromIntf<IUnquoteSplicingNode>(TransformUnquoteSplicing(Node));
end;
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): TDataValue;
begin
Result := TDataValue.FromIntf<IIndexerNode>(TransformIndexer(Node));
end;
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
begin
Result := TDataValue.FromIntf<IMemberAccessNode>(TransformMemberAccess(Node));
end;
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
begin
Result := TDataValue.FromIntf<ICreateSeriesNode>(TransformCreateSeries(Node));
end;
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
begin
Result := TDataValue.FromIntf<IAddSeriesItemNode>(TransformAddSeriesItem(Node));
end;
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
begin
Result := TDataValue.FromIntf<ISeriesLengthNode>(TransformSeriesLength(Node));
end;
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): TDataValue;
begin
Result := TDataValue.FromIntf<IRecurNode>(TransformRecur(Node));
end;
{ TAstTraverser<T> }
constructor TAstTraverser<T>.Create;
+6 -16
View File
@@ -211,13 +211,12 @@ type
TBlockExpressionNode = class(TAstNode, IBlockExpressionNode)
private
FExpressions: TList<IAstNode>;
function GetExpressions: TList<IAstNode>;
FExpressions: TArray<IAstNode>;
function GetExpressions: TArray<IAstNode>;
public
constructor Create(const AExpressions: array of IAstNode);
destructor Destroy; override;
function Accept(const Visitor: IAstVisitor): TDataValue; override;
property Expressions: TList<IAstNode> read FExpressions;
property Expressions: TArray<IAstNode> read FExpressions;
end;
TVariableDeclarationNode = class(TAstNode, IVariableDeclarationNode)
@@ -605,19 +604,10 @@ end;
{ TBlockExpressionNode }
constructor TBlockExpressionNode.Create(const AExpressions: array of IAstNode);
var
expr: IAstNode;
begin
inherited Create;
FExpressions := TList<IAstNode>.Create;
for expr in AExpressions do
FExpressions.Add(expr);
end;
destructor TBlockExpressionNode.Destroy;
begin
FExpressions.Free;
inherited;
SetLength(FExpressions, Length(AExpressions));
TArray.Copy<IAstNode>(AExpressions, FExpressions, 0, 0, Length(AExpressions));
end;
function TBlockExpressionNode.Accept(const Visitor: IAstVisitor): TDataValue;
@@ -625,7 +615,7 @@ begin
Result := Visitor.VisitBlockExpression(Self);
end;
function TBlockExpressionNode.GetExpressions: TList<IAstNode>;
function TBlockExpressionNode.GetExpressions: TArray<IAstNode>;
begin
Result := FExpressions;
end;