|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
|
unit Myc.Ast.Visualizer;
|
|
|
|
|
unit Myc.Fmx.AstEditor;
|
|
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
|
@@ -215,6 +215,8 @@ type
|
|
|
|
|
FConnections: TList<TPinConnection>;
|
|
|
|
|
FCurrentExec: TList<TControl>;
|
|
|
|
|
FMode: TVisualizationMode;
|
|
|
|
|
FResolvedIdentifierCache: TDictionary<TResolvedAddress, TAuraNodeResult>;
|
|
|
|
|
FOwnsCaches: Boolean;
|
|
|
|
|
|
|
|
|
|
procedure FinalizeNodeLayout(const Node: TAuraNode);
|
|
|
|
|
|
|
|
|
@@ -255,7 +257,8 @@ type
|
|
|
|
|
const AStartPosition: TPointF;
|
|
|
|
|
AConnections: TList<TPinConnection>;
|
|
|
|
|
const ACurrentExec: TArray<TControl>;
|
|
|
|
|
AMode: TVisualizationMode
|
|
|
|
|
AMode: TVisualizationMode;
|
|
|
|
|
AResolvedIdentifierCache: TDictionary<TResolvedAddress, TAuraNodeResult> = nil
|
|
|
|
|
);
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
|
|
|
@@ -850,7 +853,8 @@ constructor TAstToAuraNodeVisitor.Create(
|
|
|
|
|
const AStartPosition: TPointF;
|
|
|
|
|
AConnections: TList<TPinConnection>;
|
|
|
|
|
const ACurrentExec: TArray<TControl>;
|
|
|
|
|
AMode: TVisualizationMode
|
|
|
|
|
AMode: TVisualizationMode;
|
|
|
|
|
AResolvedIdentifierCache: TDictionary<TResolvedAddress, TAuraNodeResult>
|
|
|
|
|
);
|
|
|
|
|
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<TResolvedAddress, TAuraNodeResult>.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
|
|
|
|
|
valueResult := InputResults[0];
|
|
|
|
|
assignmentNode := BuildNodeControl('Assignment', Node.Identifier.Name);
|
|
|
|
|
if control is TAuraNode then
|
|
|
|
|
begin
|
|
|
|
|
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, '');
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
|
if Assigned(valueResult.OutputPin) then
|
|
|
|
|
FConnections.Add(TPinConnection.Create(valueResult.OutputPin, inputPin));
|
|
|
|
|
|
|
|
|
|
// Now finalize, all pins are present.
|
|
|
|
|
// Finalize the layout of the parent node to correctly position its own pins.
|
|
|
|
|
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);
|
|
|
|
|
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
|
|
|
|
|
initializerResult := InputResults[0];
|
|
|
|
|
varDeclNode := BuildNodeControl('VarDecl', Node.Identifier.Name);
|
|
|
|
|
if control is TAuraNode then
|
|
|
|
|
begin
|
|
|
|
|
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, '');
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
|
if Assigned(initializerResult.OutputPin) then
|
|
|
|
|
FConnections.Add(TPinConnection.Create(initializerResult.OutputPin, inputPin));
|
|
|
|
|
|
|
|
|
|
// Now finalize, all pins are present.
|
|
|
|
|
// Finalize the layout of the parent node to correctly position its own pins.
|
|
|
|
|
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;
|
|
|
|
|
|