diff --git a/ASTPlayground/ASTPlayground.delphilsp.json b/ASTPlayground/ASTPlayground.delphilsp.json
index 3d3b45f..8662a1a 100644
--- a/ASTPlayground/ASTPlayground.delphilsp.json
+++ b/ASTPlayground/ASTPlayground.delphilsp.json
@@ -1 +1 @@
-{ "settings": { "project": "file:///T%3A/Myc/ASTPlayground/ASTPlayground.dpr", "dllname": "dcc64370.dll", "dccOptions": "-$O- -$W+ --inline:off -$R+ -$Q+ --no-config -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG;;FRAMEWORK_FMX -E.\\Win64\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Bpl\\Win64 -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NU.\\Win64\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Web;Soap; -OT:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -R\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -U\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -V -VN -VR -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\hpp\\Win64 -NO.\\Win64\\Debug -LU" , "projectFiles":[ { "name": "MainForm", "file": "file:///T%3A/Myc/ASTPlayground/MainForm.pas" }, { "name": "Myc.Ast.Evaluator", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Evaluator.pas" }, { "name": "Myc.Ast.Nodes", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Nodes.pas" }, { "name": "Myc.Ast.Scope", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Scope.pas" }, { "name": "Myc.Fmx.AstEditor", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.pas" }, { "name": "Myc.Data.Value", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Data.Value.pas" }, { "name": "Myc.Ast.Debugger", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Debugger.pas" }, { "name": "Myc.Ast.Visitor", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Visitor.pas" }, { "name": "Myc.Ast.Binding", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Binding.pas" }, { "name": "Myc.Ast.RTL", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.pas" }, { "name": "Myc.Ast.Dumper", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Dumper.pas" }, { "name": "Myc.Ast.RTL.Core", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.Core.pas" }, { "name": "Myc.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Text", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Text.pas" }, { "name": "Myc.Utils", "file": "file:///T%3A/Myc/Src/Myc.Utils.pas" }, { "name": "Myc.Ast.Script", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.pas" }, { "name": "Myc.Ast.Types", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Types.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": false, "browsingPaths": [ "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/OCX/Servers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/VCL","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/RTL/SYS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win/winrt","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/ToolsAPI","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/IBX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Internet","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/PROPERTY%20EDITORS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/XML","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Core","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/System","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Protocols","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/fmx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/components","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/engine","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/graph","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ado","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/cloud","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dbx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dsnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/vclctrls","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/connectors","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/proxygen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DataExplorer","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/src","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/tests","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Experts","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/abstraction","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/implementation","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indyimpl","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Property%20Editors/Indy10","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap/wsdlimporter","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Visualizers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XMLReporting","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XPGen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/rest","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/firedac","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/tethering","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnitX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ems","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/net","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/FlatBox2D","file:///C%3A/Users/Public/Documents/Embarcadero/Studio/37.0/Dcp/%25PLATFORM%25","file:///%25TP%25/runtime/vcl","file:///S%3A/sc_famos_src/svn_vcl/cpxedits","file:///S%3A/sc_famos_src/svn_vcl/dzChart","file:///S%3A/sc_famos_src/svn_vcl/eaeditor","file:///S%3A/sc_famos_src/svn_vcl/FamosEvents","file:///S%3A/sc_famos_src/svn_vcl/HTML_Viewer/source","file:///S%3A/sc_famos_src/svn_vcl/Myc_ChartEdit","file:///S%3A/sc_famos_src/svn_vcl/Myc_HemiDrag","file:///S%3A/sc_famos_src/svn_vcl/Myc_SplitControl","file:///S%3A/sc_famos_src/svn_vcl/NiceChart","file:///S%3A/sc_famos_src/svn_vcl/plotter","file:///S%3A/sc_famos_src/svn_vcl/SimpleIPC","file:///S%3A/sc_famos_src/svn_vcl/SynEdit-master/Source","file:///S%3A/sc_famos_src/svn_vcl/ValEdits","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib/win32","file:///S%3A/sc_famos_src/svn_vcl/Virtual-TreeView/Source","file:///S%3A/sc_famos_src/svn_common/caHelpSys","file:///S%3A/sc_famos_src/svn_common/Misc","file:///S%3A/sc_famos_src/svn_common/Myc_VecMath","file:///S%3A/sc_famos_src/svn_common/PascalScript/source","file:///S%3A/sc_famos_src/svn_common/TreeBase","file:///S%3A/sc_famos_src/svn_common/TreeBase/Medien","file:///S%3A/3rdParty/spring4d/Source","file:///S%3A/3rdParty/spring4d/Source/Base","file:///S%3A/3rdParty/spring4d/Source/Base/Collections","file:///S%3A/3rdParty/spring4d/Source/Base/Logging","file:///S%3A/3rdParty/spring4d/Source/Base/Patches","file:///S%3A/3rdParty/spring4d/Source/Core/Container","file:///S%3A/3rdParty/spring4d/Source/Core/Interception","file:///S%3A/3rdParty/spring4d/Source/Core/Logging","file:///S%3A/3rdParty/spring4d/Source/Core/Mocking","file:///S%3A/3rdParty/spring4d/Source/Core/Services","file:///S%3A/3rdParty/spring4d/Source/Extensions/Cryptography","file:///S%3A/3rdParty/spring4d/Source/Extensions/Utils","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCommon%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressGDI%2B%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLibrary/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/XP%20Theme%20Manager/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLayout%20Control/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressPageControl/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressEditors%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDocking%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressBars/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressOfficeCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressMemData/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDataController/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressNavBar/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressSkins%20Library/Sources","file:///T%3A/Myc/Src","file:///T%3A/Myc/Src/Data","file:///T%3A/Myc/Src/AST" ] , "CommonAppData": "file:///C%3A/Users/Brummel/AppData/Roaming/Embarcadero/BDS/37.0/" , "Templates": "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/ObjRepos/" } }
\ No newline at end of file
+{ "settings": { "project": "file:///T%3A/Myc/ASTPlayground/ASTPlayground.dpr", "dllname": "dcc64370.dll", "dccOptions": "-$O- -$W+ --inline:off -$R+ -$Q+ --no-config -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG;;FRAMEWORK_FMX -E.\\Win64\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Bpl\\Win64 -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NU.\\Win64\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Web;Soap; -OT:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -R\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -U\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -V -VN -VR -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\hpp\\Win64 -NO.\\Win64\\Debug -LU" , "projectFiles":[ { "name": "MainForm", "file": "file:///T%3A/Myc/ASTPlayground/MainForm.pas" }, { "name": "Myc.Ast.Evaluator", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Evaluator.pas" }, { "name": "Myc.Ast.Nodes", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Nodes.pas" }, { "name": "Myc.Ast.Scope", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Scope.pas" }, { "name": "Myc.Data.Value", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Data.Value.pas" }, { "name": "Myc.Ast.Debugger", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Debugger.pas" }, { "name": "Myc.Ast.Visitor", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Visitor.pas" }, { "name": "Myc.Ast.Binding", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Binding.pas" }, { "name": "Myc.Ast.RTL", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.pas" }, { "name": "Myc.Ast.Dumper", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Dumper.pas" }, { "name": "Myc.Ast.RTL.Core", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.Core.pas" }, { "name": "Myc.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Text", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Text.pas" }, { "name": "Myc.Utils", "file": "file:///T%3A/Myc/Src/Myc.Utils.pas" }, { "name": "Myc.Ast.Script", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.pas" }, { "name": "Myc.Ast.Types", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Types.pas" }, { "name": "Myc.Fmx.AstVisualizer", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstVisualizer.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": false, "browsingPaths": [ "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/OCX/Servers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/VCL","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/RTL/SYS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win/winrt","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/ToolsAPI","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/IBX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Internet","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/PROPERTY%20EDITORS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/XML","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Core","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/System","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Protocols","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/fmx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/components","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/engine","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/graph","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ado","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/cloud","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dbx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dsnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/vclctrls","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/connectors","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/proxygen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DataExplorer","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/src","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/tests","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Experts","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/abstraction","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/implementation","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indyimpl","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Property%20Editors/Indy10","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap/wsdlimporter","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Visualizers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XMLReporting","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XPGen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/rest","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/firedac","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/tethering","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnitX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ems","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/net","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/FlatBox2D","file:///C%3A/Users/Public/Documents/Embarcadero/Studio/37.0/Dcp/%25PLATFORM%25","file:///%25TP%25/runtime/vcl","file:///S%3A/sc_famos_src/svn_vcl/cpxedits","file:///S%3A/sc_famos_src/svn_vcl/dzChart","file:///S%3A/sc_famos_src/svn_vcl/eaeditor","file:///S%3A/sc_famos_src/svn_vcl/FamosEvents","file:///S%3A/sc_famos_src/svn_vcl/HTML_Viewer/source","file:///S%3A/sc_famos_src/svn_vcl/Myc_ChartEdit","file:///S%3A/sc_famos_src/svn_vcl/Myc_HemiDrag","file:///S%3A/sc_famos_src/svn_vcl/Myc_SplitControl","file:///S%3A/sc_famos_src/svn_vcl/NiceChart","file:///S%3A/sc_famos_src/svn_vcl/plotter","file:///S%3A/sc_famos_src/svn_vcl/SimpleIPC","file:///S%3A/sc_famos_src/svn_vcl/SynEdit-master/Source","file:///S%3A/sc_famos_src/svn_vcl/ValEdits","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib/win32","file:///S%3A/sc_famos_src/svn_vcl/Virtual-TreeView/Source","file:///S%3A/sc_famos_src/svn_common/caHelpSys","file:///S%3A/sc_famos_src/svn_common/Misc","file:///S%3A/sc_famos_src/svn_common/Myc_VecMath","file:///S%3A/sc_famos_src/svn_common/PascalScript/source","file:///S%3A/sc_famos_src/svn_common/TreeBase","file:///S%3A/sc_famos_src/svn_common/TreeBase/Medien","file:///S%3A/3rdParty/spring4d/Source","file:///S%3A/3rdParty/spring4d/Source/Base","file:///S%3A/3rdParty/spring4d/Source/Base/Collections","file:///S%3A/3rdParty/spring4d/Source/Base/Logging","file:///S%3A/3rdParty/spring4d/Source/Base/Patches","file:///S%3A/3rdParty/spring4d/Source/Core/Container","file:///S%3A/3rdParty/spring4d/Source/Core/Interception","file:///S%3A/3rdParty/spring4d/Source/Core/Logging","file:///S%3A/3rdParty/spring4d/Source/Core/Mocking","file:///S%3A/3rdParty/spring4d/Source/Core/Services","file:///S%3A/3rdParty/spring4d/Source/Extensions/Cryptography","file:///S%3A/3rdParty/spring4d/Source/Extensions/Utils","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCommon%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressGDI%2B%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLibrary/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/XP%20Theme%20Manager/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLayout%20Control/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressPageControl/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressEditors%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDocking%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressBars/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressOfficeCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressMemData/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDataController/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressNavBar/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressSkins%20Library/Sources","file:///T%3A/Myc/Src","file:///T%3A/Myc/Src/Data","file:///T%3A/Myc/Src/AST" ] , "CommonAppData": "file:///C%3A/Users/Brummel/AppData/Roaming/Embarcadero/BDS/37.0/" , "Templates": "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/ObjRepos/" } }
\ No newline at end of file
diff --git a/ASTPlayground/ASTPlayground.dpr b/ASTPlayground/ASTPlayground.dpr
index 0a701a5..7341e7d 100644
--- a/ASTPlayground/ASTPlayground.dpr
+++ b/ASTPlayground/ASTPlayground.dpr
@@ -8,7 +8,6 @@ uses
Myc.Ast.Evaluator in '..\Src\AST\Myc.Ast.Evaluator.pas',
Myc.Ast.Nodes in '..\Src\AST\Myc.Ast.Nodes.pas',
Myc.Ast.Scope in '..\Src\AST\Myc.Ast.Scope.pas',
- Myc.Fmx.AstEditor in 'Myc.Fmx.AstEditor.pas',
Myc.Data.Value in 'Myc.Data.Value.pas',
Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas',
Myc.Ast.Visitor in '..\Src\AST\Myc.Ast.Visitor.pas',
@@ -21,7 +20,8 @@ uses
Myc.Fmx.AstEditor.Text in 'Myc.Fmx.AstEditor.Text.pas',
Myc.Utils in '..\Src\Myc.Utils.pas',
Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas',
- Myc.Ast.Types in '..\Src\AST\Myc.Ast.Types.pas';
+ Myc.Ast.Types in '..\Src\AST\Myc.Ast.Types.pas',
+ Myc.Fmx.AstVisualizer in 'Myc.Fmx.AstVisualizer.pas';
{$R *.res}
diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj
index facfe19..0372725 100644
--- a/ASTPlayground/ASTPlayground.dproj
+++ b/ASTPlayground/ASTPlayground.dproj
@@ -138,7 +138,6 @@
-
@@ -152,6 +151,7 @@
+
Base
diff --git a/ASTPlayground/MainForm.fmx b/ASTPlayground/MainForm.fmx
index e6bbcd2..5d3eec7 100644
--- a/ASTPlayground/MainForm.fmx
+++ b/ASTPlayground/MainForm.fmx
@@ -89,96 +89,89 @@ object Form1: TForm1
Text = 'Clear'
OnClick = ClearButtonClick
end
- object FlowOnlyBox: TCheckBox
- Position.X = 24.000000000000000000
- Position.Y = 8.000000000000000000
- TabOrder = 11
- Text = 'Flow only'
- OnChange = FlowOnlyBoxChange
- end
object SeriesTestButton: TButton
Position.X = 24.000000000000000000
Position.Y = 258.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 22.000000000000000000
Size.PlatformDefault = False
- TabOrder = 13
+ TabOrder = 12
Text = 'Series'
OnClick = SeriesTestButtonClick
end
object OHLCButton: TButton
Position.X = 24.000000000000000000
Position.Y = 288.000000000000000000
- TabOrder = 14
+ TabOrder = 13
Text = 'OHLC'
OnClick = OHLCButtonClick
end
object DebugBox: TCheckBox
Position.X = 24.000000000000000000
Position.Y = 439.000000000000000000
- TabOrder = 15
+ TabOrder = 14
Text = 'Debug'
end
object FromJSONButton: TButton
Position.X = 24.000000000000000000
Position.Y = 569.000000000000000000
- TabOrder = 16
+ TabOrder = 15
Text = 'From JSON'
OnClick = FromJSONButtonClick
end
object ToJSONButton: TButton
Position.X = 24.000000000000000000
Position.Y = 599.000000000000000000
- TabOrder = 17
+ TabOrder = 16
Text = 'To JSON'
OnClick = ToJSONButtonClick
end
object ExternalFuncButton: TButton
Position.X = 24.000000000000000000
Position.Y = 318.000000000000000000
- TabOrder = 18
+ TabOrder = 17
Text = 'External Func'
OnClick = ExternalFuncButtonClick
end
object InnerLambdaButton: TButton
Position.X = 24.000000000000000000
Position.Y = 348.000000000000000000
- TabOrder = 19
+ TabOrder = 18
Text = 'Inner Lambda'
OnClick = InnerLambdaButtonClick
end
object DumpButton: TButton
Position.X = 24.000000000000000000
Position.Y = 509.000000000000000000
- TabOrder = 20
+ TabOrder = 19
Text = 'Dump'
OnClick = DumpButtonClick
end
object FailingUpvalueButton: TButton
Position.X = 24.000000000000000000
Position.Y = 378.000000000000000000
- TabOrder = 22
+ TabOrder = 21
Text = 'Upvalue'
OnClick = FailingUpvalueButtonClick
end
object TailCallButten: TButton
Position.X = 24.000000000000000000
Position.Y = 406.000000000000000000
- TabOrder = 23
+ TabOrder = 22
Text = 'Tail calls'
OnClick = TailCallButtenClick
end
object SaveUserLibButton: TButton
Position.X = 24.000000000000000000
Position.Y = 629.000000000000000000
- TabOrder = 24
+ TabOrder = 23
Text = 'Save Lib'
OnClick = SaveUserLibButtonClick
end
object LoadUserLibButton: TButton
Position.X = 24.000000000000000000
Position.Y = 659.000000000000000000
- TabOrder = 25
+ TabOrder = 24
Text = 'LoadLib'
OnClick = LoadUserLibButtonClick
end
@@ -193,7 +186,7 @@ object Form1: TForm1
Size.Width = 113.000000000000000000
Size.Height = 184.000000000000000000
Size.PlatformDefault = False
- TabOrder = 26
+ TabOrder = 25
OnChange = RTLListViewChange
end
end
diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas
index 1fb0d9c..3655b38 100644
--- a/ASTPlayground/MainForm.pas
+++ b/ASTPlayground/MainForm.pas
@@ -36,7 +36,7 @@ uses
FMX.Layouts,
FMX.Objects,
Myc.Ast.Debugger,
- Myc.Fmx.AstEditor,
+ Myc.Fmx.AstVisualizer,
Myc.Fmx.AstEditor.Node,
Myc.Fmx.AstEditor.Workspace,
FMX.DialogService,
@@ -70,7 +70,6 @@ type
DoTrigger2Button: TButton;
Panel2: TPanel;
ClearButton: TButton;
- FlowOnlyBox: TCheckBox;
SeriesTestButton: TButton;
OHLCButton: TButton;
DebugBox: TCheckBox;
@@ -112,6 +111,7 @@ type
procedure LoadUserLibButtonClick(Sender: TObject);
procedure RTLListViewChange(Sender: TObject);
private
+ FCurrUnboundAst: IAstNode;
FCurrAst: IAstNode;
FCurrDesc: IScopeDescriptor;
FGScope: IExecutionScope;
@@ -216,6 +216,7 @@ var
visitor: IEvaluatorVisitor;
begin
// The new, streamlined pipeline: The binder handles macro expansion internally.
+ FCurrUnboundAst := ANode;
// Step 1: Create a binder, passing the evaluator factory for compile-time evaluations.
binder := TAstBinder.Create(AParentScope, CreateEvaluator);
@@ -1284,13 +1285,13 @@ begin
Memo1.Lines.Clear;
try
- // Execute the entire script block when it changes
- var result := ExecuteAst(TAstScript.Parse(ScriptMemo.Lines.Text), FGScope);
- Memo1.Lines.Add(Format('Script executed. Final result: %s', [result.ToString]));
-
- // After execution, the FCurrAst is set by ExecuteAst, so we can visualize it
- FWorkspace.DeleteChildren;
- ShowVizualization(14, 14);
+ try
+ // Execute the entire script block when it changes
+ var result := ExecuteAst(TAstScript.Parse(ScriptMemo.Lines.Text), FGScope);
+ Memo1.Lines.Add(Format('Script executed. Final result: %s', [result.ToString]));
+ finally
+ ShowVizualization(14, 14);
+ end;
except
on E: Exception do
Memo1.Lines.Add(E.Message);
@@ -1307,13 +1308,15 @@ end;
procedure TForm1.ShowVizualization(X, Y: Single);
begin
+ FWorkspace.DeleteChildren;
+
if FCurrAst <> nil then
begin
- var visu := TVisualizationMode.vmDetailed;
- if FlowOnlyBox.IsChecked then
- visu := TVisualizationMode.vmControlFlow;
-
- FWorkspace.BuildTree(FCurrAst, FCurrDesc, TPointF.Create(X, Y), visu);
+ FWorkspace.Build(FCurrAst, TPointF.Create(X, Y));
+ end
+ else if FCurrUnboundAst <> nil then
+ begin
+ FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
end;
end;
diff --git a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas
index d3c7848..bbf7783 100644
--- a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas
+++ b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas
@@ -14,9 +14,10 @@ uses
FMX.Graphics;
type
- // A movable panel with a custom-painted border and title.
+ // A movable panel with a custom-painted border. Content (like title) is added externally.
TAuraNode = class(TStyledControl)
private
+ FBackgroundColor: TAlphaColor;
// Flag to indicate if the control is currently being dragged.
FIsDragging: Boolean;
// Stores the mouse coordinates at the beginning of the drag operation.
@@ -25,20 +26,13 @@ type
FBorderColor: TAlphaColor;
FBorderWidth: Single;
FBorderRadius: Single;
- // Properties for the title
- FTitle: string;
- FTitleFont: TFont;
- FTitleFontColor: TAlphaColor;
// If true, the node is drawn without a border and with a transparent background.
FFrameless: Boolean;
+ procedure SetBackgroundColor(const Value: TAlphaColor);
procedure SetBorderColor(const Value: TAlphaColor);
procedure SetBorderWidth(const Value: Single);
procedure SetBorderRadius(const Value: Single);
- procedure SetTitle(const Value: string);
- procedure SetTitleFont(const Value: TFont);
- procedure SetTitleFontColor(const Value: TAlphaColor);
- procedure TitleFontChanged(Sender: TObject);
procedure SetFrameless(const Value: Boolean);
protected
procedure Paint; override;
@@ -53,11 +47,7 @@ type
property BorderColor: TAlphaColor read FBorderColor write SetBorderColor;
property BorderWidth: Single read FBorderWidth write SetBorderWidth;
property BorderRadius: Single read FBorderRadius write SetBorderRadius;
-
- // Title properties
- property Title: string read FTitle write SetTitle;
- property TitleFont: TFont read FTitleFont write SetTitleFont;
- property TitleFontColor: TAlphaColor read FTitleFontColor write SetTitleFontColor;
+ property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor;
// Behavior properties
property Frameless: Boolean read FFrameless write SetFrameless;
@@ -110,16 +100,8 @@ begin
// Default border settings
FBorderColor := TAlphaColors.Gray;
FBorderWidth := 1;
- FBorderRadius := 9; // Sharp corners by default
-
- // Default title settings
- FTitle := 'Node';
- FTitleFont := TFont.Create;
- FTitleFont.Family := 'Segoe UI';
- FTitleFont.Size := 11;
- FTitleFont.Style := [TFontStyle.fsBold];
- FTitleFont.OnChanged := TitleFontChanged;
- FTitleFontColor := TAlphaColors.Black;
+ FBorderRadius := 9; // Rounded corners by default
+ FBackgroundColor := $080a0a0a;
// Default state for frameless mode
FFrameless := False;
@@ -128,36 +110,51 @@ begin
Height := 45;
// The panel must be able to receive mouse events.
- // HitTest := True;
+ HitTest := True; // Make sure HitTest is True to receive mouse events
// Clip children to the panel's bounds.
ClipChildren := True;
end;
destructor TAuraNode.Destroy;
begin
- FTitleFont.Free;
inherited;
end;
procedure TAuraNode.Paint;
var
- rect, titleRect: TRectF;
+ rect: TRectF;
effectiveBorderWidth: Single;
begin
inherited; // Allow styled painting to occur first (if any)
+ // Clear background before drawing custom border/fill
+ Canvas.Fill.Kind := TBrushKind.None; // Make sure default fill is cleared if needed
+ //Canvas.ClearRect(TRectF.Create(0,0, Width, Height)); // Optional: Explicit clear
+
if FFrameless then
begin
Canvas.Fill.Kind := TBrushKind.Solid;
- Canvas.Fill.Color := $20C0C0C0; // Transparent Silver
+ Canvas.Fill.Color := FBackgroundColor;
Canvas.Stroke.Kind := TBrushKind.None;
-
- // In frameless mode, draw a transparent background and ignore the border.
Canvas.FillRect(TRectF.Create(0, 0, Width, Height), 0, 0, [], 1.0);
+ Canvas.Stroke.Kind := TBrushKind.Solid;
+ Canvas.Stroke.Color := FBorderColor;
+ Canvas.Stroke.Thickness := 1;
+ Canvas.Stroke.Dash := TStrokeDash.Dot;
+ Canvas.DrawRect(TRectF.Create(0, 0, Width, Height), 0, 0, [], 1.0);
effectiveBorderWidth := 0;
end
else
begin
+ // Draw background fill first
+ Canvas.Fill.Kind := TBrushKind.Solid;
+ Canvas.Fill.Color := FBackgroundColor;
+ rect := TRectF.Create(0, 0, Width, Height);
+ // Inflate inwards slightly if border exists to avoid overlap issues
+ if (FBorderWidth > 0) then
+ rect.Inflate(-FBorderWidth / 2, -FBorderWidth / 2);
+ Canvas.FillRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round);
+
// Custom painting for the border
if (FBorderWidth > 0) and (FBorderColor <> TAlphaColors.Null) then
begin
@@ -168,31 +165,15 @@ begin
Canvas.Stroke.Kind := TBrushKind.Solid;
Canvas.Stroke.Color := FBorderColor;
Canvas.Stroke.Thickness := FBorderWidth;
+ Canvas.Stroke.Dash := TStrokeDash.Solid;
+ Canvas.DrawRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round);
- if FFrameless then
- begin
- Canvas.Fill.Kind := TBrushKind.Solid;
- Canvas.Fill.Color := $20C0C0C0; // Transparent Silver
- Canvas.FillRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round);
- end
- else
- Canvas.DrawRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round);
- end;
- effectiveBorderWidth := FBorderWidth;
- end;
-
- // Draw the title
- if not FTitle.IsEmpty then
- begin
- // Define the rectangle for the title at the top of the control
- titleRect := TRectF.Create(0, effectiveBorderWidth, Width, effectiveBorderWidth + FTitleFont.Size * 1.8);
-
- Canvas.Font.Assign(FTitleFont);
- Canvas.Fill.Color := FTitleFontColor;
-
- // Draw the text centered horizontally and vertically within the title rectangle
- Canvas.FillText(titleRect, FTitle, False, 1.0, [], TTextAlign.Center, TTextAlign.Center);
+ effectiveBorderWidth := FBorderWidth;
+ end
+ else
+ effectiveBorderWidth := 0;
end;
+ // Title drawing removed, handled by external labels now.
end;
procedure TAuraNode.SetBorderColor(const Value: TAlphaColor);
@@ -231,45 +212,22 @@ begin
end;
end;
-procedure TAuraNode.SetTitle(const Value: string);
-begin
- if FTitle <> Value then
- begin
- FTitle := Value;
- Repaint;
- end;
-end;
-
-procedure TAuraNode.SetTitleFont(const Value: TFont);
-begin
- FTitleFont.Assign(Value);
-end;
-
-procedure TAuraNode.SetTitleFontColor(const Value: TAlphaColor);
-begin
- if FTitleFontColor <> Value then
- begin
- FTitleFontColor := Value;
- Repaint;
- end;
-end;
-
-procedure TAuraNode.TitleFontChanged(Sender: TObject);
-begin
- Repaint;
-end;
-
procedure TAuraNode.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
inherited;
if Button = TMouseButton.mbLeft then
begin
- if ObjectAtPoint(LocalToScreen(TPointF.Create(X, Y))) = Self as IControl then
+ // Only start dragging if the click is on the node itself, not its children
+ var LControl := ObjectAtPoint(LocalToScreen(TPointF.Create(X, Y)));
+ if Assigned(LControl) and (LControl.GetObject = Self) then
begin
FIsDragging := True;
FDownPos := TPointF.Create(X, Y);
Capture;
- end;
+ BringToFront; // Bring node to front when dragging starts
+ end
+ else
+ FIsDragging := False; // Ensure dragging stops if click is on a child
end;
end;
@@ -286,7 +244,7 @@ begin
Position.Y := Position.Y + deltaY;
if ParentControl <> nil then
- ParentControl.Repaint;
+ ParentControl.Repaint; // Repaint the workspace to update connections
end;
end;
@@ -297,7 +255,15 @@ begin
begin
ReleaseCapture;
FIsDragging := False;
+ if ParentControl <> nil then // Repaint one last time after drag ends
+ ParentControl.Repaint;
end;
end;
+procedure TAuraNode.SetBackgroundColor(const Value: TAlphaColor);
+begin
+ FBackgroundColor := Value;
+ Repaint;
+end;
+
end.
diff --git a/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas b/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas
index 401290d..e9f818d 100644
--- a/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas
+++ b/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas
@@ -18,6 +18,10 @@ uses
Myc.Ast.Scope,
Myc.Ast.Binding;
+const
+ cDataPinColor = TAlphaColors.Dodgerblue;
+ cExecPinColor = TAlphaColors.Lightgreen;
+
type
TPinConnection = record
OutputPin: TControl;
@@ -25,9 +29,6 @@ type
constructor Create(AOutputPin, AInputPin: TControl);
end;
- // Enum to select the visualization style.
- TVisualizationMode = (vmDetailed, vmControlFlow);
-
TAuraWorkspace = class(TStyledControl)
private
FConnections: TArray;
@@ -45,12 +46,7 @@ type
procedure DblClick; override;
public
constructor Create(AOwner: TComponent); override;
- procedure BuildTree(
- const RootNode: IAstNode;
- const RootDescriptor: IScopeDescriptor;
- const Position: TPointF;
- AMode: TVisualizationMode
- );
+ procedure Build(const RootNode: IAstNode; const Position: TPointF);
function GetChildrenMatrix(var Matrix: TMatrix; var Simple: Boolean): Boolean; override;
function Zoom(Factor: Single): Boolean;
@@ -95,7 +91,7 @@ implementation
uses
System.Math,
FMX.Platform,
- Myc.Fmx.AstEditor;
+ Myc.Fmx.AstVisualizer;
{ TPinConnection }
@@ -113,23 +109,9 @@ begin
FZoom := 1.0;
end;
-procedure TAuraWorkspace.BuildTree(
- const RootNode: IAstNode;
- const RootDescriptor: IScopeDescriptor;
- const Position: TPointF;
- AMode: TVisualizationMode
-);
-var
- connections: TList;
+procedure TAuraWorkspace.Build(const RootNode: IAstNode; const Position: TPointF);
begin
- connections := TList.Create;
- try
- // Create the scope descriptor from the execution scope provided by the binder.
- RootNode.Accept(TAstToAuraNodeVisitor.Create(Self, Self, Position, connections, nil, AMode, nil, nil, RootDescriptor));
- FConnections := FConnections + connections.ToArray;
- finally
- connections.Free;
- end;
+ RootNode.Accept(TAstVisualizer.CreateVisitor(Self, Position));
end;
procedure TAuraWorkspace.DoDeleteChildren;
diff --git a/ASTPlayground/Myc.Fmx.AstEditor.pas b/ASTPlayground/Myc.Fmx.AstEditor.pas
deleted file mode 100644
index 67cbec4..0000000
--- a/ASTPlayground/Myc.Fmx.AstEditor.pas
+++ /dev/null
@@ -1,1441 +0,0 @@
-unit Myc.Fmx.AstEditor;
-
-interface
-
-uses
- System.SysUtils,
- System.Classes,
- System.UITypes,
- System.Types,
- System.Math.Vectors,
- System.Generics.Collections,
- System.Generics.Defaults,
- FMX.Types,
- FMX.Controls,
- FMX.Objects,
- FMX.Graphics,
- Myc.Data.Value,
- Myc.Ast.Nodes,
- Myc.Ast.Scope,
- Myc.Ast.Types,
- Myc.Ast.Visitor,
- Myc.Fmx.AstEditor.Node,
- Myc.Fmx.AstEditor.Workspace;
-
-const
- // Pin Visuals
- cPinSize = 10;
- cDataPinColor = TAlphaColors.Dodgerblue;
- cExecPinColor = TAlphaColors.Lightgreen;
-
-type
- TPinShape = (psCircle, psTriangle);
- TPinAlignment = (paLeft, paRight);
-
- TAstToAuraNodeVisitor = class(TAstVisitor)
- public
- type
- TAuraNodeResult = record
- LayoutNode: TAuraNode;
- OutputPin: TControl;
- end;
- TAuraNodeResultList = TList;
- TChildVisitorProc = reference to procedure(const Visitor: IAstVisitor);
- TCreateParentNodeProc = reference to function(const InputNodes: TAuraNodeResultList): TAuraNodeResult;
- TVerticalAlignment = (vaCenter, vaTop);
-
- private
- const
- cHorizontalPadding = 25;
- cPinOffsetY = 18;
- cVerticalPadding = 10;
- cDefaultNodeHeight = 25;
- cIfNodeHeight = 40;
-
- private
- FWorkspace: TAuraWorkspace;
- FParentControl: TControl;
- FCurrentPos: TPointF;
- FSpacing: TPointF;
- FLastResult: TAuraNodeResult;
- FConnections: TList;
- FCurrentExec: TList;
- FMode: TVisualizationMode;
- FSlotCache: TArray;
- FParentVisitor: TAstToAuraNodeVisitor;
- FCurrentLambda: ILambdaExpressionNode;
-
- procedure FinalizeNodeLayout(const Node: TAuraNode);
- function FindNodeForAddress(const Address: TResolvedAddress; out NodeResult: TAuraNodeResult): Boolean;
- function VisitOperatorNode(
- const InputExpressions: TArray;
- const CreateParentProc: TCreateParentNodeProc;
- Alignment: TVerticalAlignment = vaCenter
- ): TAuraNode;
- function BuildNodeControl(const Title, Details: string): TAuraNode;
- function CreateNodeControl(const Title, Details: string): TAuraNode;
- function CreatePin(
- ParentNode: TAuraNode;
- Shape: TPinShape;
- Alignment: TPinAlignment;
- Color: TAlphaColor;
- const Tag: string
- ): TShape;
- function CreateInput(ParentNode: TAuraNode; const Caption: string): TControl;
- function CreateOutput(ParentNode: TAuraNode): TControl;
- function CreateEntry(ParentNode: TAuraNode; connect: Boolean = true): TControl;
- function CreateExit(ParentNode: TAuraNode; const Caption: string): TControl;
- function TryGetDescr(const Node: IAstNode; out Text: String): Boolean;
-
- public
- constructor Create(
- AWorkspace: TAuraWorkspace;
- AParentControl: TControl;
- const AStartPosition: TPointF;
- AConnections: TList;
- const ACurrentExec: TArray;
- AMode: TVisualizationMode;
- AParent: TAstToAuraNodeVisitor;
- ACurrentLambda: ILambdaExpressionNode;
- ADescriptor: IScopeDescriptor
- );
- destructor Destroy; override;
- property Connections: TList read FConnections;
-
- { IAstVisitor }
- procedure Execute(const RootNode: IAstNode);
- function VisitConstant(const Node: IConstantNode): TDataValue; override;
- function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
- function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
- function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
- function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
- function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
- function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
- function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
- function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
- function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
- function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
- function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
- end;
-
-implementation
-
-uses
- System.Math,
- System.StrUtils,
- FMX.Platform,
- Myc.Data.Scalar,
- Myc.Ast.Binding, // For TBound...Node classes
- Myc.Fmx.AstEditor.Text;
-
-{ TAstToAuraNodeVisitor }
-
-constructor TAstToAuraNodeVisitor.Create(
- AWorkspace: TAuraWorkspace;
- AParentControl: TControl;
- const AStartPosition: TPointF;
- AConnections: TList;
- const ACurrentExec: TArray;
- AMode: TVisualizationMode;
- AParent: TAstToAuraNodeVisitor;
- ACurrentLambda: ILambdaExpressionNode;
- ADescriptor: IScopeDescriptor
-);
-begin
- inherited Create;
- FWorkspace := AWorkspace;
- FParentControl := AParentControl;
- FCurrentPos := AStartPosition;
- FSpacing := TPointF.Create(20, 10);
- FConnections := AConnections;
- FCurrentExec := TList.Create(ACurrentExec);
- FLastResult.LayoutNode := nil;
- FLastResult.OutputPin := nil;
- FMode := AMode;
- FParentVisitor := AParent;
- FCurrentLambda := ACurrentLambda;
- // Size the array based on the number of slots in the scope descriptor.
- if Assigned(ADescriptor) then
- SetLength(FSlotCache, ADescriptor.SlotCount);
-end;
-
-destructor TAstToAuraNodeVisitor.Destroy;
-begin
- FCurrentExec.Free;
- inherited;
-end;
-
-procedure TAstToAuraNodeVisitor.FinalizeNodeLayout(const Node: TAuraNode);
-var
- control: TControl;
- leftPins, rightPins: TList;
- numLeftPins, numRightPins, maxPins: Integer;
- reqHeight: Single;
- i: Integer;
- totalHeight, startY: Single;
-begin
- leftPins := TList.Create;
- rightPins := TList.Create;
- try
- for control in Node.Controls do
- begin
- if (Pos('exec.', control.TagString) = 1) or (Pos('data.', control.TagString) = 1) then
- begin
- if Pos('.in', control.TagString) > 0 then
- begin
- leftPins.Add(control);
- control.Position.X := 0;
- end
- else
- begin
- rightPins.Add(control);
- control.Position.X := Node.Width - cPinSize;
- end;
- end;
- end;
-
- numLeftPins := leftPins.Count;
- numRightPins := rightPins.Count;
- maxPins := Max(numLeftPins, numRightPins);
-
- if maxPins = 0 then
- exit;
-
- reqHeight := cVerticalPadding + (maxPins - 1) * cPinOffsetY + cVerticalPadding;
- Node.Height := Max(Node.Height, reqHeight);
- Node.Height := Max(cDefaultNodeHeight, Node.Height);
-
- if numLeftPins > 0 then
- begin
- totalHeight := (numLeftPins - 1) * cPinOffsetY;
- startY := (Node.Height / 2) - (totalHeight / 2.0);
- for i := 0 to numLeftPins - 1 do
- leftPins[i].Position.Y := startY + i * cPinOffsetY - (cPinSize / 2);
- end;
-
- if numRightPins > 0 then
- begin
- totalHeight := (numRightPins - 1) * cPinOffsetY;
- startY := (Node.Height / 2) - (totalHeight / 2.0);
- for i := 0 to numRightPins - 1 do
- rightPins[i].Position.Y := startY + i * cPinOffsetY - (cPinSize / 2);
- end;
-
- finally
- leftPins.Free;
- rightPins.Free;
- end;
-end;
-
-function TAstToAuraNodeVisitor.BuildNodeControl(const Title, Details: string): TAuraNode;
-var
- fullTitle: string;
- textWidth: Single;
- newWidth: Single;
- measureCanvas: TCanvas;
-begin
- Result := TAuraNode.Create(FParentControl);
- Result.Parent := FParentControl;
-
- fullTitle := Title;
- if not Details.IsEmpty then
- fullTitle := fullTitle + ': ' + Details;
- Result.Title := fullTitle;
-
- measureCanvas := TCanvasManager.MeasureCanvas;
- if Assigned(measureCanvas) then
- begin
- measureCanvas.Font.Assign(Result.TitleFont);
- textWidth := measureCanvas.TextWidth(Result.Title);
- newWidth := textWidth + (2 * cHorizontalPadding);
- Result.Width := Max(Result.Width, newWidth);
- end;
-end;
-
-function TAstToAuraNodeVisitor.CreateInput(ParentNode: TAuraNode; const Caption: string): TControl;
-begin
- var cap := Caption;
- if cap <> '' then
- cap := '.' + cap;
- Result := CreatePin(ParentNode, psCircle, paLeft, cDataPinColor, 'data.in' + cap);
- Result.Hint := Caption;
- Result.ShowHint := Caption <> '';
-end;
-
-function TAstToAuraNodeVisitor.CreateNodeControl(const Title, Details: string): TAuraNode;
-begin
- Result := BuildNodeControl(Title, Details);
- Result.Position.Point := FCurrentPos;
- FCurrentPos.Y := FCurrentPos.Y + Result.Height + FSpacing.Y;
- FLastResult.LayoutNode := Result;
- FLastResult.OutputPin := nil;
-end;
-
-function TAstToAuraNodeVisitor.CreateOutput(ParentNode: TAuraNode): TControl;
-begin
- Result := CreatePin(ParentNode, psCircle, paRight, cDataPinColor, 'data.out');
-end;
-
-function TAstToAuraNodeVisitor.CreateEntry(ParentNode: TAuraNode; connect: Boolean = true): TControl;
-begin
- Result := CreatePin(ParentNode, psTriangle, paLeft, cExecPinColor, 'exec.in');
- if connect then
- begin
- for var curr in FCurrentExec do
- FConnections.Add(TPinConnection.Create(curr, Result));
- FCurrentExec.Clear;
- end;
-end;
-
-function TAstToAuraNodeVisitor.CreateExit(ParentNode: TAuraNode; const Caption: string): TControl;
-begin
- var cap := Caption;
- if cap <> '' then
- cap := '.' + cap;
- Result := CreatePin(ParentNode, psTriangle, paRight, cExecPinColor, 'exec.out' + cap);
- FCurrentExec.Add(Result);
-end;
-
-function TAstToAuraNodeVisitor.CreatePin(
- ParentNode: TAuraNode;
- Shape: TPinShape;
- Alignment: TPinAlignment;
- Color: TAlphaColor;
- const Tag: string
-): TShape;
-var
- posX, posY: Single;
-begin
- posY := 0;
- if Alignment = paLeft then
- posX := 0
- else // paRight
- posX := ParentNode.Width - cPinSize;
- case Shape of
- psCircle:
- begin
- var circle := TEllipse.Create(ParentNode);
- circle.Fill.Color := Color;
- circle.Stroke.Kind := TBrushKind.None;
- Result := circle;
- end;
- psTriangle:
- begin
- var triangle := TPath.Create(ParentNode);
- triangle.Data.Data := Format('M 0 0 L %d %d L 0 %d Z', [cPinSize, cPinSize div 2, cPinSize]);
- triangle.Fill.Color := Color;
- triangle.Stroke.Kind := TBrushKind.None;
- Result := triangle;
- end;
- else
- Result := nil;
- exit;
- end;
- Result.Parent := ParentNode;
- Result.SetBounds(posX, posY, cPinSize, cPinSize);
- Result.HitTest := true;
- Result.TagString := Tag;
-end;
-
-procedure TAstToAuraNodeVisitor.Execute(const RootNode: IAstNode);
-begin
- RootNode.Accept(Self)
-end;
-
-function TAstToAuraNodeVisitor.TryGetDescr(const Node: IAstNode; out Text: String): Boolean;
-begin
- if FMode <> vmControlFlow then
- exit(false);
-
- Text := Node.Accept(TAstToTextVisitor.Create).AsText;
- Result := Pos('{', Text) = 0;
-end;
-
-function TAstToAuraNodeVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
-var
- details: string;
- inputs: TArray;
-begin
- if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
- begin
- var addNode := CreateNodeControl('Add to Series', details);
- CreateEntry(addNode);
- CreateExit(addNode, '');
- FinalizeNodeLayout(addNode);
- end
- else
- begin
- if Assigned(Node.Lookback) then
- inputs := [Node.Series, Node.Value, Node.Lookback]
- else
- inputs := [Node.Series, Node.Value];
- VisitOperatorNode(
- inputs,
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- addNode: TAuraNode;
- seriesPin, valuePin, lookbackPin: TControl;
- begin
- addNode := BuildNodeControl('Add to Series', '');
- CreateEntry(addNode);
- seriesPin := CreateInput(addNode, 'Series');
- valuePin := CreateInput(addNode, 'Value');
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, seriesPin));
- if Assigned(InputResults[1].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[1].OutputPin, valuePin));
- if InputResults.Count > 2 then
- begin
- lookbackPin := CreateInput(addNode, 'Lookback');
- if Assigned(InputResults[2].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[2].OutputPin, lookbackPin));
- end;
- CreateExit(addNode, '');
- FinalizeNodeLayout(addNode);
- Result.LayoutNode := addNode;
- Result.OutputPin := nil;
- end,
- vaTop
- );
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
-begin
- var exprStr: String;
- if (FMode = vmControlFlow) and TryGetDescr(Node, exprStr) then
- begin
- var exprNode := CreateNodeControl('Expression', exprStr);
- FLastResult.OutputPin := CreateOutput(exprNode);
- FinalizeNodeLayout(exprNode);
- end
- else
- begin
- VisitOperatorNode(
- [Node.Left, Node.Right],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- binaryExprNode: TAuraNode;
- inputPin1, inputPin2: TControl;
- leftResult, rightResult: TAuraNodeResult;
- outPin: TControl;
- begin
- leftResult := InputResults[0];
- rightResult := InputResults[1];
- binaryExprNode := BuildNodeControl(Node.Operator.ToString, '');
- binaryExprNode.TitleFont.Size := 20;
- inputPin1 := CreateInput(binaryExprNode, '');
- inputPin2 := CreateInput(binaryExprNode, '');
- outPin := CreateOutput(binaryExprNode);
- if Assigned(leftResult.OutputPin) then
- FConnections.Add(TPinConnection.Create(leftResult.OutputPin, inputPin1));
- if Assigned(rightResult.OutputPin) then
- FConnections.Add(TPinConnection.Create(rightResult.OutputPin, inputPin2));
- FinalizeNodeLayout(binaryExprNode);
- Result.LayoutNode := binaryExprNode;
- Result.OutputPin := outPin;
- end
- );
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
-var
- blockNode: TAuraNode;
- oldParentControl: TControl;
- oldPos: TPointF;
- lastResult: TAuraNodeResult;
-begin
- // Create a visual container for the block, but do NOT create a new scope/visitor.
- blockNode := BuildNodeControl('', '');
- blockNode.Frameless := True;
- blockNode.Position.Point := FCurrentPos;
-
- // Temporarily retarget the visitor to build nodes inside the block container.
- oldParentControl := FParentControl;
- oldPos := FCurrentPos;
- try
- FParentControl := blockNode;
- FCurrentPos := TPointF.Create(FSpacing.X, FSpacing.Y);
-
- 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
- maxRight := Max(maxRight, control.Position.X + control.Width);
- maxBottom := Max(maxBottom, control.Position.Y + control.Height);
- end;
- end;
- blockNode.Width := Max(blockNode.Width, maxRight + FSpacing.X);
- blockNode.Height := Max(blockNode.Height, maxBottom + 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;
- // The output pin of a block is the output pin of its last expression.
- FLastResult.OutputPin := lastResult.OutputPin;
-end;
-
-function TAstToAuraNodeVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
-var
- constantNode: TAuraNode;
-begin
- constantNode := CreateNodeControl('Constant', Node.Value.ToString);
- FLastResult.OutputPin := CreateOutput(constantNode);
- FinalizeNodeLayout(constantNode);
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
-var
- seriesNode: TAuraNode;
-begin
- seriesNode := CreateNodeControl('Create Series', Node.Definition);
- FLastResult.OutputPin := CreateOutput(seriesNode);
- FinalizeNodeLayout(seriesNode);
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
-var
- inputExpressions: TArray;
-begin
- var details: String;
- if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
- begin
- var funcCallNode := CreateNodeControl('FunctionCall', details);
- CreateEntry(funcCallNode);
- FLastResult.OutputPin := CreateOutput(funcCallNode);
- CreateExit(funcCallNode, '');
- FinalizeNodeLayout(funcCallNode);
- end
- else
- begin
- SetLength(inputExpressions, 1 + Length(Node.Arguments));
- inputExpressions[0] := Node.Callee;
- for var i := 0 to High(Node.Arguments) do
- inputExpressions[i + 1] := Node.Arguments[i];
- VisitOperatorNode(
- inputExpressions,
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- funcCallNode: TAuraNode;
- calleePin: TControl;
- argPin: array of TControl;
- i: Integer;
- outPin: TControl;
- begin
- funcCallNode := BuildNodeControl('FunctionCall', '');
- CreateEntry(funcCallNode);
- calleePin := CreateInput(funcCallNode, 'Callee');
- SetLength(argPin, Length(Node.Arguments));
- for i := 0 to High(argPin) do
- argPin[i] := CreateInput(funcCallNode, 'Arg' + i.ToString);
- CreateExit(funcCallNode, '');
- outPin := CreateOutput(funcCallNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, calleePin));
- for i := 0 to High(Node.Arguments) do
- if Assigned(InputResults[i + 1].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[i + 1].OutputPin, argPin[i]));
- FinalizeNodeLayout(funcCallNode);
- Result.LayoutNode := funcCallNode;
- Result.OutputPin := outPin;
- end
- );
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
-begin
- // Visualize the macro expansion as a single, opaque node.
- // The original macro arguments are visited and wired up as inputs to this new node.
- // The expanded body is deliberately NOT visited to hide the complexity.
- VisitOperatorNode(
- Node.Arguments, // Use original arguments as inputs
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- macroNode: TAuraNode;
- calleeStr: string;
- argPins: TArray;
- i: Integer;
- outPin: TControl;
- begin
- // Get the name of the macro that was called.
- if (Node.Callee is TBoundIdentifierNode) then
- calleeStr := (Node.Callee as TBoundIdentifierNode).Name
- else
- calleeStr := '(...)'; // Fallback for complex callee expressions
-
- macroNode := BuildNodeControl('Macro Expansion', calleeStr);
- // Give it a distinct color to make it stand out in the graph.
- macroNode.Canvas.Fill.Color := TAlphaColors.Darkslateblue;
-
- // Create input pins for the original arguments.
- SetLength(argPins, Length(Node.Arguments));
- for i := 0 to High(argPins) do
- argPins[i] := CreateInput(macroNode, 'Arg' + i.ToString);
-
- // Create a single data output pin for the result of the expansion.
- outPin := CreateOutput(macroNode);
-
- // Connect the argument nodes to the input pins.
- for i := 0 to High(Node.Arguments) do
- if Assigned(InputResults[i].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[i].OutputPin, argPins[i]));
-
- FinalizeNodeLayout(macroNode);
- Result.LayoutNode := macroNode;
- Result.OutputPin := outPin;
- end
- );
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
-var
- details: string;
-begin
- if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
- begin
- var recurNode := CreateNodeControl('Recur', details);
- CreateEntry(recurNode);
- CreateExit(recurNode, '');
- FinalizeNodeLayout(recurNode);
- FLastResult.OutputPin := nil;
- end
- else
- begin
- VisitOperatorNode(
- Node.Arguments,
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- recurNode: TAuraNode;
- argPin: array of TControl;
- i: Integer;
- begin
- recurNode := BuildNodeControl('Recur', '');
- CreateEntry(recurNode);
- SetLength(argPin, Length(Node.Arguments));
- for i := 0 to High(argPin) do
- argPin[i] := CreateInput(recurNode, 'Arg' + i.ToString);
- CreateExit(recurNode, '');
- for i := 0 to High(Node.Arguments) do
- if Assigned(InputResults[i].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[i].OutputPin, argPin[i]));
- FinalizeNodeLayout(recurNode);
- Result.LayoutNode := recurNode;
- Result.OutputPin := nil;
- end
- );
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
-var
- existingResult: TAuraNodeResult;
- identifierNode: TAuraNode;
- boundNode: TBoundIdentifierNode;
-begin
- if Node is TBoundIdentifierNode then
- begin
- boundNode := Node as TBoundIdentifierNode;
- // FindNodeForAddress handles all cases: local cache, on-demand parameter creation, and recursive parent/upvalue lookup.
- if FindNodeForAddress(boundNode.Address, existingResult) then
- begin
- FLastResult := existingResult;
- end
- else
- begin
- // 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.OutputPin := CreateOutput(identifierNode);
- FinalizeNodeLayout(identifierNode);
- end;
- end
- else
- begin
- identifierNode := CreateNodeControl('Identifier (unbound)', Node.Name);
- FLastResult.LayoutNode := identifierNode;
- FLastResult.OutputPin := CreateOutput(identifierNode);
- FinalizeNodeLayout(identifierNode);
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
-var
- startPosition: TPointF;
- ifNode: TAuraNode;
- conditionEndY: Single;
- branchStartX: Single;
- execPathes: TList;
- thenEndY, elseEndY: Single;
-begin
- startPosition := FCurrentPos;
- var condStr: String;
- if (FMode = vmControlFlow) and TryGetDescr(Node, condStr) then
- begin
- ifNode := CreateNodeControl('If', condStr);
- ifNode.Height := cIfNodeHeight;
- end
- else
- begin
- ifNode :=
- VisitOperatorNode(
- [Node.Condition],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- conditionInputPin: TControl;
- condResult: TAuraNodeResult;
- ifNode: TAuraNode;
- begin
- condResult := InputResults[0];
- ifNode := BuildNodeControl('If', '');
- conditionInputPin := CreateInput(ifNode, 'Condition');
- if Assigned(condResult.OutputPin) then
- FConnections.Add(TPinConnection.Create(condResult.OutputPin, conditionInputPin));
- Result.LayoutNode := ifNode;
- Result.OutputPin := nil;
- end
- );
- end;
- CreateEntry(ifNode);
- conditionEndY := FCurrentPos.Y;
- branchStartX := ifNode.Position.X + ifNode.Width + FSpacing.X;
- execPathes := TList.Create;
- try
- FCurrentExec.Clear;
- CreateExit(ifNode, 'Then');
- FCurrentPos.X := branchStartX;
- FCurrentPos.Y := startPosition.Y;
- Node.ThenBranch.Accept(Self);
- thenEndY := FCurrentPos.Y;
- FLastResult.LayoutNode := ifNode;
- execPathes.AddRange(FCurrentExec);
- elseEndY := thenEndY;
- FCurrentExec.Clear;
- CreateExit(ifNode, 'Else');
- if Assigned(Node.ElseBranch) then
- begin
- FCurrentPos.X := branchStartX;
- FCurrentPos.Y := thenEndY;
- Node.ElseBranch.Accept(Self);
- elseEndY := FCurrentPos.Y;
- end;
- execPathes.AddRange(FCurrentExec);
- FinalizeNodeLayout(ifNode);
- FCurrentPos.X := startPosition.X;
- FCurrentPos.Y := Max(conditionEndY, elseEndY);
- FLastResult.LayoutNode := ifNode;
- FLastResult.OutputPin := nil;
- FCurrentExec.Clear;
- FCurrentExec.AddRange(execPathes);
- finally
- execPathes.Free;
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
-var
- details: String;
-begin
- if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
- begin
- var indexerNode := CreateNodeControl('Expression', details);
- FLastResult.OutputPin := CreateOutput(indexerNode);
- FinalizeNodeLayout(indexerNode);
- end
- else
- begin
- VisitOperatorNode(
- [Node.Base, Node.Index],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- indexerNode: TAuraNode;
- basePin, indexPin, outPin: TControl;
- begin
- indexerNode := BuildNodeControl('[]', '');
- indexerNode.TitleFont.Size := 20;
- basePin := CreateInput(indexerNode, 'Base');
- indexPin := CreateInput(indexerNode, 'Index');
- outPin := CreateOutput(indexerNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, basePin));
- if Assigned(InputResults[1].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[1].OutputPin, indexPin));
- FinalizeNodeLayout(indexerNode);
- Result.LayoutNode := indexerNode;
- Result.OutputPin := outPin;
- end
- );
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.FindNodeForAddress(const Address: TResolvedAddress; out NodeResult: TAuraNodeResult): Boolean;
-var
- originalAddress: TResolvedAddress;
- targetVisitor: TAstToAuraNodeVisitor;
- i: Integer;
- boundLambda: TBoundLambdaExpressionNode;
-begin
- if (Address.Kind = akUpvalue) and Assigned(FCurrentLambda) and (FCurrentLambda is TBoundLambdaExpressionNode) then
- begin
- boundLambda := FCurrentLambda as TBoundLambdaExpressionNode;
- if Address.SlotIndex < Length(boundLambda.Upvalues) then
- begin
- originalAddress := boundLambda.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;
-var
- paramStr: String;
- lambdaNode: TAuraNode;
- i: Integer;
- childVisitor: TAstToAuraNodeVisitor;
- childStartPos: TPointF;
- maxRight, maxBottom: Single;
- control: TControl;
- childLastResult: TAuraNodeResult;
- entryNode, exitNode: TAuraNode;
- boundNode: TBoundLambdaExpressionNode;
-begin
- // This visitor must operate on a bound AST.
- if not (Node is TBoundLambdaExpressionNode) then
- begin
- lambdaNode := CreateNodeControl(#$03BB, '(unbound)');
- FinalizeNodeLayout(lambdaNode);
- FLastResult.LayoutNode := lambdaNode;
- FLastResult.OutputPin := CreateOutput(lambdaNode);
- exit(TDataValue.Void);
- end;
- boundNode := Node as TBoundLambdaExpressionNode;
-
- // Parameter string for the title
- paramStr := '(';
- if Length(boundNode.Parameters) > 0 then
- begin
- paramStr := paramStr + boundNode.Parameters[0].Name;
- for i := 1 to High(boundNode.Parameters) do
- paramStr := paramStr + ', ' + boundNode.Parameters[i].Name;
- end;
- paramStr := paramStr + ')';
-
- // --- Inlined logic from former VisitContainerNode ---
-
- // 1. Create container node for the lambda.
- lambdaNode := BuildNodeControl(#$03BB, paramStr);
- lambdaNode.Position.Point := FCurrentPos;
-
- // 2. Create internal 'call' entry point.
- const pinNodeHeight = cPinSize + cVerticalPadding;
- entryNode := BuildNodeControl('call', '');
- entryNode.Parent := lambdaNode;
- entryNode.Position.Point := TPointF.Create(0, 0);
- entryNode.Height := pinNodeHeight;
- var internalExec: TArray := [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, boundNode.ScopeDescriptor);
-
- FCurrentExec.Clear;
-
- // 4. Let the child visitor create parameter nodes and visit the lambda body.
- for var param in boundNode.Parameters do
- begin
- if param is TBoundIdentifierNode then
- with (param as TBoundIdentifierNode) do
- begin
- var paramNode := childVisitor.CreateNodeControl('Parameter', Name);
- var paramResult: TAuraNodeResult;
- paramResult.LayoutNode := paramNode;
- paramResult.OutputPin := childVisitor.CreateOutput(paramNode);
- childVisitor.FinalizeNodeLayout(paramNode);
- childVisitor.FSlotCache[Address.SlotIndex] := paramResult;
- end;
- end;
- boundNode.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;
- FLastResult.LayoutNode := lambdaNode;
- FLastResult.OutputPin := CreateOutput(lambdaNode); // The lambda itself is an expression that yields a value.
- FinalizeNodeLayout(lambdaNode);
-
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
-var
- details: String;
-begin
- if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
- begin
- var memberNode := CreateNodeControl('Expression', details);
- FLastResult.OutputPin := CreateOutput(memberNode);
- FinalizeNodeLayout(memberNode);
- end
- else
- begin
- VisitOperatorNode(
- [Node.Base],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- memberAccessNode: TAuraNode;
- basePin, outPin: TControl;
- begin
- memberAccessNode := BuildNodeControl('Get ' + Node.Member.Name, '');
- basePin := CreateInput(memberAccessNode, 'Base');
- outPin := CreateOutput(memberAccessNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, basePin));
- FinalizeNodeLayout(memberAccessNode);
- Result.LayoutNode := memberAccessNode;
- Result.OutputPin := outPin;
- end
- );
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitOperatorNode(
- const InputExpressions: TArray;
- const CreateParentProc: TCreateParentNodeProc;
- Alignment: TVerticalAlignment
-): TAuraNode;
-var
- inputResults: TAuraNodeResultList;
- inputNode: IAstNode;
- startPosition: TPointF;
- endY, maxChildWidth: Single;
- parentNode: TAuraNode;
- createResult: TAuraNodeResult;
-begin
- startPosition := FCurrentPos;
- inputResults := TAuraNodeResultList.Create;
- try
- for inputNode in InputExpressions do
- begin
- inputNode.Accept(Self);
- inputResults.Add(FLastResult);
- end;
- endY := FCurrentPos.Y;
- maxChildWidth := 0;
- for var res in inputResults do
- if Assigned(res.LayoutNode) then
- maxChildWidth := Max(maxChildWidth, res.LayoutNode.Position.X + res.LayoutNode.Width);
- createResult := CreateParentProc(inputResults);
- parentNode := createResult.LayoutNode;
- Result := parentNode;
- var parentY: Single;
- if Alignment = vaCenter then
- begin
- var totalInputHeight := endY - startPosition.Y;
- var nodeCenterY := startPosition.Y + (totalInputHeight / 2);
- parentY := nodeCenterY - (parentNode.Height / 2);
- end
- else // vaTop
- begin
- parentY := startPosition.Y;
- end;
- parentNode.Position.Point := TPointF.Create(maxChildWidth + FSpacing.X, parentY);
- FCurrentPos.Y := Max(endY, parentNode.Position.Y + parentNode.Height + FSpacing.Y);
- FLastResult.LayoutNode := parentNode;
- FLastResult.OutputPin := createResult.OutputPin;
- finally
- inputResults.Free;
- end;
-end;
-
-function TAstToAuraNodeVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
-begin
- VisitOperatorNode(
- [Node.Series],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- lengthNode: TAuraNode;
- seriesPin, outPin: TControl;
- begin
- lengthNode := BuildNodeControl('Series Length', '');
- seriesPin := CreateInput(lengthNode, 'Series');
- outPin := CreateOutput(lengthNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, seriesPin));
- FinalizeNodeLayout(lengthNode);
- Result.LayoutNode := lengthNode;
- Result.OutputPin := outPin;
- end
- );
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
-begin
- var exprStr: String;
- if (FMode = vmControlFlow) and TryGetDescr(Node, exprStr) then
- begin
- var ternaryNode := CreateNodeControl('Expression', exprStr);
- FLastResult.OutputPin := CreateOutput(ternaryNode);
- FinalizeNodeLayout(ternaryNode);
- end
- else
- begin
- VisitOperatorNode(
- [Node.Condition, Node.ThenBranch, Node.ElseBranch],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- ternaryNode: TAuraNode;
- condPin, thenPin, elsePin, outPin: TControl;
- begin
- ternaryNode := BuildNodeControl('?', '');
- ternaryNode.TitleFont.Size := 20;
- condPin := CreateInput(ternaryNode, 'Condition');
- thenPin := CreateInput(ternaryNode, 'Then');
- elsePin := CreateInput(ternaryNode, 'Else');
- outPin := CreateOutput(ternaryNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, condPin));
- if Assigned(InputResults[1].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[1].OutputPin, thenPin));
- if Assigned(InputResults[2].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[2].OutputPin, elsePin));
- FinalizeNodeLayout(ternaryNode);
- Result.LayoutNode := ternaryNode;
- Result.OutputPin := outPin;
- end
- );
- end;
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
-begin
- var exprStr: String;
- if (FMode = vmControlFlow) and TryGetDescr(Node, exprStr) then
- begin
- var exprNode := CreateNodeControl('Expression', exprStr);
- FLastResult.OutputPin := CreateOutput(exprNode);
- FinalizeNodeLayout(exprNode);
- end
- else
- begin
- VisitOperatorNode(
- [Node.Right],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- unaryExprNode: TAuraNode;
- inputPin: TControl;
- rightResult: TAuraNodeResult;
- outPin: TControl;
- begin
- rightResult := InputResults[0];
- unaryExprNode := BuildNodeControl(Node.Operator.ToString, '');
- unaryExprNode.TitleFont.Size := 20;
- inputPin := CreateInput(unaryExprNode, '');
- outPin := CreateOutput(unaryExprNode);
- if Assigned(rightResult.OutputPin) then
- FConnections.Add(TPinConnection.Create(rightResult.OutputPin, inputPin));
- FinalizeNodeLayout(unaryExprNode);
- Result.LayoutNode := unaryExprNode;
- Result.OutputPin := outPin;
- end
- );
- end;
- Result := TDataValue.Void;
-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.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
-var
- paramStr: String;
- macroNode: TAuraNode;
- i: Integer;
- childVisitor: TAstToAuraNodeVisitor;
- childStartPos: TPointF;
- maxRight, maxBottom: Single;
- control: TControl;
- childLastResult: TAuraNodeResult;
- exitNode: TAuraNode;
- paramDescriptor: IScopeDescriptor;
-begin
- // Create a string representation of the parameters for the node title.
- paramStr := '(';
- if Length(Node.Parameters) > 0 then
- begin
- paramStr := paramStr + Node.Parameters[0].Name;
- for i := 1 to High(Node.Parameters) do
- paramStr := paramStr + ', ' + Node.Parameters[i].Name;
- end;
- paramStr := paramStr + ')';
-
- // 1. Create the main container node for the macro definition.
- macroNode := BuildNodeControl('defmacro: ' + Node.Name.Name, paramStr);
- macroNode.Position.Point := FCurrentPos;
-
- // A macro definition is a statement, so it has execution pins.
- CreateEntry(macroNode);
-
- // 2. Create a temporary scope descriptor for the macro's parameters.
- // This allows the child visitor to correctly visualize parameter nodes.
- paramDescriptor := TScope.CreateDescriptor(nil);
- for i := 0 to High(Node.Parameters) do
- paramDescriptor.Define(Node.Parameters[i].Name, TTypes.Unknown);
-
- // 3. Create a child visitor for the new scope within the macro's body.
- const pinNodeHeight = cPinSize + cVerticalPadding;
- childStartPos := TPointF.Create(FSpacing.X, FSpacing.Y + pinNodeHeight);
- childVisitor := TAstToAuraNodeVisitor.Create(FWorkspace, macroNode, childStartPos, FConnections, [], FMode, Self, nil, paramDescriptor);
-
- // 4. Create the visual nodes for the parameters inside the macro's scope.
- for i := 0 to High(Node.Parameters) do
- begin
- var param := Node.Parameters[i];
- var paramNode := childVisitor.CreateNodeControl('Parameter', param.Name);
- var paramResult: TAuraNodeResult;
- paramResult.LayoutNode := paramNode;
- paramResult.OutputPin := childVisitor.CreateOutput(paramNode);
- childVisitor.FinalizeNodeLayout(paramNode);
- // Cache the parameter node by its slot index for lookups within the body.
- if i < Length(childVisitor.FSlotCache) then
- childVisitor.FSlotCache[i] := paramResult;
- end;
-
- // 5. Let the child visitor render the macro's body.
- Node.Body.Accept(childVisitor);
- childLastResult := childVisitor.FLastResult;
-
- // 6. Resize the container to fit all internally generated nodes.
- maxRight := 0;
- maxBottom := 0;
- for control in macroNode.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;
- macroNode.Width := Max(macroNode.Width, maxRight + FSpacing.X);
- macroNode.Height := Max(macroNode.Height, maxBottom + FSpacing.Y);
-
- // 7. Create an internal 'expansion' exit point, showing what the macro produces.
- exitNode := BuildNodeControl('Expansion', '');
- exitNode.Parent := macroNode;
- exitNode.Height := pinNodeHeight;
- // Connect the data flow (the result of the last expression in the body) to the expansion node.
- if Assigned(childLastResult.OutputPin) then
- begin
- var dataResultPin := childVisitor.CreateInput(exitNode, 'AST');
- FConnections.Add(TPinConnection.Create(childLastResult.OutputPin, dataResultPin));
- end;
- childVisitor.FinalizeNodeLayout(exitNode);
- macroNode.Height := Max(macroNode.Height, maxBottom + FSpacing.Y + exitNode.Height);
- exitNode.Position.Point := TPointF.Create(FSpacing.X, macroNode.Height - exitNode.Height - FSpacing.Y);
-
- // 8. Finalize the main visitor state.
- FCurrentPos.Y := macroNode.Position.Y + macroNode.Height + FSpacing.Y;
- FLastResult.LayoutNode := macroNode;
- FLastResult.OutputPin := nil; // A macro definition itself does not return a value.
- CreateExit(macroNode, ''); // Execution flow continues after the definition.
- FinalizeNodeLayout(macroNode);
-
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
-begin
- VisitOperatorNode(
- [Node.Expression],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- qqNode: TAuraNode;
- inputPin, outPin: TControl;
- begin
- qqNode := BuildNodeControl('`', '');
- qqNode.TitleFont.Size := 20;
- inputPin := CreateInput(qqNode, '');
- outPin := CreateOutput(qqNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, inputPin));
- FinalizeNodeLayout(qqNode);
- Result.LayoutNode := qqNode;
- Result.OutputPin := outPin;
- end
- );
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue;
-begin
- VisitOperatorNode(
- [Node.Expression],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- uqNode: TAuraNode;
- inputPin, outPin: TControl;
- begin
- uqNode := BuildNodeControl('~', '');
- uqNode.TitleFont.Size := 20;
- inputPin := CreateInput(uqNode, '');
- outPin := CreateOutput(uqNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, inputPin));
- FinalizeNodeLayout(uqNode);
- Result.LayoutNode := uqNode;
- Result.OutputPin := outPin;
- end
- );
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
-begin
- VisitOperatorNode(
- [Node.Expression],
- function(const InputResults: TAuraNodeResultList): TAuraNodeResult
- var
- uqsNode: TAuraNode;
- inputPin, outPin: TControl;
- begin
- uqsNode := BuildNodeControl('~@', '');
- uqsNode.TitleFont.Size := 20;
- inputPin := CreateInput(uqsNode, '');
- outPin := CreateOutput(uqsNode);
- if Assigned(InputResults[0].OutputPin) then
- FConnections.Add(TPinConnection.Create(InputResults[0].OutputPin, inputPin));
- FinalizeNodeLayout(uqsNode);
- Result.LayoutNode := uqsNode;
- Result.OutputPin := outPin;
- end
- );
- Result := TDataValue.Void;
-end;
-
-function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
-var
- details: string;
- varDeclNode: TAuraNode;
- oldParentControl: TControl;
- oldPos: TPointF;
- control: TControl;
- boundIdentifier: TBoundIdentifierNode;
-begin
- if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
- begin
- varDeclNode := CreateNodeControl('VarDecl', details);
- CreateEntry(varDeclNode);
- CreateExit(varDeclNode, '');
- FinalizeNodeLayout(varDeclNode);
- end
- else
- begin
- // 1. Container-Knoten erstellen.
- varDeclNode := BuildNodeControl('VarDecl', Node.Identifier.Name);
- varDeclNode.Position.Point := FCurrentPos;
-
- if Assigned(Node.Initializer) then
- begin
- // 2. Visitor temporär auf den Container als Parent umleiten.
- oldParentControl := FParentControl;
- oldPos := FCurrentPos;
- try
- FParentControl := varDeclNode;
- FCurrentPos := TPointF.Create(FSpacing.X, varDeclNode.TitleFont.Size * 1.8 + FSpacing.Y);
-
- // 3. Den Initialisierungsausdruck mit dem *aktuellen* Visitor (Self) verarbeiten.
- Node.Initializer.Accept(Self);
-
- // 4. Container-Größe an den Inhalt anpassen.
- var maxRight: Single := 0;
- var maxBottom: Single := 0;
- for control in varDeclNode.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;
- 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;
-
- // 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 is TBoundIdentifierNode then
- begin
- boundIdentifier := Node.Identifier as TBoundIdentifierNode;
- FSlotCache[boundIdentifier.Address.SlotIndex] := FLastResult;
- end;
- end;
- Result := TDataValue.Void;
-end;
-
-end.
diff --git a/ASTPlayground/Myc.Fmx.AstVisualizer.pas b/ASTPlayground/Myc.Fmx.AstVisualizer.pas
new file mode 100644
index 0000000..b80e254
--- /dev/null
+++ b/ASTPlayground/Myc.Fmx.AstVisualizer.pas
@@ -0,0 +1,1397 @@
+unit Myc.Fmx.AstVisualizer;
+
+interface
+
+uses
+ System.SysUtils,
+ System.Classes,
+ System.UITypes,
+ System.Types,
+ System.Math.Vectors,
+ System.Generics.Collections,
+ System.Generics.Defaults,
+ FMX.Types,
+ FMX.Controls,
+ FMX.Objects,
+ FMX.Graphics,
+ FMX.StdCtrls,
+ Myc.Data.Value,
+ Myc.Ast.Nodes,
+ Myc.Ast.Scope,
+ Myc.Ast.Types,
+ Myc.Ast.Visitor,
+ Myc.Fmx.AstEditor.Node,
+ Myc.Fmx.AstEditor.Workspace;
+
+type
+ // Visits the AST and creates visual TAuraNode representations on a TAuraWorkspace.
+ TAstVisualizer = class(TAstVisitor)
+ private
+ FWorkspace: TAuraWorkspace;
+ FParentControl: TControl; // The current parent FMX control for newly created nodes
+ FCurrentPos: TPointF; // Current position for the next node relative to FParentControl
+ FNodeVerticalGap: Single; // Vertical gap between nodes
+ FExprDepth: Integer;
+
+ // Helper to create and initialize a TAuraNode for a given AST node.
+ function CreateNode(const Node: IAstNode): TAuraNode;
+ function AddLabel(const ParentNode: TAuraNode; const Txt: String): TLabel;
+ function AddTitleLabel(const ParentNode: TAuraNode; const Title: string): Single;
+ // Adjusts the size of the parent node to fit its children.
+ procedure AdjustParentNodeSize(const ParentNode: TAuraNode; const Children: TArray);
+ // Generic processing logic for AST nodes with children. Returns the created TAuraNode.
+ function ProcessStub(const ATitle: string; const ANode: IAstNode; const AChildren: TArray): TAuraNode;
+
+ // Calls Accept on the node and returns the resulting TAuraNode.
+ function CallAccept(const Node: IAstNode): TAuraNode;
+
+ protected
+ // Visitor implementations for each AST node type.
+ function VisitConstant(const Node: IConstantNode): TDataValue; override;
+ function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
+ function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
+ function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
+ function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
+ function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
+ function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
+ function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
+ function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override;
+ function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
+ function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
+ function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
+ function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override;
+ function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override;
+ function VisitUnquote(const Node: IUnquoteNode): TDataValue; override;
+ function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override;
+ function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
+ function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
+ function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
+ function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
+ function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
+ function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
+
+ public
+ constructor Create(AWorkspace: TAuraWorkspace; AParentControl: TControl; const AStartPosition: TPointF);
+ destructor Destroy; override;
+
+ class function CreateVisitor(AWorkspace: TAuraWorkspace; const StartPosition: TPointF): IAstVisitor;
+ end;
+
+implementation
+
+uses
+ System.Math,
+ System.StrUtils,
+ FMX.Platform,
+ FMX.Layouts, // For TLayout
+ Myc.Data.Scalar,
+ Myc.Ast.Binding, // For TBound...Node classes
+ Myc.Fmx.AstEditor.Text;
+
+const
+ cNodePadding = 10; // Reduced padding
+ cTitleTopPadding = 2;
+ cTitleBottomPadding = 4;
+ cChildAreaTopPadding = 5;
+ cNodeVerticalGap = 10; // Reduced gap
+ cMinNodeWidth = 10;
+ cMinNodeHeight = 10;
+
+{ TAstVisualizer }
+
+constructor TAstVisualizer.Create(AWorkspace: TAuraWorkspace; AParentControl: TControl; const AStartPosition: TPointF);
+begin
+ inherited Create;
+ FWorkspace := AWorkspace;
+ FParentControl := AParentControl;
+ FCurrentPos := AStartPosition;
+ FNodeVerticalGap := 15; // Vertical gap between child nodes
+ FExprDepth := 0;
+end;
+
+destructor TAstVisualizer.Destroy;
+begin
+ inherited;
+end;
+
+function TAstVisualizer.AddLabel(const ParentNode: TAuraNode; const Txt: String): TLabel;
+begin
+ Result := TLabel.Create(ParentNode); // Owner is the node itself
+ Result.Parent := ParentNode;
+
+ Result.Position.Point := TPoint.Create(cNodePadding, cNodePadding);
+ Result.Margins.Left := cNodePadding;
+ Result.Margins.Right := cNodePadding;
+ Result.Margins.Top := cTitleTopPadding;
+ Result.Margins.Bottom := cTitleBottomPadding;
+ Result.WordWrap := false;
+ Result.AutoSize := True; // Let label determine its own size initially (do NOT use Align, because it will override AutoSize)
+ Result.HitTest := False; // Title label should not intercept clicks intended for the node
+
+ Result.StyledSettings := Result.StyledSettings - [TStyledSetting.Style]; // Tell FMX not to manage Font.Style via styles
+
+ Result.Text := Txt;
+ Result.ApplyStyleLookup;
+end;
+
+class function TAstVisualizer.CreateVisitor(AWorkspace: TAuraWorkspace; const StartPosition: TPointF): IAstVisitor;
+begin
+ Result := TAstVisualizer.Create(AWorkspace, AWorkspace, StartPosition);
+end;
+
+// Creates a TAuraNode, sets its parent and position. Title is added separately.
+function TAstVisualizer.CreateNode(const Node: IAstNode): TAuraNode;
+begin
+ Result := TAuraNode.Create(FWorkspace); // Owner is always the workspace
+ Result.Parent := FParentControl; // Set visual parent
+ Result.Position.Point := FCurrentPos;
+ // Set a default minimum size, will be adjusted later
+ Result.Width := cMinNodeWidth;
+ Result.Height := cMinNodeHeight;
+
+ var cn: cardinal := $ea - (7 * FExprDepth);
+ var c: cardinal := $ff000000 or (cn shl 16) or (cn shl 8) or cn;
+
+ Result.BackgroundColor := c;
+ // Store the original AST node reference if needed later
+ // Result.TagObject := TObject(Node);
+end;
+
+// Creates and adds a TLabel for the title to the given ParentNode.
+// Returns the calculated height of the title area (label + margins).
+function TAstVisualizer.AddTitleLabel(const ParentNode: TAuraNode; const Title: string): Single;
+var
+ titleLabel: TLabel;
+begin
+ Result := 0;
+ if Title.IsEmpty then
+ Exit;
+
+ titleLabel := AddLabel(ParentNode, Title);
+ titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold];
+
+ // Force immediate resize calculation for height measurement (usually not needed with Align=Top + AutoSize)
+ // titleLabel.ApplyStyleLookup; titleLabel.Realign;
+ Result := titleLabel.Height + titleLabel.Margins.Top + titleLabel.Margins.Bottom;
+end;
+
+// Adjusts the parent node's size after all children have been added,
+// considering both labels and child AuraNodes.
+procedure TAstVisualizer.AdjustParentNodeSize(const ParentNode: TAuraNode; const Children: TArray);
+var
+ i: Integer;
+ maxLabelWidth: Single;
+ maxChildNodeWidth: Single;
+ totalChildHeight: Single;
+ childControl: TControl;
+ isLabel: Boolean;
+ requiredWidth: Single;
+begin
+ maxLabelWidth := 0;
+ maxChildNodeWidth := 0;
+ totalChildHeight := cNodePadding; // Start with top padding
+
+ // Iterate through FMX children of the ParentNode to calculate required size
+ for i := 0 to ParentNode.ControlsCount - 1 do
+ begin
+ childControl := ParentNode.Controls[i];
+ isLabel := childControl is TLabel;
+
+ // Calculate required width for this child (including its margins)
+ requiredWidth := childControl.Width + childControl.Margins.Left + childControl.Margins.Right;
+
+ if isLabel then
+ begin
+ maxLabelWidth := Max(maxLabelWidth, requiredWidth);
+ end
+ // Also consider TAuraNode children for width calculation, assuming they also have padding
+ else if childControl is TAuraNode then
+ begin
+ // Child nodes are positioned starting at cNodePadding, so their width relative to parent border is Width + cNodePadding
+ // We compare against the required width including parent padding
+ maxChildNodeWidth := Max(maxChildNodeWidth, childControl.Width);
+ end;
+
+ // Height calculation based on the last child's bottom edge relative to parent's top
+ totalChildHeight := Max(totalChildHeight, childControl.Position.Y + childControl.Height + childControl.Margins.Bottom);
+ end;
+
+ // Set parent size including padding
+ // Ensure minimum size is met AND the widest label fits AND the widest child node fits
+ ParentNode.Width :=
+ Max(
+ cMinNodeWidth, // At least min width
+ Max(
+ maxLabelWidth, // At least widest label width + padding (already included in label width calc)
+ maxChildNodeWidth + 2 * cNodePadding
+ )
+ ); // At least widest child node width + padding
+
+ // Adjust height based on the bottom-most control + bottom padding
+ if ParentNode.ControlsCount > 0 then
+ ParentNode.Height := Max(cMinNodeHeight, totalChildHeight + cNodePadding)
+ else // Empty node
+ ParentNode.Height := cMinNodeHeight;
+
+end;
+
+// Calls Accept on the node and returns the resulting TAuraNode.
+function TAstVisualizer.CallAccept(const Node: IAstNode): TAuraNode;
+var
+ DataValue: TDataValue;
+begin
+ if not Assigned(Node) then
+ Exit(nil); // Handle nil input node gracefully
+
+ DataValue := Node.Accept(Self);
+
+ // Check if the result is actually a TAuraNode before casting
+ if DataValue.Kind = TDataValueKind.vkGeneric then
+ begin
+ // It's recommended to add a type check here if possible,
+ // although AsGeneric doesn't inherently support it.
+ // Assuming the visitor correctly returns TAuraNode wrapped in TDataValue.
+ Result := DataValue.AsGeneric;
+ end
+ else
+ begin
+ // The visitor returned something unexpected.
+ // Decide how to handle this: raise an exception, return nil, log an error?
+ // For now, return nil as the caller might handle it.
+ Result := nil;
+ // Consider: Log.d('TAstVisualizer.CallAccept', 'Expected TAuraNode, got ' + DataValue.Kind.ToString);
+ // Consider: raise EMyAstError.Create('Visitor did not return a TAuraNode');
+ end;
+end;
+
+// Generic processing logic for AST nodes with children. Returns the created TAuraNode.
+function TAstVisualizer.ProcessStub(const ATitle: string; const ANode: IAstNode; const AChildren: TArray): TAuraNode;
+var
+ auraNode: TAuraNode;
+ savedParent: TControl;
+ savedPos: TPointF;
+ child: IAstNode;
+ titleLabelHeight: Single;
+ childNode: TAuraNode; // To potentially use the return value
+begin
+ auraNode := CreateNode(ANode); // Create node without title
+ auraNode.BeginUpdate;
+ try
+ // Add Title Label
+ titleLabelHeight := AddTitleLabel(auraNode, ATitle);
+
+ savedParent := FParentControl;
+ savedPos := FCurrentPos;
+ FParentControl := auraNode;
+ // Start position for children inside the new node, below the title
+ FCurrentPos := TPointF.Create(cNodePadding, titleLabelHeight + cChildAreaTopPadding);
+
+ // Visit children
+ for child in AChildren do
+ begin
+ if Assigned(child) then // Ensure child node exists
+ childNode := CallAccept(child); // Use CallAccept, store result
+ // childNode is not explicitly used here for layout *after* the call,
+ // but its creation and positioning happened *within* CallAccept.
+ end;
+
+ FParentControl := savedParent; // Restore parent
+ FCurrentPos := savedPos; // Restore position for siblings
+
+ AdjustParentNodeSize(auraNode, AChildren);
+ finally
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node in the outer scope
+ // Use the actual size of the created node
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap;
+ Result := auraNode; // Return the created node
+end;
+
+// --- Visitor Implementations ---
+
+function TAstVisualizer.VisitConstant(const Node: IConstantNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ valueStr: string;
+ isConcise: Boolean;
+begin
+ valueStr := Node.Value.ToString;
+ // Check if it's a number or a single-line text and not excessively long
+ isConcise := ((Node.Value.Kind = TDataValueKind.vkScalar) or (Pos(sLineBreak, valueStr) = 0)) and (Length(valueStr) < 40);
+
+ auraNode := CreateNode(Node); // Create node without title
+ auraNode.BeginUpdate;
+ try
+ if isConcise then
+ begin
+ // Display value directly in a centered label, frameless node
+ auraNode.Frameless := true;
+ AddLabel(auraNode, valueStr);
+ end
+ else
+ begin
+ // Use standard framed node with Title label + Value label
+ auraNode.Frameless := False;
+
+ // Title Label
+ var titleLabelHeight := AddTitleLabel(auraNode, 'Constant');
+
+ // Value Label
+ var valueLabel := AddLabel(auraNode, valueStr);
+ valueLabel.Position.Y := valueLabel.Position.Y + titleLabelHeight;
+ valueLabel.AutoSize := False; // ***Important: Disable AutoSize when Align=Client***
+ valueLabel.WordWrap := True; // Allow wrapping for long text
+ end;
+
+ AdjustParentNodeSize(auraNode, []); // Adjust based on labels' sizes
+ finally
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node
+ FCurrentPos.Y := FCurrentPos.Y + auraNode.Height + FNodeVerticalGap;
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ // Leaf node, direct creation without ProcessStub
+ auraNode := CreateNode(Node);
+ auraNode.Frameless := True; // Make identifiers frameless like concise constants
+ auraNode.BeginUpdate;
+ try
+ auraNode.Frameless := true;
+ AddLabel(auraNode, Node.Name);
+ AdjustParentNodeSize(auraNode, []); // Fallback just in case
+ finally
+ auraNode.EndUpdate;
+ end;
+ FCurrentPos.Y := FCurrentPos.Y + auraNode.Height + FNodeVerticalGap;
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ leftNode: TAuraNode; // Use TAuraNode directly
+ operatorLabel: TLabel;
+ rightNode: TAuraNode; // Use TAuraNode directly
+ savedParent: TControl;
+ savedPos: TPointF;
+ currentX: Single; // Track horizontal position
+ maxHeightInRow: Single; // Track max height for row alignment
+ yPos: Single; // Y position for elements in this row
+begin
+ // Create the main node, frameless as it primarily serves as a container
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ inc(FExprDepth);
+ try
+ auraNode.Frameless := true;
+
+ // Start position for children inside the node
+ currentX := cNodePadding;
+ yPos := cNodePadding; // Initial Y position
+ maxHeightInRow := 0;
+ leftNode := nil;
+ rightNode := nil;
+
+ // 1. Left Expression Node (Visited Recursively)
+ savedParent := FParentControl;
+ savedPos := FCurrentPos; // Store original position for siblings
+ FParentControl := auraNode; // Children are added to this node
+ FCurrentPos := TPointF.Create(currentX, yPos); // Set position for the child node
+
+ leftNode := CallAccept(Node.Left); // Use CallAccept and store result
+
+ if Assigned(leftNode) then
+ begin
+ // Update currentX based on the right edge of the left control + gap
+ currentX := leftNode.Position.X + leftNode.Width + leftNode.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, leftNode.Height + leftNode.Margins.Top + leftNode.Margins.Bottom);
+ end;
+
+ // 2. Operator Label
+ operatorLabel := AddLabel(auraNode, Node.Operator.ToString);
+ operatorLabel.Position.Point := TPointF.Create(currentX, yPos); // Position next to left operand
+ currentX := operatorLabel.Position.X + operatorLabel.Width + operatorLabel.Margins.Right + cNodePadding; // Update X pos
+ maxHeightInRow := Max(maxHeightInRow, operatorLabel.Height + operatorLabel.Margins.Top + operatorLabel.Margins.Bottom);
+
+ // 3. Right Expression Node (Visited Recursively)
+ // FParentControl is already auraNode
+ FCurrentPos := TPointF.Create(currentX, yPos); // Set position for the child node
+
+ rightNode := CallAccept(Node.Right); // Use CallAccept and store result
+
+ if Assigned(rightNode) then
+ begin
+ // Update currentX based on the right edge of the right control
+ currentX := rightNode.Position.X + rightNode.Width + rightNode.Margins.Right;
+ maxHeightInRow := Max(maxHeightInRow, rightNode.Height + rightNode.Margins.Top + rightNode.Margins.Bottom);
+ end;
+
+ FParentControl := savedParent; // Restore parent for siblings
+ FCurrentPos := savedPos; // Restore position for siblings
+
+ // --- Adjust parent size ---
+ auraNode.Width := Max(cMinNodeWidth, currentX + cNodePadding); // Total width needed + right padding
+ auraNode.Height := Max(cMinNodeHeight, maxHeightInRow + 2 * cNodePadding); // Max height + top/bottom padding
+
+ // --- Optional Vertical Centering ---
+ var centerOffsetY: Single := (auraNode.Height / 2);
+ if Assigned(leftNode) then
+ leftNode.Position.Y :=
+ centerOffsetY - (leftNode.Height + leftNode.Margins.Top + leftNode.Margins.Bottom) / 2 + leftNode.Margins.Top;
+ if Assigned(operatorLabel) then
+ operatorLabel.Position.Y :=
+ centerOffsetY
+ - (operatorLabel.Height + operatorLabel.Margins.Top + operatorLabel.Margins.Bottom) / 2
+ + operatorLabel.Margins.Top;
+ if Assigned(rightNode) then
+ rightNode.Position.Y :=
+ centerOffsetY - (rightNode.Height + rightNode.Margins.Top + rightNode.Margins.Bottom) / 2 + rightNode.Margins.Top;
+ // --- End Optional Vertical Centering ---
+
+ finally
+ dec(FExprDepth);
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node in the outer scope
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap; // Use savedPos.Y for vertical alignment with siblings
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ auraNode := ProcessStub('Unary Op: ' + Node.Operator.ToString, Node, [Node.Right]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
+var
+ children: TArray;
+ auraNode: TAuraNode;
+begin
+ children := [Node.Condition, Node.ThenBranch];
+ if Assigned(Node.ElseBranch) then
+ children := children + [Node.ElseBranch];
+ auraNode := ProcessStub('If', Node, children);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ conditionNode: TAuraNode;
+ qMarkLabel: TLabel;
+ thenNode: TAuraNode;
+ colonLabel: TLabel;
+ elseNode: TAuraNode;
+ savedParent: TControl;
+ savedPos: TPointF;
+ currentX: Single; // Track horizontal position
+ maxHeightInRow: Single; // Track max height for row alignment
+ yPos: Single; // Y position for elements in this row
+begin
+ // Create the main node, frameless
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ inc(FExprDepth);
+ try
+ auraNode.Frameless := True;
+
+ // Start position for children inside the node
+ currentX := cNodePadding;
+ yPos := cNodePadding; // Initial Y position
+ maxHeightInRow := 0;
+ conditionNode := nil;
+ thenNode := nil;
+ elseNode := nil;
+
+ // 1. Condition Node
+ savedParent := FParentControl;
+ savedPos := FCurrentPos;
+ FParentControl := auraNode;
+ FCurrentPos := TPointF.Create(currentX, yPos);
+ conditionNode := CallAccept(Node.Condition);
+ if Assigned(conditionNode) then
+ begin
+ currentX := conditionNode.Position.X + conditionNode.Width + conditionNode.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, conditionNode.Height + conditionNode.Margins.Top + conditionNode.Margins.Bottom);
+ end;
+
+ // 2. Question Mark Label
+ qMarkLabel := AddLabel(auraNode, '?');
+ qMarkLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := qMarkLabel.Position.X + qMarkLabel.Width + qMarkLabel.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, qMarkLabel.Height + qMarkLabel.Margins.Top + qMarkLabel.Margins.Bottom);
+
+ // 3. Then Branch Node
+ // FParentControl is already auraNode
+ FCurrentPos := TPointF.Create(currentX, yPos);
+ thenNode := CallAccept(Node.ThenBranch);
+ if Assigned(thenNode) then
+ begin
+ currentX := thenNode.Position.X + thenNode.Width + thenNode.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, thenNode.Height + thenNode.Margins.Top + thenNode.Margins.Bottom);
+ end;
+
+ // 4. Colon Label
+ colonLabel := AddLabel(auraNode, ':');
+ colonLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := colonLabel.Position.X + colonLabel.Width + colonLabel.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, colonLabel.Height + colonLabel.Margins.Top + colonLabel.Margins.Bottom);
+
+ // 5. Else Branch Node
+ // FParentControl is already auraNode
+ FCurrentPos := TPointF.Create(currentX, yPos);
+ elseNode := CallAccept(Node.ElseBranch);
+ if Assigned(elseNode) then
+ begin
+ currentX := elseNode.Position.X + elseNode.Width + elseNode.Margins.Right;
+ maxHeightInRow := Max(maxHeightInRow, elseNode.Height + elseNode.Margins.Top + elseNode.Margins.Bottom);
+ end;
+
+ FParentControl := savedParent;
+ FCurrentPos := savedPos;
+
+ // --- Adjust parent size ---
+ auraNode.Width := Max(cMinNodeWidth, currentX + cNodePadding);
+ auraNode.Height := Max(cMinNodeHeight, maxHeightInRow + 2 * cNodePadding);
+
+ // --- Optional Vertical Centering ---
+ var centerOffsetY: Single := (auraNode.Height / 2);
+ if Assigned(conditionNode) then
+ conditionNode.Position.Y :=
+ centerOffsetY
+ - (conditionNode.Height + conditionNode.Margins.Top + conditionNode.Margins.Bottom) / 2
+ + conditionNode.Margins.Top;
+ if Assigned(qMarkLabel) then
+ qMarkLabel.Position.Y :=
+ centerOffsetY - (qMarkLabel.Height + qMarkLabel.Margins.Top + qMarkLabel.Margins.Bottom) / 2 + qMarkLabel.Margins.Top;
+ if Assigned(thenNode) then
+ thenNode.Position.Y :=
+ centerOffsetY - (thenNode.Height + thenNode.Margins.Top + thenNode.Margins.Bottom) / 2 + thenNode.Margins.Top;
+ if Assigned(colonLabel) then
+ colonLabel.Position.Y :=
+ centerOffsetY - (colonLabel.Height + colonLabel.Margins.Top + colonLabel.Margins.Bottom) / 2 + colonLabel.Margins.Top;
+ if Assigned(elseNode) then
+ elseNode.Position.Y :=
+ centerOffsetY - (elseNode.Height + elseNode.Margins.Top + elseNode.Margins.Bottom) / 2 + elseNode.Margins.Top;
+ // --- End Optional Vertical Centering ---
+
+ finally
+ dec(FExprDepth);
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node in the outer scope
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap;
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ savedParent: TControl;
+ savedPos: TPointF;
+ savedExprDepth: Integer;
+ i: Integer;
+ paramLabel: TLabel;
+ lambdaLabel: TLabel; // Label for the lambda symbol
+ titleParamHeight: Single; // Height required for the combined title/param line
+ currentX: Single; // Horizontal position tracker for title/params
+ maxHeightInTitleRow: Single; // Max height in the first row
+ lambdaSymbol: string;
+ bodyNode: TAuraNode;
+begin
+ lambdaSymbol := WideChar($03BB); // Unicode for Greek Small Letter Lambda
+ auraNode := CreateNode(Node); // Create standard framed node
+ auraNode.BeginUpdate;
+ try
+ auraNode.Frameless := False;
+ auraNode.BackgroundColor := $090000ff;
+
+ // --- Create Title/Parameter Line ---
+ currentX := cNodePadding;
+ maxHeightInTitleRow := 0;
+ var yPos := cNodePadding; // Y position for the first row
+
+ // 1. Lambda Symbol Label (Title)
+ lambdaLabel := AddLabel(auraNode, lambdaSymbol);
+ lambdaLabel.Position.Point := TPointF.Create(currentX, yPos);
+ lambdaLabel.Font.Style := lambdaLabel.Font.Style + [TFontStyle.fsBold]; // Make symbol bold
+ currentX := lambdaLabel.Position.X + lambdaLabel.Width + lambdaLabel.Margins.Right + cNodePadding / 2; // Add small gap
+ maxHeightInTitleRow := Max(maxHeightInTitleRow, lambdaLabel.Height + lambdaLabel.Margins.Top + lambdaLabel.Margins.Bottom);
+
+ // 2. Add Parameter Labels (inline)
+ if High(Node.Parameters) >= 0 then
+ begin
+ // Add parenthesis
+ var openParen := AddLabel(auraNode, '(');
+ openParen.Font.Style := openParen.Font.Style - [TFontStyle.fsBold]; // Normal style
+ openParen.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := openParen.Position.X + openParen.Width + openParen.Margins.Right + cNodePadding / 2;
+ maxHeightInTitleRow := Max(maxHeightInTitleRow, openParen.Height + openParen.Margins.Top + openParen.Margins.Bottom);
+
+ for i := 0 to High(Node.Parameters) do
+ begin
+ paramLabel := AddLabel(auraNode, Node.Parameters[i].Name);
+ paramLabel.Font.Style := paramLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ paramLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX :=
+ paramLabel.Position.X
+ + paramLabel.Width
+ + paramLabel.Margins.Right
+ + IfThen(i < High(Node.Parameters), cNodePadding / 2, 0); // Gap or no gap
+ maxHeightInTitleRow := Max(maxHeightInTitleRow, paramLabel.Height + paramLabel.Margins.Top + paramLabel.Margins.Bottom);
+
+ // Add comma if not the last parameter
+ if i < High(Node.Parameters) then
+ begin
+ var commaLabel := AddLabel(auraNode, ',');
+ commaLabel.Font.Style := commaLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ commaLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := commaLabel.Position.X + commaLabel.Width + commaLabel.Margins.Right + cNodePadding / 2;
+ maxHeightInTitleRow := Max(maxHeightInTitleRow, commaLabel.Height + commaLabel.Margins.Top + commaLabel.Margins.Bottom);
+ end;
+ end;
+
+ // Add closing parenthesis
+ var closeParen := AddLabel(auraNode, ')');
+ closeParen.Font.Style := closeParen.Font.Style - [TFontStyle.fsBold]; // Normal style
+ closeParen.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := closeParen.Position.X + closeParen.Width + closeParen.Margins.Right + cNodePadding / 2; // Add final gap
+ maxHeightInTitleRow := Max(maxHeightInTitleRow, closeParen.Height + closeParen.Margins.Top + closeParen.Margins.Bottom);
+ end
+ else
+ begin
+ // No parameters, add empty () for clarity
+ var emptyParens := AddLabel(auraNode, '()');
+ emptyParens.Font.Style := emptyParens.Font.Style - [TFontStyle.fsBold];
+ emptyParens.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := emptyParens.Position.X + emptyParens.Width + emptyParens.Margins.Right + cNodePadding / 2;
+ maxHeightInTitleRow := Max(maxHeightInTitleRow, emptyParens.Height + emptyParens.Margins.Top + emptyParens.Margins.Bottom);
+ end;
+
+ titleParamHeight := maxHeightInTitleRow; // Total height needed for the first row
+
+ // --- Optional Vertical Centering for Title/Param Row ---
+ var centerOffsetYTitleRow: Single := yPos + titleParamHeight / 2;
+ for i := 0 to auraNode.ControlsCount - 1 do // Iterate controls created so far for the title row
+ begin
+ var ctrl := auraNode.Controls[i];
+ ctrl.Position.Y := centerOffsetYTitleRow - (ctrl.Height + ctrl.Margins.Top + ctrl.Margins.Bottom) / 2 + ctrl.Margins.Top;
+ end;
+ // --- End Centering ---
+
+ // 3. Visit Body
+ savedParent := FParentControl;
+ savedPos := FCurrentPos;
+ savedExprDepth := FExprDepth;
+ try
+ FParentControl := auraNode; // Body node is child of auraNode
+ // Start position for the body, below the title/parameter line
+ FCurrentPos :=
+ TPointF.Create(cNodePadding, yPos + titleParamHeight + cChildAreaTopPadding); // Start below the calculated height
+ FExprDepth := 0;
+
+ bodyNode := CallAccept(Node.Body); // Visit the body, get the node
+
+ bodyNode.Frameless := false;
+ finally
+ FParentControl := savedParent; // Restore parent for siblings
+ FCurrentPos := savedPos; // Restore position for siblings
+ FExprDepth := savedExprDepth;
+ end;
+
+ AdjustParentNodeSize(auraNode, [Node.Body]); // Adjust size based on title, params, and body
+ finally
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap; // Use savedPos.Y
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ savedParent: TControl;
+ savedPos: TPointF;
+ i: Integer;
+ calleeNode: TAuraNode; // Use TAuraNode
+ argNode: TAuraNode; // Use TAuraNode
+ parenLabel, commaLabel: TLabel;
+ // Layout variables
+ yPos: Single; // Current vertical position for the row
+ currentX: Single; // Horizontal position tracker
+ maxHeightInRow: Single; // Max height for row alignment
+ argStartIndex: Integer; // Index of first argument-related control
+begin
+ auraNode := CreateNode(Node); // Create standard framed node
+ auraNode.Frameless := False; // Function calls generally have a frame
+ auraNode.BeginUpdate;
+ inc(FExprDepth);
+ try
+ yPos := cNodePadding; // Start directly with padding
+ currentX := cNodePadding;
+ maxHeightInRow := 0;
+ calleeNode := nil;
+
+ // --- Visit Callee ---
+ savedParent := FParentControl;
+ savedPos := FCurrentPos;
+ FParentControl := auraNode;
+ FCurrentPos := TPointF.Create(currentX, yPos); // Position for callee
+
+ calleeNode := CallAccept(Node.Callee); // Create callee's visual node
+
+ if Assigned(calleeNode) then
+ begin
+ // Position callee explicitly (redundant if FCurrentPos worked, but safe)
+ calleeNode.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := calleeNode.Position.X + calleeNode.Width + calleeNode.Margins.Right + cNodePadding / 2; // Update X pos
+ maxHeightInRow := Max(maxHeightInRow, calleeNode.Height + calleeNode.Margins.Top + calleeNode.Margins.Bottom);
+ end
+ else
+ currentX := cNodePadding; // Start parens from left if no callee
+
+ // --- Add Arguments (Horizontal Layout) ---
+
+ // Add opening parenthesis
+ parenLabel := AddLabel(auraNode, '(');
+ parenLabel.Font.Style := parenLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ parenLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := parenLabel.Position.X + parenLabel.Width + parenLabel.Margins.Right + cNodePadding / 2;
+ maxHeightInRow := Max(maxHeightInRow, parenLabel.Height + parenLabel.Margins.Top + parenLabel.Margins.Bottom);
+
+ for i := 0 to High(Node.Arguments) do
+ begin
+ // Set position for the argument node
+ FCurrentPos := TPointF.Create(currentX, yPos);
+
+ // FParentControl is already auraNode
+ argNode := CallAccept(Node.Arguments[i]); // Create argument node
+
+ if Assigned(argNode) then
+ begin
+ // Position it correctly (redundant, but safe)
+ argNode.Position.Point := TPointF.Create(currentX, yPos);
+
+ currentX :=
+ argNode.Position.X
+ + argNode.Width
+ + argNode.Margins.Right
+ + IfThen(i < High(Node.Arguments), cNodePadding / 2, 0); // Gap or no gap after arg
+ maxHeightInRow := Max(maxHeightInRow, argNode.Height + argNode.Margins.Top + argNode.Margins.Bottom);
+
+ // Add comma if not the last argument
+ if i < High(Node.Arguments) then
+ begin
+ commaLabel := AddLabel(auraNode, ',');
+ commaLabel.Font.Style := commaLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ commaLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := commaLabel.Position.X + commaLabel.Width + commaLabel.Margins.Right + cNodePadding / 2;
+ maxHeightInRow := Max(maxHeightInRow, commaLabel.Height + commaLabel.Margins.Top + commaLabel.Margins.Bottom);
+ end;
+ end
+ else // Should not happen
+ begin
+ currentX := currentX + cMinNodeWidth + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, cMinNodeHeight);
+ end;
+ end;
+
+ // Add closing parenthesis
+ parenLabel := AddLabel(auraNode, ')');
+ parenLabel.Font.Style := parenLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ parenLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := parenLabel.Position.X + parenLabel.Width + parenLabel.Margins.Right; // Final X position
+ maxHeightInRow := Max(maxHeightInRow, parenLabel.Height + parenLabel.Margins.Top + parenLabel.Margins.Bottom);
+
+ // --- Vertical Centering for the single row ---
+ var centerOffsetYRow: Single := yPos + maxHeightInRow / 2;
+ argStartIndex := 0; // Start check from first control
+ for i := argStartIndex to auraNode.ControlsCount - 1 do // Iterate ALL controls in the row
+ begin
+ var ctrl := auraNode.Controls[i];
+ // Adjust Y based on calculated center and control's own height/margins
+ ctrl.Position.Y := centerOffsetYRow - (ctrl.Height + ctrl.Margins.Top + ctrl.Margins.Bottom) / 2 + ctrl.Margins.Top;
+ end;
+ // --- End Centering ---
+ yPos := yPos + maxHeightInRow; // Update yPos based on row height for final size calculation
+
+ FParentControl := savedParent; // Restore parent for siblings
+ FCurrentPos := savedPos; // Restore position for siblings
+
+ // --- Adjust Size ---
+ auraNode.Width := Max(cMinNodeWidth, currentX + cNodePadding); // Width determined by final currentX
+ auraNode.Height := Max(cMinNodeHeight, yPos + cNodePadding); // Height determined by final yPos
+
+ finally
+ dec(FExprDepth);
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap; // Use savedPos.Y
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ // Visualize the expanded body
+ auraNode := ProcessStub('Macro Expansion', Node, [Node.ExpandedBody]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ savedParent: TControl;
+ savedPos: TPointF;
+ expr: IAstNode;
+ prefixLabel: TLabel;
+ prefixText: string;
+ i: Integer;
+ prefixWidth: Single; // Width needed only for the 'return ' label
+ maxChildWidth: Single; // To calculate overall width
+ yPos: Single; // Current vertical position for the row
+ currentXOffset: Single; // Horizontal offset for the expression node
+ returnLabel: TLabel; // Label for measuring 'return '
+ exprNode: TAuraNode;
+begin
+ // Create the main node, frameless and without title
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ try
+ auraNode.Frameless := True;
+
+ savedParent := FParentControl;
+ savedPos := FCurrentPos; // Store original position for siblings
+ FParentControl := auraNode;
+ // Start position for children inside the node
+ yPos := cNodePadding;
+ prefixWidth := 0;
+ maxChildWidth := 0; // Max width including prefix + expression
+
+ // --- Determine width needed for 'return ' prefix ---
+ returnLabel := AddLabel(auraNode, 'return '); // Create temporarily to measure
+ prefixWidth := returnLabel.Width + returnLabel.Margins.Left + returnLabel.Margins.Right + cNodePadding; // Width + gap
+ returnLabel.Free; // Free temporary label
+ // --- End prefix width calculation ---
+
+ // --- Visit children ---
+ for i := 0 to High(Node.Expressions) do
+ begin
+ expr := Node.Expressions[i];
+ prefixLabel := nil; // Initialize prefixLabel for this iteration
+
+ // 1. Create Prefix Label ONLY for the last statement
+ if i = High(Node.Expressions) then
+ begin
+ prefixText := 'return ';
+ prefixLabel := AddLabel(auraNode, prefixText);
+ prefixLabel.Position.Point := TPointF.Create(cNodePadding, yPos); // Place prefix label
+ currentXOffset := cNodePadding + prefixWidth; // Start expression node after the 'return ' prefix space
+ end
+ else
+ begin
+ prefixText := ''; // No prefix for other statements
+ currentXOffset := cNodePadding; // Start expression node directly at the padding edge
+ end;
+
+ // 2. Visit Expression Node
+ // Set position for the expression node
+ FCurrentPos := TPointF.Create(currentXOffset, yPos);
+
+ exprNode := CallAccept(expr); // This creates the node/label for the expression
+
+ // Get the created control and update max width/height
+ if Assigned(exprNode) then
+ begin
+ var currentExprHeight := exprNode.Height + exprNode.Margins.Top + exprNode.Margins.Bottom;
+ var currentPrefixHeight: Single := 0;
+ if Assigned(prefixLabel) then
+ currentPrefixHeight := prefixLabel.Height + prefixLabel.Margins.Top + prefixLabel.Margins.Bottom;
+
+ var rowHeight := Max(currentExprHeight, currentPrefixHeight);
+
+ // --- Optional Vertical Centering within the row ---
+ if Assigned(prefixLabel) then
+ prefixLabel.Position.Y := yPos + (rowHeight / 2) - currentPrefixHeight / 2 + prefixLabel.Margins.Top;
+ exprNode.Position.Y :=
+ yPos + (rowHeight / 2) - currentExprHeight / 2 + exprNode.Margins.Top; // Position expr node correctly
+ // --- End Vertical Centering ---
+
+ // Update max width needed for the block node
+ maxChildWidth := Max(maxChildWidth, exprNode.Position.X + exprNode.Width + exprNode.Margins.Right);
+
+ // Update Y position for the next row
+ yPos := yPos + rowHeight + FNodeVerticalGap; // Move down for the next statement
+
+ end
+ else // Only prefix label exists (shouldn't happen if expression is valid)
+ begin
+ if Assigned(prefixLabel) then
+ begin
+ yPos := yPos + prefixLabel.Height + prefixLabel.Margins.Top + prefixLabel.Margins.Bottom + FNodeVerticalGap;
+ maxChildWidth := Max(maxChildWidth, prefixLabel.Position.X + prefixLabel.Width + prefixLabel.Margins.Right);
+ end
+ else
+ begin
+ yPos := yPos + cMinNodeHeight + FNodeVerticalGap; // Add minimal height if no control added
+ maxChildWidth := Max(maxChildWidth, cMinNodeWidth);
+ end;
+ end;
+
+ end;
+ // --- Children Visited ---
+
+ FParentControl := savedParent; // Restore parent for siblings
+ FCurrentPos := savedPos; // Restore position for siblings
+
+ // --- Adjust parent size ---
+ auraNode.Width := Max(cMinNodeWidth, maxChildWidth + cNodePadding); // Total width + right padding
+ if auraNode.ControlsCount > 0 then // Check if anything was added
+ auraNode.Height :=
+ Max(cMinNodeHeight, yPos - FNodeVerticalGap + cNodePadding) // Use final yPos (minus last gap) + bottom padding
+ else
+ auraNode.Height := cMinNodeHeight; // Ensure minimal height if block is empty
+
+ finally
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node in the outer scope
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap; // Use savedPos.Y
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ varLabel: TLabel;
+ nameLabel: TLabel;
+ opLabel: TLabel;
+ initNode: TAuraNode; // Use TAuraNode
+ savedParent: TControl;
+ savedPos: TPointF;
+ currentX: Single; // Track horizontal position
+ maxHeightInRow: Single; // Track max height for row alignment
+ yPos: Single; // Y position for elements in this row
+begin
+ // Create the main node, frameless
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ try
+ auraNode.Frameless := true;
+
+ // Start position for children inside the node
+ currentX := cNodePadding;
+ yPos := cNodePadding; // Initial Y position
+ maxHeightInRow := 0;
+ initNode := nil;
+
+ // 1. "var" Label
+ varLabel := AddLabel(auraNode, 'var');
+ varLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := varLabel.Position.X + varLabel.Width + varLabel.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, varLabel.Height + varLabel.Margins.Top + varLabel.Margins.Bottom);
+
+ // 2. Identifier Label
+ nameLabel := AddLabel(auraNode, Node.Identifier.Name);
+ nameLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := nameLabel.Position.X + nameLabel.Width + nameLabel.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, nameLabel.Height + nameLabel.Margins.Top + nameLabel.Margins.Bottom);
+
+ if Assigned(Node.Initializer) then
+ begin
+ // 3. Operator Label (only if initializer exists)
+ opLabel := AddLabel(auraNode, ':=');
+ opLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := opLabel.Position.X + opLabel.Width + opLabel.Margins.Right + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, opLabel.Height + opLabel.Margins.Top + opLabel.Margins.Bottom);
+
+ // 4. Initializer Expression Node (Visited Recursively)
+ savedParent := FParentControl;
+ savedPos := FCurrentPos; // Store original position for siblings
+ FParentControl := auraNode; // Children are added to this node
+ FCurrentPos := TPointF.Create(currentX, yPos); // Set position for the child node
+
+ initNode := CallAccept(Node.Initializer); // Create the node/label
+
+ if Assigned(initNode) then
+ begin
+ currentX := initNode.Position.X + initNode.Width + initNode.Margins.Right;
+ maxHeightInRow := Max(maxHeightInRow, initNode.Height + initNode.Margins.Top + initNode.Margins.Bottom);
+ end;
+
+ FParentControl := savedParent; // Restore parent for siblings
+ FCurrentPos := savedPos; // Restore position for siblings
+ end;
+
+ // --- Adjust parent size ---
+ auraNode.Width := Max(cMinNodeWidth, currentX + cNodePadding); // Total width needed + right padding
+ auraNode.Height := Max(cMinNodeHeight, maxHeightInRow + 2 * cNodePadding); // Max height + top/bottom padding
+
+ // --- Optional Vertical Centering ---
+ var centerOffsetY: Single := (auraNode.Height / 2);
+ if Assigned(varLabel) then
+ varLabel.Position.Y :=
+ centerOffsetY - (varLabel.Height + varLabel.Margins.Top + varLabel.Margins.Bottom) / 2 + varLabel.Margins.Top;
+ if Assigned(nameLabel) then
+ nameLabel.Position.Y :=
+ centerOffsetY - (nameLabel.Height + nameLabel.Margins.Top + nameLabel.Margins.Bottom) / 2 + nameLabel.Margins.Top;
+ if Assigned(opLabel) then
+ opLabel.Position.Y := centerOffsetY - (opLabel.Height + opLabel.Margins.Top + opLabel.Margins.Bottom) / 2 + opLabel.Margins.Top;
+ if Assigned(initNode) then
+ initNode.Position.Y :=
+ centerOffsetY - (initNode.Height + initNode.Margins.Top + initNode.Margins.Bottom) / 2 + initNode.Margins.Top;
+ // --- End Optional Vertical Centering ---
+
+ finally
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node in the outer scope
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap; // Use savedPos.Y
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitAssignment(const Node: IAssignmentNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ identifierLabel: TLabel;
+ operatorLabel: TLabel;
+ valueNode: TAuraNode; // Use TAuraNode
+ savedParent: TControl;
+ savedPos: TPointF;
+ currentX: Single; // Track horizontal position
+ maxHeightInRow: Single; // Track max height for row alignment
+ labelYPos: Single; // Y position for labels in this row
+ valueYPos: Single; // Y position for the value node in this row
+begin
+ // Create the main node, frameless as it primarily serves as a container
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ try
+ auraNode.Frameless := true;
+
+ // Start position for children inside the node
+ currentX := cNodePadding;
+ labelYPos := cNodePadding; // Initial Y position for labels
+ maxHeightInRow := 0;
+ valueNode := nil;
+
+ // 1. Identifier Label
+ identifierLabel := AddLabel(auraNode, Node.Identifier.Name);
+ identifierLabel.Position.Point := TPointF.Create(currentX, labelYPos);
+ currentX :=
+ identifierLabel.Position.X
+ + identifierLabel.Width
+ + identifierLabel.Margins.Right
+ + cNodePadding; // Update X pos after adding Margins.Right and gap
+ maxHeightInRow := Max(maxHeightInRow, identifierLabel.Height + identifierLabel.Margins.Top + identifierLabel.Margins.Bottom);
+
+ // 2. Operator Label
+ operatorLabel := AddLabel(auraNode, ':=');
+ operatorLabel.Position.Point := TPointF.Create(currentX, labelYPos);
+ currentX := operatorLabel.Position.X + operatorLabel.Width + operatorLabel.Margins.Right + cNodePadding; // Update X pos
+ maxHeightInRow := Max(maxHeightInRow, operatorLabel.Height + operatorLabel.Margins.Top + operatorLabel.Margins.Bottom);
+
+ // 3. Expression Node (Visited Recursively)
+ savedParent := FParentControl;
+ savedPos := FCurrentPos; // Store original position for siblings
+ FParentControl := auraNode; // Children are added to this node
+ valueYPos := cNodePadding; // Align top of value node with top of labels
+ FCurrentPos := TPointF.Create(currentX, valueYPos); // Set position for the child node
+
+ valueNode := CallAccept(Node.Value); // This creates the node/label for the expression
+
+ // Get the created control (it's the last one added to auraNode)
+ if Assigned(valueNode) then
+ begin
+ // Update currentX based on the right edge of the value control
+ currentX := valueNode.Position.X + valueNode.Width + valueNode.Margins.Right; // Added margin
+ // Update maxHeightInRow considering the value control's height and potential margins
+ maxHeightInRow := Max(maxHeightInRow, valueNode.Height + valueNode.Margins.Top + valueNode.Margins.Bottom);
+ end;
+
+ FParentControl := savedParent; // Restore parent for siblings
+ FCurrentPos := savedPos; // Restore position for siblings
+
+ // --- Adjust parent size ---
+ auraNode.Width := Max(cMinNodeWidth, currentX + cNodePadding); // Total width needed + right padding
+ auraNode.Height := Max(cMinNodeHeight, maxHeightInRow + 2 * cNodePadding); // Max height + top/bottom padding
+
+ // --- Optional Vertical Centering ---
+ var centerOffsetY: Single := (auraNode.Height / 2);
+ if Assigned(identifierLabel) then
+ identifierLabel.Position.Y :=
+ centerOffsetY
+ - (identifierLabel.Height + identifierLabel.Margins.Top + identifierLabel.Margins.Bottom) / 2
+ + identifierLabel.Margins.Top;
+ if Assigned(operatorLabel) then
+ operatorLabel.Position.Y :=
+ centerOffsetY
+ - (operatorLabel.Height + operatorLabel.Margins.Top + operatorLabel.Margins.Bottom) / 2
+ + operatorLabel.Margins.Top;
+ if Assigned(valueNode) then
+ valueNode.Position.Y :=
+ centerOffsetY - (valueNode.Height + valueNode.Margins.Top + valueNode.Margins.Bottom) / 2 + valueNode.Margins.Top;
+ // --- End Optional Vertical Centering ---
+
+ finally
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node in the outer scope
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap; // Use savedPos.Y
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
+var
+ i: Integer;
+ paramStr: string;
+ auraNode: TAuraNode;
+begin
+ paramStr := '';
+ for i := 0 to High(Node.Parameters) do
+ begin
+ if i > 0 then
+ paramStr := paramStr + ', ';
+ paramStr := paramStr + Node.Parameters[i].Name;
+ end;
+ auraNode := ProcessStub('Macro Def: ' + Node.Name.Name + ' (' + paramStr + ')', Node, [Node.Body]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ auraNode := ProcessStub('Quasiquote', Node, [Node.Expression]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitUnquote(const Node: IUnquoteNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ auraNode := ProcessStub('Unquote', Node, [Node.Expression]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ auraNode := ProcessStub('Unquote-Splicing', Node, [Node.Expression]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitIndexer(const Node: IIndexerNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ auraNode := ProcessStub('Indexer []', Node, [Node.Base, Node.Index]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
+var
+ auraNode: TAuraNode;
+begin
+ auraNode := ProcessStub('Member Access: .' + Node.Member.Name, Node, [Node.Base]);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ labelHeight: Single;
+begin
+ // Leaf node
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ try
+ labelHeight := AddTitleLabel(auraNode, 'Create Series: ' + Node.Definition);
+ // Adjust size based on label size
+ if auraNode.ControlsCount > 0 then
+ begin
+ var Lbl := auraNode.Controls[0] as TLabel;
+ auraNode.Width := Max(cMinNodeWidth, Lbl.Width + Lbl.Margins.Left + Lbl.Margins.Right);
+ auraNode.Height := Max(cMinNodeHeight, labelHeight + cNodePadding);
+ end
+ else
+ AdjustParentNodeSize(auraNode, []);
+
+ finally
+ auraNode.EndUpdate;
+ end;
+ FCurrentPos.Y := FCurrentPos.Y + auraNode.Height + FNodeVerticalGap;
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
+var
+ children: TArray;
+ auraNode: TAuraNode;
+begin
+ children := [Node.Value];
+ if Assigned(Node.Lookback) then
+ children := children + [Node.Lookback];
+ auraNode := ProcessStub('Add Item to: ' + Node.Series.Name, Node, children);
+ Result := TDataValue.FromGeneric(auraNode);
+end;
+
+function TAstVisualizer.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ labelHeight: Single;
+begin
+ // Leaf node
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ try
+ labelHeight := AddTitleLabel(auraNode, 'Length of: ' + Node.Series.Name);
+ // Adjust size based on label size
+ if auraNode.ControlsCount > 0 then
+ begin
+ var Lbl := auraNode.Controls[0] as TLabel;
+ auraNode.Width := Max(cMinNodeWidth, Lbl.Width + Lbl.Margins.Left + Lbl.Margins.Right);
+ auraNode.Height := Max(cMinNodeHeight, labelHeight + cNodePadding);
+ end
+ else
+ AdjustParentNodeSize(auraNode, []);
+
+ finally
+ auraNode.EndUpdate;
+ end;
+ FCurrentPos.Y := FCurrentPos.Y + auraNode.Height + FNodeVerticalGap;
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+function TAstVisualizer.VisitRecurNode(const Node: IRecurNode): TDataValue;
+var
+ auraNode: TAuraNode;
+ savedParent: TControl;
+ savedPos: TPointF;
+ i: Integer;
+ recurLabel: TLabel;
+ argNode: TAuraNode; // Use TAuraNode
+ parenLabel, commaLabel: TLabel;
+ // Layout variables
+ yPos: Single; // Current vertical position for the row
+ currentX: Single; // Horizontal position tracker
+ maxHeightInRow: Single; // Max height for row alignment
+ argStartIndex: Integer; // Index of first argument-related control
+begin
+ // Create the main node, frameless
+ auraNode := CreateNode(Node);
+ auraNode.BeginUpdate;
+ inc(FExprDepth);
+ try
+ auraNode.Frameless := True;
+
+ // Start position for children inside the node
+ currentX := cNodePadding;
+ yPos := cNodePadding; // Initial Y position
+ maxHeightInRow := 0;
+
+ // 1. "recur" Label
+ recurLabel := AddLabel(auraNode, WideChar($03BB));
+ recurLabel.Font.Style := recurLabel.Font.Style + [TFontStyle.fsBold]; // Make keyword bold
+ recurLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := recurLabel.Position.X + recurLabel.Width + recurLabel.Margins.Right + cNodePadding / 2; // Update X pos
+ maxHeightInRow := Max(maxHeightInRow, recurLabel.Height + recurLabel.Margins.Top + recurLabel.Margins.Bottom);
+
+ // --- Add Arguments (Horizontal Layout) ---
+
+ // Add opening parenthesis
+ parenLabel := AddLabel(auraNode, '(');
+ parenLabel.Font.Style := parenLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ parenLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := parenLabel.Position.X + parenLabel.Width + parenLabel.Margins.Right + cNodePadding / 2;
+ maxHeightInRow := Max(maxHeightInRow, parenLabel.Height + parenLabel.Margins.Top + parenLabel.Margins.Bottom);
+
+ savedParent := FParentControl;
+ savedPos := FCurrentPos;
+ FParentControl := auraNode; // Arguments are children of auraNode
+
+ for i := 0 to High(Node.Arguments) do
+ begin
+ // Set position for the argument node
+ FCurrentPos := TPointF.Create(currentX, yPos);
+
+ argNode := CallAccept(Node.Arguments[i]); // Create argument node
+
+ if Assigned(argNode) then
+ begin
+ // Position it correctly (redundant, but safe)
+ argNode.Position.Point := TPointF.Create(currentX, yPos);
+
+ currentX :=
+ argNode.Position.X
+ + argNode.Width
+ + argNode.Margins.Right
+ + IfThen(i < High(Node.Arguments), cNodePadding / 2, 0); // Gap or no gap after arg
+ maxHeightInRow := Max(maxHeightInRow, argNode.Height + argNode.Margins.Top + argNode.Margins.Bottom);
+
+ // Add comma if not the last argument
+ if i < High(Node.Arguments) then
+ begin
+ commaLabel := AddLabel(auraNode, ',');
+ commaLabel.Font.Style := commaLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ commaLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := commaLabel.Position.X + commaLabel.Width + commaLabel.Margins.Right + cNodePadding / 2;
+ maxHeightInRow := Max(maxHeightInRow, commaLabel.Height + commaLabel.Margins.Top + commaLabel.Margins.Bottom);
+ end;
+ end
+ else // Should not happen
+ begin
+ currentX := currentX + cMinNodeWidth + cNodePadding;
+ maxHeightInRow := Max(maxHeightInRow, cMinNodeHeight);
+ end;
+ end;
+
+ // Add closing parenthesis
+ parenLabel := AddLabel(auraNode, ')');
+ parenLabel.Font.Style := parenLabel.Font.Style - [TFontStyle.fsBold]; // Normal style
+ parenLabel.Position.Point := TPointF.Create(currentX, yPos);
+ currentX := parenLabel.Position.X + parenLabel.Width + parenLabel.Margins.Right; // Final X position
+ maxHeightInRow := Max(maxHeightInRow, parenLabel.Height + parenLabel.Margins.Top + parenLabel.Margins.Bottom);
+
+ // --- Vertical Centering for the single row ---
+ var centerOffsetYRow: Single := yPos + maxHeightInRow / 2;
+ argStartIndex := 0; // Start check from first control ("recur")
+ for i := argStartIndex to auraNode.ControlsCount - 1 do // Iterate ALL controls in the row
+ begin
+ var ctrl := auraNode.Controls[i];
+ // Adjust Y based on calculated center and control's own height/margins
+ ctrl.Position.Y := centerOffsetYRow - (ctrl.Height + ctrl.Margins.Top + ctrl.Margins.Bottom) / 2 + ctrl.Margins.Top;
+ end;
+ // --- End Centering ---
+ yPos := yPos + maxHeightInRow; // Update yPos based on row height for final size calculation
+
+ FParentControl := savedParent; // Restore parent for siblings
+ FCurrentPos := savedPos; // Restore position for siblings
+
+ // --- Adjust Size ---
+ auraNode.Width := Max(cMinNodeWidth, currentX + cNodePadding); // Width determined by final currentX
+ auraNode.Height := Max(cMinNodeHeight, yPos + cNodePadding); // Height determined by final yPos
+
+ finally
+ dec(FExprDepth);
+ auraNode.EndUpdate;
+ end;
+ // Move current position down for the next sibling node
+ FCurrentPos.Y := savedPos.Y + auraNode.Height + FNodeVerticalGap; // Use savedPos.Y
+ Result := TDataValue.FromGeneric(auraNode); // Return the created node
+end;
+
+end.