added macrodef

This commit is contained in:
Michael Schimmel
2025-10-03 13:49:12 +02:00
parent d47c1417f5
commit fd97799b7b
10 changed files with 382 additions and 5 deletions
+28
View File
@@ -20,6 +20,7 @@ type
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 VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
@@ -164,6 +165,33 @@ begin
end;
end;
function TAstToTextVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
var
i: Integer;
sb: TStringBuilder;
begin
// Added text representation for macro definitions.
sb := TStringBuilder.Create;
try
sb.Append('defmacro ');
sb.Append(Node.Name.Name);
sb.Append('(');
if Length(Node.Parameters) > 0 then
begin
for i := 0 to High(Node.Parameters) do
begin
sb.Append(Node.Parameters[i].Name);
if i < High(Node.Parameters) then
sb.Append(', ');
end;
end;
sb.Append(') => {...}');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TAstToTextVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
var
baseStr: string;
+98
View File
@@ -114,6 +114,7 @@ type
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 VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
@@ -1147,6 +1148,103 @@ begin
Result := TDataValue.Void;
end;
function TAstToAuraNodeVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
var
paramStr: String;
macroNode: TAuraNode;
i: Integer;
childVisitor: TAstToAuraNodeVisitor;
childStartPos: TPointF;
maxRight, maxBottom: Single;
control: TControl;
childLastResult: TAuraNodeResult;
entryNode, exitNode: TAuraNode;
paramDescriptor: IScopeDescriptor;
begin
// Create a string representation of the parameters for the node title.
paramStr := '(';
if Length(Node.Parameters) > 0 then
begin
paramStr := paramStr + Node.Parameters[0].Name;
for i := 1 to High(Node.Parameters) do
paramStr := paramStr + ', ' + Node.Parameters[i].Name;
end;
paramStr := paramStr + ')';
// 1. Create the main container node for the macro definition.
macroNode := BuildNodeControl('defmacro: ' + Node.Name.Name, paramStr);
macroNode.Position.Point := FCurrentPos;
// A macro definition is a statement, so it has execution pins.
CreateEntry(macroNode);
// 2. Create a temporary scope descriptor for the macro's parameters.
// This allows the child visitor to correctly visualize parameter nodes.
paramDescriptor := TScope.CreateDescriptor(nil);
for i := 0 to High(Node.Parameters) do
paramDescriptor.Define(Node.Parameters[i].Name);
// 3. Create a child visitor for the new scope within the macro's body.
const pinNodeHeight = cPinSize + cVerticalPadding;
childStartPos := TPointF.Create(FSpacing.X, FSpacing.Y + pinNodeHeight);
childVisitor := TAstToAuraNodeVisitor.Create(FWorkspace, macroNode, childStartPos, FConnections, [], FMode, Self, nil, paramDescriptor);
// 4. Create the visual nodes for the parameters inside the macro's scope.
for i := 0 to High(Node.Parameters) do
begin
var param := Node.Parameters[i];
var paramNode := childVisitor.CreateNodeControl('Parameter', param.Name);
var paramResult: TAuraNodeResult;
paramResult.LayoutNode := paramNode;
paramResult.OutputPin := childVisitor.CreateOutput(paramNode);
childVisitor.FinalizeNodeLayout(paramNode);
// Cache the parameter node by its slot index for lookups within the body.
if i < Length(childVisitor.FSlotCache) then
childVisitor.FSlotCache[i] := paramResult;
end;
// 5. Let the child visitor render the macro's body.
Node.Body.Accept(childVisitor);
childLastResult := childVisitor.FLastResult;
// 6. Resize the container to fit all internally generated nodes.
maxRight := 0;
maxBottom := 0;
for control in macroNode.Controls do
begin
if control is TAuraNode then
begin
maxRight := Max(maxRight, control.Position.X + control.Width);
maxBottom := Max(maxBottom, control.Position.Y + control.Height);
end;
end;
macroNode.Width := Max(macroNode.Width, maxRight + FSpacing.X);
macroNode.Height := Max(macroNode.Height, maxBottom + FSpacing.Y);
// 7. Create an internal 'expansion' exit point, showing what the macro produces.
exitNode := BuildNodeControl('Expansion', '');
exitNode.Parent := macroNode;
exitNode.Height := pinNodeHeight;
// Connect the data flow (the result of the last expression in the body) to the expansion node.
if Assigned(childLastResult.OutputPin) then
begin
var dataResultPin := childVisitor.CreateInput(exitNode, 'AST');
FConnections.Add(TPinConnection.Create(childLastResult.OutputPin, dataResultPin));
end;
childVisitor.FinalizeNodeLayout(exitNode);
macroNode.Height := Max(macroNode.Height, maxBottom + FSpacing.Y + exitNode.Height);
exitNode.Position.Point := TPointF.Create(FSpacing.X, macroNode.Height - exitNode.Height - FSpacing.Y);
// 8. Finalize the main visitor state.
FCurrentPos.Y := macroNode.Position.Y + macroNode.Height + FSpacing.Y;
FLastResult.LayoutNode := macroNode;
FLastResult.OutputPin := nil; // A macro definition itself does not return a value.
CreateExit(macroNode, ''); // Execution flow continues after the definition.
FinalizeNodeLayout(macroNode);
Result := TDataValue.Void;
end;
function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
details: string;