400 lines
13 KiB
ObjectPascal
400 lines
13 KiB
ObjectPascal
unit Myc.Fmx.AstEditor.Visualizer;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Classes,
|
|
FMX.Controls,
|
|
Myc.Data.Value,
|
|
Myc.Ast.Nodes,
|
|
Myc.Ast.Visitor,
|
|
Myc.Fmx.AstEditor.Render, // Virtual Engine
|
|
Myc.Fmx.AstEditor.Node, // View Nodes
|
|
Myc.Fmx.AstEditor.Workspace; // Host
|
|
|
|
type
|
|
// Implementation of the Factory/Visitor for the Virtual Tree
|
|
// Uses TAstVisitor<TAstViewNode> which now uses closures for dispatch.
|
|
TAstVisualizer = class(TAstVisitor<TAstViewNode>, IAstVisualizer)
|
|
private
|
|
FExprDepth: Integer;
|
|
FParentNode: TVisualNode;
|
|
FWorkspace: TWorkspace;
|
|
|
|
function CallAccept(const Node: IAstNode): TAstViewNode;
|
|
|
|
// IAstVisualizer Implementation
|
|
function GetExprDepth: Integer;
|
|
function GetParentNode: TVisualNode;
|
|
function GetWorkspace: TWorkspace;
|
|
|
|
protected
|
|
// Setup registry using closures
|
|
procedure SetupHandlers; override;
|
|
|
|
// --- Visitor Implementations (Strict IAstNode signature) ---
|
|
function VisitConstant(const Node: IAstNode): TAstViewNode;
|
|
function VisitIdentifier(const Node: IAstNode): TAstViewNode;
|
|
function VisitKeyword(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Unified List Handler
|
|
function VisitTuple(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Elements
|
|
function VisitRecordField(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Control Flow
|
|
function VisitIfExpression(const Node: IAstNode): TAstViewNode;
|
|
function VisitCondExpression(const Node: IAstNode): TAstViewNode;
|
|
function VisitBlockExpression(const Node: IAstNode): TAstViewNode;
|
|
function VisitRecurNode(const Node: IAstNode): TAstViewNode;
|
|
function VisitNop(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Functions
|
|
function VisitLambdaExpression(const Node: IAstNode): TAstViewNode;
|
|
function VisitFunctionCall(const Node: IAstNode): TAstViewNode;
|
|
function VisitMacroExpansionNode(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Declarations
|
|
function VisitVariableDeclaration(const Node: IAstNode): TAstViewNode;
|
|
function VisitAssignment(const Node: IAstNode): TAstViewNode;
|
|
function VisitMacroDefinition(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Metaprogramming
|
|
function VisitQuasiquote(const Node: IAstNode): TAstViewNode;
|
|
function VisitUnquote(const Node: IAstNode): TAstViewNode;
|
|
function VisitUnquoteSplicing(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Data Access
|
|
function VisitIndexer(const Node: IAstNode): TAstViewNode;
|
|
function VisitMemberAccess(const Node: IAstNode): TAstViewNode;
|
|
function VisitRecordLiteral(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Series
|
|
function VisitCreateSeries(const Node: IAstNode): TAstViewNode;
|
|
function VisitAddSeriesItem(const Node: IAstNode): TAstViewNode;
|
|
function VisitSeriesLength(const Node: IAstNode): TAstViewNode;
|
|
|
|
// Pipes
|
|
function VisitPipeInput(const Node: IAstNode): TAstViewNode;
|
|
function VisitPipeSelectorList(const Node: IAstNode): TAstViewNode;
|
|
function VisitPipeInputList(const Node: IAstNode): TAstViewNode;
|
|
function VisitPipe(const Node: IAstNode): TAstViewNode;
|
|
|
|
public
|
|
constructor Create(AWorkspace: TWorkspace; AParentNode: TVisualNode; AExprDepth: Integer);
|
|
destructor Destroy; override;
|
|
|
|
function Clone(ParentNode: TVisualNode; ExprDepth: Integer): IAstVisualizer;
|
|
|
|
property ExprDepth: Integer read GetExprDepth;
|
|
property ParentNode: TVisualNode read GetParentNode;
|
|
property Workspace: TWorkspace read GetWorkspace;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.StrUtils,
|
|
Myc.Fmx.AstEditor.Handlers,
|
|
Myc.Fmx.AstEditor.Handlers.Data,
|
|
Myc.Fmx.AstEditor.Handlers.Control,
|
|
Myc.Fmx.AstEditor.Handlers.Primitives,
|
|
Myc.Fmx.AstEditor.Handlers.Lists;
|
|
|
|
function IsStandardOperator(const Name: string): Boolean;
|
|
begin
|
|
Result := MatchStr(Name, ['+', '-', '*', '/', 'div', 'mod', '=', '<>', '<', '>', '<=', '>=', 'and', 'or', 'xor', 'not', 'shl', 'shr']);
|
|
end;
|
|
|
|
{ TAstVisualizer }
|
|
|
|
constructor TAstVisualizer.Create(AWorkspace: TWorkspace; AParentNode: TVisualNode; AExprDepth: Integer);
|
|
begin
|
|
inherited Create; // Calls SetupHandlers
|
|
FWorkspace := AWorkspace;
|
|
FParentNode := AParentNode;
|
|
FExprDepth := AExprDepth;
|
|
end;
|
|
|
|
destructor TAstVisualizer.Destroy;
|
|
begin
|
|
inherited;
|
|
end;
|
|
|
|
procedure TAstVisualizer.SetupHandlers;
|
|
begin
|
|
// Core Primitives
|
|
Register(akConstant, VisitConstant);
|
|
Register(akIdentifier, VisitIdentifier);
|
|
Register(akKeyword, VisitKeyword);
|
|
Register(akNop, VisitNop);
|
|
|
|
// Lists (Unified)
|
|
Register(akTuple, VisitTuple);
|
|
Register(akRecordField, VisitRecordField);
|
|
|
|
// Control Flow
|
|
Register(akIfExpression, VisitIfExpression);
|
|
Register(akCondExpression, VisitCondExpression);
|
|
Register(akBlockExpression, VisitBlockExpression);
|
|
Register(akRecur, VisitRecurNode);
|
|
|
|
// Functions
|
|
Register(akLambdaExpression, VisitLambdaExpression);
|
|
Register(akFunctionCall, VisitFunctionCall);
|
|
Register(akMacroExpansion, VisitMacroExpansionNode);
|
|
|
|
// Declarations
|
|
Register(akVariableDeclaration, VisitVariableDeclaration);
|
|
Register(akAssignment, VisitAssignment);
|
|
Register(akMacroDefinition, VisitMacroDefinition);
|
|
|
|
// Metaprogramming
|
|
Register(akQuasiquote, VisitQuasiquote);
|
|
Register(akUnquote, VisitUnquote);
|
|
Register(akUnquoteSplicing, VisitUnquoteSplicing);
|
|
|
|
// Data Access
|
|
Register(akIndexer, VisitIndexer);
|
|
Register(akMemberAccess, VisitMemberAccess);
|
|
Register(akRecordLiteral, VisitRecordLiteral);
|
|
|
|
// Series
|
|
Register(akCreateSeries, VisitCreateSeries);
|
|
Register(akAddSeriesItem, VisitAddSeriesItem);
|
|
Register(akSeriesLength, VisitSeriesLength);
|
|
|
|
// Pipes
|
|
Register(akPipeInput, VisitPipeInput);
|
|
Register(akPipeSelectorList, VisitPipeSelectorList);
|
|
Register(akPipeInputList, VisitPipeInputList);
|
|
Register(akPipe, VisitPipe);
|
|
end;
|
|
|
|
function TAstVisualizer.CallAccept(const Node: IAstNode): TAstViewNode;
|
|
var
|
|
dataValue: TDataValue;
|
|
begin
|
|
if not Assigned(Node) then
|
|
exit(nil);
|
|
|
|
dataValue := Node.Accept(Self);
|
|
|
|
if dataValue.Kind = vkGeneric then
|
|
Result := dataValue.AsGeneric<TAstViewNode>
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TAstVisualizer.Clone(ParentNode: TVisualNode; ExprDepth: Integer): IAstVisualizer;
|
|
begin
|
|
Result := TAstVisualizer.Create(FWorkspace, ParentNode, ExprDepth);
|
|
end;
|
|
|
|
function TAstVisualizer.GetExprDepth: Integer;
|
|
begin
|
|
Result := FExprDepth;
|
|
end;
|
|
|
|
function TAstVisualizer.GetParentNode: TVisualNode;
|
|
begin
|
|
Result := FParentNode;
|
|
end;
|
|
|
|
function TAstVisualizer.GetWorkspace: TWorkspace;
|
|
begin
|
|
Result := FWorkspace;
|
|
end;
|
|
|
|
// --- Visitor Implementations (Cast-heavy for efficiency) ---
|
|
|
|
function TAstVisualizer.VisitConstant(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TConstantNodeHandler.Create(Node.AsConstant));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitIdentifier(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TIdentifierNodeHandler.Create(Node.AsIdentifier));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitKeyword(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TKeywordNodeHandler.Create(Node.AsKeyword));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitNop(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
|
end;
|
|
|
|
// Lists
|
|
|
|
function TAstVisualizer.VisitTuple(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TTupleHandler.Create(Node.AsTuple));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitRecordField(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TRecordFieldHandler.Create(Node.AsRecordField));
|
|
end;
|
|
|
|
// Control Flow
|
|
|
|
function TAstVisualizer.VisitBlockExpression(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TBlockExpressionNodeHandler.Create(Node.AsBlockExpression));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitIfExpression(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TIfExpressionNodeHandler.Create(Node.AsIfExpression));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitCondExpression(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TCondExpressionNodeHandler.Create(Node.AsCondExpression));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitRecurNode(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TRecurNodeHandler.Create(Node.AsRecur));
|
|
end;
|
|
|
|
// Functions
|
|
|
|
function TAstVisualizer.VisitLambdaExpression(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TLambdaExpressionNodeHandler.Create(Node.AsLambdaExpression));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitFunctionCall(const Node: IAstNode): TAstViewNode;
|
|
var
|
|
C: IFunctionCallNode;
|
|
calleeName: string;
|
|
isOp: Boolean;
|
|
begin
|
|
C := Node.AsFunctionCall;
|
|
isOp := False;
|
|
|
|
if C.Callee.Kind = akIdentifier then
|
|
begin
|
|
calleeName := C.Callee.AsIdentifier.Name;
|
|
isOp := IsStandardOperator(calleeName);
|
|
end
|
|
else if C.Callee.Kind = akKeyword then
|
|
begin
|
|
calleeName := C.Callee.AsKeyword.Value.Name;
|
|
isOp := IsStandardOperator(calleeName);
|
|
end;
|
|
|
|
if isOp then
|
|
Result := TAstViewNode.Create(Self, TOperatorCallHandler.Create(C))
|
|
else
|
|
Result := TAstViewNode.Create(Self, TFunctionCallNodeHandler.Create(C));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitMacroExpansionNode(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TMacroExpansionNodeHandler.Create(Node.AsMacroExpansion));
|
|
end;
|
|
|
|
// Declarations
|
|
|
|
function TAstVisualizer.VisitVariableDeclaration(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TVariableDeclarationNodeHandler.Create(Node.AsVariableDeclaration));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitAssignment(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TAssignmentNodeHandler.Create(Node.AsAssignment));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitMacroDefinition(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TMacroDefinitionNodeHandler.Create(Node.AsMacroDefinition));
|
|
end;
|
|
|
|
// Metaprogramming
|
|
|
|
function TAstVisualizer.VisitQuasiquote(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TQuasiquoteNodeHandler.Create(Node.AsQuasiquote));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitUnquote(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TUnquoteNodeHandler.Create(Node.AsUnquote));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitUnquoteSplicing(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TUnquoteSplicingNodeHandler.Create(Node.AsUnquoteSplicing));
|
|
end;
|
|
|
|
// Data Access
|
|
|
|
function TAstVisualizer.VisitIndexer(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TIndexerNodeHandler.Create(Node.AsIndexer));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitMemberAccess(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TMemberAccessNodeHandler.Create(Node.AsMemberAccess));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitRecordLiteral(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TRecordLiteralNodeHandler.Create(Node.AsRecordLiteral));
|
|
end;
|
|
|
|
// Series
|
|
|
|
function TAstVisualizer.VisitCreateSeries(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TCreateSeriesNodeHandler.Create(Node.AsCreateSeries));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitAddSeriesItem(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TAddSeriesItemNodeHandler.Create(Node.AsAddSeriesItem));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitSeriesLength(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TSeriesLengthNodeHandler.Create(Node.AsSeriesLength));
|
|
end;
|
|
|
|
// Pipes
|
|
|
|
function TAstVisualizer.VisitPipeInput(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
// Needs a dedicated handler for better visualization, fallback to Nop for now?
|
|
// Actually, we don't have a specific handler for PipeInputNode in the library yet.
|
|
// It's a typed node with children, so we should probably create one or reuse generic.
|
|
// For this refactoring step, I'll use Nop to keep it compiling, but this should be improved.
|
|
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitPipeSelectorList(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TPipeSelectorListHandler.Create(Node.AsPipeSelectorList));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitPipeInputList(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TPipeInputListHandler.Create(Node.AsPipeInputList));
|
|
end;
|
|
|
|
function TAstVisualizer.VisitPipe(const Node: IAstNode): TAstViewNode;
|
|
begin
|
|
Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
|
|
end;
|
|
|
|
end.
|