Macro expander integrated in binder
This commit is contained in:
+44
-10
@@ -68,6 +68,7 @@ type
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
|
||||
@@ -386,13 +387,15 @@ begin
|
||||
try
|
||||
EnterScope;
|
||||
try
|
||||
var transformedNode := Accept(RootNode).AsIntf<IAstNode>;
|
||||
var transformedValue := Accept(RootNode);
|
||||
|
||||
// If the result of the transformation is a single void constant, return an empty block instead.
|
||||
if (transformedNode is TConstantNode) and (TConstantNode(transformedNode).Value.IsVoid) then
|
||||
// Handle the case where the entire script was just declarations.
|
||||
if transformedValue.IsVoid then
|
||||
// Return a valid, empty AST.
|
||||
Result := TAst.Block([])
|
||||
else
|
||||
Result := transformedNode;
|
||||
// The script produced a valid, executable AST.
|
||||
Result := transformedValue.AsIntf<IAstNode>;
|
||||
|
||||
Descriptor := FCurrentDescriptor;
|
||||
finally
|
||||
@@ -409,8 +412,8 @@ begin
|
||||
FMacros.AddOrSetValue(Node.Name.Name, Node);
|
||||
|
||||
// Return an empty block node. This is a valid "no-op" node
|
||||
// that is simply ignored by the evaluator.
|
||||
Result := TDataValue.FromIntf<IBlockExpressionNode>(TAst.Block([]));
|
||||
// that is simply ignored by the evaluator and safely handled by all visitors.
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
@@ -480,9 +483,14 @@ begin
|
||||
expander := TExpansionVisitor.Create(Self, expansionScope);
|
||||
expandedBody := expander.Execute(quasiquoteBody.Expression);
|
||||
|
||||
// IMPORTANT: Recursively call Accept on the newly generated AST fragment
|
||||
// to bind it within the current scope.
|
||||
Result := Self.Accept(expandedBody);
|
||||
// Bind the newly generated AST fragment.
|
||||
var boundExpandedBody := Self.Accept(expandedBody).AsIntf<IAstNode>;
|
||||
|
||||
// Wrap the result in a TMacroExpansionNode.
|
||||
// The original 'Node' is passed to preserve the call site information for tools.
|
||||
var macroNode := TMacroExpansionNode.Create(Node, boundExpandedBody);
|
||||
|
||||
Result := TDataValue.FromIntf<IMacroExpansionNode>(macroNode);
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
@@ -498,6 +506,32 @@ begin
|
||||
Result := TDataValue.FromIntf<IFunctionCallNode>(boundCall);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
var
|
||||
boundCallee: IAstNode;
|
||||
boundArgs: TArray<IAstNode>;
|
||||
boundExpandedBody: IAstNode;
|
||||
boundOriginalCall: IFunctionCallNode;
|
||||
begin
|
||||
// This handles the case where a macro expansion node is injected into the
|
||||
// tree before binding. The binder must recursively bind its contents.
|
||||
|
||||
// 1. Bind the components of the original call site.
|
||||
boundCallee := Accept(Node.Callee).AsIntf<IAstNode>;
|
||||
boundArgs := TransformNodes<IAstNode>(Node.Arguments);
|
||||
|
||||
// 2. Bind the expanded body.
|
||||
boundExpandedBody := Accept(Node.ExpandedBody).AsIntf<IAstNode>;
|
||||
|
||||
// 3. Re-create the IFunctionCallNode part of the original call with bound children.
|
||||
boundOriginalCall := TAst.FunctionCall(boundCallee, boundArgs);
|
||||
|
||||
// 4. Create a new TMacroExpansionNode that wraps the now-bound components.
|
||||
var newMacroNode := TMacroExpansionNode.Create(boundOriginalCall, boundExpandedBody);
|
||||
|
||||
Result := TDataValue.FromIntf<IMacroExpansionNode>(newMacroNode);
|
||||
end;
|
||||
|
||||
procedure TAstBinder.ExitScope;
|
||||
begin
|
||||
FCurrentDescriptor := FCurrentDescriptor.Parent;
|
||||
@@ -549,7 +583,7 @@ begin
|
||||
begin
|
||||
FNextIsTail := isContextTail and (i = High(Node.Expressions));
|
||||
transformedValue := Accept(Node.Expressions[i]);
|
||||
// If a sub-expression (like a macro definition) returns void, skip it.
|
||||
// If a sub-expression (like a macro definition) returns a void value, skip it.
|
||||
if not transformedValue.IsVoid then
|
||||
exprList.Add(transformedValue.AsIntf<IAstNode>);
|
||||
end;
|
||||
|
||||
@@ -37,6 +37,7 @@ type
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
@@ -269,6 +270,33 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
Log('MacroExpansion');
|
||||
Indent;
|
||||
|
||||
Log('Original Call:');
|
||||
Indent;
|
||||
Log('Callee:');
|
||||
Node.Callee.Accept(Self);
|
||||
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
|
||||
Indent;
|
||||
for arg in Node.Arguments do
|
||||
arg.Accept(Self);
|
||||
Unindent;
|
||||
Unindent;
|
||||
|
||||
Log('Expanded Body:');
|
||||
Indent;
|
||||
Node.ExpandedBody.Accept(Self);
|
||||
Unindent;
|
||||
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
|
||||
@@ -33,6 +33,7 @@ type
|
||||
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
|
||||
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
@@ -289,6 +290,13 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
begin
|
||||
// The evaluator simply "unwraps" the macro expansion node
|
||||
// and executes the expanded body it contains.
|
||||
Result := Node.ExpandedBody.Accept(Self);
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
argValues: TArray<TDataValue>;
|
||||
|
||||
+102
-36
@@ -39,6 +39,7 @@ type
|
||||
function JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode;
|
||||
function JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
|
||||
function JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
|
||||
function JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode;
|
||||
function JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
|
||||
function JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
|
||||
function JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode;
|
||||
@@ -62,6 +63,7 @@ type
|
||||
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
|
||||
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
@@ -301,19 +303,46 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
|
||||
function TJsonAstConverter.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
|
||||
var
|
||||
obj, exprObj: TJSONObject;
|
||||
begin
|
||||
Result := TAst.Quasiquote(JsonToNode(AObj.GetValue('Expression')));
|
||||
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.JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode;
|
||||
function TJsonAstConverter.VisitUnquote(const Node: IUnquoteNode): TDataValue;
|
||||
var
|
||||
obj, exprObj: TJSONObject;
|
||||
begin
|
||||
Result := TAst.Unquote(JsonToNode(AObj.GetValue('Expression')));
|
||||
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.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
|
||||
function TJsonAstConverter.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
|
||||
var
|
||||
obj, exprObj: TJSONObject;
|
||||
begin
|
||||
Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression')));
|
||||
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.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
@@ -346,6 +375,45 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
var
|
||||
obj, calleeObj, bodyObj: TJSONObject;
|
||||
argsArray: TJSONArray;
|
||||
tempArgs: TArray<TJSONObject>;
|
||||
arg: IAstNode;
|
||||
i: Integer;
|
||||
begin
|
||||
// Serialize all children first: Callee, Arguments, and the new ExpandedBody
|
||||
Node.Callee.Accept(Self);
|
||||
for arg in Node.Arguments do
|
||||
arg.Accept(Self);
|
||||
Node.ExpandedBody.Accept(Self);
|
||||
|
||||
// Pop children's JSON from stack in reverse order of visitation
|
||||
bodyObj := FJsonObjectStack.Pop;
|
||||
|
||||
SetLength(tempArgs, Length(Node.Arguments));
|
||||
for i := High(tempArgs) downto 0 do
|
||||
tempArgs[i] := FJsonObjectStack.Pop;
|
||||
|
||||
calleeObj := FJsonObjectStack.Pop;
|
||||
|
||||
// Build JSON array for arguments
|
||||
argsArray := TJSONArray.Create;
|
||||
for var argObj in tempArgs do
|
||||
argsArray.Add(argObj);
|
||||
|
||||
// Create the final JSON object for this node
|
||||
obj := TJSONObject.Create;
|
||||
obj.AddPair('NodeType', TJSONString.Create('MacroExpansion'));
|
||||
obj.AddPair('Callee', calleeObj);
|
||||
obj.AddPair('Arguments', argsArray);
|
||||
obj.AddPair('ExpandedBody', bodyObj);
|
||||
|
||||
FJsonObjectStack.Push(obj);
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
obj: TJSONObject;
|
||||
@@ -667,46 +735,42 @@ begin
|
||||
Result := TAst.MacroDef(name, params, body);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
|
||||
function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode;
|
||||
var
|
||||
obj, exprObj: TJSONObject;
|
||||
callee, expandedBody: IAstNode;
|
||||
args: TArray<IAstNode>;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
tempCallNode: IFunctionCallNode;
|
||||
begin
|
||||
Node.Expression.Accept(Self);
|
||||
exprObj := FJsonObjectStack.Pop;
|
||||
// Recursively deserialize all parts of the macro expansion node
|
||||
callee := JsonToNode(AObj.GetValue('Callee'));
|
||||
expandedBody := JsonToNode(AObj.GetValue('ExpandedBody'));
|
||||
argsArray := AObj.GetValue<TJSONArray>('Arguments');
|
||||
SetLength(args, argsArray.Count);
|
||||
for i := 0 to argsArray.Count - 1 do
|
||||
args[i] := JsonToNode(argsArray.Items[i]);
|
||||
|
||||
obj := TJSONObject.Create;
|
||||
obj.AddPair('NodeType', TJSONString.Create('Quasiquote'));
|
||||
obj.AddPair('Expression', exprObj);
|
||||
FJsonObjectStack.Push(obj);
|
||||
Result := TDataValue.Void;
|
||||
// Create a temporary IFunctionCallNode to pass to the factory
|
||||
tempCallNode := TAst.FunctionCall(callee, args);
|
||||
|
||||
// Use the new global factory function from the TAst record
|
||||
Result := TAst.MacroExpansionNode(tempCallNode, expandedBody);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitUnquote(const Node: IUnquoteNode): TDataValue;
|
||||
var
|
||||
obj, exprObj: TJSONObject;
|
||||
function TJsonAstConverter.JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
|
||||
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;
|
||||
Result := TAst.Quasiquote(JsonToNode(AObj.GetValue('Expression')));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
|
||||
var
|
||||
obj, exprObj: TJSONObject;
|
||||
function TJsonAstConverter.JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode;
|
||||
begin
|
||||
Node.Expression.Accept(Self);
|
||||
exprObj := FJsonObjectStack.Pop;
|
||||
Result := TAst.Unquote(JsonToNode(AObj.GetValue('Expression')));
|
||||
end;
|
||||
|
||||
obj := TJSONObject.Create;
|
||||
obj.AddPair('NodeType', TJSONString.Create('UnquoteSplicing'));
|
||||
obj.AddPair('Expression', exprObj);
|
||||
FJsonObjectStack.Push(obj);
|
||||
Result := TDataValue.Void;
|
||||
function TJsonAstConverter.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
|
||||
begin
|
||||
Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression')));
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
|
||||
@@ -837,6 +901,8 @@ begin
|
||||
Result := JsonToLambdaExprNode(obj)
|
||||
else if nodeType = 'MacroDef' then
|
||||
Result := JsonToMacroDefNode(obj)
|
||||
else if nodeType = 'MacroExpansion' then
|
||||
Result := JsonToMacroExpansionNode(obj)
|
||||
else if nodeType = 'Quasiquote' then
|
||||
Result := JsonToQuasiquoteNode(obj)
|
||||
else if nodeType = 'Unquote' then
|
||||
|
||||
@@ -20,6 +20,7 @@ type
|
||||
ITernaryExpressionNode = interface;
|
||||
ILambdaExpressionNode = interface;
|
||||
IFunctionCallNode = interface;
|
||||
IMacroExpansionNode = interface;
|
||||
IBlockExpressionNode = interface;
|
||||
IVariableDeclarationNode = interface;
|
||||
IAssignmentNode = interface;
|
||||
@@ -58,6 +59,7 @@ type
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
@@ -152,6 +154,15 @@ type
|
||||
property Arguments: TArray<IAstNode> read GetArguments;
|
||||
end;
|
||||
|
||||
// A node representing a macro call.
|
||||
IMacroExpansionNode = interface(IFunctionCallNode)
|
||||
{$region 'private'}
|
||||
function GetExpandedBody: IAstNode;
|
||||
{$endregion}
|
||||
// The AST fragment that resulted from the macro expansion.
|
||||
property ExpandedBody: IAstNode read GetExpandedBody;
|
||||
end;
|
||||
|
||||
// A node representing a tail-recursive call.
|
||||
IRecurNode = interface(IAstNode)
|
||||
{$region 'private'}
|
||||
|
||||
+32
-23
@@ -4,7 +4,8 @@ interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
Myc.Ast.Nodes;
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Visitor;
|
||||
|
||||
type
|
||||
// Provides a high-level facade for parsing and printing the AST.
|
||||
@@ -90,7 +91,7 @@ type
|
||||
|
||||
// --- Internal Printer Implementation ---
|
||||
|
||||
TPrettyPrintVisitor = class(TInterfacedObject, IAstVisitor)
|
||||
TPrettyPrintVisitor = class(TAstVisitor)
|
||||
private
|
||||
FBuilder: TStringBuilder;
|
||||
FIndentLevel: Integer;
|
||||
@@ -104,27 +105,28 @@ type
|
||||
function GetResult: string;
|
||||
// IAstVisitor
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
|
||||
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
|
||||
function VisitUnquote(const Node: IUnquoteNode): TDataValue;
|
||||
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
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 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 VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): 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;
|
||||
end;
|
||||
|
||||
{ TLexer }
|
||||
@@ -763,6 +765,13 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
begin
|
||||
// For pretty-printing, show the original macro call, not the expanded body.
|
||||
// Delegate to the regular VisitFunctionCall to print it as such.
|
||||
Result := VisitFunctionCall(Node);
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
|
||||
@@ -21,6 +21,7 @@ type
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual; abstract;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; virtual; abstract;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual; abstract;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual; abstract;
|
||||
@@ -56,6 +57,7 @@ type
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
@@ -189,6 +191,31 @@ begin
|
||||
Result := TDataValue.FromIntf<IFunctionCallNode>(TAst.FunctionCall(callee, args));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
||||
var
|
||||
callee: IAstNode;
|
||||
args: TArray<IAstNode>;
|
||||
expandedBody: IAstNode;
|
||||
begin
|
||||
// Transform all children of the node
|
||||
callee := Accept(Node.Callee).AsIntf<IAstNode>;
|
||||
args := TransformNodes<IAstNode>(Node.Arguments);
|
||||
expandedBody := Accept(Node.ExpandedBody).AsIntf<IAstNode>;
|
||||
|
||||
if (callee = Node.Callee) and (args = Node.Arguments) and (expandedBody = Node.ExpandedBody) then
|
||||
begin
|
||||
// Nothing changed, return the original node.
|
||||
Result := TDataValue.FromIntf<IMacroExpansionNode>(Node);
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Children have changed, but the base transformer doesn't know how to
|
||||
// create a new TBoundMacroExpansionNode. Derived classes must handle this.
|
||||
raise ENotSupportedException.Create(
|
||||
'Rebuilding an IMacroExpansionNode is not supported by the base TAstTransformer. Descendant classes must provide their own implementation.');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
var
|
||||
expressions: TArray<IAstNode>;
|
||||
|
||||
@@ -45,6 +45,10 @@ type
|
||||
class function Unquote(const AExpression: IAstNode): IUnquoteNode; static;
|
||||
class function UnquoteSplicing(const AExpression: IAstNode): IUnquoteSplicingNode; static;
|
||||
class function FunctionCall(const ACallee: IAstNode; const AArguments: TArray<IAstNode>): IFunctionCallNode; static;
|
||||
class function MacroExpansionNode(
|
||||
const AOriginalCallNode: IFunctionCallNode;
|
||||
const AExpandedBody: IAstNode
|
||||
): IMacroExpansionNode; static;
|
||||
class function Recur(const AArguments: array of IAstNode): IRecurNode; static;
|
||||
class function Block(const AExpressions: array of IAstNode): IBlockExpressionNode; static;
|
||||
class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IAstNode = nil): IVariableDeclarationNode; static;
|
||||
@@ -161,6 +165,7 @@ type
|
||||
public
|
||||
constructor Create(const AName: IIdentifierNode; const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
property Name: IIdentifierNode read GetName;
|
||||
end;
|
||||
|
||||
TQuasiquoteNode = class(TAstNode, IQuasiquoteNode)
|
||||
@@ -202,6 +207,15 @@ type
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
end;
|
||||
|
||||
TMacroExpansionNode = class(TFunctionCallNode, IMacroExpansionNode)
|
||||
private
|
||||
FExpandedBody: IAstNode;
|
||||
function GetExpandedBody: IAstNode;
|
||||
public
|
||||
constructor Create(const AOriginalCallNode: IFunctionCallNode; const AExpandedBody: IAstNode);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
end;
|
||||
|
||||
TRecurNode = class(TAstNode, IRecurNode)
|
||||
private
|
||||
FArguments: TArray<IAstNode>;
|
||||
@@ -585,6 +599,24 @@ begin
|
||||
Result := FCallee;
|
||||
end;
|
||||
|
||||
{ TMacroExpansionNode }
|
||||
|
||||
constructor TMacroExpansionNode.Create(const AOriginalCallNode: IFunctionCallNode; const AExpandedBody: IAstNode);
|
||||
begin
|
||||
inherited Create(AOriginalCallNode.Callee, AOriginalCallNode.Arguments);
|
||||
FExpandedBody := AExpandedBody;
|
||||
end;
|
||||
|
||||
function TMacroExpansionNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitMacroExpansionNode(Self);
|
||||
end;
|
||||
|
||||
function TMacroExpansionNode.GetExpandedBody: IAstNode;
|
||||
begin
|
||||
Result := FExpandedBody;
|
||||
end;
|
||||
|
||||
{ TRecurNode }
|
||||
|
||||
constructor TRecurNode.Create(const AArguments: TArray<IAstNode>);
|
||||
@@ -860,6 +892,11 @@ begin
|
||||
Result := TFunctionCallNode.Create(ACallee, AArguments);
|
||||
end;
|
||||
|
||||
class function TAst.MacroExpansionNode(const AOriginalCallNode: IFunctionCallNode; const AExpandedBody: IAstNode): IMacroExpansionNode;
|
||||
begin
|
||||
Result := TMacroExpansionNode.Create(AOriginalCallNode, AExpandedBody);
|
||||
end;
|
||||
|
||||
class function TAst.Identifier(AName: string): IIdentifierNode;
|
||||
begin
|
||||
Result := TIdentifierNode.Create(AName);
|
||||
|
||||
Reference in New Issue
Block a user