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.