diff --git a/ASTPlayground/ASTPlayground.dpr b/ASTPlayground/ASTPlayground.dpr
index 086c243..5e384b2 100644
--- a/ASTPlayground/ASTPlayground.dpr
+++ b/ASTPlayground/ASTPlayground.dpr
@@ -8,7 +8,7 @@ uses
Myc.Ast.Printer in '..\Src\AST\Myc.Ast.Printer.pas',
Myc.Ast.Nodes in '..\Src\AST\Myc.Ast.Nodes.pas',
Myc.Ast.Scope in '..\Src\AST\Myc.Ast.Scope.pas',
- Myc.Ast.Visualizer in 'Myc.Ast.Visualizer.pas',
+ Myc.Fmx.AstEditor in 'Myc.Fmx.AstEditor.pas',
Myc.Ast.ViewModel in '..\Src\AST\Myc.Ast.ViewModel.pas',
Myc.Data.Value in 'Myc.Data.Value.pas',
Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas';
diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj
index 4b373f7..769b924 100644
--- a/ASTPlayground/ASTPlayground.dproj
+++ b/ASTPlayground/ASTPlayground.dproj
@@ -139,7 +139,7 @@
-
+
diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas
index c27df51..f4c5415 100644
--- a/ASTPlayground/MainForm.pas
+++ b/ASTPlayground/MainForm.pas
@@ -21,7 +21,7 @@ uses
FMX.ScrollBox,
FMX.Memo,
FMX.Controls.Presentation,
- Myc.Ast.Visualizer,
+ Myc.Fmx.AstEditor,
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Ast.Nodes,
diff --git a/ASTPlayground/Myc.Ast.Visualizer.pas b/ASTPlayground/Myc.Fmx.AstEditor.pas
similarity index 88%
rename from ASTPlayground/Myc.Ast.Visualizer.pas
rename to ASTPlayground/Myc.Fmx.AstEditor.pas
index 49c558c..2d87439 100644
--- a/ASTPlayground/Myc.Ast.Visualizer.pas
+++ b/ASTPlayground/Myc.Fmx.AstEditor.pas
@@ -1,4 +1,4 @@
-unit Myc.Ast.Visualizer;
+unit Myc.Fmx.AstEditor;
interface
@@ -215,6 +215,8 @@ type
FConnections: TList;
FCurrentExec: TList;
FMode: TVisualizationMode;
+ FResolvedIdentifierCache: TDictionary;
+ FOwnsCaches: Boolean;
procedure FinalizeNodeLayout(const Node: TAuraNode);
@@ -255,7 +257,8 @@ type
const AStartPosition: TPointF;
AConnections: TList;
const ACurrentExec: TArray;
- AMode: TVisualizationMode
+ AMode: TVisualizationMode;
+ AResolvedIdentifierCache: TDictionary = nil
);
destructor Destroy; override;
@@ -499,7 +502,7 @@ begin
Height := 45;
// The panel must be able to receive mouse events.
- // HitTest := True;
+ // HitTest := True;
// Clip children to the panel's bounds.
ClipChildren := True;
end;
@@ -850,7 +853,8 @@ constructor TAstToAuraNodeVisitor.Create(
const AStartPosition: TPointF;
AConnections: TList;
const ACurrentExec: TArray;
- AMode: TVisualizationMode
+ AMode: TVisualizationMode;
+ AResolvedIdentifierCache: TDictionary
);
begin
inherited Create;
@@ -863,10 +867,25 @@ begin
FLastResult.LayoutNode := nil;
FLastResult.OutputPin := nil;
FMode := AMode;
+
+ if Assigned(AResolvedIdentifierCache) then
+ begin
+ FResolvedIdentifierCache := AResolvedIdentifierCache;
+ FOwnsCaches := False;
+ end
+ else
+ begin
+ FResolvedIdentifierCache := TDictionary.Create;
+ FOwnsCaches := True;
+ end;
end;
destructor TAstToAuraNodeVisitor.Destroy;
begin
+ if FOwnsCaches then
+ begin
+ FResolvedIdentifierCache.Free;
+ end;
FCurrentExec.Free;
inherited;
end;
@@ -889,10 +908,16 @@ begin
if (Pos('exec.', control.TagString) = 1) or (Pos('data.', control.TagString) = 1) then
begin
// Use the pin's X position to determine if it's on the left or right side.
- if control.Position.X < (Node.Width / 2) then
- leftPins.Add(control)
+ if Pos('.in', control.TagString) > 0 then
+ begin
+ leftPins.Add(control);
+ control.Position.X := 0;
+ end
else
+ begin
rightPins.Add(control);
+ control.Position.X := Node.Width - cPinSize;
+ end;
end;
end;
@@ -1128,47 +1153,54 @@ begin
end;
function TAstToAuraNodeVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
+var
+ details: string;
+ assignmentNode: TAuraNode;
+ control: TControl;
begin
- var details: String;
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
begin
- var assignmentNode := CreateNodeControl('Assignment', details);
+ // Keep the simple view for control flow mode
+ assignmentNode := CreateNodeControl('Assignment', details);
CreateEntry(assignmentNode);
CreateExit(assignmentNode, '');
FinalizeNodeLayout(assignmentNode);
end
else
begin
- VisitOperatorNode(
- [Node.Value],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- assignmentNode: TAuraNode;
- inputPin: TControl;
- valueResult: TAuraNodeResult;
- outPin: TControl; // variable for the output pin
+ // Create the main container node for the assignment.
+ assignmentNode := CreateNodeControl('Assignment', Node.Identifier.Name);
+
+ // Create a child visitor to render the value expression inside the assignment node.
+ // We pass an empty array for ACurrentExec so the child's execution flow doesn't connect to anything externally.
+ var childStartPos := TPointF.Create(FSpacing.X, assignmentNode.TitleFont.Size * 1.8 + FSpacing.Y);
+ var childVisitor :=
+ TAstToAuraNodeVisitor.Create(FWorkspace, assignmentNode, childStartPos, FConnections, [], FMode, FResolvedIdentifierCache);
+ Node.Value.Accept(childVisitor);
+
+ // Resize the parent assignmentNode to fit the child node(s).
+ var maxRight: Single := 0;
+ var maxBottom: Single := 0;
+ for control in assignmentNode.Controls do
+ begin
+ if control is TAuraNode then
begin
- valueResult := InputResults[0];
- assignmentNode := BuildNodeControl('Assignment', Node.Identifier.Name);
+ maxRight := Max(maxRight, control.Position.X + control.Width);
+ maxBottom := Max(maxBottom, control.Position.Y + control.Height);
+ end;
+ end;
+ assignmentNode.Width := Max(assignmentNode.Width, maxRight + FSpacing.X);
+ assignmentNode.Height := Max(assignmentNode.Height, maxBottom + FSpacing.Y);
- CreateEntry(assignmentNode);
- inputPin := CreateInput(assignmentNode, Node.Identifier.Name);
- CreateExit(assignmentNode, '');
+ CreateEntry(assignmentNode);
+ CreateExit(assignmentNode, '');
+ // An assignment expression can be used in another expression, so it needs a data output.
+ FLastResult.OutputPin := CreateOutput(assignmentNode);
- // Create the output pin before finalizing the layout.
- outPin := CreateOutput(assignmentNode);
+ // Finalize the layout of the parent node to correctly position its own pins.
+ FinalizeNodeLayout(assignmentNode);
- if Assigned(valueResult.OutputPin) then
- FConnections.Add(TPinConnection.Create(valueResult.OutputPin, inputPin));
-
- // Now finalize, all pins are present.
- FinalizeNodeLayout(assignmentNode);
-
- Result.LayoutNode := assignmentNode;
- Result.OutputPin := outPin;
- end,
- vaTop
- );
+ FCurrentPos.Y := assignmentNode.Position.Y + assignmentNode.Height + FSpacing.Y;
end;
Result := TDataValue.Void;
end;
@@ -1320,7 +1352,9 @@ begin
// Step 2: Create child visitor and run the closure.
// The child visitor starts its execution flow from the entry node's exit pin.
- childVisitor := TAstToAuraNodeVisitor.Create(FWorkspace, containerNode, childStartPos, FConnections, FCurrentExec.ToArray, FMode);
+ childVisitor :=
+ TAstToAuraNodeVisitor
+ .Create(FWorkspace, containerNode, childStartPos, FConnections, FCurrentExec.ToArray, FMode, FResolvedIdentifierCache);
ChildVisitorProc(childVisitor);
// Capture the result from the child visitor.
@@ -1448,10 +1482,34 @@ begin
end;
function TAstToAuraNodeVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
+var
+ existingResult: TAuraNodeResult;
+ identifierNode: TAuraNode;
begin
- var identifierNode := CreateNodeControl('Identifier', Node.Name);
- FLastResult.OutputPin := CreateOutput(identifierNode);
- FinalizeNodeLayout(identifierNode);
+ if Node.IsResolved then
+ begin
+ // Use the resolved address as the key.
+ if FResolvedIdentifierCache.TryGetValue(Node.Address, existingResult) then
+ begin
+ FLastResult := existingResult;
+ end
+ else
+ begin
+ identifierNode := CreateNodeControl('Identifier', Node.Name);
+ FLastResult.LayoutNode := identifierNode;
+ FLastResult.OutputPin := CreateOutput(identifierNode);
+ FinalizeNodeLayout(identifierNode);
+ FResolvedIdentifierCache.Add(Node.Address, FLastResult);
+ end;
+ end
+ else
+ begin
+ // Unresolved identifiers are not cached and drawn each time.
+ identifierNode := CreateNodeControl('Identifier', Node.Name);
+ FLastResult.LayoutNode := identifierNode;
+ FLastResult.OutputPin := CreateOutput(identifierNode);
+ FinalizeNodeLayout(identifierNode);
+ end;
Result := TDataValue.Void;
end;
@@ -1838,12 +1896,14 @@ end;
function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
- outPin: TControl;
+ details: string;
+ varDeclNode: TAuraNode;
+ control: TControl;
begin
- var details: String;
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
begin
- var varDeclNode := CreateNodeControl('VarDecl', details);
+ // Keep the simple view for control flow mode
+ varDeclNode := CreateNodeControl('VarDecl', details);
CreateEntry(varDeclNode);
CreateExit(varDeclNode, '');
FinalizeNodeLayout(varDeclNode);
@@ -1852,49 +1912,49 @@ begin
begin
if Assigned(Node.Initializer) then
begin
- VisitOperatorNode(
- [Node.Initializer],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- varDeclNode: TAuraNode;
- inputPin: TControl;
- initializerResult: TAuraNodeResult;
- outPin: TControl; // variable for the output pin
+ // Create the main container node for the declaration.
+ varDeclNode := CreateNodeControl('VarDecl', Node.Identifier.Name);
+
+ // Create a child visitor to render the initializer expression inside the node.
+ var childStartPos := TPointF.Create(FSpacing.X, varDeclNode.TitleFont.Size * 1.8 + FSpacing.Y);
+ var childVisitor :=
+ TAstToAuraNodeVisitor.Create(FWorkspace, varDeclNode, childStartPos, FConnections, [], FMode, FResolvedIdentifierCache);
+ Node.Initializer.Accept(childVisitor);
+
+ // Resize the parent node to fit the child node(s).
+ var maxRight: Single := 0;
+ var maxBottom: Single := 0;
+ for control in varDeclNode.Controls do
+ begin
+ if control is TAuraNode then
begin
- initializerResult := InputResults[0];
- varDeclNode := BuildNodeControl('VarDecl', Node.Identifier.Name);
+ maxRight := Max(maxRight, control.Position.X + control.Width);
+ maxBottom := Max(maxBottom, control.Position.Y + control.Height);
+ end;
+ end;
+ varDeclNode.Width := Max(varDeclNode.Width, maxRight + FSpacing.X);
+ varDeclNode.Height := Max(varDeclNode.Height, maxBottom + FSpacing.Y);
- CreateEntry(varDeclNode);
- inputPin := CreateInput(varDeclNode, 'Value');
- CreateExit(varDeclNode, '');
+ CreateEntry(varDeclNode);
+ CreateExit(varDeclNode, '');
+ // The new variable can be used in other expressions, so it needs a data output.
+ FLastResult.OutputPin := CreateOutput(varDeclNode);
- // Create the output pin before finalizing the layout.
- outPin := CreateOutput(varDeclNode);
+ // Finalize the layout of the parent node to correctly position its own pins.
+ FinalizeNodeLayout(varDeclNode);
- if Assigned(initializerResult.OutputPin) then
- FConnections.Add(TPinConnection.Create(initializerResult.OutputPin, inputPin));
-
- // Now finalize, all pins are present.
- FinalizeNodeLayout(varDeclNode);
- Result.LayoutNode := varDeclNode;
- Result.OutputPin := outPin;
- end
- );
+ FCurrentPos.Y := varDeclNode.Position.Y + varDeclNode.Height + FSpacing.Y;
end
else
begin
// Case without initializer is a simple sequential node.
- var varDeclNode := CreateNodeControl('VarDecl', Node.Identifier.Name);
+ varDeclNode := CreateNodeControl('VarDecl', Node.Identifier.Name);
CreateEntry(varDeclNode);
CreateExit(varDeclNode, '');
- // Create the output pin before finalizing the layout.
- outPin := CreateOutput(varDeclNode);
- FLastResult.OutputPin := outPin;
- // Now finalize, all pins are present.
+ FLastResult.OutputPin := CreateOutput(varDeclNode);
FinalizeNodeLayout(varDeclNode);
end;
end;
-
Result := TDataValue.Void;
end;