Ast control refactoring
This commit is contained in:
@@ -380,7 +380,8 @@ begin
|
|||||||
var visu := TVisualizationMode.vmDetailed;
|
var visu := TVisualizationMode.vmDetailed;
|
||||||
if FlowOnlyBox.IsChecked then
|
if FlowOnlyBox.IsChecked then
|
||||||
visu := TVisualizationMode.vmControlFlow;
|
visu := TVisualizationMode.vmControlFlow;
|
||||||
FWorkspace.BuildTree(FLastAst, TPointF.Create(X, Y), visu);
|
|
||||||
|
FWorkspace.BuildTree(FLastAst, TAst.Bind(FLastAst, FGScope), TPointF.Create(X, Y), visu);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ uses
|
|||||||
FMX.Controls,
|
FMX.Controls,
|
||||||
FMX.Graphics,
|
FMX.Graphics,
|
||||||
FMX.Objects,
|
FMX.Objects,
|
||||||
|
Myc.Ast,
|
||||||
Myc.Ast.Nodes;
|
Myc.Ast.Nodes;
|
||||||
|
|
||||||
type
|
type
|
||||||
@@ -42,7 +43,7 @@ type
|
|||||||
procedure DblClick; override;
|
procedure DblClick; override;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
procedure BuildTree(const Root: IAstNode; const Position: TPointF; AMode: TVisualizationMode);
|
procedure BuildTree(const RootNode: IAstNode; const RootScope: IExecutionScope; const Position: TPointF; AMode: TVisualizationMode);
|
||||||
function GetChildrenMatrix(var Matrix: TMatrix; var Simple: Boolean): Boolean; override;
|
function GetChildrenMatrix(var Matrix: TMatrix; var Simple: Boolean): Boolean; override;
|
||||||
function Zoom(Factor: Single): Boolean;
|
function Zoom(Factor: Single): Boolean;
|
||||||
|
|
||||||
@@ -105,11 +106,21 @@ begin
|
|||||||
FZoom := 1.0;
|
FZoom := 1.0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TAuraWorkspace.BuildTree(const Root: IAstNode; const Position: TPointF; AMode: TVisualizationMode);
|
procedure TAuraWorkspace.BuildTree(
|
||||||
|
const RootNode: IAstNode;
|
||||||
|
const RootScope: IExecutionScope;
|
||||||
|
const Position: TPointF;
|
||||||
|
AMode: TVisualizationMode
|
||||||
|
);
|
||||||
|
var
|
||||||
|
connections: TList<TPinConnection>;
|
||||||
|
rootDescriptor: IScopeDescriptor;
|
||||||
begin
|
begin
|
||||||
var connections := TList<TPinConnection>.Create;
|
connections := TList<TPinConnection>.Create;
|
||||||
try
|
try
|
||||||
Root.Accept(TAstToAuraNodeVisitor.Create(Self, Self, Position, connections, nil, AMode));
|
// Create the scope descriptor from the execution scope provided by the binder.
|
||||||
|
rootDescriptor := TAst.CreateDescriptor(RootScope);
|
||||||
|
RootNode.Accept(TAstToAuraNodeVisitor.Create(Self, Self, Position, connections, nil, AMode, nil, nil, rootDescriptor));
|
||||||
FConnections := FConnections + connections.ToArray;
|
FConnections := FConnections + connections.ToArray;
|
||||||
finally
|
finally
|
||||||
connections.Free;
|
connections.Free;
|
||||||
|
|||||||
+299
-246
@@ -9,12 +9,14 @@ uses
|
|||||||
System.Types,
|
System.Types,
|
||||||
System.Math.Vectors,
|
System.Math.Vectors,
|
||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
|
System.Generics.Defaults,
|
||||||
FMX.Types,
|
FMX.Types,
|
||||||
FMX.Controls,
|
FMX.Controls,
|
||||||
FMX.Objects,
|
FMX.Objects,
|
||||||
FMX.Graphics,
|
FMX.Graphics,
|
||||||
Myc.Data.Value,
|
Myc.Data.Value,
|
||||||
Myc.Ast.Nodes,
|
Myc.Ast.Nodes,
|
||||||
|
Myc.Ast.Scope,
|
||||||
Myc.Fmx.AstEditor.Node,
|
Myc.Fmx.AstEditor.Node,
|
||||||
Myc.Fmx.AstEditor.Workspace;
|
Myc.Fmx.AstEditor.Workspace;
|
||||||
|
|
||||||
@@ -31,27 +33,22 @@ type
|
|||||||
TAstToAuraNodeVisitor = class(TInterfacedObject, IAstVisitor)
|
TAstToAuraNodeVisitor = class(TInterfacedObject, IAstVisitor)
|
||||||
public
|
public
|
||||||
type
|
type
|
||||||
// Holds the result of a node visitation, separating layout and connection concerns.
|
|
||||||
TAuraNodeResult = record
|
TAuraNodeResult = record
|
||||||
LayoutNode: TAuraNode;
|
LayoutNode: TAuraNode;
|
||||||
OutputPin: TControl;
|
OutputPin: TControl;
|
||||||
end;
|
end;
|
||||||
TAuraNodeResultList = TList<TAuraNodeResult>;
|
TAuraNodeResultList = TList<TAuraNodeResult>;
|
||||||
TChildVisitorProc = reference to procedure(const Visitor: IAstVisitor);
|
TChildVisitorProc = reference to procedure(const Visitor: IAstVisitor);
|
||||||
// The lambda now returns the full result, including the output pin reference.
|
|
||||||
TCreateParentNodeProc = reference to function(const InputNodes: TAuraNodeResultList): TAuraNodeResult;
|
TCreateParentNodeProc = reference to function(const InputNodes: TAuraNodeResultList): TAuraNodeResult;
|
||||||
TVerticalAlignment = (vaCenter, vaTop);
|
TVerticalAlignment = (vaCenter, vaTop);
|
||||||
|
|
||||||
private
|
private
|
||||||
const
|
const
|
||||||
// Node Layout
|
cHorizontalPadding = 25;
|
||||||
cHorizontalPadding = 25; // Padding inside the node title bar
|
cPinOffsetY = 18;
|
||||||
cPinOffsetY = 18; // Vertical distance between pins
|
cVerticalPadding = 10;
|
||||||
cVerticalPadding = 10; // General vertical padding inside container nodes
|
cDefaultNodeHeight = 25;
|
||||||
|
cIfNodeHeight = 40;
|
||||||
// Specific Node Heights
|
|
||||||
cDefaultNodeHeight = 25; // Default height for simple nodes like BinaryExpr
|
|
||||||
cIfNodeHeight = 40; // Taller height for If nodes with more pins
|
|
||||||
|
|
||||||
private
|
private
|
||||||
FWorkspace: TAuraWorkspace;
|
FWorkspace: TAuraWorkspace;
|
||||||
@@ -62,24 +59,17 @@ type
|
|||||||
FConnections: TList<TPinConnection>;
|
FConnections: TList<TPinConnection>;
|
||||||
FCurrentExec: TList<TControl>;
|
FCurrentExec: TList<TControl>;
|
||||||
FMode: TVisualizationMode;
|
FMode: TVisualizationMode;
|
||||||
FResolvedIdentifierCache: TDictionary<TResolvedAddress, TAuraNodeResult>;
|
FSlotCache: TArray<TAuraNodeResult>;
|
||||||
FOwnsCaches: Boolean;
|
FParentVisitor: TAstToAuraNodeVisitor;
|
||||||
|
FCurrentLambda: ILambdaExpressionNode;
|
||||||
|
|
||||||
procedure FinalizeNodeLayout(const Node: TAuraNode);
|
procedure FinalizeNodeLayout(const Node: TAuraNode);
|
||||||
|
function FindNodeForAddress(const Address: TResolvedAddress; out NodeResult: TAuraNodeResult): Boolean;
|
||||||
function VisitContainerNode(
|
|
||||||
const Title, Details: string;
|
|
||||||
const InPin, OutPin: String;
|
|
||||||
IsExec: Boolean;
|
|
||||||
HasResult: Boolean;
|
|
||||||
const ChildVisitorProc: TChildVisitorProc
|
|
||||||
): TAuraNode;
|
|
||||||
function VisitOperatorNode(
|
function VisitOperatorNode(
|
||||||
const InputExpressions: TArray<IAstNode>;
|
const InputExpressions: TArray<IAstNode>;
|
||||||
const CreateParentProc: TCreateParentNodeProc;
|
const CreateParentProc: TCreateParentNodeProc;
|
||||||
Alignment: TVerticalAlignment = vaCenter
|
Alignment: TVerticalAlignment = vaCenter
|
||||||
): TAuraNode;
|
): TAuraNode;
|
||||||
|
|
||||||
function BuildNodeControl(const Title, Details: string): TAuraNode;
|
function BuildNodeControl(const Title, Details: string): TAuraNode;
|
||||||
function CreateNodeControl(const Title, Details: string): TAuraNode;
|
function CreateNodeControl(const Title, Details: string): TAuraNode;
|
||||||
function CreatePin(
|
function CreatePin(
|
||||||
@@ -89,12 +79,10 @@ type
|
|||||||
Color: TAlphaColor;
|
Color: TAlphaColor;
|
||||||
const Tag: string
|
const Tag: string
|
||||||
): TShape;
|
): TShape;
|
||||||
|
|
||||||
function CreateInput(ParentNode: TAuraNode; const Caption: string): TControl;
|
function CreateInput(ParentNode: TAuraNode; const Caption: string): TControl;
|
||||||
function CreateOutput(ParentNode: TAuraNode): TControl;
|
function CreateOutput(ParentNode: TAuraNode): TControl;
|
||||||
function CreateEntry(ParentNode: TAuraNode; connect: Boolean = true): TControl;
|
function CreateEntry(ParentNode: TAuraNode; connect: Boolean = true): TControl;
|
||||||
function CreateExit(ParentNode: TAuraNode; const Caption: string): TControl;
|
function CreateExit(ParentNode: TAuraNode; const Caption: string): TControl;
|
||||||
|
|
||||||
function TryGetDescr(const Node: IAstNode; out Text: String): Boolean;
|
function TryGetDescr(const Node: IAstNode; out Text: String): Boolean;
|
||||||
|
|
||||||
public
|
public
|
||||||
@@ -105,11 +93,11 @@ type
|
|||||||
AConnections: TList<TPinConnection>;
|
AConnections: TList<TPinConnection>;
|
||||||
const ACurrentExec: TArray<TControl>;
|
const ACurrentExec: TArray<TControl>;
|
||||||
AMode: TVisualizationMode;
|
AMode: TVisualizationMode;
|
||||||
AResolvedIdentifierCache: TDictionary<TResolvedAddress, TAuraNodeResult> = nil
|
AParent: TAstToAuraNodeVisitor;
|
||||||
|
ACurrentLambda: ILambdaExpressionNode;
|
||||||
|
ADescriptor: IScopeDescriptor
|
||||||
);
|
);
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
// Public access to the collected connections
|
|
||||||
property Connections: TList<TPinConnection> read FConnections;
|
property Connections: TList<TPinConnection> read FConnections;
|
||||||
|
|
||||||
{ IAstVisitor }
|
{ IAstVisitor }
|
||||||
@@ -149,7 +137,9 @@ constructor TAstToAuraNodeVisitor.Create(
|
|||||||
AConnections: TList<TPinConnection>;
|
AConnections: TList<TPinConnection>;
|
||||||
const ACurrentExec: TArray<TControl>;
|
const ACurrentExec: TArray<TControl>;
|
||||||
AMode: TVisualizationMode;
|
AMode: TVisualizationMode;
|
||||||
AResolvedIdentifierCache: TDictionary<TResolvedAddress, TAuraNodeResult>
|
AParent: TAstToAuraNodeVisitor;
|
||||||
|
ACurrentLambda: ILambdaExpressionNode;
|
||||||
|
ADescriptor: IScopeDescriptor
|
||||||
);
|
);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
@@ -162,25 +152,15 @@ begin
|
|||||||
FLastResult.LayoutNode := nil;
|
FLastResult.LayoutNode := nil;
|
||||||
FLastResult.OutputPin := nil;
|
FLastResult.OutputPin := nil;
|
||||||
FMode := AMode;
|
FMode := AMode;
|
||||||
|
FParentVisitor := AParent;
|
||||||
if Assigned(AResolvedIdentifierCache) then
|
FCurrentLambda := ACurrentLambda;
|
||||||
begin
|
// Size the array based on the number of slots in the scope descriptor.
|
||||||
FResolvedIdentifierCache := AResolvedIdentifierCache;
|
if Assigned(ADescriptor) then
|
||||||
FOwnsCaches := False;
|
SetLength(FSlotCache, ADescriptor.SlotCount);
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
FResolvedIdentifierCache := TDictionary<TResolvedAddress, TAuraNodeResult>.Create;
|
|
||||||
FOwnsCaches := True;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TAstToAuraNodeVisitor.Destroy;
|
destructor TAstToAuraNodeVisitor.Destroy;
|
||||||
begin
|
begin
|
||||||
if FOwnsCaches then
|
|
||||||
begin
|
|
||||||
FResolvedIdentifierCache.Free;
|
|
||||||
end;
|
|
||||||
FCurrentExec.Free;
|
FCurrentExec.Free;
|
||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
@@ -327,12 +307,10 @@ var
|
|||||||
posX, posY: Single;
|
posX, posY: Single;
|
||||||
begin
|
begin
|
||||||
posY := 0;
|
posY := 0;
|
||||||
|
|
||||||
if Alignment = paLeft then
|
if Alignment = paLeft then
|
||||||
posX := 0
|
posX := 0
|
||||||
else // paRight
|
else // paRight
|
||||||
posX := ParentNode.Width - cPinSize;
|
posX := ParentNode.Width - cPinSize;
|
||||||
|
|
||||||
case Shape of
|
case Shape of
|
||||||
psCircle:
|
psCircle:
|
||||||
begin
|
begin
|
||||||
@@ -353,7 +331,6 @@ begin
|
|||||||
Result := nil;
|
Result := nil;
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result.Parent := ParentNode;
|
Result.Parent := ParentNode;
|
||||||
Result.SetBounds(posX, posY, cPinSize, cPinSize);
|
Result.SetBounds(posX, posY, cPinSize, cPinSize);
|
||||||
Result.HitTest := true;
|
Result.HitTest := true;
|
||||||
@@ -388,7 +365,6 @@ begin
|
|||||||
inputs := [Node.Series, Node.Value, Node.Lookback]
|
inputs := [Node.Series, Node.Value, Node.Lookback]
|
||||||
else
|
else
|
||||||
inputs := [Node.Series, Node.Value];
|
inputs := [Node.Series, Node.Value];
|
||||||
|
|
||||||
VisitOperatorNode(
|
VisitOperatorNode(
|
||||||
inputs,
|
inputs,
|
||||||
function(const InputResults: TAuraNodeResultList): TAuraNodeResult
|
function(const InputResults: TAuraNodeResultList): TAuraNodeResult
|
||||||
@@ -398,27 +374,22 @@ begin
|
|||||||
begin
|
begin
|
||||||
addNode := BuildNodeControl('Add to Series', '');
|
addNode := BuildNodeControl('Add to Series', '');
|
||||||
CreateEntry(addNode);
|
CreateEntry(addNode);
|
||||||
|
|
||||||
seriesPin := CreateInput(addNode, 'Series');
|
seriesPin := CreateInput(addNode, 'Series');
|
||||||
valuePin := CreateInput(addNode, 'Value');
|
valuePin := CreateInput(addNode, 'Value');
|
||||||
|
|
||||||
if Assigned(InputResults[0].OutputPin) then
|
if Assigned(InputResults[0].OutputPin) then
|
||||||
FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, seriesPin));
|
FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, seriesPin));
|
||||||
if Assigned(InputResults[1].OutputPin) then
|
if Assigned(InputResults[1].OutputPin) then
|
||||||
FConnections.Add(TPinConnection.Create(InputResults[1].OutputPin, valuePin));
|
FConnections.Add(TPinConnection.Create(InputResults[1].OutputPin, valuePin));
|
||||||
|
|
||||||
if InputResults.Count > 2 then
|
if InputResults.Count > 2 then
|
||||||
begin
|
begin
|
||||||
lookbackPin := CreateInput(addNode, 'Lookback');
|
lookbackPin := CreateInput(addNode, 'Lookback');
|
||||||
if Assigned(InputResults[2].OutputPin) then
|
if Assigned(InputResults[2].OutputPin) then
|
||||||
FConnections.Add(TPinConnection.Create(InputResults[2].OutputPin, lookbackPin));
|
FConnections.Add(TPinConnection.Create(InputResults[2].OutputPin, lookbackPin));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
CreateExit(addNode, '');
|
CreateExit(addNode, '');
|
||||||
|
|
||||||
FinalizeNodeLayout(addNode);
|
FinalizeNodeLayout(addNode);
|
||||||
Result.LayoutNode := addNode;
|
Result.LayoutNode := addNode;
|
||||||
Result.OutputPin := nil; // No data output
|
Result.OutputPin := nil;
|
||||||
end,
|
end,
|
||||||
vaTop
|
vaTop
|
||||||
);
|
);
|
||||||
@@ -426,47 +397,6 @@ begin
|
|||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TAstToAuraNodeVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
|
||||||
var
|
|
||||||
details: string;
|
|
||||||
assignmentNode: TAuraNode;
|
|
||||||
control: TControl;
|
|
||||||
begin
|
|
||||||
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
|
|
||||||
begin
|
|
||||||
assignmentNode := CreateNodeControl('Assignment', details);
|
|
||||||
CreateEntry(assignmentNode);
|
|
||||||
CreateExit(assignmentNode, '');
|
|
||||||
FinalizeNodeLayout(assignmentNode);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
assignmentNode := CreateNodeControl('Assignment', Node.Identifier.Name);
|
|
||||||
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);
|
|
||||||
var maxRight: Single := 0;
|
|
||||||
var maxBottom: Single := 0;
|
|
||||||
for control in assignmentNode.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;
|
|
||||||
assignmentNode.Width := Max(assignmentNode.Width, maxRight + FSpacing.X);
|
|
||||||
assignmentNode.Height := Max(assignmentNode.Height, maxBottom + FSpacing.Y);
|
|
||||||
CreateEntry(assignmentNode);
|
|
||||||
CreateExit(assignmentNode, '');
|
|
||||||
FLastResult.OutputPin := CreateOutput(assignmentNode);
|
|
||||||
FinalizeNodeLayout(assignmentNode);
|
|
||||||
FCurrentPos.Y := assignmentNode.Position.Y + assignmentNode.Height + FSpacing.Y;
|
|
||||||
end;
|
|
||||||
Result := TDataValue.Void;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TAstToAuraNodeVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
function TAstToAuraNodeVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
var exprStr: String;
|
var exprStr: String;
|
||||||
@@ -510,32 +440,54 @@ end;
|
|||||||
function TAstToAuraNodeVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
function TAstToAuraNodeVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||||
var
|
var
|
||||||
blockNode: TAuraNode;
|
blockNode: TAuraNode;
|
||||||
childLastResult: TAuraNodeResult;
|
oldParentControl: TControl;
|
||||||
|
oldPos: TPointF;
|
||||||
|
lastResult: TAuraNodeResult;
|
||||||
begin
|
begin
|
||||||
childLastResult.LayoutNode := nil;
|
// Create a visual container for the block, but do NOT create a new scope/visitor.
|
||||||
childLastResult.OutputPin := nil;
|
blockNode := BuildNodeControl('', '');
|
||||||
blockNode :=
|
blockNode.Frameless := True;
|
||||||
VisitContainerNode(
|
blockNode.Position.Point := FCurrentPos;
|
||||||
'',
|
|
||||||
'',
|
// Temporarily retarget the visitor to build nodes inside the block container.
|
||||||
'',
|
oldParentControl := FParentControl;
|
||||||
'',
|
oldPos := FCurrentPos;
|
||||||
true,
|
try
|
||||||
false,
|
FParentControl := blockNode;
|
||||||
procedure(const Visitor: IAstVisitor)
|
FCurrentPos := TPointF.Create(FSpacing.X, FSpacing.Y);
|
||||||
var
|
|
||||||
expression: IAstNode;
|
Result := TDataValue.Void;
|
||||||
|
for var expression in Node.Expressions do
|
||||||
|
begin
|
||||||
|
Result := expression.Accept(Self);
|
||||||
|
// The result of the block is the result of the last expression.
|
||||||
|
// We need to capture the last FLastResult before it's overwritten.
|
||||||
|
lastResult := FLastResult;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var maxRight: Single := 0;
|
||||||
|
var maxBottom: Single := 0;
|
||||||
|
for var control in blockNode.Controls do
|
||||||
|
begin
|
||||||
|
if control is TAuraNode then
|
||||||
begin
|
begin
|
||||||
for expression in Node.Expressions do
|
maxRight := Max(maxRight, control.Position.X + control.Width);
|
||||||
expression.Accept(Visitor);
|
maxBottom := Max(maxBottom, control.Position.Y + control.Height);
|
||||||
childLastResult := (Visitor as TAstToAuraNodeVisitor).FLastResult;
|
end;
|
||||||
end
|
end;
|
||||||
);
|
blockNode.Width := Max(blockNode.Width, maxRight + FSpacing.X);
|
||||||
blockNode.Frameless := true;
|
blockNode.Height := Max(blockNode.Height, maxBottom + FSpacing.Y);
|
||||||
FCurrentPos.Y := blockNode.Position.Y + blockNode.Height + FSpacing.Y;
|
|
||||||
|
finally
|
||||||
|
// Restore the visitor's state.
|
||||||
|
FParentControl := oldParentControl;
|
||||||
|
FCurrentPos := oldPos;
|
||||||
|
FCurrentPos.Y := blockNode.Position.Y + blockNode.Height + FSpacing.Y;
|
||||||
|
end;
|
||||||
|
|
||||||
FLastResult.LayoutNode := blockNode;
|
FLastResult.LayoutNode := blockNode;
|
||||||
FLastResult.OutputPin := childLastResult.OutputPin;
|
// The output pin of a block is the output pin of its last expression.
|
||||||
Result := TDataValue.Void;
|
FLastResult.OutputPin := lastResult.OutputPin;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TAstToAuraNodeVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
function TAstToAuraNodeVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||||
@@ -548,94 +500,6 @@ begin
|
|||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TAstToAuraNodeVisitor.VisitContainerNode(
|
|
||||||
const Title, Details: string;
|
|
||||||
const InPin, OutPin: String;
|
|
||||||
IsExec: Boolean;
|
|
||||||
HasResult: Boolean;
|
|
||||||
const ChildVisitorProc: TChildVisitorProc
|
|
||||||
): TAuraNode;
|
|
||||||
var
|
|
||||||
containerNode: TAuraNode;
|
|
||||||
childVisitor: IAstVisitor;
|
|
||||||
childStartPos: TPointF;
|
|
||||||
maxRight: Single;
|
|
||||||
maxBottom: Single;
|
|
||||||
control: TControl;
|
|
||||||
childLastResult: TAuraNodeResult;
|
|
||||||
begin
|
|
||||||
const pinNodeHeight = cPinSize + cVerticalPadding;
|
|
||||||
containerNode := BuildNodeControl(Title, Details);
|
|
||||||
containerNode.Position.Point := FCurrentPos;
|
|
||||||
Result := containerNode;
|
|
||||||
childStartPos :=
|
|
||||||
TPointF.Create(FSpacing.X, FSpacing.Y + max(IfThen(InPin <> '', pinNodeHeight, 0), IfThen(Title <> '', cVerticalPadding, 0)));
|
|
||||||
var currExec := FCurrentExec.ToArray;
|
|
||||||
if InPin <> '' then
|
|
||||||
begin
|
|
||||||
var entryNode := BuildNodeControl(InPin, '');
|
|
||||||
entryNode.Parent := containerNode;
|
|
||||||
entryNode.Position.Point := TPointF.Create(0, 0);
|
|
||||||
entryNode.Height := pinNodeHeight;
|
|
||||||
|
|
||||||
if IsExec then
|
|
||||||
begin
|
|
||||||
var entryPin := CreateEntry(containerNode);
|
|
||||||
var entryExitPin := CreateExit(entryNode, '');
|
|
||||||
FinalizeNodeLayout(entryNode);
|
|
||||||
entryPin.Position.Y := containerNode.AbsoluteToLocal(entryNode.LocalToAbsolute(entryExitPin.Position.Point)).Y;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
FCurrentExec.Clear;
|
|
||||||
CreateExit(entryNode, '');
|
|
||||||
FinalizeNodeLayout(entryNode);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
childVisitor :=
|
|
||||||
TAstToAuraNodeVisitor
|
|
||||||
.Create(FWorkspace, containerNode, childStartPos, FConnections, FCurrentExec.ToArray, FMode, FResolvedIdentifierCache);
|
|
||||||
ChildVisitorProc(childVisitor);
|
|
||||||
childLastResult := (childVisitor as TAstToAuraNodeVisitor).FLastResult;
|
|
||||||
FCurrentExec.Clear;
|
|
||||||
FCurrentExec.AddRange((childVisitor as TAstToAuraNodeVisitor).FCurrentExec);
|
|
||||||
maxRight := 0;
|
|
||||||
maxBottom := 0;
|
|
||||||
for control in containerNode.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;
|
|
||||||
containerNode.Width := Max(containerNode.Width, maxRight + FSpacing.X);
|
|
||||||
containerNode.Height := Max(containerNode.Height, maxBottom + FSpacing.Y);
|
|
||||||
if OutPin <> '' then
|
|
||||||
begin
|
|
||||||
var exitNode := BuildNodeControl(OutPin, '');
|
|
||||||
exitNode.Parent := containerNode;
|
|
||||||
exitNode.Height := pinNodeHeight;
|
|
||||||
var exitPin := CreateEntry(exitNode);
|
|
||||||
if HasResult and Assigned(childLastResult.OutputPin) then
|
|
||||||
begin
|
|
||||||
var dataResultPin := CreateInput(exitNode, 'Result');
|
|
||||||
FConnections.Add(TPinConnection.Create(childLastResult.OutputPin, dataResultPin));
|
|
||||||
end;
|
|
||||||
FinalizeNodeLayout(exitNode);
|
|
||||||
containerNode.Height := Max(containerNode.Height, maxBottom + FSpacing.Y + exitNode.Height);
|
|
||||||
exitNode.Position.Point := TPointF.Create(containerNode.Width - exitNode.Width, containerNode.Height - exitNode.Height);
|
|
||||||
if not IsExec then
|
|
||||||
begin
|
|
||||||
FCurrentExec.Clear;
|
|
||||||
FCurrentExec.AddRange(currExec);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
CreateExit(containerNode, '').Position.Y := containerNode.AbsoluteToLocal(exitNode.LocalToAbsolute(exitPin.Position.Point)).Y;
|
|
||||||
end;
|
|
||||||
FinalizeNodeLayout(containerNode);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TAstToAuraNodeVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
function TAstToAuraNodeVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||||
var
|
var
|
||||||
seriesNode: TAuraNode;
|
seriesNode: TAuraNode;
|
||||||
@@ -702,19 +566,21 @@ var
|
|||||||
existingResult: TAuraNodeResult;
|
existingResult: TAuraNodeResult;
|
||||||
identifierNode: TAuraNode;
|
identifierNode: TAuraNode;
|
||||||
begin
|
begin
|
||||||
if Node.IsResolved then
|
if Node.Address.Kind <> akUnresolved then
|
||||||
begin
|
begin
|
||||||
if FResolvedIdentifierCache.TryGetValue(Node.Address, existingResult) then
|
// FindNodeForAddress now handles all cases: local cache, on-demand parameter creation, and recursive parent/upvalue lookup.
|
||||||
|
if FindNodeForAddress(Node.Address, existingResult) then
|
||||||
begin
|
begin
|
||||||
FLastResult := existingResult;
|
FLastResult := existingResult;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
identifierNode := CreateNodeControl('Identifier', Node.Name);
|
// If the lookup still fails, it's a genuine unresolved variable.
|
||||||
|
identifierNode := CreateNodeControl('Identifier (unresolved!)', Node.Name);
|
||||||
|
identifierNode.BorderColor := TAlphaColors.Red;
|
||||||
FLastResult.LayoutNode := identifierNode;
|
FLastResult.LayoutNode := identifierNode;
|
||||||
FLastResult.OutputPin := CreateOutput(identifierNode);
|
FLastResult.OutputPin := CreateOutput(identifierNode);
|
||||||
FinalizeNodeLayout(identifierNode);
|
FinalizeNodeLayout(identifierNode);
|
||||||
FResolvedIdentifierCache.Add(Node.Address, FLastResult);
|
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -838,12 +704,56 @@ begin
|
|||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstToAuraNodeVisitor.FindNodeForAddress(const Address: TResolvedAddress; out NodeResult: TAuraNodeResult): Boolean;
|
||||||
|
var
|
||||||
|
originalAddress: TResolvedAddress;
|
||||||
|
targetVisitor: TAstToAuraNodeVisitor;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
if (Address.Kind = akUpvalue) and Assigned(FCurrentLambda) then
|
||||||
|
begin
|
||||||
|
if Address.SlotIndex < Length(FCurrentLambda.Upvalues) then
|
||||||
|
begin
|
||||||
|
originalAddress := FCurrentLambda.Upvalues[Address.SlotIndex];
|
||||||
|
if Assigned(FParentVisitor) then
|
||||||
|
exit(FParentVisitor.FindNodeForAddress(originalAddress, NodeResult));
|
||||||
|
end;
|
||||||
|
exit(False);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if (Address.Kind = akLocalOrParent) then
|
||||||
|
begin
|
||||||
|
targetVisitor := Self;
|
||||||
|
for i := 1 to Address.ScopeDepth do
|
||||||
|
begin
|
||||||
|
if not Assigned(targetVisitor) then
|
||||||
|
exit(False);
|
||||||
|
targetVisitor := targetVisitor.FParentVisitor;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if Assigned(targetVisitor) and (Address.SlotIndex < Length(targetVisitor.FSlotCache)) then
|
||||||
|
begin
|
||||||
|
NodeResult := targetVisitor.FSlotCache[Address.SlotIndex];
|
||||||
|
// An entry is valid if a layout node has been created for it.
|
||||||
|
exit(Assigned(NodeResult.LayoutNode));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
function TAstToAuraNodeVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
function TAstToAuraNodeVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||||
var
|
var
|
||||||
paramStr: String;
|
paramStr: String;
|
||||||
lambdaNode: TAuraNode;
|
lambdaNode: TAuraNode;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
childVisitor: TAstToAuraNodeVisitor;
|
||||||
|
childStartPos: TPointF;
|
||||||
|
maxRight, maxBottom: Single;
|
||||||
|
control: TControl;
|
||||||
|
childLastResult: TAuraNodeResult;
|
||||||
|
entryNode, exitNode: TAuraNode;
|
||||||
begin
|
begin
|
||||||
|
// Parameter string for the title
|
||||||
paramStr := '(';
|
paramStr := '(';
|
||||||
if Length(Node.Parameters) > 0 then
|
if Length(Node.Parameters) > 0 then
|
||||||
begin
|
begin
|
||||||
@@ -852,20 +762,84 @@ begin
|
|||||||
paramStr := paramStr + ', ' + Node.Parameters[i].Name;
|
paramStr := paramStr + ', ' + Node.Parameters[i].Name;
|
||||||
end;
|
end;
|
||||||
paramStr := paramStr + ')';
|
paramStr := paramStr + ')';
|
||||||
lambdaNode :=
|
|
||||||
VisitContainerNode(
|
// --- Inlined logic from former VisitContainerNode ---
|
||||||
#$03BB,
|
|
||||||
paramStr,
|
// 1. Create container node for the lambda.
|
||||||
'call',
|
lambdaNode := BuildNodeControl(#$03BB, paramStr);
|
||||||
'return',
|
lambdaNode.Position.Point := FCurrentPos;
|
||||||
false,
|
|
||||||
true,
|
// 2. Create internal 'call' entry point.
|
||||||
procedure(const Visitor: IAstVisitor) begin Node.Body.Accept(Visitor); end
|
const pinNodeHeight = cPinSize + cVerticalPadding;
|
||||||
);
|
entryNode := BuildNodeControl('call', '');
|
||||||
|
entryNode.Parent := lambdaNode;
|
||||||
|
entryNode.Position.Point := TPointF.Create(0, 0);
|
||||||
|
entryNode.Height := pinNodeHeight;
|
||||||
|
// A lambda expression is a value, it doesn't connect to the parent execution flow.
|
||||||
|
// Its internal flow starts with the exit of the 'call' node.
|
||||||
|
var internalExec: TArray<TControl> := [CreateExit(entryNode, '')];
|
||||||
|
FinalizeNodeLayout(entryNode);
|
||||||
|
|
||||||
|
// 3. Create child visitor for the new scope.
|
||||||
|
childStartPos := TPointF.Create(FSpacing.X, FSpacing.Y + pinNodeHeight);
|
||||||
|
childVisitor :=
|
||||||
|
TAstToAuraNodeVisitor
|
||||||
|
.Create(FWorkspace, lambdaNode, childStartPos, FConnections, internalExec, FMode, Self, Node, Node.ScopeDescriptor);
|
||||||
|
|
||||||
|
FCurrentExec.Clear;
|
||||||
|
|
||||||
|
// 4. Let the child visitor create parameter nodes and visit the lambda body.
|
||||||
|
for var param in Node.Parameters do
|
||||||
|
begin
|
||||||
|
if param.Address.Kind <> akUnresolved then
|
||||||
|
begin
|
||||||
|
var paramNode := childVisitor.CreateNodeControl('Parameter', param.Name);
|
||||||
|
var paramResult: TAuraNodeResult;
|
||||||
|
paramResult.LayoutNode := paramNode;
|
||||||
|
paramResult.OutputPin := childVisitor.CreateOutput(paramNode);
|
||||||
|
childVisitor.FinalizeNodeLayout(paramNode);
|
||||||
|
childVisitor.FSlotCache[param.Address.SlotIndex] := paramResult;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Node.Body.Accept(childVisitor);
|
||||||
|
childLastResult := childVisitor.FLastResult;
|
||||||
|
|
||||||
|
// 5. Resize container to fit all internally generated nodes.
|
||||||
|
maxRight := 0;
|
||||||
|
maxBottom := 0;
|
||||||
|
for control in lambdaNode.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;
|
||||||
|
lambdaNode.Width := Max(lambdaNode.Width, maxRight + FSpacing.X);
|
||||||
|
lambdaNode.Height := Max(lambdaNode.Height, maxBottom + FSpacing.Y);
|
||||||
|
|
||||||
|
// 6. Create internal 'return' exit point.
|
||||||
|
exitNode := BuildNodeControl('return', '');
|
||||||
|
exitNode.Parent := lambdaNode;
|
||||||
|
exitNode.Height := pinNodeHeight;
|
||||||
|
// Connect the execution flow from the lambda body to the 'return' node's entry.
|
||||||
|
childVisitor.CreateEntry(exitNode, true);
|
||||||
|
// Connect the data flow (the result of the last expression) to the 'return' node.
|
||||||
|
if Assigned(childLastResult.OutputPin) then
|
||||||
|
begin
|
||||||
|
var dataResultPin := childVisitor.CreateInput(exitNode, 'Result');
|
||||||
|
FConnections.Add(TPinConnection.Create(childLastResult.OutputPin, dataResultPin));
|
||||||
|
end;
|
||||||
|
childVisitor.FinalizeNodeLayout(exitNode);
|
||||||
|
lambdaNode.Height := Max(lambdaNode.Height, maxBottom + FSpacing.Y + exitNode.Height);
|
||||||
|
exitNode.Position.Point := TPointF.Create(lambdaNode.Width - exitNode.Width, lambdaNode.Height - exitNode.Height);
|
||||||
|
|
||||||
|
// 7. Finalize the main visitor state.
|
||||||
FCurrentPos.Y := lambdaNode.Position.Y + lambdaNode.Height + FSpacing.Y;
|
FCurrentPos.Y := lambdaNode.Position.Y + lambdaNode.Height + FSpacing.Y;
|
||||||
FLastResult.LayoutNode := lambdaNode;
|
FLastResult.LayoutNode := lambdaNode;
|
||||||
FLastResult.OutputPin := CreateOutput(lambdaNode);
|
FLastResult.OutputPin := CreateOutput(lambdaNode); // The lambda itself is an expression that yields a value.
|
||||||
FinalizeNodeLayout(lambdaNode);
|
FinalizeNodeLayout(lambdaNode);
|
||||||
|
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -1048,10 +1022,74 @@ begin
|
|||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstToAuraNodeVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||||
|
var
|
||||||
|
details: string;
|
||||||
|
assignmentNode: TAuraNode;
|
||||||
|
oldParentControl: TControl;
|
||||||
|
oldPos: TPointF;
|
||||||
|
control: TControl;
|
||||||
|
begin
|
||||||
|
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
|
||||||
|
begin
|
||||||
|
assignmentNode := CreateNodeControl('Assignment', details);
|
||||||
|
CreateEntry(assignmentNode);
|
||||||
|
CreateExit(assignmentNode, '');
|
||||||
|
FinalizeNodeLayout(assignmentNode);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// 1. Container-Knoten erstellen.
|
||||||
|
assignmentNode := BuildNodeControl('Assignment', Node.Identifier.Name);
|
||||||
|
assignmentNode.Position.Point := FCurrentPos;
|
||||||
|
|
||||||
|
// 2. Visitor temporär auf den Container als Parent umleiten.
|
||||||
|
oldParentControl := FParentControl;
|
||||||
|
oldPos := FCurrentPos;
|
||||||
|
try
|
||||||
|
FParentControl := assignmentNode;
|
||||||
|
FCurrentPos := TPointF.Create(FSpacing.X, assignmentNode.TitleFont.Size * 1.8 + FSpacing.Y);
|
||||||
|
|
||||||
|
// 3. Den Zuweisungswert mit dem *aktuellen* Visitor (Self) verarbeiten. Es wird kein neuer Scope erzeugt.
|
||||||
|
Node.Value.Accept(Self);
|
||||||
|
|
||||||
|
// 4. Container-Größe an den Inhalt anpassen.
|
||||||
|
var maxRight: Single := 0;
|
||||||
|
var maxBottom: Single := 0;
|
||||||
|
for control in assignmentNode.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;
|
||||||
|
assignmentNode.Width := Max(assignmentNode.Width, maxRight + FSpacing.X);
|
||||||
|
assignmentNode.Height := Max(assignmentNode.Height, maxBottom + FSpacing.Y);
|
||||||
|
finally
|
||||||
|
// 5. Visitor-Zustand wiederherstellen.
|
||||||
|
FParentControl := oldParentControl;
|
||||||
|
FCurrentPos := oldPos;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 6. Pins anbringen und Layout finalisieren.
|
||||||
|
CreateEntry(assignmentNode);
|
||||||
|
CreateExit(assignmentNode, '');
|
||||||
|
FLastResult.OutputPin := CreateOutput(assignmentNode);
|
||||||
|
FinalizeNodeLayout(assignmentNode);
|
||||||
|
|
||||||
|
// 7. FCurrentPos für den nächsten Knoten korrekt setzen.
|
||||||
|
FCurrentPos.Y := assignmentNode.Position.Y + assignmentNode.Height + FSpacing.Y;
|
||||||
|
end;
|
||||||
|
Result := TDataValue.Void;
|
||||||
|
end;
|
||||||
|
|
||||||
function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||||
var
|
var
|
||||||
details: string;
|
details: string;
|
||||||
varDeclNode: TAuraNode;
|
varDeclNode: TAuraNode;
|
||||||
|
oldParentControl: TControl;
|
||||||
|
oldPos: TPointF;
|
||||||
control: TControl;
|
control: TControl;
|
||||||
begin
|
begin
|
||||||
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
|
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
|
||||||
@@ -1063,39 +1101,54 @@ begin
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
|
// 1. Container-Knoten erstellen.
|
||||||
|
varDeclNode := BuildNodeControl('VarDecl', Node.Identifier.Name);
|
||||||
|
varDeclNode.Position.Point := FCurrentPos;
|
||||||
|
|
||||||
if Assigned(Node.Initializer) then
|
if Assigned(Node.Initializer) then
|
||||||
begin
|
begin
|
||||||
varDeclNode := CreateNodeControl('VarDecl', Node.Identifier.Name);
|
// 2. Visitor temporär auf den Container als Parent umleiten.
|
||||||
var childStartPos := TPointF.Create(FSpacing.X, varDeclNode.TitleFont.Size * 1.8 + FSpacing.Y);
|
oldParentControl := FParentControl;
|
||||||
var childVisitor :=
|
oldPos := FCurrentPos;
|
||||||
TAstToAuraNodeVisitor.Create(FWorkspace, varDeclNode, childStartPos, FConnections, [], FMode, FResolvedIdentifierCache);
|
try
|
||||||
Node.Initializer.Accept(childVisitor);
|
FParentControl := varDeclNode;
|
||||||
var maxRight: Single := 0;
|
FCurrentPos := TPointF.Create(FSpacing.X, varDeclNode.TitleFont.Size * 1.8 + FSpacing.Y);
|
||||||
var maxBottom: Single := 0;
|
|
||||||
for control in varDeclNode.Controls do
|
// 3. Den Initialisierungsausdruck mit dem *aktuellen* Visitor (Self) verarbeiten.
|
||||||
begin
|
Node.Initializer.Accept(Self);
|
||||||
if control is TAuraNode then
|
|
||||||
|
// 4. Container-Größe an den Inhalt anpassen.
|
||||||
|
var maxRight: Single := 0;
|
||||||
|
var maxBottom: Single := 0;
|
||||||
|
for control in varDeclNode.Controls do
|
||||||
begin
|
begin
|
||||||
maxRight := Max(maxRight, control.Position.X + control.Width);
|
if control is TAuraNode then
|
||||||
maxBottom := Max(maxBottom, control.Position.Y + control.Height);
|
begin
|
||||||
|
maxRight := Max(maxRight, control.Position.X + control.Width);
|
||||||
|
maxBottom := Max(maxBottom, control.Position.Y + control.Height);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
varDeclNode.Width := Max(varDeclNode.Width, maxRight + FSpacing.X);
|
||||||
|
varDeclNode.Height := Max(varDeclNode.Height, maxBottom + FSpacing.Y);
|
||||||
|
finally
|
||||||
|
// 5. Visitor-Zustand wiederherstellen.
|
||||||
|
FParentControl := oldParentControl;
|
||||||
|
FCurrentPos := oldPos;
|
||||||
end;
|
end;
|
||||||
varDeclNode.Width := Max(varDeclNode.Width, maxRight + FSpacing.X);
|
|
||||||
varDeclNode.Height := Max(varDeclNode.Height, maxBottom + FSpacing.Y);
|
|
||||||
CreateEntry(varDeclNode);
|
|
||||||
CreateExit(varDeclNode, '');
|
|
||||||
FLastResult.OutputPin := CreateOutput(varDeclNode);
|
|
||||||
FinalizeNodeLayout(varDeclNode);
|
|
||||||
FCurrentPos.Y := varDeclNode.Position.Y + varDeclNode.Height + FSpacing.Y;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
varDeclNode := CreateNodeControl('VarDecl', Node.Identifier.Name);
|
|
||||||
CreateEntry(varDeclNode);
|
|
||||||
CreateExit(varDeclNode, '');
|
|
||||||
FLastResult.OutputPin := CreateOutput(varDeclNode);
|
|
||||||
FinalizeNodeLayout(varDeclNode);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// 6. Pins anbringen und Layout finalisieren.
|
||||||
|
CreateEntry(varDeclNode);
|
||||||
|
CreateExit(varDeclNode, '');
|
||||||
|
FLastResult.OutputPin := CreateOutput(varDeclNode);
|
||||||
|
FinalizeNodeLayout(varDeclNode);
|
||||||
|
|
||||||
|
// 7. FCurrentPos für den nächsten Knoten korrekt setzen.
|
||||||
|
FCurrentPos.Y := varDeclNode.Position.Y + varDeclNode.Height + FSpacing.Y;
|
||||||
|
|
||||||
|
// 8. Die neue Variable im Cache des aktuellen Scopes registrieren.
|
||||||
|
if Node.Identifier.Address.Kind <> akUnresolved then
|
||||||
|
FSlotCache[Node.Identifier.Address.SlotIndex] := FLastResult;
|
||||||
end;
|
end;
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ type
|
|||||||
SlotIndex: Integer;
|
SlotIndex: Integer;
|
||||||
constructor Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer);
|
constructor Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer);
|
||||||
class operator Initialize(out Dest: TResolvedAddress);
|
class operator Initialize(out Dest: TResolvedAddress);
|
||||||
|
public
|
||||||
|
class operator Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
IValueCell = interface
|
IValueCell = interface
|
||||||
@@ -112,12 +114,10 @@ type
|
|||||||
|
|
||||||
IIdentifierNode = interface(IAstNode)
|
IIdentifierNode = interface(IAstNode)
|
||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
function GetIsResolved: Boolean;
|
|
||||||
function GetAddress: TResolvedAddress;
|
function GetAddress: TResolvedAddress;
|
||||||
function GetName: string;
|
function GetName: string;
|
||||||
{$endregion}
|
{$endregion}
|
||||||
property Name: string read GetName;
|
property Name: string read GetName;
|
||||||
property IsResolved: Boolean read GetIsResolved;
|
|
||||||
property Address: TResolvedAddress read GetAddress;
|
property Address: TResolvedAddress read GetAddress;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -269,6 +269,11 @@ begin
|
|||||||
SlotIndex := ASlotIndex;
|
SlotIndex := ASlotIndex;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class operator TResolvedAddress.Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (A.Kind = B.Kind) and (A.ScopeDepth = B.ScopeDepth) and (A.SlotIndex = B.SlotIndex);
|
||||||
|
end;
|
||||||
|
|
||||||
class operator TResolvedAddress.Initialize(out Dest: TResolvedAddress);
|
class operator TResolvedAddress.Initialize(out Dest: TResolvedAddress);
|
||||||
begin
|
begin
|
||||||
Dest.Kind := akUnresolved;
|
Dest.Kind := akUnresolved;
|
||||||
|
|||||||
+1
-11
@@ -112,12 +112,10 @@ type
|
|||||||
// --- Annotation fields added for the binder ---
|
// --- Annotation fields added for the binder ---
|
||||||
FResolvedAddress: TResolvedAddress;
|
FResolvedAddress: TResolvedAddress;
|
||||||
function GetName: string;
|
function GetName: string;
|
||||||
function GetIsResolved: Boolean; inline;
|
|
||||||
function GetAddress: TResolvedAddress; inline;
|
function GetAddress: TResolvedAddress; inline;
|
||||||
public
|
public
|
||||||
constructor Create(AName: string);
|
constructor Create(AName: string);
|
||||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||||
property IsResolved: Boolean read GetIsResolved;
|
|
||||||
property Name: string read FName;
|
property Name: string read FName;
|
||||||
property Address: TResolvedAddress read GetAddress;
|
property Address: TResolvedAddress read GetAddress;
|
||||||
end;
|
end;
|
||||||
@@ -437,12 +435,6 @@ begin
|
|||||||
Result := Visitor.VisitIdentifier(Self);
|
Result := Visitor.VisitIdentifier(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TIdentifierNode.GetIsResolved: Boolean;
|
|
||||||
begin
|
|
||||||
// An identifier is resolved if its kind is not unresolved anymore.
|
|
||||||
Result := FResolvedAddress.Kind <> akUnresolved;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TIdentifierNode.GetName: string;
|
function TIdentifierNode.GetName: string;
|
||||||
begin
|
begin
|
||||||
Result := FName;
|
Result := FName;
|
||||||
@@ -450,8 +442,6 @@ end;
|
|||||||
|
|
||||||
function TIdentifierNode.GetAddress: TResolvedAddress;
|
function TIdentifierNode.GetAddress: TResolvedAddress;
|
||||||
begin
|
begin
|
||||||
if not IsResolved then
|
|
||||||
raise EInvalidOpException.Create('Identifier not bound');
|
|
||||||
Result := FResolvedAddress;
|
Result := FResolvedAddress;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -867,7 +857,7 @@ var
|
|||||||
upvalueIndex: Integer;
|
upvalueIndex: Integer;
|
||||||
begin
|
begin
|
||||||
identNode := Node as TIdentifierNode;
|
identNode := Node as TIdentifierNode;
|
||||||
if identNode.IsResolved then
|
if identNode.Address.Kind <> akUnresolved then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
if (FCurrentDescriptor as TScopeDescriptor).FindSymbol(identNode.Name, depth, idx) then
|
if (FCurrentDescriptor as TScopeDescriptor).FindSymbol(identNode.Name, depth, idx) then
|
||||||
|
|||||||
Reference in New Issue
Block a user