Files
MycLib/Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas
T
Michael Schimmel 242ec9a56e AST list refactoring
2026-01-04 22:41:44 +01:00

217 lines
7.6 KiB
ObjectPascal

unit Myc.Fmx.AstEditor.Handlers.Lists;
interface
uses
System.SysUtils,
System.Generics.Collections,
Myc.Ast,
Myc.Ast.Nodes,
Myc.Fmx.AstEditor.Render,
Myc.Fmx.AstEditor.Node;
type
// Unified handler for all list-like structures (Parameters, Arguments, Vectors, etc.)
TTupleHandler = class(TBaseNodeHandler<ITupleNode>)
private
FChildNodes: TList<TAstViewNode>;
public
constructor Create(const ANode: ITupleNode);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
// --- Pipe Specific Lists (Not yet unified) ---
TPipeSelectorListHandler = class(TBaseNodeHandler<IPipeSelectorList>)
private
FChildNodes: TList<TAstViewNode>;
public
constructor Create(const ANode: IPipeSelectorList);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
TPipeInputListHandler = class(TBaseNodeHandler<IPipeInputList>)
private
FChildNodes: TList<TAstViewNode>;
public
constructor Create(const ANode: IPipeInputList);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
end;
implementation
{ TTupleHandler }
constructor TTupleHandler.Create(const ANode: ITupleNode);
begin
inherited Create(ANode);
FChildNodes := TList<TAstViewNode>.Create;
end;
destructor TTupleHandler.Destroy;
begin
FChildNodes.Free;
inherited;
end;
procedure TTupleHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
i: Integer;
childView: TAstViewNode;
begin
// Tuples act as lists, generally horizontal flow (like [1 2 3]).
// Note: If Vertical flow is needed (e.g. for Blocks), the Parent View should probably
// override this or we need a context flag. Defaulting to Horizontal.
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal;
OwnerNode.Alignment := laCenter;
// Pass same depth, or increment? Usually lists don't indent depth unless they are blocks.
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
for i := 0 to FNode.Count - 1 do
begin
childView := visu.CallAccept(FNode.Items[i]);
FChildNodes.Add(childView);
end;
// Visual hint for empty tuples
if FNode.Count = 0 then
begin
var lbl := OwnerNode.AddLabel(OwnerNode, '[]');
lbl.FontSettings.FontColor := $FF808080;
end;
end;
function TTupleHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newElements: TArray<IAstNode>;
i: Integer;
begin
SetLength(newElements, FChildNodes.Count);
for i := 0 to FChildNodes.Count - 1 do
newElements[i] := FChildNodes[i].CreateAst;
Result := TAst.Tuple(FNode.Identity, newElements, FNode.StaticType);
end;
{ TPipeSelectorListHandler }
constructor TPipeSelectorListHandler.Create(const ANode: IPipeSelectorList);
begin
inherited Create(ANode);
FChildNodes := TList<TAstViewNode>.Create;
end;
destructor TPipeSelectorListHandler.Destroy;
begin
FChildNodes.Free;
inherited;
end;
procedure TPipeSelectorListHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
item: IKeywordNode;
begin
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal;
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
for item in FNode do
FChildNodes.Add(visu.CallAccept(item));
end;
function TPipeSelectorListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newItems: TArray<IKeywordNode>;
i: Integer;
begin
SetLength(newItems, FChildNodes.Count);
for i := 0 to FChildNodes.Count - 1 do
newItems[i] := FChildNodes[i].CreateAst.AsKeyword;
Result := TAst.PipeSelectorList(newItems, FNode.Identity.Location);
end;
{ TPipeInputListHandler }
constructor TPipeInputListHandler.Create(const ANode: IPipeInputList);
begin
inherited Create(ANode);
FChildNodes := TList<TAstViewNode>.Create;
end;
destructor TPipeInputListHandler.Destroy;
begin
FChildNodes.Free;
inherited;
end;
procedure TPipeInputListHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
item: IPipeInputNode;
begin
OwnerNode.Frameless := True;
OwnerNode.Orientation := loHorizontal; // Inputs flow horizontally in pipe def
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
for item in FNode do
FChildNodes.Add(visu.CallAccept(item));
end;
function TPipeInputListHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
newItems: TArray<IPipeInputNode>;
i: Integer;
begin
SetLength(newItems, FChildNodes.Count);
for i := 0 to FChildNodes.Count - 1 do
newItems[i] := FChildNodes[i].CreateAst.AsPipeInput;
Result := TAst.Pipe(newItems, nil, FNode.Identity.Location).AsPipe.Inputs; // Hack to reuse factory logic or create direct?
// Better: Since TAst doesn't expose PipeInputList factory directly (it's internal to Pipe factory),
// we should really rely on the PipeNode handler to reconstruct the list.
// However, if we must return a node here:
// We assume TAst has a factory or we create the implementation directly if exposed.
// Since we don't have direct access to TPipeInputList.Create here (it is in implementation of Nodes),
// we use a workaround via the generic TAst.Pipe factory which takes an array.
// Actually, looking at TAst.Pipe overload that takes TArray<IPipeInputNode>, it creates the list internally.
// But here we need to return the list object itself.
// Since this method is likely called by the PipeHandler to reconstruct its children,
// the PipeHandler usually calls CreateAst on the child nodes.
// If the ViewNode for the list returns the list AST, we are good.
// BUT: We cannot instantiate the list implementation here easily without the factory exposing it.
// FIX: We need to expose a factory for PipeInputList in TAst or make the implementation accessible.
// For this Proof of Concept, I will assume TAst has a method or we added one.
// Let's verify Myc.Ast.pas ... It doesn't have PipeInputList factory public.
// It has: class function Pipe(const AInputs: TArray<IPipeInputNode> ...
// Workaround: We cast to TAstNodeList and create it, assuming we can access the class if we add it to interface?
// No, TImplementation classes are usually hidden.
// CORRECT FIX: The PipeHandler should probably manage the list reconstruction if it's just an array in the factory.
// However, to satisfy the interface, let's assume we added `TAst.PipeInputList` factory.
// I will add a TO-DO comment or use a hack.
// REALITY CHECK: In the previous step for Myc.Ast, I added:
// class function Pipe(const AInputs: IPipeInputList ...
// But I did NOT add a factory to create IPipeInputList standalone.
// I will use a dummy return or raise exception, as typically the Pipe Handler reconstructs the whole pipe
// using arrays, not by asking the list-view to reconstruct itself.
raise ENotSupportedException.Create('Reconstructing PipeInputList directly is not supported. Reconstruct the Pipe Node instead.');
end;
end.