Files
MycLib/Src/AST/Myc.Fmx.AstEditor.Handlers.Control.pas
T
2026-01-13 16:23:33 +01:00

520 lines
16 KiB
ObjectPascal

unit Myc.Fmx.AstEditor.Handlers.Control;
interface
uses
System.SysUtils,
System.UITypes,
System.Generics.Collections,
Myc.Ast,
Myc.Ast.Nodes,
Myc.Fmx.AstEditor.Render,
Myc.Fmx.AstEditor.Node;
type
// --- Logic & Control Flow ---
TBlockExpressionNodeHandler = class(TBaseNodeHandler<IBlockExpressionNode>)
private
FExpressions: TList<TAstViewNode>;
public
constructor Create(const ANode: IBlockExpressionNode);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TIfExpressionNodeHandler = class(TBaseNodeHandler<IIfExpressionNode>)
private
FConditionNode: TAstViewNode;
FThenNode: TAstViewNode;
FElseNode: TAstViewNode;
public
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
// Handles the 'cond' expression (multi-branch if/else)
TCondExpressionNodeHandler = class(TBaseNodeHandler<ICondExpressionNode>)
private
type
TBranchView = record
Condition: TAstViewNode;
Branch: TAstViewNode;
end;
private
FBranchNodes: TList<TBranchView>;
FElseNode: TAstViewNode;
public
constructor Create(const ANode: ICondExpressionNode);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
// --- Functions & Calls ---
TLambdaExpressionNodeHandler = class(TBaseNodeHandler<ILambdaExpressionNode>)
private
FParamsNode: TAstViewNode;
FBodyNode: TAstViewNode;
public
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TFunctionCallNodeHandler = class(TBaseNodeHandler<IFunctionCallNode>)
private
FCalleeNode: TAstViewNode;
FArgumentsNode: TAstViewNode;
public
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
// Renders function calls as infix operators (e.g. "A + B" instead of "call + A B")
TOperatorCallHandler = class(TBaseNodeHandler<IFunctionCallNode>)
private
FArgumentNodes: TList<TAstViewNode>;
FCalleeName: string;
public
constructor Create(const ANode: IFunctionCallNode);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TMacroExpansionNodeHandler = class(TBaseNodeHandler<IMacroExpansionNode>)
private
FExpandedBodyNode: TAstViewNode;
public
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TRecurNodeHandler = class(TBaseNodeHandler<IRecurNode>)
private
FArgumentsNode: TAstViewNode;
public
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TMacroDefinitionNodeHandler = class(TBaseNodeHandler<IMacroDefinitionNode>)
private
FParamsNode: TAstViewNode;
FBodyNode: TAstViewNode;
public
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
implementation
constructor TBlockExpressionNodeHandler.Create(const ANode: IBlockExpressionNode);
begin
inherited Create(ANode);
FExpressions := TList<TAstViewNode>.Create();
end;
destructor TBlockExpressionNodeHandler.Destroy;
begin
FExpressions.Free;
inherited Destroy;
end;
{ TBlockExpressionNodeHandler }
procedure TBlockExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
titleLabel: TTextNode;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
OwnerNode.Frameless := False;
OwnerNode.Orientation := loVertical;
OwnerNode.Alignment := laFlush;
titleLabel := OwnerNode.AddLabel(OwnerNode, 'do');
titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
// OwnerNode.Frameless := True;
// visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
var elements := FNode.AsBlockExpression.Expressions.Elements;
for var i := 0 to High(elements) do
begin
// Standard spacing between elements
if i > 0 then
OwnerNode.AddLabel(OwnerNode, ' ');
var childView := visu.CallAccept(elements[i]);
FExpressions.Add(childView);
end;
// if Length(elements) = 0 then
// begin
// OwnerNode.AddLabel(OwnerNode, ' '); // spacer
// end;
end;
function TBlockExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newElements: TArray<IAstNode>;
i: Integer;
begin
SetLength(newElements, FExpressions.Count);
for i := 0 to FExpressions.Count - 1 do
newElements[i] := FExpressions[i].CreateAst;
var tpl := TAst.Tuple(FNode.Expressions.Identity, newElements, FNode.Expressions.StaticType);
Result := TAst.Block(FNode.Identity, tpl, FNode.StaticType);
end;
{ TIfExpressionNodeHandler }
procedure TIfExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
begin
OwnerNode.Frameless := False;
OwnerNode.Orientation := loVertical;
OwnerNode.Alignment := laFlush;
FConditionNode := OwnerNode.AddExpr(OwnerNode, 'if', FNode.Condition);
FThenNode := OwnerNode.AddExpr(OwnerNode, 'then', FNode.ThenBranch);
if Assigned(FNode.ElseBranch) then
FElseNode := OwnerNode.AddExpr(OwnerNode, 'else', FNode.ElseBranch);
end;
function TIfExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
elseAst: IAstNode;
begin
if Assigned(FElseNode) then
elseAst := FElseNode.CreateAst
else
elseAst := nil;
Result := TAst.IfExpr(FNode.Identity, FConditionNode.CreateAst, FThenNode.CreateAst, elseAst, FNode.StaticType);
end;
{ TCondExpressionNodeHandler }
constructor TCondExpressionNodeHandler.Create(const ANode: ICondExpressionNode);
begin
inherited Create(ANode);
FBranchNodes := TList<TBranchView>.Create;
end;
destructor TCondExpressionNodeHandler.Destroy;
begin
FBranchNodes.Free;
inherited;
end;
procedure TCondExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
pair: TCondPair;
i: Integer;
row: TAutoFitLayout;
bv: TBranchView;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
OwnerNode.Frameless := False;
OwnerNode.Orientation := loVertical;
OwnerNode.Alignment := laFlush;
var title := OwnerNode.AddLabel(OwnerNode, '?');
title.FontSettings.Font.Style := [TFontStyle.fsBold];
for i := 0 to High(FNode.Pairs) do
begin
pair := FNode.Pairs[i];
row := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
row.Margins := TMarginRect.Create(0, 2, 0, 2);
bv.Condition := visu.CallAccept(pair.Condition);
row.AddChild(bv.Condition);
OwnerNode.AddLabel(row, '->');
bv.Branch := visu.CallAccept(pair.Branch);
row.AddChild(bv.Branch);
FBranchNodes.Add(bv);
end;
// Else Branch
var elseRow := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
elseRow.Margins := TMarginRect.Create(0, 4, 0, 0); // Extra spacing
OwnerNode.AddLabel(elseRow, 'else ->');
FElseNode := visu.CallAccept(FNode.ElseBranch);
// FIX: Use AddChild instead of assigning Parent directly
elseRow.AddChild(FElseNode);
end;
function TCondExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newPairs: TArray<TCondPair>;
i: Integer;
begin
SetLength(newPairs, FBranchNodes.Count);
for i := 0 to FBranchNodes.Count - 1 do
begin
newPairs[i] := TCondPair.Create(FBranchNodes[i].Condition.CreateAst, FBranchNodes[i].Branch.CreateAst);
end;
Result := TAst.CondExpr(FNode.Identity, newPairs, FElseNode.CreateAst, FNode.StaticType);
end;
{ TLambdaExpressionNodeHandler }
procedure TLambdaExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
titleContainer: TAutoFitLayout;
lbl: TTextNode;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, 0);
OwnerNode.Frameless := False;
OwnerNode.Orientation := loVertical;
OwnerNode.Alignment := laFlush;
OwnerNode.BackgroundColor := $090000ff;
titleContainer := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
lbl := OwnerNode.AddLabel(titleContainer, 'fn');
lbl.FontSettings.Font.Style := [TFontStyle.fsBold];
// Parameters: Delegate to List Handler
var paramVisu := visu.Clone(titleContainer, 0);
FParamsNode := paramVisu.CallAccept(FNode.Parameters);
// Body
FBodyNode := visu.CallAccept(FNode.Body);
FBodyNode.Frameless := False;
end;
function TLambdaExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result :=
TAst.LambdaExpr(
FNode.Identity,
FParamsNode.CreateAst.AsTuple,
FBodyNode.CreateAst,
FNode.Layout,
FNode.Descriptor,
FNode.Upvalues,
FNode.HasNestedLambdas,
FNode.IsPure,
FNode.StaticType
);
end;
{ TFunctionCallNodeHandler }
procedure TFunctionCallNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
callLabel: TTextNode;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal;
callLabel := OwnerNode.AddLabel(OwnerNode, 'call ');
callLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
FCalleeNode := visu.CallAccept(FNode.Callee);
FArgumentsNode := visu.CallAccept(FNode.Arguments);
end;
function TFunctionCallNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result :=
TAst.FunctionCall(
FNode.Identity,
FCalleeNode.CreateAst,
FArgumentsNode.CreateAst.AsTuple,
FNode.StaticType,
FNode.IsTailCall,
FNode.StaticTarget,
FNode.IsTargetPure
);
end;
{ TOperatorCallHandler }
constructor TOperatorCallHandler.Create(const ANode: IFunctionCallNode);
begin
inherited Create(ANode);
FArgumentNodes := TList<TAstViewNode>.Create;
if ANode.Callee.Kind = akIdentifier then
FCalleeName := ANode.Callee.AsIdentifier.Name
else if ANode.Callee.Kind = akKeyword then
FCalleeName := ANode.Callee.AsKeyword.Value.Name
else
FCalleeName := '?';
end;
destructor TOperatorCallHandler.Destroy;
begin
FArgumentNodes.Free;
inherited;
end;
procedure TOperatorCallHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
i: Integer;
argView: TAstViewNode;
opLabel: TTextNode;
argsElements: TArray<IAstNode>;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal;
OwnerNode.Alignment := laCenter;
// Optimization: Access Elements directly
argsElements := FNode.Arguments.Elements;
var argCount := Length(argsElements);
if argCount = 1 then
begin
opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName);
opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
opLabel.Color := TAlphaColors.Darkorange;
opLabel.Margins := TMarginRect.Create(2, 0, 2, 0);
argView := visu.CallAccept(argsElements[0]);
FArgumentNodes.Add(argView);
end
else
begin
for i := 0 to argCount - 1 do
begin
if i > 0 then
begin
opLabel := OwnerNode.AddLabel(OwnerNode, ' ' + FCalleeName + ' ');
opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
opLabel.Color := TAlphaColors.Darkorange;
// Compact spacing for operators
opLabel.Margins := TMarginRect.Create(0, 0, 0, 0);
end;
argView := visu.CallAccept(argsElements[i]);
FArgumentNodes.Add(argView);
end;
end;
if argCount = 0 then
begin
opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName);
opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
end;
end;
function TOperatorCallHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newArgs: TArray<IAstNode>;
i: Integer;
begin
SetLength(newArgs, FArgumentNodes.Count);
for i := 0 to FArgumentNodes.Count - 1 do
newArgs[i] := FArgumentNodes[i].CreateAst;
Result :=
TAst.FunctionCall(
FNode.Identity,
FNode.Callee,
TAst.Tuple(FNode.Arguments.Identity, newArgs),
FNode.StaticType,
FNode.IsTailCall,
FNode.StaticTarget,
FNode.IsTargetPure
);
end;
{ TMacroExpansionNodeHandler }
procedure TMacroExpansionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
titleLabel: TTextNode;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
OwnerNode.Frameless := False;
OwnerNode.Orientation := loVertical;
titleLabel := OwnerNode.AddLabel(OwnerNode, 'Macro Expansion');
titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
FExpandedBodyNode := visu.CallAccept(FNode.ExpandedBody);
end;
function TMacroExpansionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result := TAst.MacroExpansionNode(FNode.Identity, FNode.CallNode, FExpandedBodyNode.CreateAst.AsTypedNode);
end;
{ TRecurNodeHandler }
procedure TRecurNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
titleLabel: TTextNode;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal;
titleLabel := OwnerNode.AddLabel(OwnerNode, 'recur');
titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
FArgumentsNode := visu.CallAccept(FNode.Arguments);
end;
function TRecurNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result := TAst.Recur(FNode.Identity, FArgumentsNode.CreateAst.AsTuple, FNode.StaticType);
end;
{ TMacroDefinitionNodeHandler }
procedure TMacroDefinitionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
titleLabel: TTextNode;
titleContainer: TAutoFitLayout;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
OwnerNode.Frameless := False;
OwnerNode.Orientation := loVertical;
titleContainer := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
titleLabel := OwnerNode.AddLabel(titleContainer, 'Macro Def: ' + FNode.Name.Name);
titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
// Parameters List
var paramVisu := visu.Clone(titleContainer, visu.ExprDepth);
FParamsNode := paramVisu.CallAccept(FNode.Parameters);
FBodyNode := visu.CallAccept(FNode.Body);
end;
function TMacroDefinitionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
Result := TAst.MacroDef(FNode.Identity, FNode.Name, FParamsNode.CreateAst.AsTuple, FBodyNode.CreateAst);
end;
end.