From f03d250d2b09432114c0007479b24e9ca6132225 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sat, 6 Dec 2025 19:48:15 +0100 Subject: [PATCH] Operator-Visualization in Ast editor --- Src/AST/Myc.Fmx.AstEditor.Handlers.pas | 100 +++++++++++++++++++++++ Src/AST/Myc.Fmx.AstEditor.Visualizer.pas | 47 ++++++++++- 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/Src/AST/Myc.Fmx.AstEditor.Handlers.pas b/Src/AST/Myc.Fmx.AstEditor.Handlers.pas index 049c33a..bee0e69 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Handlers.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Handlers.pas @@ -154,6 +154,18 @@ type 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) + private + FArgumentNodes: TList; + 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) private FExpandedBodyNode: TAstViewNode; @@ -726,6 +738,94 @@ begin ); end; +{ TOperatorCallHandler } + +constructor TOperatorCallHandler.Create(const ANode: IFunctionCallNode); +begin + inherited Create(ANode); + FArgumentNodes := TList.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; +begin + visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1); + + OwnerNode.Frameless := True; + OwnerNode.Orientation := loHorizontal; + OwnerNode.Alignment := laCenter; + + if FNode.Arguments.Count = 1 then + begin + opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName); + opLabel.FontSettings.Font.Style := [TFontStyle.fsBold]; + opLabel.Color := TAlphaColors.Darkorange; + + argView := visu.CallAccept(FNode.Arguments[0]); + FArgumentNodes.Add(argView); + end + else + begin + for i := 0 to FNode.Arguments.Count - 1 do + begin + if i > 0 then + begin + opLabel := OwnerNode.AddLabel(OwnerNode, ' ' + FCalleeName + ' '); + opLabel.FontSettings.Font.Style := [TFontStyle.fsBold]; + opLabel.Color := TAlphaColors.Darkorange; + opLabel.Margins := TMarginRect.Create(2, 2, 2, 2); + end; + + argView := visu.CallAccept(FNode.Arguments[i]); + FArgumentNodes.Add(argView); + end; + end; + + if FNode.Arguments.Count = 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; + 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, + TArgumentList.Create(newArgs, FNode.Arguments.Identity), + FNode.StaticType, + FNode.IsTailCall, + FNode.StaticTarget, + FNode.IsTargetPure + ); +end; + { TMacroExpansionNodeHandler } procedure TMacroExpansionNodeHandler.BuildUI(OwnerNode: TAstViewNode); diff --git a/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas b/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas index 78589be..12dcbd6 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas @@ -87,6 +87,31 @@ type implementation +// Helper to identify standard operators +function IsStandardOperator(const Name: string): Boolean; +begin + // You can extend this list based on your RTL + Result := + (Name = '+') + or (Name = '-') + or (Name = '*') + or (Name = '/') + or (Name = 'div') + or (Name = 'mod') + or (Name = '=') + or (Name = '<>') + or (Name = '<') + or (Name = '>') + or (Name = '<=') + or (Name = '>=') + or (Name = 'and') + or (Name = 'or') + or (Name = 'xor') + or (Name = 'not') + or (Name = 'shl') + or (Name = 'shr'); +end; + { TAstVisualizer } constructor TAstVisualizer.Create(AWorkspace: TWorkspace; AParentNode: TVisualNode; AExprDepth: Integer); @@ -217,8 +242,28 @@ begin end; function TAstVisualizer.VisitFunctionCall(const Node: IFunctionCallNode): TAstViewNode; +var + calleeName: string; + isOp: Boolean; begin - Result := TAstViewNode.Create(Self, TFunctionCallNodeHandler.Create(Node)); + isOp := False; + + // Check if callee is a simple identifier or keyword that matches an operator + if Node.Callee.Kind = akIdentifier then + begin + calleeName := Node.Callee.AsIdentifier.Name; + isOp := IsStandardOperator(calleeName); + end + else if Node.Callee.Kind = akKeyword then + begin + calleeName := Node.Callee.AsKeyword.Value.Name; + isOp := IsStandardOperator(calleeName); + end; + + if isOp then + Result := TAstViewNode.Create(Self, TOperatorCallHandler.Create(Node)) + else + Result := TAstViewNode.Create(Self, TFunctionCallNodeHandler.Create(Node)); end; function TAstVisualizer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TAstViewNode;